4. 仿真控制与运行管理
编译通过只是把设计“翻译”成了可执行的仿真快照,真正让验证平台跑起来、跑得久、跑得可控的是 xmsim。本节围绕五个相互配合的控制面展开:启动选项决定仿真以什么模式进入、时间控制决定仿真跑多久、波形记录决定留下哪些可回看的证据、断点与交互调试决定出问题时如何停下来观察、多核加速决定在可接受时间内能否收敛。理解它们之间的配合关系,比单独记住某个开关更重要。
一、xmsim 的启动方式与关键运行选项
Xcelium 的典型流程是 xrun 一步完成编译与仿真,或先 xmelab 生成快照、再用 xmsim 反复运行。当需要多次以不同参数运行同一快照时,分离式流程更高效:编译一次,仿真多次。一个常见的启动命令形态如下:
xmsim -snapshot tb_top -timescale 1ns/1ps \
-input run.tcl \
-log xmsim.log \
-sv_seed random \
+TESTNAME=smoke_test \
+UVM_VERBOSITY=UVM_LOW
其中 -snapshot 指定要运行的快照名;-input 指定启动后自动执行的 Tcl 脚本,用于加载波形、设置断点、启动 run;-log 把控制台输出同时写入日志文件,便于回归时批量检索;-sv_seed 控制 SystemVerilog 随机种子,固定种子可复现问题,随机种子用于探索覆盖;+TESTNAME 与 +UVM_VERBOSITY 是传给测试平台的 plusarg,用于选择用例和调整打印级别。这些选项的共同作用是:让同一次编译产物在不同运行配置下被复用,而不是每次改参数都重新编译。
二、仿真时间控制:run、时间精度与超时保护
仿真时间控制的核心是“跑多久”和“以什么步长推进”。在交互模式下,run 命令支持多种粒度:
run # 一直运行到 $finish 或遇到断点
run 100ns # 运行 100ns 仿真时间后暂停
run -absolute 2us # 运行到绝对时刻 2us
run -step # 单步推进一个时间片
时间精度由编译期的 -timescale 决定,例如 1ns/1ps 表示时间单位 1ns、精度 1ps。精度设置过粗会掩盖亚纳秒级竞争,过细则显著增加事件调度开销。工程上通常让 DUT 与 TB 使用一致的时间精度,避免跨模块延时被舍入。
长时间仿真必须有超时保护。可在 Tcl 脚本中设置看门狗:
# 若 30 分钟真实时间仍未结束,则强制停止并保存现场
set timeout 1800
after $timeout { puts "TIMEOUT: dumping state"; stop -save }
run
超时后不要直接杀掉进程,而应先 stop -save 保存当前状态,再检查是否卡在某个 wait、死循环或握手未完成。判断依据是:日志最后一条有效打印的时间戳、当前仿真时间、以及各关键信号的最后跳变时刻。
三、波形记录:从“全量 dump”到“按需记录”
波形是调试的主要证据,但全量记录会让波形文件迅速膨胀。合理的做法是分层记录:顶层接口信号全记,内部深层信号按需开启。常用控制方式包括:
- 编译期用
-access +r或+rwc开放信号可见性,否则运行时无法 probe 内部信号; - 运行期用 Tcl 的
database -open打开波形库,probe指定要记录的信号; - 用
+define+DUMP_LEVEL在代码中条件编译$dumpvars,控制记录深度; - 对长仿真采用分段记录,例如每 1ms 生成一个波形文件,避免单文件过大。
database -open waves -shm -into waves.shm
probe -create tb_top.dut -depth 3 -all -memories
run 1ms
database -close waves
记录范围要服务于调试目标:验证总线协议时重点记录 valid/ready/data 与握手时刻;验证状态机时重点记录状态寄存器与转移条件。记录过少会在出问题时无法回溯,记录过多则拖慢仿真并淹没关键跳变。
四、断点与交互调试:让仿真在关键条件处停下
断点分为时间断点、信号断点和条件断点。时间断点用于在指定时刻暂停;信号断点用于在信号跳变时暂停;条件断点则把暂停条件写成表达式,只在满足业务条件时停下,避免在无关跳变上反复中断。
stop -time 500ns
stop -condition {tb_top.dut.state == 3'b101 && tb_top.dut.err_flag}
stop -signal tb_top.dut.req -edge rising
停下之后,交互调试的价值才真正体现。常用命令包括:examine 查看信号当前值,deposit 强制赋值以验证假设,step 单步推进,where 查看调用栈,trace 追踪驱动来源。一个典型排障路径是:条件断点命中 → examine 检查输入与状态 → 判断是激励未到位还是 DUT 响应错误 → 用 deposit 临时修正输入验证后续逻辑 → 记录结论后继续 run。
五、多核仿真加速:把事件调度压力分摊出去
当设计规模增大、单核仿真时间不可接受时,可启用多核仿真。Xcelium 提供多核编译与多核仿真能力,典型选项包括 -mce(多核引擎)与并行求解相关开关。其基本思路是把设计按模块或分区拆分到多个核上,每个核负责一部分事件的调度与求值,核间通过同步点交换必要数据。
加速效果并非线性。假设单核运行 1000ns 需要 100 秒,理想四核可降到 25 秒,但实际受三方面制约:一是分区是否均衡,若某个核承担了大部分活跃逻辑,其他核会空等;二是跨核通信量,握手频繁的模块被拆到不同核会引入同步开销;三是仿真本身的串行部分,如全局时钟、复位、共享总线仲裁,难以并行。因此工程上通常先做分区评估,再逐步增加核数,观察加速比是否进入收益递减区间。
一个可参考的配置形态如下:
xmsim -snapshot tb_top -mce 4 \
-parallel \
-input run.tcl \
-log xmsim_mce.log
启用后应重点观察:日志中是否报告分区信息与核间同步次数;相同激励下仿真结果是否与单核一致;加速比是否达到预期。若结果不一致,优先检查跨核同步点附近的竞争条件,而不是直接怀疑设计逻辑。
六、把五个控制面串成一次可复现的运行
实际工作中,这五项能力通常组合使用。一个可复现的排障流程是:先用固定 -sv_seed 和 +TESTNAME 复现失败;用 run 配合超时看门狗定位卡死区间;在可疑时刻设置条件断点,命中后用 examine 与 trace 找到错误驱动源;确认问题后开启针对性波形记录,保留证据;最后在回归中启用多核加速,缩短整体收敛时间。每一步的输入、观察点和判断依据都不同,但目标一致:让仿真从“能跑”变成“可控、可查、可复现”。
run、stop、probe、database 命令固化到 run.tcl 中,配合 -input 自动加载。这样每次启动 xmsim 都进入一致的调试环境,减少手工输入带来的差异。