ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

深入解析Xcelium xrun:从编译到覆盖率收集的数字IC仿真实战指南

深入解析Xcelium xrun:从编译到覆盖率收集的数字IC仿真实战指南 仿真工具用了不少从 ModelSim 到 VCS 再到 Xcelium真正让我觉得“命令行原来可以这么顺手”的还是 Cadence 的 Xcelium尤其是它的统一启动命令 XRUN。这篇东西不是官方文档的翻译是我在实际项目里用 xrun 跑编译、跑回归、查覆盖率、调 UVM 环境时攒下来的一套操作习惯。如果你正准备进数字 IC 验证或者刚从别的仿真器切过来希望这篇能帮你少走点弯路。1. 芯路起跑为什么是 Xcelium 和 XRUN1.1 数字 IC 仿真验证里工具选型这件事很多刚接触数字 IC 的人都会问Xcelium 和 VCS 到底该学哪个行业内这两家在数字仿真领域确实长期并排VCS 在部分场景下速度快Xcelium 的优势则体现在稳定性、覆盖率收集、UVM 支持以及和 Cadence 全家桶的联动上。多数公司实际是两套都有但看招聘要求会写 Xcelium 的岗位并不少。对于想入门的人我更推荐从 xrun 入手。它最大的特点是你用不着去记 xmvlog、xmverilog、xmelab、xmsim 这一串独立命令一条 xrun 就能把前面所有阶段串完。它像是一个“大型编译器 仿真器”的调度框架内部也兼容 Verilog、SystemVerilog、VHDL、UVM 甚至混合语言仿真。日常做验证你有 90% 的时间只需要跟 xrun 打交道。我用 Xcelium 跑过的项目里既有几百万门级的 SoC 回归也有几 KB 的小模块快速冒烟。实话讲小模块场景下 xrun 启动速度比某些重量级流程快不少这对 Iteration 很频繁的调试阶段特别友好。1.2 xrun 到底帮你省了什么时间有人把 xrun 理解成“一条命令跑仿真”这个说法对但不够准确。xrun 实际是三个阶段的统一入口分析阶段Parse——读文件检查语法等价于 xmvlog / xmverilog / xmhdl 的工作。细化阶段Elaboration——把分析后的模块实例化、连接信号、解析参数等价于 xmelab 的工作。仿真阶段Simulation——跑行为、跑时序、产生波形和覆盖率等价于 xmsim 的工作。用一条 xrun 命令它会自动判断哪些文件需要重新分析哪些模块在上一次细化后没有变化能省掉大量手动清理工程的时间。它还支持类似增量编译的缓存机制改动小模块后重跑速度提升非常明显。我记得第一次在一份 40 多个文件的验证环境里用 xrun 做增量编译当时只是改了 testbench 里一个断言重跑居然只花了原来三分之一的时间那种体验确实让我对命令行工具彻底改观。2. 上手第一步构造最小仿真闭环2.1 一个可以直接跑的计数器例子理论讲多了没用先来一个最小例子。假设你有一个简单的 4-bit 计数器counter.svmodule counter ( input logic clk, input logic rst_n, output logic [3:0] count ); always_ff (posedge clk or negedge rst_n) begin if (!rst_n) count 4d0; else count count 1b1; end endmodule再写一个最简单的 testbenchtb_counter.svmodule tb_counter; logic clk 0; logic rst_n 0; logic [3:0] count; counter u_counter (.*); always #5 clk ~clk; initial begin repeat (4) (posedge clk); rst_n 1; repeat (20) (posedge clk); $display(PASS: counter reached %0d, count); $finish; end endmodule然后一条命令跑起来xrun counter.sv tb_counter.sv -access rw -q这条命令会完成编译、细化、仿真最后你会在终端看到类似下面的输出PASS: counter reached 16 Simulation complete via $finish(1)这里-access rw给所有信号开了读写权限方便后面调试-q是安静模式减少不必要的日志刷屏。对新手来说这样一个闭环已经足够让你理解 xrun 的基本用法了。2.2 从编译到退出细看每个关键选项如果你只记住了上面那条命令已经能跑很多练习了。但实际项目里你需要掌握的选项远不止这几个。我把高频的选项按作用分成五类类别典型选项作用文件控制-f filelist.f、-v lib.v、-y libdir指定文件列表、库文件、库目录语言与标准-sv、-v2001、-sv2009、-sv2012选择 SystemVerilog 或 Verilog 标准仿真行为-timescale 1ns/1ps、-seed 42、-access rw时间单位、随机种子、访问权限覆盖率与UVM-coverage all、-uvm、-uvmhome …打开覆盖率收集、启用 UVM 库调试与波形-input sim.tcl、-gui、-wave控制交互仿真、启动 SimVision、导出波形我最常踩的一个坑是-timescale。如果设计文件里没写timescale而 testbench 里写了一些模块可能会因为时间精度不一致出现莫名其妙的时序偏差特别是做后仿真时尤其明显。建议从一开始就在仿真命令里显式声明统一的时间刻度。另外-seed不是 UVM 专用。哪怕你只是用$random在 xrun 里设固定 seed 也能让随机行为可复现。回归时通常随机跑但 debug 时固定 seed这是我个人的固定习惯。2.3 日志和退出码到底怎么读很多新人跑完仿真看到终端刷了一堆信息不知道哪些是警告、哪些是致命错误。xrun 默认会有清晰的前缀但如果你加了-q部分信息会被吞掉。我的建议是调试阶段不要加-q直接看标准输出。退出码也很重要。仿真结束如果返回 0说明正常$finish返回非 0常见原因包括断言失败、仿真超时或者 simulator 内部错误。你可以在脚本里这样判断xrun counter.sv tb_counter.sv -access rw -q if [ $? -ne 0 ]; then echo Simulation failed exit 1 fi进一步你还可以用-finish count控制仿真在指定$finish次数后彻底退出防止 testbench 里多个initial块满天飞时进程挂住不返回。这一点在自动化回归脚本里尤其重要。3. 玩转 XRUN 的高效调试与提速技巧3.1 编译选项和代码风格里藏着的性能密码仿真速度一直是验证团队的痛点。xrun 提供了一组性能调节开关我用过之后最直观的感受是同样是跑 1000 个 UVM 测试用例把编译优化选项打开后整体回归时间能缩短 20% 到 40%有些模块甚至更多。首先是-autocc。它会自动识别设计中适合并行的代码块在细化阶段生成多线程代码。对循环较多的 testbench 和参考模型效果非常显著。但它也可能增加编译时间所以适合在稳定回归阶段打开开发初期可以先关掉以便快速迭代。其次是-hal全称是 Hardware Accelerated Logic? 实际上它是把一些行为级代码转换成更快的形式。配合-xmsim一起用时能把 simulation kernel 的调度效率提上去。还有个比较小众但好用的选项是-simperformance在仿真阶段对常用路径做进一步优化。代码风格上也有影响。你写的 testbench 里如果动不动就用#10做延时控制时间精度会被拖到皮秒级影响整体效率。尽量用(posedge clk)和时钟事件来驱动仿真器就不必反复处理海量时间片。从 ModelSim 转过来的朋友经常惊讶于 xrun 编译速度这其实和它的增量缓存机制有关。改一个文件后它只重新分析改动文件及受影响模块不会把整个工程重新编译一遍。你只要别老是用-clean清空缓存这个加速效果就能一直在。3.2 多核、随机种子与编译缓存的实战组合现代服务器都是几十核起步如果仿真工具只用单核就太浪费了。xrun 支持多核并行仿真常用的是-xmsim -i -l这种方式吗不是我用过的实际组合是xrun counter.sv tb_counter.sv -access rw -autocc -xmsim -l sim.log -seed 1234-autocc做编译期自动并行化-xmsim是 Xcelium 的仿真内核名-l sim.log是把日志写到文件-seed 1234固定随机种子。如果你想尝试不同随机种子可以这样for seed in 1 2 3 4 5; do xrun counter.sv tb_counter.sv -access rw -seed $seed -l sim_$seed.log -q done在实际回归脚本里我是用 Makefile 管理 seed 列表并行分发到不同机器上最后统一收集日志和覆盖率数据库。xrun 的覆盖率输出默认是xcelium.d目录多个 seed 跑完后需要用xcrg或imc做 merge得到合并后的覆盖率报告。编译缓存的目录默认是.xcov或者仿真目录下的临时文件夹具体和版本有关。如果你发现改了文件但仿真结果没变化极有可能是缓存没失效。xrun 平时会自动判断文件时间戳和校验和但如果文件通过 NFS 共享且时间戳有问题可能误判。这种情况我一般会主动加-clean强制全量重新编译。3.3 覆盖率与 UVM从“能跑”走向“能查”单纯把 testbench 跑通只能算验证的第一步。行业里更看重覆盖率xrun 在覆盖率收集上做得挺顺手。打开覆盖率最直接的方式是xrun counter.sv tb_counter.sv -coverage all -covtest my_test -q跑完以后仿真目录会生成覆盖率数据库。你可以用xcrg -dir xcelium.d -report coverage_report生成报告也可以用图形界面imc打开看。报告里能看到行覆盖率Line、翻转覆盖率Toggle、条件覆盖率Condition、分支覆盖率Branch、有限状态机覆盖率FSM等等。重点说下 FSM 覆盖率。如果你的状态机写成了enum建议在状态变量声明处加上一段属性比如typedef enum logic [1:0] {IDLE, RUN, DONE} state_t; state_t state, next_state;Xcelium 对这样的状态机默认就能识别并收集状态转移覆盖率不需要额外配置。如果你的状态编码是自定义 one-hot可能要加// coverage fsm之类的注释帮助工具识别具体可以查xrun -help中 FSM 相关说明。UVM 方面xrun 不需要单独编译 UVM 库只要在命令里加-uvm工具会自动链接对应版本的 UVM。跑 UVM test 时常用xrun -uvm -sv -f filelist.f UVM_TESTNAMEmy_test -coverage all -qUVM_TESTNAME是 UVM 本身提供的运行时参数xrun 会透传给仿真内核。这个参数不用写死在代码里换 test 的时候只需要改命令行非常灵活。这里有个细节如果你把UVM_TESTNAME放到了-f filelist.f后面某些版本里可能有解析顺序问题建议把它放在所有文件列表之后、命令末尾前。3.4 调试波形让信号自己说话没有波形的调试就像闭着眼睛修车。xrun 支持导出 VPD、FSDB、VCD 等格式也能直接调用 SimVision 看波形。我最常用的非交互式做法是写一个sim.tcl文件database -open -wave wave_db -default probe -create -database wave_db tb_counter -all -depth all run -all exit然后在 xrun 命令里用-input sim.tcl导入xrun counter.sv tb_counter.sv -access rw -input sim.tcl -q仿真结束会在目录下生成wave_db波形库然后用 SimVision 打开simvision wave_db probe -create … -depth all会把 testbench 下面所有层次的所有信号都记录下来。对大型设计来说这个操作会牺牲一点仿真速度但调试时值得。等 bug 修完回归阶段再把-depth收小或只记录关键信号。如果你更习惯看 VCD 或者需要兼容其它波形工具xrun 也支持xrun counter.sv tb_counter.sv -access rw -input sim.vcd.tcl -q其中sim.vcd.tcl里用$vcdpluson或vcd相关命令。但老实说VCD 文件体积比 FSDB 大不少能不用尽量别用。3.5 交互式仿真断点调试比想象中好用命令行仿真的优势不只是批处理。xrun 支持进入交互模式这个过程其实不用手动打断而是通过-input脚本在特定位置停下。比如你想在仿真跑到 100ns 时停住看看某个信号值可以写run -time 100ns examine tb_counter.count run -all exit配合xrun -input使用等价于“脚本化断点”。当你在脚本里发现自己需要反复执行一段操作时完全可以把 xrun 的 Tcl 交互能力当成自动化工具来用。很多人以为-input只能用来开波形实际上它支持大量仿真控制命令包括stop、run、examine、deposit、force等。调试致命 bug 时我经常这样操作在 initial 块里临时加上$stop;仿真跑到那里就停下来然后再用-input脚本的run -all继续结合波形一层层看。等定位完毕把$stop;删掉再重新跑回归。这种方式比在代码里打一堆$display高效得多。4. 坑与修我在真实项目中踩过的 XRUN 问题4.1 仿真不收敛、数组越界、断点不生效的排查套路先说不收敛。XRUN 仿真不收敛的原因很多但我在实际项目中最常遇到的是组合逻辑环路。你会在日志里看到类似“Zero delay loop detected”的提示。排查方法是先用xrun -clean重新编译然后在细化阶段加-xmelab -notimingcheck试试是不是时序检查引发的问题但更根本的是检查 RTL 里有没有把输出反馈回输入的锁存环路。数组越界在 SystemVerilog 里有时只给警告但 Xcelium 抓得比某些工具严。若日志里出现Index out of range而仿真没有立刻退出说明代码里某个ref类型参数或者动态数组越界了。建议在编译时开启-assert enable_dformat或者直接搜索日志中的Warning关键字逐条处理。断点不生效的问题我一开始也遇到过。后来发现是-access权限没给够。如果设计里某些信号没有读/写权限你在 Tcl 里examine或force就会失效。解决办法很简单仿真命令加上-access r或者更常用的-access rw但要提醒的是rw权限会额外消耗内存所以大回归时不要轻易全开只在 debug 时开。4.2 “仿真器件未定义”这类编译错误怎么快速定位很多从 Cadence 工具链过来的同学会遇到“仿真器件未定义”的编译报错。这个信息其实比较模糊。最常见的根因是Verilog 里例化了一个模块但这个文件没被加进文件列表或者库路径没配对。我处理这种问题通常三步走看报错行号找到例化名。在工程里全局搜索模块定义确认是哪个文件。如果是自己的文件把路径加到-f或-y里如果是公司的 IP检查库映射文件cds.lib是否包含正确的DEFINE行。还有一个容易被忽略的点如果模块名和文件名不一致xrun 默认不会主动扫描目录。你必须通过-v指定库文件或通过-y指定库目录仿真器才知道去哪找。这一条对刚从 ModelSim 转过来的人尤其容易踩。4.3 波形是红线的真正含义X 态是怎么来的ModelSim 里波形红线一般是“高阻或未初始化”Xcelium 的 SimVision 里类似。看到波形上大片红色先别急着怀疑工具九成原因是 RTL 里存在未初始化信号。最典型的例子是复位没处理好。如果某个寄存器只在rst_n0时复位而 testbench 一开始没把rst_n拉低足够时间第一次有效时钟沿到来时寄存器值就是 X。解决办法检查 testbench 里复位持续时间是不是至少覆盖一个时钟周期检查是否信号被多驱动比如两个模块同时给同一条线赋值也会产生 X。另一个常见问题是把logic信号接在 inout 端口上。SystemVerilog 里 inout 端口最好用wire如果用logic强制连接某些版本下会产生解析歧义导致 X。这一点我从一个 adc 接口模型的项目里体会很深当时仿真波形一片红排查半天最后把端口类型从logic改成wire就全好了。4.4 modelsim / VCS 用户转过来的三个观念差从 ModelSim 转 xrun最先不适应的就是它“太像编译器”。ModelSim 默认会帮你隐含处理很多东西xrun 则更强调显式控制。比如你必须清楚地知道自己的文件按什么顺序被解析这在混合 Verilog/VHDL 项目里特别重要。VCS 用户转过来通常会不习惯 xrun 的增量缓存和覆盖率数据库结构。VCS 用simv可执行文件xrun 则是xmsim内部执行两者生成物目录不同。但这只是习惯问题xrun 的-clean、-f、-access在工程化集成上很方便。还有一点xrun 对 SystemVerilog 标准支持更细尤其是在 interface、clocking block 和随机约束方面的报错提示相对友好。很多人说 VCS 跑 UVM 速度快但我个人的体验是Xcelium 的 debug 体验更顺滑尤其在跑大型 UVM 环境的断点回溯时日志里的uvm_info层级比某些工具更好筛选。4.5 一套可以照抄的 Makefile 回归脚本最后分享一个我一直在用的精简版 Makefile支持编译、单测、回归、清理四个目标# 使用方法 # make compile # 编译 细化 # make test TESTmy_test # 跑指定 UVM test # make regress # 跑回归读取 seeds.txt # make clean # 清理中间文件 FILELIST filelist.f SEEDS $(shell cat seeds.txt) SIM_DIR sim_out compile: mkdir -p $(SIM_DIR) cd $(SIM_DIR) xrun -f ../$(FILELIST) \ -uvm -sv -access rw \ -coverage all -l compile.log test: mkdir -p $(SIM_DIR) cd $(SIM_DIR) xrun -f ../$(FILELIST) \ -uvm -sv -access rw \ UVM_TESTNAME$(TEST) \ -coverage all -seed $(SEED) -l test_$(TEST).log regress: for s in $(SEEDS); do \ echo Running seed $$s; \ make test TEST$(TEST) SEED$$s; \ done clean: rm -rf $(SIM_DIR) xcelium.d *.log这段脚本我基本每个项目都在用改一改路径就能适配大多数 UVM 环境。需要说明的是xrun覆盖率数据库默认生成在当前运行目录所以我把所有操作都cd到sim_out里避免中间产物散落整个工程。如果项目更大我还会在 Makefile 里加并行分发比如用make -j8 regress同时跑多个 seed不过要注意覆盖率数据库合并时不能并发写同一个目录否则会把 xcelium.d 弄坏。多 seed 并行跑完后我会用xcrg把结果 merge 到一份报告里再放进回归邮件。5. 一些关于日常使用 XRUN 的小结在实际项目中摸爬滚打之后我对 xrun 最大的感受是它给了验证工程师很强的“掌控感”。你完全能通过一条命令把编译、细化、仿真、覆盖率生成全部串起来也能用-input脚本完全控制仿真行为。对于刚入行的人来说与其纠结 Xcelium 和 VCS 哪个是行业标准不如先把 xrun 的编译调试闭环练熟。学工具的最好方法就是拿一个小模块反复跑。你可以故意在 RTL 里埋几个 bug再用 xrun 导出波形定位练习多了很多选项就会形成肌肉记忆。等哪一天你能不看-help直接把一个 UVM test 从空目录跑到覆盖率报告说明你已经真正上手了。
返回列表