仿真器怎样跑 RTL:EDA 入门 01
本文承接 从 RTL 到 GDS 的第一阶段。设计概念见 仿真是什么,验证方法见 EDA 验证工具链;这里只问一个工具问题:仿真器到底吃什么、怎样跑、吐出什么。
一、先定位:仿真器不是综合器
RTL 仿真器执行的是硬件描述语言的行为语义,不是把代码直接变成门。testbench 里的延时、打印、force/release、动态对象都可以仿真,但不能综合;DUT(Design Under Test,被测设计)才要求可综合。
| 输入/对象 | 例子 | 仿真器如何使用 |
|---|---|---|
| RTL 设计文件 | .v/.sv/.vhd | 编译、例化、形成 DUT 层次 |
| Testbench | 时钟、复位、驱动、scoreboard | 提供虚拟时间、激励和检查 |
| filelist/宏/包含路径 | .f、+incdir、+define+ | 决定编译顺序和条件编译 |
| 参数 | +TESTNAME、seed、plusarg | 选择回归用例和随机种子 |
| 波形控制 | VCD/FSDB/VPD 命令 | 记录信号变化供调试 |
| 覆盖率选项 | line/cond/fsm/assert cover | 产出代码、断言和功能覆盖率 |
输出通常包括 pass/fail 日志、波形、覆盖率数据库、断言记录和回归报告。仿真器回答的是:在给定激励和给定初始状态下,RTL 行为是否符合 testbench 期望;它不回答真实走线延迟下能否收时序。
二、一次仿真的四个阶段

- 编译解析:读 filelist、宏和包,检查语法、类型、模块端口和包依赖。SystemVerilog package 必须先编译再 import,文件顺序错误常在这里暴露。
- elaborate/link:展开参数、生成块和层次例化,找到每个模块实体。缺模块、参数位宽不合法、重复定义通常在这一步失败。
- 建执行模型:商用编译型仿真器常先生成 C/C++ 或中间模型,再调用本机编译器;Verilator 更激进,直接把可综合风格 RTL 编成 C++。解释/编译型内核差异见 EDA 验证工具链。
- 运行与记录:仿真器沿虚拟时间调度事件,执行 initial/always/assign,驱动断言和覆盖率,最后由
$finish或超时结束。
三、事件调度:为什么“仿真通过”也会有竞争
数字仿真是离散事件系统。信号变化不是立即波及全芯片,而是放入带时间戳的事件队列;同一时间步还分 active、inactive、NBA 等区域。非阻塞赋值 <= 的更新被推迟,正是为了模拟时序逻辑在时钟沿后采样旧值、更新新值的习惯。
always @(posedge clk) q <= d; // 推荐的时序逻辑写法
always_comb begin y = a & b; end // 组合逻辑应立即求值新手常犯三类错误:
- 零时延竞争:testbench 在时钟沿同一时刻用阻塞赋值改输入,DUT 采样到旧值还是新值取决于调度顺序。时钟沿后错开极小时间驱动激励更稳。
- X 当 0 用:未初始化寄存器、case 缺分支、跨域信号可能是
x。仿真器若把 X 悄悄当成某个值,可能掩盖复位和低功耗 bug。 - 依赖初值:FPGA 可以配置寄存器初值,ASIC 上电初值未必确定;复位序列必须显式驱动。
四、波形、日志和覆盖率不是越大越好
| 产物 | 用途 | 工程注意 |
|---|---|---|
| 日志 | 快速定位第一个失败检查 | 回归中保留失败用例完整日志,成功用例只留摘要 |
| VCD | 开放文本波形 | 易读但巨大,适合小例子 |
| FSDB/VPD/WLF | 高效压缩波形 | 工具生态绑定,常按层次、时间段按需 dump |
| 覆盖率 | 说明激励碰过什么 | 覆盖 100% 不等于检查正确,只说明代码被执行 |
| 断言记录 | 协议违例和证明轨迹 | 第一条 assertion failure 往往比末尾超时更关键 |
大规模回归默认 dump 全层次波形,会把时间耗在磁盘 IO 上。常见做法是“失败自动打开波形、成功只收数据库”,或先环形缓存最后若干周期,断言失败时落盘。格式体积和压缩原理见 仿真波形数据库横评。
五、失败时怎样沿工具链往回退
| 现象 | 先查哪里 | 典型动作 |
|---|---|---|
| 编译不过 | filelist、package、宏、include | 修顺序、补文件、缩小最小用例 |
| elaborate 缺模块 | 模块名、参数、库编译 | 查 +libext、条件编译和顶层名 |
| 启动即 X 海洋 | 复位、电源态、存储初值 | 检查复位释放时间、X-prop 设置 |
| 断言失败但无波形 | 波形开关和层次 | 复现同一 seed,失败前后按需 dump |
| 小用例过、回归随机失败 | 随机种子、时序竞争、检查器 | 固定 seed,找第一个偏离预期的事务 |
| RTL 很快但门级慢 | 网表规模和 SDF/X-prop | 分层跑,先零延迟再带延迟 |
排查原则是找第一个错误,而不是最后一个症状。超时、scoreboard 不匹配、多驱动冲突往往只是连锁反应;第一条协议断言、第一次 X 扩散、第一笔错误事务才是入口。
常见误区
| 误区 | 更准确的理解 |
|---|---|
| 仿真器就是“免费跑 Verilog 的播放器” | 它有编译模型、事件调度、接口和覆盖率数据库,是完整工具链 |
| 仿真过了就能综合 | testbench 可包含大量不可综合结构,DUT 还要过 Lint 和综合 |
| 波形越全越安全 | 全量波形可能拖垮回归,关键是可复现、能定位第一现场 |
| Verilator 能平替所有仿真器 | 它速度快,但语言语义、时序精度和签核口径不同 |
| 随机失败是工具不稳定 | 多数来自 seed、竞争、未初始化或检查器假设不完整 |
总结
RTL 仿真器把 RTL 和 testbench 编译成一个按虚拟时间调度事件的软件模型,吐出 pass/fail、波形、断言和覆盖率;它最擅长暴露激励下的行为错误,但不负责证明物理时序成立。 学会仿真器的第一课,是能区分编译/建摸/运行错误,知道波形和覆盖率从哪里来,并在失败时沿事件时间找到第一个真正偏离协议的位置。