3. 代码覆盖率收集实战:使用VCS/IUS工具收集代码覆盖率,生成报告解读
代码覆盖率,说白了就是衡量你的验证到底跑了多少“路”。
我刚开始做验证那会儿,总觉得功能覆盖点写够了就万事大吉。直到有一次,一个很隐蔽的bug逃逸到了后端——原因很简单:有一段代码分支,仿真从来没进去过。从那以后,我对代码覆盖率再也不敢马虎了。
这一章,我们就来聊聊怎么用VCS和IUS(现在叫Xcelium)这两个主流工具,把代码覆盖率跑起来,并且把报告看懂。
3.1 什么是代码覆盖率?为什么它重要?
代码覆盖率衡量的是:你的仿真用例,到底执行了设计代码的哪些部分。
它不像功能覆盖率那样关心“业务场景是否完备”,它只关心“代码本身有没有被跑到”。
核心观点:代码覆盖率是验证完备性的“底线检查”。如果一段代码从未被执行过,那它里面的bug永远不可能被发现。
常见的代码覆盖率类型有这些:
| 覆盖率类型 | 含义 | 我关注的点 |
|---|---|---|
| 行覆盖率 (Line) | 每一行代码是否被执行过 | 最基础,但容易被满足 |
| 翻转覆盖率 (Toggle) | 每个信号是否发生过0→1和1→0的跳变 | 检查信号活动性,对低功耗验证很重要 |
| 条件覆盖率 (Condition) | 条件表达式中的每个子条件是否取过真/假 | 容易遗漏,我吃过亏 |
| 分支覆盖率 (Branch) | if/case语句的每个分支是否进入过 | 必须100%,否则有死代码风险 |
| 状态机覆盖率 (FSM) | 状态机的每个状态和转移是否被访问过 | 复杂FSM的噩梦 |
3.2 使用VCS收集代码覆盖率
VCS是Synopsys家的仿真器。我个人习惯在编译和运行阶段分别加开关。
3.2.1 编译阶段:开启覆盖率收集
编译时,加上 -cm 选项来指定要收集的覆盖率类型。
vcs -sverilog -cm line+cond+tgl+branch+fsm \
-cm_dir ./cov_dir \
top_tb.sv top_design.v \
-l vcs_compile.log
这里 -cm_dir 指定覆盖率数据库存放目录。如果不指定,默认会生成 simv.cm 目录。
我的小技巧:建议每次回归都指定独立的 -cm_dir,比如 ./cov_dir/run_001,方便后续合并。
3.2.2 运行阶段:生成覆盖率数据
运行仿真时,同样需要加 -cm 选项:
./simv -cm line+cond+tgl+branch+fsm \
-cm_dir ./cov_dir \
-l vcs_sim.log
仿真结束后,会在 ./cov_dir 下生成 .vdb 文件。这就是原始的覆盖率数据库。
3.2.3 合并与报告生成
多轮回归跑完后,可以用 urg 工具合并并生成报告:
urg -dir ./cov_dir/run_001 \
./cov_dir/run_002 \
./cov_dir/run_003 \
-dbname merged_cov \
-report urg_report \
-l urg.log
执行后,urg_report 目录下会生成HTML报告。用浏览器打开 urg_report/dashboard.html 就能看到汇总信息。
3.3 使用IUS/Xcelium收集代码覆盖率
Cadence家的工具,命令稍有不同,但思路一样。
3.3.1 编译与运行
使用 irun 或 xrun 命令,加 -coverage 选项:
xrun -sverilog -coverage all \
-covoverwrite \
-covdirectory ./cov_dir \
top_tb.sv top_design.v \
-l xrun.log
-coverage all 表示收集所有类型。你也可以指定 -coverage b(分支)、-coverage c(条件)等。
注意:IUS默认不会覆盖旧的覆盖率数据库。加上 -covoverwrite 可以避免每次都要手动删目录。但如果你需要保留历史数据,就别加这个选项。
3.3.2 报告生成
使用 imc 工具来查看和生成报告:
imc -load ./cov_dir \
-report ./imc_report \
-severity all \
-log imc.log
或者用图形界面:
imc -load ./cov_dir &
我个人更喜欢命令行方式,方便集成到自动化流程里。
3.4 报告解读:怎么看?看什么?
报告生成了,但很多人只是看一眼“总覆盖率90%”就完事了。这远远不够。
我一般会按以下步骤来解读:
- 看全局汇总:先看总覆盖率,但别太当真。90%和95%在数字上差别不大,但背后可能差了好几个bug。
- 看最低的模块:找到覆盖率最低的模块。这通常是验证的薄弱环节。
- 看未覆盖的分支:重点分析分支覆盖率和条件覆盖率。行覆盖率很容易到100%,但分支未必。
- 分析未覆盖的原因:是死代码?还是遗漏了测试场景?
避坑指南:我曾经遇到过一个项目,行覆盖率99%,但分支覆盖率只有60%。一查,发现一个复杂的case语句,有十几个分支只跑到了3个。那里面藏了一个地址译码错误——如果当时只看行覆盖率,这个bug就漏了。
3.5 代码覆盖率收集流程总览
下面这张图,是我自己总结的代码覆盖率收集流程。你可以把它贴在工位旁边。
3.6 常见问题与避坑
- 覆盖率数据为0? 检查编译和运行是否都加了
-cm或-coverage选项。我经常忘记在运行阶段加。 - 合并后覆盖率反而降低了? 检查是否合并了不同版本的代码。不同版本的代码覆盖率不能合并。
- 某些分支永远覆盖不到? 可能是死代码,也可能是验证场景遗漏。先确认是否是设计冗余,再决定是否要补用例。
- 报告打开是空的? 检查
-cm_dir路径是否正确,以及是否有写权限。
重要提醒:代码覆盖率不是越高越好。100%的代码覆盖率不代表没有bug。它只是告诉你“代码被执行了”,但不保证执行的结果是正确的。功能覆盖率才是验证完备性的最终裁判。