二、仿真工具与覆盖率收集:VCS/IUS/Xcelium等主流工具覆盖率编译选项与运行选项设置
说到覆盖率收集,很多刚入行的朋友第一反应就是「加个选项跑一下」。嗯,没那么简单。我见过太多项目,因为覆盖率选项配错了,最后报告里一堆假缺口,白白浪费几周时间追查。今天我就把几个主流工具的用法掰开揉碎了讲清楚。
2.1 覆盖率类型与基本概念
在动手之前,我们先搞清楚要收集什么。覆盖率说白了就是「你测了哪些东西」。我个人习惯把覆盖率分成两大类:
- 代码覆盖率:行覆盖、条件覆盖、分支覆盖、状态机覆盖、翻转覆盖
- 功能覆盖率:由断言(assertion)和 covergroup 定义的用户自定义覆盖点
这里有个坑——代码覆盖率只能告诉你「这段代码被执行了」,但不能告诉你「执行得对不对」。我曾经在一个项目中,行覆盖率达到 95%,但功能覆盖率只有 30%,结果流片回来发现一个 corner case 没测到。所以,两种覆盖率要配合着看。
2.2 VCS 覆盖率编译与运行选项
VCS 是 Synopsys 家的工具,也是目前市面上用得最多的仿真器。它的覆盖率选项分两阶段:编译时和运行时。
2.2.1 编译选项
编译时,我们需要告诉 VCS 要收集哪些类型的覆盖率。常用的选项如下:
| 选项 | 说明 | 我的建议 |
|---|---|---|
-cm line+cond+tgl+fsm+branch |
收集行、条件、翻转、状态机、分支覆盖率 | 项目初期全开,后期按需关闭 |
-cm_dir <dir> |
指定覆盖率数据库存放目录 | 建议用 ./cov_vcs,方便管理 |
-cm_name <name> |
指定覆盖率数据库名称 | 用 testcase 名命名,方便追溯 |
-cm_hier <file> |
指定层次化覆盖率文件,可以排除某些模块 | 排除 IP 或第三方模块,减少干扰 |
举个例子,我常用的编译命令长这样:
vcs -full64 -sverilog \
-cm line+cond+tgl+fsm+branch \
-cm_dir ./cov_vcs \
-cm_hier cm_hier.cfg \
-f filelist.f \
-l vcs_compile.log
cm_hier.cfg 里用 -exclude_tree 把 tb 和 assertion 模块排除掉。因为 testbench 的覆盖率对我们验证 DUT 没有意义,反而会拉高整体覆盖率,给你一种「已经测够了」的错觉。
2.2.2 运行选项
仿真运行时,覆盖率选项主要控制「什么时候收集」和「怎么收集」:
| 选项 | 说明 |
|---|---|
-cm line+cond+tgl+fsm+branch |
与编译时一致,指定运行时收集的类型 |
-cm_name <name> |
覆盖编译时的名称,可以按 testcase 区分 |
-cm_dir <dir> |
覆盖编译时的目录 |
-cm_start |
延迟覆盖率收集的起始时间(单位:时间单位) |
-cm_stop |
停止覆盖率收集的时间 |
运行命令示例:
./simv \
-cm line+cond+tgl+fsm+branch \
-cm_name test_uart_rx \
-cm_dir ./cov_vcs \
-cm_start 100ns \
-cm_stop 1ms \
-l simv_run.log
-cm_start,结果复位阶段的 X 态被当成有效覆盖率收集进去了。复位阶段的状态机跳转是无效的,一定要用 -cm_start 跳过复位窗口。
2.3 IUS/Xcelium 覆盖率选项
Cadence 家的工具,早期叫 IUS(Incisive Unified Simulator),现在叫 Xcelium。说实话,Xcelium 的覆盖率引擎比 VCS 要快一些,尤其是在大型 SoC 项目中。我最近两个项目都在用 Xcelium。
2.3.1 编译选项
Xcelium 使用 irun 或 xrun 命令,覆盖率选项通过 -cov 系列参数控制:
| 选项 | 说明 |
|---|---|
-covoverwrite |
覆盖已有的覆盖率数据库 |
-covdut <module> |
指定 DUT 顶层模块,只收集该模块的覆盖率 |
-covfile <file> |
指定覆盖率配置文件,可以精细控制每个模块的收集策略 |
-covscope <scope> |
指定收集范围,如 +tb 或 -tb |
编译命令示例:
xrun -f filelist.f \
-covoverwrite \
-covdut top.dut \
-covfile cov_config.cfg \
-l xrun_compile.log
2.3.2 运行选项
运行时,Xcelium 的覆盖率选项通过 -covtest 和 -covdirectory 控制:
xrun -R \
-covtest test_uart_rx \
-covdirectory ./cov_xcelium \
-covfile cov_config.cfg \
-l xrun_sim.log
-covfile 功能非常强大。你可以在配置文件中按模块、按层次、甚至按行号来设置覆盖率收集策略。比如,你可以只收集 dut.ctrl 模块的条件覆盖率,而跳过 dut.ram 的翻转覆盖率。这在定位特定模块的覆盖率缺口时特别好用。
2.4 覆盖率数据库管理与合并
跑完所有 testcase 后,我们需要把各个 testcase 的覆盖率合并起来。这里我画了一张流程图,帮你理清整个流程:
合并覆盖率时,VCS 用 urg 工具,Xcelium 用 iccr 或 imc。命令如下:
// VCS 合并
urg -dir ./cov_vcs/*.vdb -dbname merged_vcs
// Xcelium 合并
iccr -merge ./cov_xcelium/*.cov -out merged_xcelium
2.5 常见问题与调试技巧
最后,分享几个我在项目中踩过的坑:
- 覆盖率为零:检查编译和运行时的
-cm选项是否一致。我见过有人编译时开了-cm line,运行时忘了加,结果跑完发现覆盖率全是 0%。 - 覆盖率异常高:检查是否把 testbench 的覆盖率也收集进来了。用
-cm_hier或-covdut排除掉。 - 合并后覆盖率下降:检查数据库目录是否被覆盖了。建议每个 testcase 用不同的
-cm_name。 - 仿真速度变慢:覆盖率收集本身会消耗性能。如果项目很大,建议只收集关键模块的覆盖率,或者用
-cm_start跳过复位阶段。
嗯,以上就是主流仿真工具覆盖率选项的核心内容。说白了,覆盖率收集不是「加个选项就完事」的活,需要根据项目特点灵活配置。多跑几次,多看看报告,慢慢就能找到感觉了。