
见过不少同学在Formality里被一堆unmatched points折腾得焦头烂额其实问题往往不在验证阶段而是在更早的综合流程里就埋下了——SVF文件没生成对、set_svf命令没加载好、或者加载时机错了。这篇文章我就专门聊聊SVF文件与Formality中set_svf命令的那些门道从文件本身是什么、加载时机怎么选到几个文档里不怎么强调但实测很管用的隐藏技巧再附上几条我这些年踩出来的排查链路希望能帮正在debug LEC问题的你少走点弯路。1. SVF文件到底记录了什么东西set_svf命令的对象得先搞清楚很多人把SVF当成一个“综合产物”就完事了实际上它更像是DC写给Formality的一本优化账本。你只有先知道这本账本记了什么才能理解set_svf在Formality里为什么那么重要。1.1 综合优化到底做了什么SVF就记什么Design Compiler在做逻辑综合时会对RTL做一系列优化组合逻辑的flatten和structure、寄存器合并register merging、重定时retiming、常量传播constant propagation、死逻辑移除还有跨层次边界的逻辑移动。这些优化的共同特点是它们把RTL中原本清晰可见的逻辑结构改得面目全非。拿retiming来说一条路径上的两级触发器被综合工具挪了位置、搬了方向逻辑功能没变但寄存器在网表里的名字、位置、层级路径全变了。如果Formality不知道这个变换过程它面对的就是一份“看起来完全不一样”的网表match阶段会非常吃力。SVFSynopsys Verification Format干的事就是把综合过程中每一步变换记录下来。它不关心变换背后的算法只记录结果哪些寄存器和原始设计里哪些寄存器对应、哪些组合逻辑被重构了、哪些常量被传播掉了、哪些点被映射到了工艺库单元。Formality拿到SVF之后等于拿到了一条从RTL到门级网表的逻辑路径索引match的时候顺着走就行。1.2 SVF里关键信息的几个类别SVF文件本身是纯文本格式你可以直接打开看。我之前调试一个卡在验证阶段的设计时就通过直接看SVF内容确认了寄存器合并的具体对应关系。从实用角度SVF里最主要的信息可以分为这么几类命名映射综合前后的层次路径变化。比如RTL里的top/gen_regs[0].reg综合后变成了top/u_phys/reg_023中间经过了几次renameSVF里会有记录。寄存器级优化信息retiming、register merging、寄存器重组等变换。这类优化如果SVF缺失Formality经常会报出大量的unmatched registers。常量信息哪些寄存器、哪些输入端口被tie到0或1了哪些逻辑被常量传播优化掉了。设计边界信息跨层次逻辑移动、partition调整导致的设计层级变化。这些信息在Formality里会被转成match时的辅助引导说白了就是“这些点应该互相匹配”的线索。注意它是辅助、是引导不是绝对命令Formality最终还要结合自身的逻辑等价性分析来做判断。1.3 SVF版本和格式跨版本使用要留个心眼SVF文件跟工具版本是有绑定关系的。DC某个小版本生成的SVF放到稍旧一点的Formality上加载有时候能读有时候会报格式不认识。我遇到过DC 2018.06生成的SVF在Formality 2015.12上直接报SVF command syntax错误的情况。这不是个例。Synopsys工具的大版本跨度一大SVF中记录的guide命令格式就可能变化。所以最稳妥的做法是综合和验证尽量使用同一主版本的工具至少保证Formality的版本不比DC低太多。如果你的环境里工具版本杂那在自动化脚本里最好加一道检查确认DC和FM版本兼容性别让问题拖到验证阶段才暴露。2. set_svf在Formality全流程中的正确位置加载时机比想象中重要Formality的基本流程是读入参考设计和实现设计然后match、verify。set_svf命令看似简单但在脚本里的摆放位置直接影响match的效果。2.1 从读入设计到verify的标准脚本骨架一个标准的Formality脚本流程大致是这样的# 加载参考设计通常是RTL read_verilog -r /path/to/rtl/top.v set_top rtl_top # 加载实现设计通常是综合后网表 read_db -i /path/to/dc_output/top.db set_top impl_top # 加载SVF文件 set_svf -f /path/to/dc_output/top.svf # 执行匹配并验证 match verify这里最关键的一点是set_svf要在match之前执行。因为SVF信息是在match阶段被消费的match之后再加载等于验证引擎已经按没有SVF的方式做了关键点匹配SVF后面再进来不会重新触发匹配流程你等于白加载了。2.2 为什么必须在match之前加载关键点匹配的底层逻辑Formality的match阶段核心工作是找到参考设计和实现设计之间对应的逻辑点这些关键点包括主输入输出、寄存器、黑盒端口等等。match结果的好坏直接决定verify阶段能不能快速通过。如果不加载SVFFormality就只能靠名字相似性和逻辑锥分析来猜对应关系。RTL里一个寄存器叫cnt_reg综合后变成了u0/u1/count_reg_2名字上还有一些线索但如果是retiming之后的寄存器或者被合并掉一半的寄存器光靠猜就非常费劲。SVF给Formality提供的是优化路径上的精确线索。像寄存器合并这种变换SVF会指明哪些寄存器被合并了、合并后的点跟原始点是什么关系。没有SVF的时候Formality需要把这些寄存器当作不匹配点处理然后再尝试用复杂的时序优化验证算法去证明它们在逻辑上等价——这个过程不一定100%成功即便成功耗时也会翻好几倍。2.3 设计名对不上时怎么办先别急着对SVF动刀子加载SVF时一个常见问题是报design name不匹配。比如你Formality里reference design的名字叫TOP_rtl但SVF文件开头记录的综合设计名是top_synthset_svf回去提示design name mismatch。这种时候我的建议是先用report_design看看当前design list里的设计名然后确认SVF里的名字。如果两者对不上可以尝试在参考设计上重新set_top让当前reference与SVF中记录的设计名对上。还有一个小技巧DV环境下一个设计被综合了很多次每次DC工程名不同SVF里记录的名字可能五花八门这种就需要回到DC侧去确认顶层RTL的module名和设计名统一规范化。不建议在Formality里直接强行修改SVF文件。它是文本格式没错但里面很多条目之间是有联动关系的你手动改一个名字可能引发后续一串解析错误。2.4 set_svf的几种调用写法set_svf在fm_shell里常用的写法其实就那几种我列一下常见的# 方式一直接带文件名 set_svf -f ./output/design.svf # 方式二先设置变量再加载 set svf_file ./output/design.svf set_svf -f $svf_file # 方式三不带参数查看当前SVF设置 set_svf有些场景下你会看到别人脚本里写了set_svf -f $svf_file之后又执行了set_svf -change_design之类的选项这通常用于处理同一个SVF在不同design list下加载的情况。如果你的脚本里没有对design name做严格管理加载SVF后务必看一眼输出日志里有没有warning别一带而过。3. 加载SVF之后Formality在背后做了什么几个不易察觉的内部机制很多用Formality的人把set_svf当成一个“黑盒开关”敲完就完事。但了解它背后的行为对排查问题很有帮助。3.1 SVF信息如何转换成匹配引导SVF文件里记录的变换信息在Formality加载之后会被转换为一系列内部的匹配引导信息。这些引导分成两类一类是显式的点对应关系告诉match引擎“参考设计的A点对应实现设计的B点”另一类是属性信息比如某条路径上的逻辑被优化掉了、某个寄存器被常量替换了。在match阶段Formality会对这些引导信息进行综合权衡。不是说SVF说有对应关系就100%匹配它还要求两边的逻辑锥在布尔层面等价至少在一定的逻辑深度内是一致的。不过有了SVF引导match引擎至少知道该往哪个方向去验证搜索空间小了很多。3.2 为什么有些设计不加载SVF也能过有些一定过不了有位在代工厂做后端的朋友问过我他之前做的某个模块不加载SVFFormality也能verify通过为什么现在这个设计不加载就废原因是设计类型和优化策略不一样。组合逻辑优化为主的设计比如只是做了flatten和structure的纯组合逻辑Formality本身的布尔等价性引擎就足以应付。这种情况下SVF更像一个锦上添花的加速器。但一旦涉及寄存器级别的优化——retiming、register merging、regroup以及跨层次优化——情况就完全不同了。Formality确实有自己的时序优化验证机制但它的处理方式是在“发现不匹配”之后再去尝试用复杂的时序逻辑等价算法去证明。这个过程慢不说还容易失败特别是优化幅度较大的设计。这时候SVF就是刚需没有它你会在unmatched points的泥潭里挣扎很久。3.3 怎么确认SVF加载效果几个报告命令配合使用加载完SVF之后我会习惯性地跑一下report_svf_data确认SVF确实被正确解析了。这个命令会报告SVF文件的基本信息包括文件路径、读取状态、识别的设计名等。match之后再看report_matched_points和report_unmatched_points。如果这时候matched points数量明显偏少unmatched里又有大量寄存器点那多半是SVF没起作用或者加载时机不对。顺着这个线索往回查比直接怀疑SVF文件本身要高效得多。还要注意观察日志里有没有类似“Repeated sequence”或“Old combination circuit”的提示。SVF里有时会包含历史设计版本的信息如果Formality认出了某些点是repeated sequence它会在后续verify中做专门处理。这些信息通常会以warning或者info的形式出现别直接忽略。4. 实战中的隐藏技巧这些用法文档里不常强调set_svf命令本身很简单但放到复杂的工程环境里就有很多值得琢磨的地方。4.1 分块综合、多SVF文件的处理大型SoC的设计很少是顶层一次性综合的基本都是模块级综合然后顶层整合。这时候你会遇到一个比较尴尬的局面顶层验证时reference是完整RTLimplementation是整合后的网表但综合SVF是每个小模块各一份。这种情况下我在实践中比较推荐的做法是如果顶层网表是由各block网表拼接后再优化的那么每个block的SVF都有参考价值。你可以在读入实现设计之后按顺序加载多个block的SVFset_svf -f ./block_a/output/block_a.svf # ... 读入block_a相关设计 set_svf -f ./block_b/output/block_b.svf # ... 读入block_b相关设计但要注意set_svf设置的是全局的SVF多次调用是覆盖关系不是叠加关系。如果你需要同时让多个block的SVF信息都生效得在同一个SVF文件里整合或者在综合阶段就使用set_svf配合current_design在顶层统一管理再或者分开验证每个block单独做一次LEC。这一点很多人会踩坑在fm_shell里连续加载两个SVF以为是追加结果是覆盖。所以你在跑分块验证的时候建议一个block一个fm_shell会话或者用脚本显式清空再加载。4.2 ECO之后SVF的失效与重新生成ECOEngineering Change Order是后端流程里不可避免的环节。网表被手工修改或者用ECO工具改过之后原来DC生成的SVF是否还能用我的经验是能用的程度取决于改动的范围。如果ECO只是修改了某个组合逻辑门的连接寄存器结构没动那么SVF里关于寄存器和主要逻辑点的对应信息依然有效可以继续用它做LEC。但如果ECO涉及寄存器增删、重定时调整那就危险了——SVF里的寄存器对应关系会和实际网表产生偏差Formality跟着旧SVF的引导去match反而会把match结果带偏。遇到这种情况我的建议是如果ECO区域集中且小可以尝试手动set_user_match把受影响的点单独指定更稳妥的方案是回到DC环境对ECO后的网表重新综合一遍并生成新的SVF。重新综合的成本没有想象中高但能省下后面Debug LEC的大量时间。4.3 用SVF避免大量手动set_user_match的“体力活”有些工程师在match结果不理想的时候喜欢立刻上手set_user_match去强行为unmatched points建立对应关系。这是个危险的习惯。set_user_match是硬约束一旦设置Formality就直接按照你的指定去做match不再做逻辑等价性检验。如果设置错了一个点后面verify阶段就会奇怪地fail而且fail的原因会非常隐蔽。相比之下SVF提供的是软引导它给Formality一个匹配方向但匹配是否成立还是要经过逻辑检查。所以当match结果不理想时优先排查SVF是否加载正确、是否加载完整用SVF去解决unmatched points比一上来就手动硬设要安全和省力得多。只有当SVF确实没有覆盖到、且你对设计结构非常确定的时候才考虑补充手动指定。4.4 SVF文件异常小或者为空的排查有时候你会遇到SVF文件只有几百字节甚至只有文件头的情况。这通常不是因为优化少而是DC侧生成有问题。我在DC脚本里一般会在综合开始前就设置set_svf $svf_file_name然后综合结束前统一关闭set_svf -off关键就在于这个-off它会把SVF文件完整关闭、确保所有缓冲内容落盘。如果综合过程中DC异常中断或者脚本里压根没执行set_svf -off就有可能导致SVF不完整。我遇到过最典型的情况是综合脚本里设置了SVF文件但后面跑了多轮compile_ultra迭代优化SVF在中间被覆盖了最终落盘的只有最后一轮的信息。这类SVF加载后Formality不报错但效果很差match结果依旧大量unmatched。4.5 直接阅读SVF内容做debug的实用技巧SVF既然是文本文件直接打开看就是一个非常高效的debug手段。我调试过一个比较难缠的unmatched point某个寄存器在RTL里叫en_reg综合后网表里对应点似乎消失了。报告里只显示unmatched找不到去向。后来我直接在SVF里grep这个寄存器的名字很快就看到了一条常量传播的记录说明这个寄存器被DC tie到了常量值。有了这个信息我再回网表里查果然发现一个tie-off的buffer。这就是SVF的另一层价值——它不仅仅是给Formality用的数据文件也是一份记录综合优化细节的调试日志。平时遇到奇怪的点先grep SVF经常能省掉大量翻网表的时间。5. 踩坑实录set_svf命令导致的典型问题与完整排查链路理论说再多不如直接给几条排查路径。下面这几个场景都是我实际工作中碰到过或者帮忙解决过的每一条都有清晰的排查链路可以参考。5.1 排查链路案例一加载了SVF仍然大量unmatched症状Formality脚本里写了set_svfmatch之后report_unmatched_points仍然一大片。排查步骤先确认SVF文件是否存在、路径是否正确。很多时候是脚本变量拼接错误文件路径多了一层或少了一层目录。查看加载日志。set_svf之后Formality通常会打印读取状态。如果出现design name不匹配的warning按前文方法处理。确认set_svf在match之前执行。这一点虽然基础但我在好几个团队里都见过把它写在match后面的情况。用report_svf_data查看SVF里实际记录了多少信息。如果文件很大但报告显示识别的点数很少说明SVF内容与当前design list对不上很可能版本或设计名不一致。回到DC侧确认SVF生成时使用的是哪一版网表。如果实现设计是ECO后的网表而SVF是ECO前生成的那unmatched就不可避免了。这条链路走下来基本能定位九成的问题。5.2 排查链路案例二SVF版本不匹配导致的解析失败症状set_svf加载时出现大段SVF命令解析错误Formality日志里全是不认识的命令关键字。排查步骤查看DC和Formality的版本号。如果版本差距过大比如跨了两个大版本直接统一版本最省事。如果暂时没法统一版本可以先用Formality读DC的DDC文件利用DDC里自带的SVF信息来替代外部SVF。DDC格式里会内嵌综合信息和约束信息Formality读取时能自动利用其中的匹配指引。这种做法虽然不是100%等同于外部SVF但能在版本不匹配的时候救急。另一个替代方案是让DC侧重新导出一次SVF用低版本的SVF格式输出。有些版本的DC提供SVF版本兼容选项但具体可用性要查对应版本的release note。5.3 排查链路案例三连续验证多个设计SVF污染症状同一个fm_shell会话里先验证了Block A成功接着验证Block Bset_svf加载了B的SVF但match结果异常连B的寄存器都匹配得乱七八糟。原因set_svf在同一个会话里的状态是持久的。前面的SVF信息不会自动清空当你加载B的SVF时如果B的SVF文件本身不完全或者加载前后没有正确更替Formality可能在match阶段同时参考到了两份SVF的信息导致引导混乱。解决在开始验证B之前先执行set_svf -off或者明确重新加载B的SVF文件。更稳妥的是写脚本时每个设计的验证流程独立SVF加载放在读入设计之后、match之前统一设置。验证完一个设计后把fm_shell会话退出或者重置避免跨设计状态残留。5.4 排查链路案例四第三方IP的SVF与自家顶层SVF冲突场景设计中集成了几个第三方IPIP厂商提供了综合后的网表和对应SVF。你顶层用DC综合时可能也会带上IP的SVF信息。到了Formality里如果同时加载了IP的SVF和顶层的SVF可能产生冲突。处理第三方IP的SVF应当跟在IP网表配套使用。如果顶层网表里已经把IP当作黑盒处理那么IP内部的SVF信息对顶层验证没有意义不加载反而干净。如果顶层综合时对IP做了展开pipelining或boundary optimization那么你更需要在顶层验证时加载的是顶层综合的SVF而不是IP自己的SVF。这个分类要搞清楚否则Formality里会出现大量自相矛盾的引导信息。5.5 排查链路案例五PT里生成的SVF用到Formality场景有些流程里PrimeTime也会生成SVF文件用于保持PT反标网表和综合网表的一致性验证。工程师图省事把PT生成的SVF直接拿给Formality用。结果能用的概率不高。PT生成的SVF是面向时序分析和网表一致性验证场景的它记录的优化信息跟DC综合优化不完全一致。如果非要让Formality能用更可靠的做法是回到DC流程里重新生成一份专门的综合SVF。最后再分享一点个人经验SVF这种文件在整个数字流程里不起眼但它的质量直接影响后端验证效率。我自己的习惯是在DC综合脚本模板里把set_svf的生成和关闭写成固定写法每次综合完成后自动检查SVF文件大小如果明显小于预期比如几十KB这种量级就手动打开看几眼确认内容合理再往后端release。这种“多看一眼”的习惯已经帮我的团队挡掉了好几次后端LEC验证的返工。另外验证工程师不要只坐在Formality前面等结果。偶尔打开SVF文件看看DC对你的设计做了什么你才能对“逻辑等价性验证到底在验证什么”有更深的体感。后续遇到任何unmatched或者verify fail你排查的路径都会比身边同事快不少。