3. 覆盖率收集:代码覆盖率与功能覆盖率开关、覆盖率数据库合并、排除文件设置
覆盖率这东西,说白了就是检验你仿真到底跑没跑透。我见过不少团队,仿真跑得轰轰烈烈,结果一查覆盖率,关键分支根本没走到。嗯,今天咱们就把覆盖率收集的坑一个个填平。
3.1 代码覆盖率 vs 功能覆盖率:到底该开哪个?
很多新手一上来就把所有覆盖率开关全打开,觉得越多越安全。其实不是这样。代码覆盖率和功能覆盖率,完全是两码事。
| 类型 | 衡量对象 | 自动收集? | 典型坑点 |
|---|---|---|---|
| 代码覆盖率 | RTL代码的行、分支、条件、状态机跳转 | 工具自动统计,无需额外代码 | 高覆盖率≠功能正确,空转也能刷高 |
| 功能覆盖率 | 设计规范中定义的功能点、协议场景 | 需要手动写covergroup/coverpoint | 写不全等于白测,漏场景是常态 |
我个人习惯的做法是:代码覆盖率全程开着,因为它不费事,跑仿真时顺手就收了。功能覆盖率则要精打细算——只针对关键功能点写covergroup,别试图覆盖所有信号。
3.2 覆盖率开关设置:别让工具白干活
不同仿真器的开关写法不一样,但核心逻辑相通。我以Synopsys VCS为例,给你看一套我常用的配置:
# 开启所有代码覆盖率类型
cover report +line +branch +cond +tgl +fsm
# 关闭某些模块的覆盖率(后面会细讲)
-cover_off -module uart_top
-cover_off -file exclude_list.txt
# 功能覆盖率开关
-cm_assertion +covergroup
这里有个坑:条件覆盖率(cond)默认只覆盖条件表达式整体为真/假,不会拆开看每个子条件。你想想看,一个 if (a && b) 语句,如果a和b同时为1的情况没测到,工具不会告诉你。我曾经因为这个漏掉了一个握手信号的组合逻辑bug,查了三天才定位到。
-cm_cond_allops 开关,让工具把每个子条件单独统计。否则条件覆盖率就是自欺欺人。
3.3 覆盖率数据库合并:别让数据打架
仿真通常跑很多testcase,每个case生成一个覆盖率数据库。最后需要合并成一个总库,才能看到全局覆盖情况。
合并的坑主要在两点:增量合并 vs 全量合并,以及合并顺序。
我推荐的做法是:
- 每个testcase独立生成一个 .vdb 目录(VCS)或 .ucdb 文件(Questa)
- 用工具提供的合并命令统一合并,不要手动复制文件
- 合并时指定 -full 参数,避免增量合并导致数据丢失
# VCS 合并示例
urg -dir test1.vdb test2.vdb test3.vdb -full -dbname merged.vdb
# Questa 合并示例
vcover merge -full merged.ucdb test1.ucdb test2.ucdb test3.ucdb
为什么强调 -full?因为增量合并默认只保留最后一次运行的数据,如果你某个testcase跑了两遍,前面的数据会被覆盖。嗯,我吃过这个亏,合并完发现覆盖率反而比单个case还低,查了半天才发现是增量合并搞的鬼。
3.4 排除文件设置:该忽略的就别测
不是所有代码都需要覆盖。有些代码天生就是“不可测”或“没必要测”的。比如:
- 仿真专用的调试代码(如 $display、$monitor 相关逻辑)
- 死代码(设计遗留的未使用分支)
- 第三方IP(你无权修改,也不需要你测)
- 断言检查器(本身是验证组件,不是设计)
排除文件怎么写?我一般用两种方式:
方式一:按模块排除
# exclude_modules.txt
module uart_top
module i2c_slave
module ddr_phy
方式二:按文件/行号排除
# exclude_lines.txt
./rtl/debug_monitor.v:1-50
./rtl/clock_gating.v:120-130
然后在仿真命令中引用:
urg -dir merged.vdb -exclude exclude_modules.txt -exclude exclude_lines.txt
3.5 覆盖率收集的完整流程
把上面这些串起来,一个标准的覆盖率收集流程长这样:
这个流程里,最容易出问题的就是合并和排除这两步。我建议你在回归测试脚本里加一个自动化检查:合并后自动对比每个case的覆盖率,如果某个case的覆盖率在合并后反而下降了,立刻报错。
3.6 覆盖率收集的常见误区
- 误区一:代码覆盖率100% = 验证充分
错。代码覆盖率只告诉你代码被执行了,不告诉你执行得对不对。功能覆盖率才是验证质量的真正标尺。 - 误区二:功能覆盖率写得越多越好
错。写太多coverpoint会导致仿真速度变慢,而且很多冗余覆盖点根本用不上。我建议只覆盖设计规范中明确的功能场景。 - 误区三:排除文件可以随便写
错。排除文件必须经过评审,并且要有明确的排除理由。我习惯在排除文件里加注释,说明为什么排除,谁批准的。