ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

VCS覆盖率分析实战:从仿真回归到merge合并的完整指南

VCS覆盖率分析实战:从仿真回归到merge合并的完整指南 跑完一轮回归之后真正的验证工作才算开始。这个说法听起来矛盾但做过数字IC验证的人都懂仿真跑完只是一堆pass/fail回答不了功能覆盖到什么程度、代码里哪些分支还没走到、下一轮该加什么激励这几个关键问题。这些问题的答案全在覆盖率分析里。VCS的覆盖率链路从编译、仿真、数据落盘到Verdi和DVE查看再到多用例merge和URG报告每一步都有各种隐性坑。这篇文章把我平时在项目里踩过的路完整走一遍重点放在最容易出错的merge环节。1. 先认识VCS覆盖率体系六类指标和vdb目录VCS的覆盖率收集本质上分两个层面。一层是设计代码层面的结构覆盖率也就是常说的代码覆盖率VCS根据RTL代码自动统计不需要额外写任何代码另一层是功能覆盖率要靠你在SystemVerilog测试平台里手写covergroup、coverpoint、cross来定义VCS只负责收集和汇总。这篇主要围绕前者因为它是每个项目签核时绕不过去的硬指标。功能覆盖率的方法学可以单独写一篇长文这里只讲和代码覆盖率配合的部分。1.1 VCS支持的覆盖率指标VCS的-cm选项后面可以跟多种指标常见的有line、cond、tgl、branch、fsm、path还有assert。每个指标代表一个维度的覆盖情况我整理成一张表指标统计内容常见目标值line可执行语句被执行的比例90%~100%cond条件表达式内每个原子条件的真/假都被覆盖85%~95%tgl信号、端口在0/1之间发生翻转的比例70%~90%branchif、case、三元表达式等分支路径是否全部走到90%~100%fsm状态机的state和transition是否全部到达90%~100%path组合逻辑路径覆盖开销较大较少单独用assertSVA断言被触发的覆盖情况100%这里面最容易被忽视的是line和branch的差异。line只看可执行语句是否被命中branch关心分支的两个方向。一个if语句哪怕条件表达式恒真也说明branch覆盖不完整因为else方向从来没走过。很多同学看覆盖率只盯总百分比一旦低于预期就慌了其实应该先看是哪个指标拖的后腿再去定位具体模块。FSM覆盖对代码风格有隐性要求VCS默认按统一的always块建模方式识别状态机。如果你习惯用多个always块拆开写状态跳转和输出逻辑VCS也能识别但偶尔会出现状态枚举识别不全的情况这点在merge阶段也可能引发数据异常。1.2 simv.vdb里到底装了什么跑完一次带-cm的仿真后目录下会生成一个simv.vdb目录名字可以通过-cm_dir改。很多人对这个目录的理解停留在覆盖率数据存在里面但说不清具体结构。简单讲simv.vdb下面是一层snps/coverage/db的层级里面按testdata组织每次运行的覆盖率数据。这个目录是整条覆盖率分析链路的中枢Verdi、DVE、URG报告、merge操作全部围绕它转。从使用角度你完全可以把vdb目录理解成仿真器写出来的一本账本每个测试用例在账本上记一笔谁执行了哪条语句、哪个分支没走都能查得到。1.3 功能覆盖率和代码覆盖率的配合实际项目里不可能只看代码覆盖率。代码覆盖率100%只说明代码都被执行过并不代表功能验证完备。举个极端例子一个乘法器代码可能全执行了但你从没测过两个操作数都是最大值的情况这种场景必须靠covergroup去捕获。所以标准做法是用功能覆盖率定义该测的功能点测到了没有用代码覆盖率兜底检查有没有代码是死的或者从没被激活过。VCS会把两类数据写进同一个vdb报告里分开呈现这也是VCS覆盖率体系设计得比较顺手的地方。2. 编译和仿真两个阶段的覆盖率开关VCS采集覆盖率参数必须同时出现在编译和仿真两个阶段少任何一个都是白跑。这个坑我见得太多了有人编译时带了-cm仿真时忘带跑到半夜看报告发现一片空白急得直挠头。覆盖率收集的参数设计就是如此编译期负责把采集逻辑插进代码里仿真期负责把运行时数据写出来两件事缺一不可。2.1 编译阶段要带的参数一个典型带覆盖率收集的VCS编译命令长这样vcs -sverilog \ -f filelist.f \ -cm linecondtglbranchfsmpathassert \ -cm_dir simv.vdb \ -debug_accessall \ -kdb \ -o simv其中-cm指定编译要支持收集哪些覆盖率指标-cm_dir指定默认的覆盖率数据库目录名不写默认是simv.vdb-debug_accessall和-kdb是给调试工具用的如果你希望之后在Verdi里既能看覆盖率又能联动波形最好带上。这里强调一个一致性原则-cm的参数在编译、仿真、后处理三个环节尽量保持一致。编译时开了linecondtgl仿真时只传-cm line那cond和tgl的数据就丢了后面URG报告里这两项必然是0。这个坑看似低级但我在多个项目里都见过一旦发生后期的补救成本非常高。2.2 仿真阶段的数据落盘编译完成后跑仿真的命令也要带上对应的-cm./simv \ -cm linecondtglbranchfsmpath \ -cm_name top_test \ -cm_dir ./vdb/top_test.vdb \ -cm_log cov_top_test.log这里几个参数值得展开。-cm_name是给这次运行起名字它写进覆盖率数据库后你在Verdi或URG报告里能看到每个测试名称对应的覆盖情况。如果不指定默认用simv的名字连续跑多个用例后数据库里全叫simv根本分不清谁是谁。-cm_dir控制这次仿真产生的覆盖率数据写到哪个目录这是回归脚本里最重要的管理参数。如果你不管它连续跑10个用例数据会全部叠加到同一个vdb目录里后面想单独看某个用例的覆盖情况就完全没法拆。我项目里的固定习惯是每个测试用例一个独立目录命名直接带用例名./simv UVM_TESTNAMEtest_a -cm_dir ./vdb/test_a.vdb -cm_name test_a ./simv UVM_TESTNAMEtest_b -cm_dir ./vdb/test_b.vdb -cm_name test_b ./simv UVM_TESTNAMEtest_c -cm_dir ./vdb/test_c.vdb -cm_name test_c这样跑完后./vdb下面有三个独立的数据库后面merge的时候直接用通配符一把梭。另外VCS还有-cm_hier这类参数可以控制收集的层次范围比如只收集DUT内部而不收集testbench的覆盖率有兴趣可以去查手册按需使用。2.3 用-cm_log确认采集是否正常每次仿真结束后建议打开-cm_log指定的日志文件看一眼。这个文件会打印本次运行的覆盖率汇总例如各模块的line、cond覆盖率百分比以及覆盖率数据写入的位置信息。如果日志里出现异常或者数据根本没进vdb趁早发现还能补救等regression全跑完了再回头查就晚了。我第一次用VCS收集覆盖率时就遇到过日志里汇总全是0的情况排查下来是仿真时漏了-cm参数白白浪费了大半天重跑。后来我在回归脚本里加了一个主动检查if ! grep -q Coverage data written cov_*.log; then echo coverage collection failed exit 1 fi不同VCS版本的日志措辞可能不完全一样但思路是通的脚本主动检查关键标记别指望人肉盯屏。2.4 回归过程中vdb目录的清理策略vdb目录最坑的一点是它会累积。同一个目录连续跑多次后一次的数据会叠加上去。如果今天跑完一批用例明天不清目录又跑一批后天的merge结果会把你今天和昨天的数据搅在一起最后得出的覆盖率到底是哪轮代码的自己都说不清。每次回归开始前把本次运行要用的vdb根目录整个清掉每个测试单独建目录保证每次回归都是干净的数据集。如果你想做覆盖率趋势跟踪那就把当天的vdb目录按日期归档而不是反复用同一个目录叠加。数据管理这件事看起来不起眼但直接决定你后面分析的置信度。3. Verdi看覆盖率层次树、源码标注和报告导出数据采集完成之后下一步是人肉看覆盖率。Verdi是Synopsys目前主推的调试工具看覆盖率的功能比老工具强不少。下面这套操作是我平时用得最多的也是我团队里新人的入门培训内容。3.1 用verdi -cov打开数据打开命令很简单verdi -cov -dir simv.vdb 如果是多用例回归产生的合并数据就把-dir指到merge后的vdb目录。另外如果希望Verdi里看覆盖率时能直接跳波形可以加上-ssf指定fsdb文件verdi -cov -dir merged.vdb -ssf test_a.fsdb 打开之后Verdi会弹出一个独立的Coverage窗口和nWave、源码窗口平级。这里有个环境层面的提醒Verdi能不能看到覆盖率数据取决于license里是否有相应的Coverage feature。有些公司只买了基础的调试license打开vdb时会报错或直接不显示覆盖率窗口遇到这种情况先让IT查一下license的feature列表。3.2 覆盖率窗口的核心操作Coverage窗口左侧是设计层次树从testbench顶层一路展开到各个module和instance右侧是每个层次对应的覆盖率百分比表格列就是line、cond、tgl、branch、fsm这些指标。常用操作有三个一是展开层次树逐级往下看定位哪个子模块覆盖率低二是用窗口上方的过滤器按指标类型或百分比阈值筛出低于目标的模块三是点表格列头按覆盖率升序排列把最低的顶到最上面。我比较习惯从最底层模块看起因为高层模块的覆盖率往往被大量组合逻辑和连线稀释总数字看着还行实际某个关键子模块可能只有60%。覆盖率分析一定不要只盯顶层总数字那是自欺欺人。3.3 从总览到源码标注的定位路径找到可疑模块后双击对应的instanceVerdi会自动打开源码窗口用颜色标注每一条可执行语句的覆盖状态。绿色表示覆盖到红色表示没覆盖到分支语句还会在T/F方向分别标注。这套标注是Verdi覆盖率功能最值钱的地方。你不需要对着报告猜直接看代码一眼就能看出某个if的else分支从来没走过或者某一行代码从头到尾没执行过。接下来就是业务判断了这段代码是不是死代码还是缺一个特定的激励判断结果决定你是写exclude文件还是补用例。这种从数字到代码再到测试意图的链路是覆盖率分析的核心循环。3.4 Verdi里生成文字报告有些同事习惯用文档汇报覆盖率Verdi支持直接导出报告。Coverage窗口菜单里选Report生成可以选择输出文本或网页格式内容涵盖各层次覆盖汇总和未覆盖明细。不过实测下来Verdi导出的报告适合开会展示不太适合做数据分析。真正要拿数字做决策我一般还是用URG生成报告它按测试用例、按层次、按指标的组织方式更规整也更方便脚本处理。Verdi的角色更适合定位单点问题URG更适合看全貌两者配合使用效率最高。4. DVE看覆盖率老伙计也能干这活Verdi再好也架不住有些环境里没有它的license。尤其是老版本VCS自带的图形环境DVE在不少公司里仍然是看覆盖率的唯一选择。DVE界面老派但覆盖率分析该有的功能都有熟练之后效率不低别因为界面旧就嫌弃它。4.1 用dve -cov打开数据打开方式和Verdi非常类似dve -cov -dir simv.vdb DVE会新开一个标题为Coverage的窗口里面有层次树和覆盖率表格布局和Verdi有点像但交互细节差不少。双击层次树的某个instance也能打开源码窗口按颜色标注覆盖情况。DVE里看FSM覆盖率有一个比较顺手的视图它能列出状态机的每个state和每个transition并标出哪些没被覆盖。这个功能Verdi也有但DVE的呈现更直接我用DVE看FSM反而更习惯。4.2 DVE与Verdi的差异两个工具各有侧重我用下面这张表总结它们的差异维度DVEVerdi界面风格传统、简洁现代、功能密度高源码覆盖率标注支持更细致支持条件级着色数据过滤简单过滤复杂过滤器、脚本化数据库merge需配合外部URGGUI里直接merge波形联动较弱配合fsdb很流畅适用版本老版本VCS自带新版本VCS配套使用从功能完整性看Verdi明显胜出但DVE有个不可替代的优势轻。在服务器资源紧张或者只是想快速瞄一眼数据的时候DVE启动更快操作路径更短。有时候我只是确认某个vdb有没有数据、大概覆盖率多少用DVE一两分钟就有答案没必要起一个重量级Verdi。4.3 什么时候你只能靠DVE我实际遇到过几种场景DVE是唯一选择。一是公司还在用比较老的VCS版本Verdi和当前VCS版本的配套关系没理顺打开vdb时直接报版本不兼容。二是license问题老版本VCS附带DVE的权限Verdi的Coverage license没买或者并发lane不够。三是客户环境只装了DVE远程上去看问题现场装不了Verdi。这三种场景下DVE看覆盖率完全够用。如果你是刚接触验证工具链的新人建议把DVE的基本操作也顺手练熟因为不少存量项目的脚本和文档还是DVE时代的产物你能看懂老工具才能更好地理解新工具的设计思路。5. merge实战多用例覆盖率合并的正确方法接下来到这篇文章的重头戏merge。覆盖率分析里最容易被忽视也最容易出问题的就是这一步标题里单独点出来是有原因的。5.1 为什么必须merge而不是直接看最后一次单个测试用例的覆盖率没有签核意义。回归跑50个用例每个用例可能覆盖了不同模块的不同路径只有把它们的数据合并起来才能回答这一轮回归整体覆盖了多少这个问题。merge的本质是求并集对于同一个设计层次、同一个指标只要任何一个用例覆盖到了合并结果就算覆盖到。举个例子模块A有10个分支用例1覆盖了3个用例2覆盖了另外7个merge之后分支覆盖率就是100%不是两个用例里较大的那个值。用Modelsim的同事可能习惯把text report导出再用脚本拼VCS这边不需要这么原始URG直接吃二进制vdb准确度和效率都高一个量级。5.2 用urg合并多个vdbVCS自带的URG是merge的标准工具。最基本的用法urg -dir vdb/test_a.vdb \ -dir vdb/test_b.vdb \ -dir vdb/test_c.vdb \ -dbname merged.vdb \ -format both \ -report urgReport其中每个-dir指定一个覆盖率数据库-dbname指定合并后的输出目录-format both表示同时生成文本和网页报告-report指定报告输出目录。回归用例多的时候一个个写-dir不现实直接上通配符urg -dir vdb/*.vdb -dbname merged.vdb -format text -report urgReport注意这里的引号一定要加。如果不加引号shell会把vdb/*.vdb展开成多个独立参数而URG的-dir后面只认一个路径多出来的参数会解析错乱命令直接报错。想只merge数据不生成报告加-noreport想限定部分指标用-metricurg -dir vdb/*.vdb -dbname merged.vdb -metric linecondbranch -noreport这样merge出来的数据库只含line、cond、branch三类指标的合并结果文件更小后续打开也更快。5.3 merge后覆盖率为0的典型原因merge完打开合并数据库覆盖率显示为0这是新手最容易遇到也最崩溃的问题。我总结一下踩过和见过的几类原因。第一类也是最多的一类参与merge的vdb来自不同的编译版本。比如昨天编译的设计和今天编译的设计代码有改动虽然目录名一样但设计层次或内部标识已经变了merge时URG会警告甚至直接忽略部分数据。这种问题没有捷径只能重新编译、重新跑相关用例。第二类是指标集合不一致。有的用例编译时开了-cm linecondtgl有的只开了-cm linemerge出来的结果里cond和tgl自然是0或者缺失。2.1节强调的编译、仿真、后处理参数一致就是这里最深的教训。第三类是vdb目录本身是空的。仿真阶段漏了-cm或者-cm_dir指错了路径生成的就是个空壳数据库。遇到merge结果异常第一步永远是单独打开每个原始vdb确认各自有数据再谈合并。5.4 在Verdi GUI里merge数据不想记urg命令的时候Verdi里也能merge。操作路径是Coverage窗口菜单里选Merge Database在弹出的对话框里把需要合并的vdb目录一个个加进去设置输出数据库名点确定。Verdi会执行合并并在结束后自动加载合并数据库直接就能看结果。GUI方式的底层逻辑和urg一样只是交互友好一些。我自己习惯是少量数据库用GUI大批量回归一律写脚本用urg因为urg可以精确控制输出路径和报告格式方便集成到CI流程里。另外要注意merge的本质是生成一个新的vdb所以输出目录不能和任何输入目录重叠否则会污染原始数据。5.5 合并完成后的验证merge不是点一下就结束了。合并完我必做的验证动作是把merge后的总覆盖率和各用例单独覆盖率对比确保合并结果不小于任何一个单独用例且大概率高于它们。如果出现合并结果反而低的情况基本可以判定某个vdb有问题或者指标集合不匹配回头查。验证命令也很简单urg -dir merged.vdb -format text -report merged_check然后对照merged_check里的汇总和merge前各用例报告的汇总逻辑上应该满足合集 任意子集。还有一个容易被忽略的点merged.vdb生成之后它本身也是一个合法的覆盖率数据库可以继续被下一轮urg合并。所以你可以做分层merge——先按模块合并、再按整个回归合并这样既能看整体又能定位具体模块的问题。我在大项目里经常这样用底层模块各自merge出模块覆盖率顶层再全部合并出系统级覆盖率两个数字分开考核。6. 从merge结果到验证收敛我的实操习惯工具链走通之后最后聊一聊怎么用覆盖率数据驱动验证收敛。这部分更偏方法论也是我从多个项目里总结出来的实操习惯。6.1 从URG报告里快速找问题模块URG生成的文本报告里有按层次排的覆盖率汇总表列是各指标行是各模块。拿到报告的第一件事不是看总数字而是把各个子模块按branch或line覆盖率升序排列找出最低的几个。文本报告可以直接用sort命令处理网页版报告点击列头就能排序。这些低覆盖模块才是需要投入精力分析的对象。总覆盖率70%不可怕可怕的是你不知道那30%分散在哪里。定位到具体模块后打开Verdi按3.3的方法看源码标注找到没覆盖到的分支和语句。整个流程的时间分布大概是定位模块花20%看代码判断原因花60%写用例和回归花20%。6.2 把覆盖率目标拆到模块级项目签核时领导问的是总覆盖率多少但落地执行必须拆到模块级。我在项目里习惯用这样的目标模板顶层总line覆盖率不低于95%每个功能子模块branch覆盖率不低于90%关键状态机FSM覆盖率不低于95%toggle覆盖率作为参考项不做硬性签核门禁。toggle覆盖率很容易被某些只翻转一次的复位信号拖低硬性追求高toggle收益很低。设为参考项可以让团队把精力集中在有实际价值的分支和状态覆盖上。另外断言覆盖率如果项目里用了SVA建议直接设为100%目标因为断言本身就是要被触发的功能检查点。6.3 关于覆盖率闭环的几条个人经验最后分享几条我在项目里反复验证过的经验。第一exclude文件要尽早建立。项目进行到中后期总会有死代码、冗余分支、不可达状态这些拉低覆盖率的东西应该用exclude文件排除而不是花几周去写永远触发不了的激励。URG可以通过-exclude_file把排除规则合并进报告Verdi里也能做标记排除。第二别让覆盖率收集拖慢日常仿真。全指标收集可能让仿真变慢20%到30%。我的做法是平时回归跑快速冒烟不收集覆盖率每周跑一次全量覆盖率回归既不影响日常迭代速度又能拿到足够的数据做收敛判断。第三断言覆盖率记得算进去。如果代码里有SVA断言编译时加-assert enable_diag仿真时-cm里加assertURG报告里会多出一列断言覆盖率。断言触发情况往往比代码覆盖率更能暴露功能场景缺失。第四也是我认为最重要的merge命令一旦调通立刻固化成脚本。#!/bin/bash # cov_merge.sh # Usage: ./cov_merge.sh vdb_dir output_name URG_DIR$1 OUT$2 rm -rf ${OUT}.vdb ${OUT}_report urg -dir ${URG_DIR}/*.vdb \ -dbname ${OUT}.vdb \ -format both \ -report ${OUT}_report \ -metric linecondtglbranchfsm echo merged coverage: ${OUT}.vdb我见过很多团队在merge命令上反复手敲每次费时不说还容易敲错参数。固化成一个脚本之后每次回归完跑一下脚本就能拿到合并数据和报告既省时间又不容易出错。覆盖率分析的难点从来不在某一个单独的工具而在整条链路的参数一致性和数据管理习惯。编译、仿真、merge、查看每个环节都踩过一次坑之后你会形成一套自己的固定流程。把这套流程固化下来覆盖率签核也就没那么可怕了。
返回列表