3. VCS覆盖率编译与仿真流程

覆盖率不是仿真跑完自动出现的“副产品”,它需要在编译阶段被显式打开、在仿真阶段被持续采集、在仿真结束后被写入数据库,最后还要把多次仿真的数据库合并成一份可分析的整体。VCS 的覆盖率闭环正是围绕这条链路展开的:编译期决定采什么,仿真期决定记什么,合并期决定看什么。本节把这条链路拆成可执行的命令与工程配置,让覆盖率从“能跑出来”变成“可控、可复现、可累积”。

一、编译期:用选项打开覆盖率采集

VCS 的覆盖率采集能力由编译选项控制,最核心的是 -cm。它后面跟的字母组合决定了采集哪几类覆盖率:

  • line:行覆盖率,统计 RTL 每一行是否被执行;
  • cond:条件覆盖率,统计 if/case 等条件表达式的取值组合;
  • fsm:状态机覆盖率,统计状态与状态转移的覆盖情况;
  • tgl:翻转覆盖率,统计信号 0→1、1→0 的翻转;
  • branch:分支覆盖率,统计分支路径是否被走到;
  • assert:断言覆盖率,统计 SVA 断言的触发情况。

工程中常用组合写法,例如同时采集行、条件、状态机和翻转:

vcs -full64 -sverilog \
    -cm line+cond+fsm+tgl+branch \
    -cm_dir ./cov.vdb \
    -cm_name compile_base \
    -debug_access+all \
    -f filelist.f \
    -o simv_cov

这里有几个关键点需要理解。-cm_dir 指定覆盖率数据库的根目录,VCS 会在该目录下生成 .vdb 结构;-cm_name 给本次编译的覆盖率数据一个名字,便于后续区分不同编译版本。注意:编译选项决定的是“采集能力”,真正是否记录数据由仿真运行时的 -cm 决定,两者必须匹配,否则会出现“编译开了但仿真没记”的常见问题。

另外,-cm_hier 用于限定采集范围,避免把整个 SoC 都纳入采集而拖慢仿真。例如只关心 DUT 内部、排除验证平台:

vcs -full64 -sverilog \
    -cm line+cond+fsm+tgl \
    -cm_hier cm_hier.cfg \
    -f filelist.f -o simv_cov

其中 cm_hier.cfg 的典型内容如下,+tree 表示包含整棵子树,-tree 表示排除:

+tree tb_top.u_dut
-tree tb_top.u_dut.u_ram
+module tb_top.u_dut.u_ctrl

二、仿真期:让覆盖率数据真正落盘

编译完成后,运行仿真时必须再次带上 -cm,否则覆盖率不会被记录。同时用 -cm_name 为本次运行命名,用 -cm_dir 指向同一个数据库目录,实现多次运行累积:

./simv_cov -cm line+cond+fsm+tgl \
           -cm_dir ./cov.vdb \
           -cm_name test_smoke_01 \
           +ntb_random_seed=12345 \
           +UVM_TESTNAME=smoke_test

每次仿真结束后,VCS 会在 cov.vdb 下生成一个以 -cm_name 命名的子目录,里面包含本次运行的覆盖率数据。这样做的价值在于:不同测试用例、不同随机种子、不同回归批次的数据可以独立保存,又能在同一数据库下统一合并。

如果希望仿真过程中实时观察覆盖率增长,可以加 -cm_log 或使用 URG 的增量报告;如果只想在仿真结束时输出一次汇总,保持默认即可。对于长回归,建议在脚本中为每个用例设置唯一的 -cm_name,例如 test_${TESTNAME}_${SEED},避免同名覆盖导致数据丢失。

编译和仿真两阶段的 -cm 必须一致或仿真阶段包含编译阶段的子集。若编译只开了 line+cond,仿真却写 -cm tgl,翻转覆盖率不会产生有效数据。

三、合并期:用 URG 把多次仿真汇成一份报告

多次仿真的覆盖率数据分散在 cov.vdb 的各个子目录中,需要用 Synopsys 的 URG(Unified Report Generator) 合并并生成报告。基本命令如下:

urg -full64 \
    -dir ./cov.vdb \
    -format both \
    -report ./urg_report \
    -show tests \
    -metric line+cond+fsm+tgl+branch

其中 -dir 指向覆盖率数据库根目录,URG 会自动扫描其下所有 -cm_name 子目录并合并;-format both 同时生成文本和 HTML 报告;-report 指定输出目录;-show tests 会在报告中列出每个测试对覆盖率的贡献,便于定位“哪个用例补了哪块覆盖”。

如果只想合并指定几次运行,可以用 -dir 多次指定具体子目录,或使用 -cm_name 过滤。合并后的报告通常包含 dashboard.html、tests.html 以及各模块的覆盖率明细,是覆盖率收敛分析的主要入口。

四、工程配置建议与常见问题

把上述命令固化到 Makefile 或回归脚本中,是覆盖率闭环落地的关键。一个可参考的工程结构如下:

cov/
├── cov.vdb/              # 覆盖率数据库根目录
│   ├── compile_base/     # 编译阶段数据
│   ├── test_smoke_01/    # 各次仿真数据
│   └── test_regress_07/
├── cm_hier.cfg           # 采集范围配置
└── urg_report/           # URG 合并报告输出

常见问题有三类:第一,编译开了覆盖率但仿真没加 -cm,导致数据库为空;第二,多次仿真用了相同 -cm_name,后一次覆盖前一次;第三,-cm_hier 排除范围写错,把真正关心的模块排除了,报告里看不到目标覆盖率。排查时先确认 cov.vdb 下是否有对应子目录,再用 URG 的 -show tests 检查每个用例是否被识别。

VCS 覆盖率闭环:编译 → 仿真 → 合并 ① 编译期 vcs -cm line+cond+fsm+tgl -cm_dir ./cov.vdb -cm_name compile_base -cm_hier cm_hier.cfg -o simv_cov 作用:决定采集哪些覆盖率类型 ② 仿真期 simv ./simv_cov -cm line+cond+fsm+tgl -cm_dir ./cov.vdb -cm_name test_smoke_01 +ntb_random_seed=12345 +UVM_TESTNAME=smoke_test 作用:把本次运行数据写入数据库 ③ 合并期 urg urg -full64 -dir ./cov.vdb -format both -report ./urg_report -show tests -metric line+cond+fsm+tgl 作用:合并多次运行并生成报告 cov.vdb 数据库结构 cov.vdb/ ├─ compile_base/ (编译期数据) ├─ test_smoke_01/ (第1次仿真) ├─ test_regress_07/(第N次仿真) └─ urg_report/ (合并报告输出) 关键约束与常见问题 编译与仿真 -cm 必须匹配或仿真为子集 每次仿真 -cm_name 必须唯一,避免覆盖 -cm_hier 排除范围要核对,防止漏采 URG -dir 指向根目录,自动扫描子目录 数据库为空:先查 cov.vdb 下有无子目录 报告缺模块:检查 -cm_hier 与 -metric