二、仿真工具与覆盖率收集: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
关键区别: Xcelium 的 -covfile 功能非常强大。你可以在配置文件中按模块、按层次、甚至按行号来设置覆盖率收集策略。比如,你可以只收集 dut.ctrl 模块的条件覆盖率,而跳过 dut.ram 的翻转覆盖率。这在定位特定模块的覆盖率缺口时特别好用。

2.4 覆盖率数据库管理与合并

跑完所有 testcase 后,我们需要把各个 testcase 的覆盖率合并起来。这里我画了一张流程图,帮你理清整个流程:

覆盖率收集与合并流程 编译阶段 -cm / -cov 选项 仿真运行 每个 testcase 独立收集 覆盖率数据库 .vdb / .cov 覆盖率合并 urg / iccr / imc 覆盖率报告 HTML / 文本 / 数据库 关键点: • 每个 testcase 独立目录 • 合并时排除复位阶段 • 注意不同工具的兼容性

合并覆盖率时,VCS 用 urg 工具,Xcelium 用 iccr 或 imc。命令如下:

// VCS 合并
urg -dir ./cov_vcs/*.vdb -dbname merged_vcs

// Xcelium 合并
iccr -merge ./cov_xcelium/*.cov -out merged_xcelium
避坑指南: 我曾经在合并时发现覆盖率不增反降,查了半天才发现是不同 testcase 的覆盖率数据库版本不一致。VCS 和 Xcelium 的数据库格式在不同版本间可能不兼容。所以,建议整个项目组统一工具版本,并且每次回归都重新编译。

2.5 常见问题与调试技巧

最后,分享几个我在项目中踩过的坑:

  • 覆盖率为零:检查编译和运行时的 -cm 选项是否一致。我见过有人编译时开了 -cm line,运行时忘了加,结果跑完发现覆盖率全是 0%。
  • 覆盖率异常高:检查是否把 testbench 的覆盖率也收集进来了。用 -cm_hier 或 -covdut 排除掉。
  • 合并后覆盖率下降:检查数据库目录是否被覆盖了。建议每个 testcase 用不同的 -cm_name。
  • 仿真速度变慢:覆盖率收集本身会消耗性能。如果项目很大,建议只收集关键模块的覆盖率,或者用 -cm_start 跳过复位阶段。

嗯,以上就是主流仿真工具覆盖率选项的核心内容。说白了,覆盖率收集不是「加个选项就完事」的活,需要根据项目特点灵活配置。多跑几次,多看看报告,慢慢就能找到感觉了。