1. 课程导入与工程环境搭建
芯片级回归验证不是把若干模块级测试简单堆叠,而是要在统一的编译、仿真、覆盖率与回归调度框架下,让成百上千个 UVM 用例可重复、可追踪地运行。Xcelium 作为 Cadence 的并行仿真器,配合 UVM 库与规范的目录结构,是搭建这条流水线的起点。本章先建立工具链的整体认知,再完成 UVM 库编译、目录结构设计与首个冒烟测试用例的运行。
Xcelium 工具链概览
Xcelium 的核心可执行程序是 xrun,它把编译、精化(elaboration)和仿真三个阶段封装为一条命令,同时保留 xmvlog、xmelab、xmsim 分步调用的能力。对于 UVM 工程,常用的关键选项包括:
-uvmhome <path>:指定 UVM 库根目录,xrun 会自动加入 UVM 源码路径与宏定义。-sv/-sysv:启用 SystemVerilog 编译。-timescale 1ns/1ps:统一时间单位与精度,避免不同文件默认值冲突。-access +rwc:开放读写访问,便于波形与调试。-coverage all:开启代码与功能覆盖率采集。-seed <n>:指定随机种子,回归中用于复现失败用例。-l <log>:输出编译与仿真日志。
需要区分的是:xrun 是“一站式”入口,适合日常开发与冒烟;而回归场景常把编译产物(snapshot)固化,用 xmsim -R 反复加载,避免每个用例重复编译,从而把回归时间集中在仿真本身。理解这一点,是后续设计目录结构与回归脚本的前提。
UVM 库编译流程
UVM 库有两种使用方式:一是直接引用 Xcelium 自带的 UVM 实现,二是编译用户指定版本的 UVM 源码。前者通过 -uvmhome 指向安装目录下的 tools/uvm 即可;后者需要显式编译 uvm_pkg.sv 并保证 UVM_NO_DPI 等宏与 DPI 库匹配。工程中更推荐固定 UVM 版本,避免工具升级导致行为漂移。
一个典型的编译命令如下:
xrun -sv -uvmhome $UVM_HOME \
-timescale 1ns/1ps \
-access +rwc \
-coverage all \
-f filelist.f \
-top tb_top \
-l compile.log
其中 filelist.f 按顺序列出接口、包、UVM 组件与顶层模块。编译顺序很关键:uvm_pkg 必须先于任何 import uvm_pkg::* 的文件;tb_top 中 run_test() 的调用必须在所有组件类定义之后。若出现 “class not found” 或 “package not imported” 类错误,优先检查 filelist 顺序与 -uvmhome 是否生效。
芯片级回归验证目录结构设计
芯片级回归的目录结构要同时满足三件事:源码可分层、编译产物可复用、回归结果可按用例与种子归档。下面给出一个可直接落地的结构示例:
proj/
├── rtl/ # DUT RTL 与 filelist
├── tb/
│ ├── env/ # UVM env、agent、scoreboard
│ ├── seq/ # sequence 与 virtual sequence
│ ├── tests/ # 各测试用例
│ └── top/ # tb_top、接口绑定
├── sim/
│ ├── filelist.f
│ ├── Makefile
│ └── run_regress.sh
├── cov/ # 覆盖率合并与报告
└── regress/
├── seeds/ # 种子列表
└── results/ # 按 test/seed 归档日志与波形
设计要点:tb/ 只放可综合无关的验证代码;sim/ 集中编译脚本与 filelist,保证从任意路径调用都能定位源码;regress/results/ 以 <test>_<seed> 命名子目录,便于失败用例一键复现。覆盖率数据库建议统一输出到 cov/,回归结束后用 imc 合并,避免分散在各用例目录中难以汇总。
首个冒烟测试用例运行
冒烟测试的目标不是覆盖率,而是确认“编译—精化—仿真—报告”这条链路通畅。一个最小可运行的 UVM 用例包含三部分:继承自 uvm_test 的测试类、在 tb_top 中调用 run_test()、以及通过 +UVM_TESTNAME 选择用例。
class smoke_test extends uvm_test;
`uvm_component_utils(smoke_test)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("SMOKE", "smoke test started", UVM_LOW)
#100ns;
`uvm_info("SMOKE", "smoke test finished", UVM_LOW)
phase.drop_objection(this);
endtask
endclass
运行命令与判读方式:
xmsim -R -l smoke.log +UVM_TESTNAME=smoke_test +ntb_random_seed=1
运行结束后,在 smoke.log 中检查三项:是否出现 UVM_INFO ... SMOKE ... smoke test started 与 finished;UVM_ERROR 与 UVM_FATAL 计数是否为 0;仿真是否正常结束而非超时挂起。若 run_phase 中忘记 raise_objection,仿真会在 0 时刻直接结束,日志中看不到任何 SMOKE 信息,这是新手最常见的“仿真秒退”原因。冒烟通过后,再把该用例纳入 run_regress.sh 的用例列表,逐步扩展为完整回归集。
sim/Makefile 中把编译与仿真拆成 comp 和 run 两个目标:make comp 生成 snapshot,make run TEST=smoke_test SEED=1 只做仿真。这样回归时无需重复编译,单次仿真时间可显著下降。