尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

VCS覆盖率分析全攻略:类型、命令、合并与收敛实战

VCS覆盖率分析全攻略:类型、命令、合并与收敛实战 做数字IC验证的朋友对VCS这名字肯定不陌生。Synopsys家的VCS加上覆盖率分析这套东西基本是流片前验证绕不开的关卡。很多人用VCS跑仿真跑得飞起但一到“覆盖率”这仨字就有点发怵选项一堆报告一堆merge来merge去最后review的时候还被问得哑口无言。这篇文章我就把VCS覆盖率相关的完整玩法拆开讲一遍从覆盖率类型、编译仿真选项、报告生成到回归合并、常见坑位全部按我实际项目里验证过的路径来写希望能帮你少走点弯路。这套东西适合谁看刚接手数字IC验证、被老大要求“把覆盖率拉上去”的初级工程师以及用VCS跑了好几年仿真但没系统整理过覆盖率流程的朋友。我尽量不念手册就说人话把那些文档里写得不痛不痒、但实际特别关键的细节都扒出来。1. 覆盖率到底在cover什么——先想清楚再动手1.1 三种覆盖率的分工与逻辑先说一个我见过无数次的误区很多人一上来就开-cm linecondbranchfsmtgl跑完看数字“98%”很开心但review的时候被问“功能覆盖率多少”直接愣住。这俩完全不是一回事。代码覆盖率Code Coverage工具自动收集不需要你写任何额外代码。Line、Cond、Branch、FSM、Toggle这些本质上是回答“RTL代码有没有被执行到、条件组合有没有被覆盖到”。它衡量的是“代码被激活的程度”不是你设计意图的实现程度。功能覆盖率Functional Coverage需要你手动写covergroup、coverpoint、cross用来回答“设计的功能点有没有被验证到”。比如一个FIFO代码覆盖率可能100%但“almost_full标志在深度90%时拉高”这条功能场景你压根没测过——代码覆盖率完全看不出来。断言覆盖率Assertion CoverageSVA断言被触发的次数和状态。断言挂了说明bug断言没触发未必是好事可能说明你设计的协议时序根本没被刺激到。三者关系我的理解是代码覆盖率是地基功能覆盖率是设计意图的标尺断言覆盖率是衔接两者的胶水。只盯其中一个在流片前都可能翻车。1.2 覆盖率分析的常见误区和正确姿势我见过不少团队把代码覆盖率当KPI冲到99%就觉得稳了。但实际上代码覆盖率有它天然的盲区Line覆盖率100%不代表代码所有行为都被验证。if-else里的条件真值表可能只测了一半。Condition覆盖率关注的是每个条件独立翻转的效果但多个条件组合下的短路逻辑单独看condition是看不见的。FSM覆盖率只覆盖状态跳转不覆盖状态机每个状态里输出的具体数据路径。Toggle覆盖率在低功耗设计里尤其容易踩坑电源关断域的信号你toggle到了但它根本没在正确的工作模式下翻转。正确的姿势应该是先设计你的验证计划Verification Plan明确功能点再映射到功能覆盖率模型然后才用代码覆盖率作为“代码有没有被执行到”的兜底检查。顺序不能反。2. VCS覆盖率分析的完整流程与工具链拆解2.1 整体流程从编译到报告用VCS做覆盖率分析标准链路是这样的编译阶段把RTL和testbench用vcs命令编译成simv可执行文件编译时打开覆盖率代理开关。仿真阶段运行simv仿真器在后台收集覆盖率数据数据以simv.vdb目录形式落盘。报告阶段用urg工具读取vdb目录生成文本、HTML格式的覆盖率报告。合并阶段回归跑完几十上百个用例用urg -dir把多个vdb合并成统一数据库整体分析。听起来很简单对吧但每一步都有细节。尤其是编译选项写错了仿真跑完发现vdb目录是空的这种亏我吃过不止一次。2.2 关键工具与文件类型覆盖率相关的核心概念我列个表对照一下工具/文件作用备注simv.vdb覆盖率数据库目录每次仿真默认生成包含所有覆盖率数据urg覆盖率报告生成器VCS自带命令行工具verdi波形/覆盖率可视化工具可以打开vdb查看覆盖率也能合并覆盖率ucliVCS交互式命令行仿真时可动态查询覆盖率状态*.vdb合并多用例汇总必须确认testbench里没有$finish提前退出导致数据未落盘这里重点说下Verdi。VCS和Verdi都是Synopsys家的配合使用有天然优势。Verdi不仅能看fsdb波形还能直接打开vdb把覆盖率标注在源码上——哪行没跑到红色高亮一眼就看到了比看HTML报告直观太多了。后面实操部分我会详细说怎么联仿。3. 实操从编译选项到覆盖率报告生成含Verdi联仿3.1 编译阶段如何正确开启覆盖率收集VCS编译的时候需要指定覆盖率类型。命令大概长这样vcs -sverilog \ -cm linecondbranchfsmtgl \ -cm_name test_top \ -cm_dir simv.vdb \ -f filelist.f \ -l compile.log几个参数逐个说-cm linecondbranchfsmtgl收集哪些覆盖率类型。号分隔。我一般全开反正仿真性能开销能接受后期分析时只想看某类也可以过滤。-cm_name给这次编译的覆盖率集合起个名字。多配置编译时特别有用不然多个vdb目录混在一起最后merge会乱套。-cm_dir指定vdb数据库路径。不指定默认是当前目录下的simv.vdb。如果你跑并行回归强烈建议指定独立目录不然多个simv同时写一个vdb直接打架。-cm_hier覆盖率层级限制。如果你有IP核、submodule不想纳入统计用这个参数排除。-cm_hier vmodule -tree tb_top.u_dut这种语法可以精确定位要采集的模块。我实际用下来这个选项能帮你把注意力集中在自己写的RTL上不会被第三方IP拖低覆盖率数字。编译选项我可以再补充一个很实用的-assert cover。这个不仅使能SVA断言检查还会收集断言覆盖率。设计里写了property没这个选项断言覆盖率的统计就是空的。3.2 仿真阶段-cm与-assert的使用细节编译完了运行simv的时候也要带覆盖率相关选项./simv \ -cm linecondbranchfsmtgl \ -cm_name test_case_001 \ -cm_dir simv_test001.vdb \ -assert cover \ -l sim_test001.log这里有个特别容易踩的坑仿真命令行里的-cm类型必须和编译时一致或者更小范围。比如编译时开了tgl仿真时只写-cm line那这次仿真只更新line覆盖率tgl数据不更新。但反过来说如果你想细粒度控制某次仿真只收集某类覆盖率这是可行的手段。还有-cm_name跑回归的时候每个用例必须不同不然所有用例往同一个vdb里写数据互相覆盖——不是merge是覆盖我之前一个项目就因为这问题覆盖率从90%“掉”回60%排查了整整半天。仿真方式上如果你跑的是UVM环境testbench里通常有uvm_root控制结束别忘了在final阶段调用$fcov相关任务或者直接由VCS在仿真退出时自动flush覆盖率数据。如果testbench用$finish硬退VCS默认会写入覆盖率但如果你用$exit或者仿真被kill掉数据可能不完整。如果确实需要提前结束仿真可以通过UCLI命令控制./simv -ucli -do save_cov.tclsave_cov.tcl里写run coverage save simv_interrupt.vdb quit这样即使仿真中途退出也能手动把覆盖率数据保存出来。3.3 用urg生成报告用Verdi看波形与覆盖率仿真跑完vdb目录里就是原始数据了。生成报告用urg这工具没有图形界面纯命令行urg -dir simv_test001.vdb \ -format text \ -report urg_report-format text会在urg_report目录下生成text文件-format html则会生成urg_report.html支持浏览器直接看。我个人习惯是text和html都出text用脚本做数据提取html用来人工review。HTML报告里每类覆盖率都有单独tab点进去还能看到具体到每个module、每个instance的细分数据。哪个模块line coverage低了点进去就能看到未覆盖的行号。但说实话看报告不如直接上Verdi。VCS和Verdi联仿我在项目里这么配./simv \ -cm linecondfsm \ -assert cover \ fsdbautoflush \ -ucli -do dump_fsdb.tcl其中dump_fsdb.tcl内容fsdbDumpfile tb_top.fsdb fsdbDumpvars 0 tb_top run quit这样仿真会同时产出vdb覆盖率数据库和fsdb波形文件。跑完打开Verdiverdi -f filelist.f -ssf tb_top.fsdb -cov -covdir simv_test001.vdbVerdi打开后源码窗口能看到覆盖率标注高亮。红色是未覆盖行绿色是已覆盖。用鼠标点一下红色行Verdi还能告诉你这个分支如果走到的话应该走哪个方向配合波形能快速判断是激励不够还是RTL本身有dead code。说实话这比对着HTML报告猜半天高效太多了。3.4 覆盖率采集的“最后一公里”数据落盘检查仿真结束很多人直接就去urg了结果发现vdb目录是空的或者数据明显不对。优先检查这几件事simv命令行是否带了-cm没带相当于裸奔根本没有覆盖率采集。编译时的-cm_dir和仿真时的-cm_dir是否指向同一个目录如果在不同目录下跑的查数据就要指向仿真目录。仿真进程是否正常退出被kill -9干掉的数据大概率没写全。fsdbautoflush对fsdb有效但vdb怎么确认写入了可以用vcs -cm_report直接用vdb生成简单报告测试一下或看*.vdb目录里*.db文件的修改时间。其实还有一个办法在testbench里用SystemVerilog的$cov相关系统函数在仿真结束后主动输出覆盖率状态。比如final begin $display(Coverage: %0.2f%%, $get_coverage()); end$get_coverage()返回的是当前所有覆盖率类型加权的综合覆盖率虽然不是细分数据但用来确认“这次仿真有数据”已经够了。4. 覆盖率合并、回归与收敛策略4.1 多用例覆盖率合并的要点单个用例跑完的覆盖率通常只有百分之二三十。回归跑完几百个用例你总不可能逐个报告看吧。这时候就要合并。合并的命令是urg -dir simv_case1.vdb simv_case2.vdb simv_case3.vdb ... \ -format texthtml \ -report merged_report注意urg -dir后面可以跟很多vdb目录。但如果用例跑了几百个命令行会写一大串我一般用脚本生成这个命令。需要注意一下如果vdb目录名有通配符比如simv_*.vdb放在固定目录下写脚本遍历会方便很多。合并后的覆盖率不是简单的算术平均而是“并集”关系——某个case覆盖到的行、分支、状态合并后只要任意一个用例覆盖到了就认为是覆盖到的。所以回归用例越多合并覆盖率通常越高。但也有一个常见陷阱如果两个用例的-cm_name一样urg合并时会认为是同一个配置直接合并数据如果不一样会按不同配置分开统计。所以回归脚本里统一-cm_name合并出来才是一个干净的整体。合并之后还有个操作我强烈建议做——加一个负向过滤。如果设计里有不可避免的dead code比如DFT测试模式相关的always块某些配置下永远不会执行。这些代码会一直拉低行覆盖率拉低分支覆盖率review时说不清楚。用urg -dir ... -warn看看输出也可以直接在编译时用-cm_hier把这些模块排除掉这样报告更“干净”汇报的时候也更有底气。4.2 从“低覆盖率”到收敛的实操路径覆盖率收敛这件事是最考验验证工程师功力的。我常用的路径是这么几条第一步先看结构覆盖率。打开urg报告按模块排个序锁定覆盖率最低的几个模块。如果某个模块line覆盖率低于80%大概率是这个模块的功能根本没有被激励到先别急着写covergroup先把基础激励补上。第二步结合波形分析未覆盖点。用Verdi打开未覆盖的红色行倒推一下“什么场景能走到这里”。比如一个仲裁器某个state的idle跳转没覆盖可能是你所有测试用例里仲裁请求一直满负荷从没出现过空闲周期。那就加一个“空载突发”交替的场景。第三步针对功能覆盖率补coverpoint。结构覆盖率补齐后就要看功能覆盖率了。Verdi里同样可以查看covergroup的覆盖情况未命中的bin会被列出来。挨个分析是激励没给够还是bin定义本身就有问题。第四步跑定向用例或加约束。有些场景随机约束永远也砸不中就得手写定向用例。比如cache line的eviction随机激励概率太低直接写一个定向序列制造冲突。第五步回归验证环比。每次向回归集里加了新用例都要重新跑merge手工检查覆盖率增量。如果加了个用例覆盖率一点没涨说明用例重复了或者激励落在未覆盖点的路径被其他机制挡住了。再说一个特别容易忽略的点亚稳态、跨时钟域CDC场景对覆盖率的影响。如果DUT里有异步FIFO或同步器VCS的功能覆盖率模型如果没有正确建模跨时钟域事件覆盖率报告里会一直出现“不可能覆盖”的case。这种情况不要硬逼覆盖率数字应该先修正验证环境否则纯属凑数据。4.3 覆盖率随回归波动的处理有时候你会发现同一个回归集这次跑出来line coverage 92%下次跑变成90%。先别慌这通常有两个原因一是随机种子导致激励分布不同。UVM跑固定回归时每个用例最好固定seed否则每次回归的覆盖率天然会抖动。正确的做法是回归脚本里指定固定seed比如ntb_random_seed1、uvm_set_seedxxx这样才能保证回归的可复现性。二是你收集覆盖率时把testbench也纳入了统计。testbench里的赋值语句、断言表达式尤其是UVM scoreboard里的比较逻辑覆盖率和DUT是两回事。发现波动时建议用-cm_hier把DUT单独摘出来做统计主体testbench的覆盖率单独看这样DUT的收敛曲线就稳定多了。5. 常见问题与排查技巧实录5.1 高频报错与处理我把项目里遇到过的典型问题整理一下不一定全但出现的概率挺高现象可能原因解决办法仿真日志报Coverage data is not written.进程非正常退出或-cm未在仿真命令中声明确认simv带-cm确认没有kill -9urg报Cannot open file simv.vdbvdb目录不存在或路径错误先确认仿真真的产出了vdb再检查-cm_dir路径覆盖率低的离谱-cm_hier排除了过多模块检查排除规则确认DUT顶层没有被误杀多case合并后覆盖率反而下降不同case的-cm_name不一致统一回归脚本里的-cm_name单case跑得慢覆盖率采集开销大开了过多的覆盖类型或层次按需裁剪-cm类型必要时只保留linecondVCS跑仿真时直接报Coverage collection requires a valid licenseLicense缺少覆盖率feature联系EDA管理员确认license是否包含对应feature最后一个问题单独说一句。覆盖率收集其实是需要专门的license feature的有些团队用的VCS license只包含仿真功能不包含覆盖率收集。这种时候你选了-cmVCS不是直接报错而是可能在仿真中途无法写vdb或者生成的文件是空的。遇到这种情况先查license别在这上面浪费时间。5.2 覆盖率数据对不上的排查思路很多时候你会遇到Verdi里显示的覆盖率和urg报告里的对不上。好几年前碰上过折腾了一晚上。最后发现是vdb目录混了同一个目录下有多个-cm_dir指向每个case跑的时候往同一个vdb里写数据但-cm_name设置一样导致数据互相覆盖Verdi打开时默认读其中一个数据urg合并时又是另一套。排查思路是这样的先查simv运行日志确认-cm_dir实际路径。检查vdb目录下.db文件的修改时间看看数据落盘时间和仿真结束时间是否一致。用urg -dir只指定该vdb生成报告对比Verdi的显示。如果还不一致多半是你Verdi打开时的-covdir路径指错了。仔细看下Verdi日志窗口的路径提示。另一个常见场景是merge报告已经包含所有case但你在Verdi打开单个vdb覆盖率自然更低。这不是bug是你看的数据范围不同。merge后的数据要用merge后的vdb目录来看或者直接在Verdi里做merge操作。5.3 独家避坑覆盖率驱动的回归速度控制覆盖率收集是有性能开销的尤其开了tgl——信号翻转覆盖率要追踪海量信号的0/1变化仿真速度能慢上30%到50%。这在大型SoC验证环境里是不可忽视的代价。我现在的做法是分阶段策略早期跑冒烟测试不开覆盖率全速推进功能调试功能基本稳定后开启linecondfsm做定向回归到最后freeze之前才全开linecondbranchfsmtgl做最终回归和覆盖率汇总。这样既不牺牲前期的迭代速度又在关键节点拿到了完整的覆盖率数据。如果你是做FPGA验证Vivado的XSim其实也支持代码覆盖率分析xsim -cover是类似的思路。但VCS这边生态更成熟功能覆盖率建模也更灵活所以在数字IC前端验证里VCS覆盖率这套流程几乎成了标配。关于VCS和Xcelium的对比也经常有人问。这两个都是商业仿真器里的顶流选哪个更多取决于公司已有IP、脚本环境和团队熟悉度。覆盖率框架上两者大同小异核心的数据结构、合并思路、报告形态都差不多。如果你已经熟悉了VCS这套切到Xcelium其实也就一两周的学习曲线。6. 覆盖率的“最后一课”用数据说话而不是堆数字项目里我见过太多人把覆盖率当成纯数字游戏拼命往100%凑。但覆盖率是用来帮助验证的不应该是验证的目标本身。一个设计line coverage有95%但feature coverage只有70%另一个设计line coverage 88%但feature coverage 95%后者作为流片前的评估依据往往更可靠。近几年的实践里我个人强烈建议在覆盖率报告里带上一页“已知未覆盖项说明”哪些是dead code哪些是未来版本的功能预留哪些是当前env的约束限制。这样review的时候老大就不会揪着某个红色行跟你死磕了因为你已经解释清楚了为什么红。不看原因的覆盖率数字是没有任何工程价值的。最后分享一个小习惯每次回归生成报告后我习惯把覆盖率报告归档到带日期和git commit号的目录里。这样哪天覆盖率掉下来我能快速定位是代码改动导致的还是环境导致的不用翻聊天记录找上一版数据。做覆盖率分析这东西说难吧其实就是编译选项加几个参数、仿真跑完看报告说简单吧真正把数据采集、合并、收敛、汇报形成一套闭环还是需要反复踩坑。希望这篇能把VCS覆盖率相关的关键节点都给你捋清楚实际操作的时候能少点手忙脚乱。
返回列表