想象你是一位餐厅老板,手里拿着一份数字电路“菜谱”——图纸上画好了各种锅碗瓢盆(模块)、灶眼(时钟)和食材管道(连线)。你不想真的开火炒菜,只想先在脑海中预演一遍流程:什么时候放盐、什么时候翻勺、会不会糊锅。传统仿真器就像一位“满厨房跑的总厨”,每隔一瞬就要跑到每个灶台前看看有没有冒泡,动作多、时间慢。而 Verilator 是一位“动作卡翻译官”:它先把整本菜谱读透,翻译成一张张白纸黑字的动作卡,交给帮厨(CPU)照着卡片一口气把菜炒完。翻译本身要一点时间,但一旦完成,炒菜速度就是数量级的提升。本文就用这条“厨房主线”,带你认识这位开源翻译官——它如何把 Verilog 编译成 C++,如何与测试台协作,又如何用波形录像做调试。
学完本文,你将理解编译型仿真器的设计精髓,能亲手用 Verilator 搭建 RTL + C++ 验证环境,在芯片验证与 RISC-V 开发中把回归测试时间从小时级压到分钟级。
目录
可视化演示
展示Verilator将Verilog编译为C++的全流程:RTL解析→AST优化→C++生成→二进制执行,波形Dump调试与商业仿真器性能对比,交互式探索硬件仿真核心环节。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Verilog到C++编译流水线 Verilog源码经词法分析生成AST抽象语法树 AST优化后生成C++代码再编译为原生二进制 verilator --cc main.v → sim 编译型仿真比解释型快10-100倍
认识Verilator:厨房里的动作卡翻译官
传统仿真器像一位“满厨房跑的总厨”:每道菜(每个时钟周期)要跑到所有灶台前检查一遍,看看哪些锅冒泡了、哪些事件被触发了。这种“事件驱动”模式每检查一次都要把整个厨房扫一遍,灶台越多,无效跑动越多。现在来了一位新帮手——Verilator,它不开火不试吃,而是先把整本菜谱通读一遍。
Verilator 是一款开源的编译型仿真器,它不像传统商业仿真器那样“边解释边执行”,而是把 Verilog 和 SystemVerilog 代码先整体“翻译”成 C++ 源码,再用 GCC/Clang 编译成可执行程序。这个“翻译+编译”的动作,相当于总厨把菜谱画成一张标准动作卡:所有并行关系在翻译时已经被理顺,帮厨只要照卡执行即可,省去了满厨房乱跑的开销。
性能方面,传统仿真器的开销集中在“每事件全局扫描”,而 Verilator 把扫描工作提前在编译期完成。设计越大、仿真周期越长,两者的差距越明显:常见功能仿真可以快 2~10 倍,门级或大规模回归场景甚至能快几十上百倍。总耗时的模型很简单:
回到厨房比喻:传统总厨是“跑出来的慢”,Verilator 是“翻译出来的快”。如果只是试验一道菜,翻译开销可能不划算;但真正的芯片验证要炒成千上万道菜,翻译一次、执行万次,这笔账怎么算都值。
- 总耗时 \(T_{\text{total}}\)从拿到设计到跑完仿真的全部时间,相当于“翻译菜谱 + 厨师炒菜”的总工时。
- 编译开销 \(T_{\text{compile}}\)翻译官把图纸变成动作卡的一次性固定成本,只付一次,就像买一本菜谱只花一次钱。
- 周期成本每个时钟周期(每道菜)的平均仿真耗时,乘以总周期数就是执行阶段的总花销。
编译原理:把菜谱拆成动作卡
翻译官拿到菜谱后的第一件事,不是直接炒菜,而是把菜谱拆成一张“食材关系树”:哪口锅先热、哪把盐后放、哪些动作可以同时准备。这份抽象语法树(AST)就是菜谱的骨架。
Verilator 的编译流水线分三步。第一步“解析”:Verilog 文本变成 AST,相当于把每句话归位到树上;第二步“优化”:在 AST 上做常量传播、死代码消除、模块扁平化,相当于把一道菜里“永远不加的味精”提前扔掉,把重复的备料合并;第三步“生成 C++”:把树上并行的 always 块翻译成顺序化的 C++ 求值函数 eval(),每个时钟周期调用一次,所有信号更新按依赖关系排好队。
为什么这样快?传统事件驱动仿真在运行时反复判断“这个信号变了,谁在等它”,而 Verilator 在编译期就把这份“谁等谁”的依赖图算好了。如果用事件数来衡量,加速比可以粗略表示为:
厨房里最耗时的往往不是炒菜本身,而是“到处找锅、反复确认状态”的无效走动。Verilator 把这份走动全部前移到翻译阶段,运行时帮厨只管机械地照卡执行——这就是编译型仿真的本质优势。
- 加速倍数 \(S\)同样的仿真任务,Verilator 相对传统仿真器快多少倍。
- 事件数 \(N_{\text{event}}\)传统总厨每道菜要跑动的次数,灶台越多,这个数越爆炸。
- 步骤数 \(N_{\text{step}}\)翻译后动作卡上实际要执行的步骤,紧凑且无重复跑动。
快速上手:炒第一道小菜
备好食材、支起灶台,我们来做第一道菜。假设拿到一个简单的 4 位加法器模块,想验证它算得对不对。传统流程要装仿真器、写脚本、开波形,而 Verilator 的世界里,一杯咖啡的工夫就能跑通。
安装很简单:Debian/Ubuntu 系直接用 apt install verilator,想用新特性就从 GitHub 源码编译安装。然后准备两个文件:一个 Verilog 设计文件(菜谱),一个 C++ 测试文件(试吃员)。核心命令只有一行:
这条命令把 Verilog 编译成 C++、自动链接测试台、构建出可执行文件,一气呵成。运行 ./obj_dir/Vcounter 就能看到试吃员打印的结果。日常开发中 --lint-only 可以只做语法检查,-Wall 开启全面警告,-j 0 让本机所有核心并行编译。验证是否通过,最终看的是断言和打印比对:
第一道小菜跑通后,你会发现后续只是重复同一套动作:改设计、改测试、跑命令、看输出。Verilator 把你从“等待仿真器慢慢解释执行”中解放出来,剩下的时间可以专心研究菜品本身——也就是你的硬件设计。
- 通过率 \(R\)测试通过的比率,100% 代表全部样例符合预期。
- 通过样例数 \(P_{\text{pass}}\)试吃员点头说“好吃”的菜。
- 总样例数 \(P_{\text{total}}\)菜单上所有要试吃的菜。
verilator --cc --exe --build --trace counter.v tb.cpp集成测试台:试吃员与厨师的配合
菜谱再好,也得有人试吃把关。在 Verilator 世界里,C++ 测试台就是那位试吃员,它负责把食材(输入信号)递进厨房,然后等着看成品(输出信号)是否合格。它和 Verilog 设计(DUT)之间的配合方式,决定了验证效率。
C++ 测试台的核心操作是三件事:给输入赋值、推进仿真、读输出断言。不像传统仿真器有专门的仿真内核帮忙管理时间,Verilator 把时间推进权完全交给了你——每次调用 dut->eval(),设计就会根据当前输入求值一次,相当于菜谱上的“翻一次勺”。时钟信号也是手动翻转的:置 1、eval()、置 0、eval(),如此循环,每一轮就是一个时钟周期。
这种“裸奔式”推进初看不便,实则给了验证极高的自由度:你可以不依赖仿真器的事件队列,随时插入输入变化,甚至用多线程并发喂激励。如果 C++ 侧需要与设计内部交互,Verilator 原生支持 DPI-C 接口,相当于给试吃员开了一扇可以直接看厨房内景的小窗。仿真时间的推进公式也一目了然:
试吃员和厨师的配合越来越默契:你写 C++ 就像写普通的软件测试,唯一的区别是“被测对象”是一份硬件设计。Verilator 生成的 C++ 模型可以直接和你的软件测试框架(如 Google Test)无缝衔接,硬件验证从此有了软件工程的待遇。
- 仿真时间 \(\Delta t_{\text{sim}}\)模拟世界里流逝的时间,不是真实墙钟时间。
- 周期数 \(N_{\text{cycles}}\)试吃员翻勺的次数,也是
eval()被调用的轮数。 - 时钟周期 \(T_{\text{clk}}\)每次翻勺的时间间隔,由测试台手动模拟的时钟决定。
波形调试:给厨房装一台录像机
再厉害的试吃员也有翻车的时候。当测试失败、信号异常,你需要的不只是“菜好不好吃”,而是“哪一步盐放多了”。Verilator 提供了波形追踪功能,相当于在厨房装了一台录像机,把每个灶台的每个动作都录下来,事后可以逐帧回放。
开启录像只需要在编译时加一个 --trace 开关,生成的可执行程序运行后会输出 VCD 波形文件。如果担心文件太大,可以用 --trace-fst 输出 FST 格式——FST 是机电波形的压缩格式,体积通常只有 VCD 的几分之一。录下来的波形用 GTKWave 等免费工具打开,就能像看心电图纸一样观察每个信号的跳变。
调试技巧有一条很实用:不要全程开录像。波形文件体积与信号数和周期数成正比,全程录制会让文件膨胀到几十 GB。可以只在可疑阶段用条件编译开启 dump,或者缩小追踪信号范围——录像机只盯着最可疑的那口锅,而不是整间厨房。存储量模型如下:
回到厨房:总厨不需要把整天的监控都存下来,他只需要在出锅异常的那一刻按下录制。Verilator 的波形调试同样讲究“精准取证”——用最少的录像成本,最快定位翻车现场。
- 波形数据量 \(D_{\text{trace}}\)录像占用的存储空间,通常以 MB/GB 计。
- 信号数 \(M_{\text{signals}}\)被录像的灶台数量,信号越少文件越小。
- 周期数×位宽录像时长乘以每帧画面的清晰度,周期越长、位宽越大,文件越肥。
适用场景:大饭店才请得起的翻译官
翻译官请一次不便宜,但一旦入驻,就能撑起整间大饭店的出品速度。哪个厨房最需要 Verilator?答案是那些“出菜量大、反复试菜”的地方——芯片设计验证。
最典型的场景是 RISC-V 处理器核验证。一颗 CPU 核要跑启动代码、跑操作系统、跑应用测试,动辄几百万甚至上亿个时钟周期。用传统事件驱动仿真,一个 Linux 启动可能跑上一天;用 Verilator 编译成 C++ 后,同样任务可能缩短到一两个小时,甚至在 X86 服务器上直接“模拟出”一颗能启动 Linux 的 RISC-V 芯片。这也是开源 RISC-V 生态(如平头哥、SiFive 相关项目)大量采用 Verilator 的原因。
AI 加速器验证是另一个黄金场景。NPU 的 MAC 阵列、数据通路往往要跑成千上万张图片的长序列矩阵运算,验证时间极长。Verilator 配合 C++ 黄金模型做数据比对,可以把大批量回归从“每周一次”变成“每夜一次”,让 bug 在合入代码的第二天清晨就被发现。回归测试节省的时间用个简单模型就能看到:
大饭店的菜单厚、翻台率高,翻译官的一次性投入很快被摊薄。同样,凡是“长仿真、反复跑”的验证场景——CPU 启动、AI 长序列、CI 每日回归——Verilator 都是值得优先考虑的主力选手。
- 节省时间 \(T_{\text{saved}}\)改用 Verilator 后省下的总工时。
- 速度差传统与编译型每次仿真耗时的差值,这就是翻译带来的每一步优势。
- 运行次数 \(N_{\text{runs}}\)回归测试跑多少遍,次数越多,省时越可观。
优势边界:翻译官也有不擅长的菜系
动作卡翻译官并非万能厨师。它最鲜明的取舍是“两态仿真”:Verilog 标准定义的四态是 0、1、X(未知)、Z(高阻),而 Verilator 默认只认 0 和 1。这就像总厨做菜时只关心“锅里有没有菜”,不关心“这菜半生不熟还是焦了”。对周期级功能验证来说,两态足够;但涉及 X 传播分析、复位竞争这类精细问题,两态模型就会“睁一只眼闭一只眼”。
SystemVerilog 的支持也只是一部分:可综合的语法(always、assign、generate、interface 等)支持得很好,但时序控制(如 #5 延迟)、部分断言和部分随机约束就不在默认支持范围内。门级时序仿真、混合信号仿真更是它的禁区——那种带精确延迟和模拟行为的仿真,需要回到传统事件驱动工具。位数上的对比很直观:
所以选型建议也很清晰:做功能验证、跑长回归、搭开源验证环境——请翻译官出马;做时序签核、功耗分析、混合信号验证——还是请传统商业仿真器坐镇。认清边界,才能把每把刀用在它最擅长的砧板上。
- 位宽比 \(R_{\text{bits}}\)两态相对四态的存储压缩比,省了一半资源。
- 两态位宽 \(B_{\text{two}}\)0 和 1 两个状态,1 bit 足够表达。
- 四态位宽 \(B_{\text{four}}\)0、1、X、Z 四种状态,需要 2 bit 编码。
AI 应用场景
RISC-V 处理器核启动验证
阶段:AI 芯片硬件架构验证
解决的问题:RISC-V 核要从 Boot ROM 一路跑到 Linux 启动,涉及几百万周期。传统仿真器一次启动要跑数小时到一天,迭代速度极慢,团队只能靠猜和等。Verilator 把 RTL 编译成 C++ 后,同一颗“虚拟核”在 X86 服务器上能以接近实时或数倍加速的速度跑软件,让硬件验证赶上软件迭代的节奏。
具体案例:某开源 RISC-V 项目在合入新指令前,先用 Verilator 编译整个核,挂上一份裁剪过的 Linux 镜像,跑启动测试。每次提交代码后自动执行,启动失败或寄存器行为异常时,波形文件精准指向出错的流水线级。原来一周只能跑三次的“上电大考”,现在每个 commit 都能来一次。
AI 加速器数据通路长序列回归
阶段:AI 芯片数据通路(MAC 阵列/片上网络)功能验证
解决的问题:NPU 的矩阵乘模块跑完一整张图片的推理需要数万周期,若用传统仿真器做上百张图的连续性回归,时间呈线性堆积到不可接受。Verilator 的编译型执行让每个周期开销大幅下降,还能用 C++ 黄金模型逐层比对计算结果,快速暴露精度截断和地址越界问题。
具体案例:某 AI 加速器团队在验证卷积数据通路时,用 Verilator 把 RTL 编成 C++,写了一个 C++ 的“浮点黄金参考模型”。仿真每跑完一层卷积,就把输出与黄金模型逐元素比对,误差超过阈值即打印所在层和坐标。结果,原先需要跑一整夜的 500 组回归用例,现在 40 分钟跑完。
CI 流水线的每日硬件回归
阶段:持续集成(CI)中的硬件回归测试
解决的问题:硬件设计频繁迭代,每次提交都可能引入功能退化。传统验证方式跑得慢,CI 中根本插不进去,最终退化靠人工“抽查”。Verilator 编译出的可执行文件只是普通二进制,可以像软件测试一样直接挂进 Jenkins/GitLab CI,实现硬件版本的“每日构建 + 冒烟测试 + 回归套件”。
具体案例:某团队把 Verilator 生成的可执行文件和测试用例全部容器化,每个 Merge Request 自动触发一个验证 job:先 verilator --lint-only 做静态检查,再跑核心回归子集,失败时自动归档 VCD 波形并 @ 到提交者。硬件验证从此获得了和软件 CI 一样的即时反馈闭环。
Python 演示
#!/usr/bin/env python3# 演示 Verilator 编译型仿真工作流:Verilog 加法器 -> C++ testbench -> 编译运行 + 波形import osimport subprocessimport tempfile VERILOG = """module adder ( input logic [3:0] a, input logic [3:0] b, output logic [4:0] sum); assign sum = a + b;endmodule""" TESTBENCH = r'''#include "verilated.h"#include "verilated_vcd_c.h"#include "Vadder.h"#include <cstdio> int main() { Verilated::traceEverOn(true); VerilatedVcdC* tfp = new VerilatedVcdC; Vadder* dut = new Vadder; dut->trace(tfp, 99); tfp->open("adder.vcd"); dut->a = 5; dut->b = 7; dut->eval(); tfp->dump(0); printf("a=%d b=%d sum=%d\n", dut->a, dut->b, dut->sum); tfp->close(); delete dut; return 0;}''' def build_and_run(tmp): vfile = os.path.join(tmp, "adder.v") cfile = os.path.join(tmp, "test_adder.cpp") with open(vfile, "w", encoding="utf-8") as f: f.write(VERILOG) with open(cfile, "w", encoding="utf-8") as f: f.write(TESTBENCH) cmd = [ "verilator", "--cc", "--exe", "--trace", "--build", "--Mdir", os.path.join(tmp, "obj_dir"), vfile, cfile, ] subprocess.run(cmd, check=True) return os.path.join(tmp, "obj_dir", "Vadder") def main(): with tempfile.TemporaryDirectory() as tmp: exe = build_and_run(tmp) print("=== 编译完成,运行仿真 ===") subprocess.run([exe], cwd=tmp, check=True) vcd_path = os.path.join(tmp, "adder.vcd") if os.path.exists(vcd_path): print("=== 波形文件已生成 ===") print("VCD 大小:", os.path.getsize(vcd_path), "bytes") if __name__ == "__main__": main()
cmd = [...]构造 Verilator 命令行:--cc表示生成 C++,--exe表示链接测试台,--trace开启波形追踪,--build自动编译出可执行文件。subprocess.run(cmd, check=True)执行编译命令,check=True保证编译失败时立即抛错,避免带着错误的产物继续跑。with tempfile.TemporaryDirectory() as tmp:在临时目录里干活,用完自动清理,不污染工作区。dut->trace(tfp, 99)让设计把信号变化记录到波形对象tfp中,99 是追踪深度,数字越大覆盖层次越全。dut->eval()每调用一次,设计就按当前输入求值一轮,这就是“周期级推进”的最小单元。tfp->open("adder.vcd")` / `tfp->dump(0)打开 VCD 波形文件并在时间点 0 采样一次,回放事故现场就靠它。
常见误区
误区很多人以为 Verilator 就是开源的 ModelSim/Questa,功能差不多,只是免费、速度稍好一点。于是照旧用四态、事件驱动的思路去理解它,结果发现一堆语法不支持,于是吐槽“开源工具就是难用”。
正解Verilator 和商业事件驱动仿真器的设计哲学完全不同:它是编译型 + 周期级仿真,把 Verilog 翻译成 C++ 再执行。它快不是“稍快”,而是因为运行时的全局事件扫描被编译期优化替换了,通常快一个数量级。用对待“翻译官”而不是“另一个仿真器”的心态去学,很多设计取舍就说得通了。
为何常见:人总是从已知事物出发理解新事物。Verilator 表面也是“跑 Verilog 的仿真器”,很容易被归入熟悉的类别,忽略了底层机制的差异。
误区既然叫“SystemVerilog 仿真器”,那应该所有语法都支持吧?有人把带 `#5` 延迟的激励、带 force/release 的测试代码原样丢进去,编译报错后一脸茫然。
正解Verilator 面向的是“可综合设计 + 周期级验证”子集,时序控制(delay)、部分断言、部分随机约束并不支持。它更像一个“只认动作卡”的帮厨:菜谱必须是事先能翻译成固定步骤的。用 --lint-only 提前检查语法兼容性,是避坑的第一步。
为何常见:SystemVerilog 是庞大的语言,工具支持度各有不同。人们容易把“支持这门语言”误解为“支持这门语言的全部特性”。
误区有初学者以为 `verilator --cc top.v` 之后就能直接运行仿真,结果发现只生成了 C++ 源文件,根本没有可执行程序,更没有任何输出。
正解--cc 只是“翻译”,翻译完还要用 GCC/Clang 编译链接。日常用一条 --cc --exe --build 组合命令,Verilator 会调用 make 和 g++,自动构建出可执行文件。没有 --exe 和测试台 main,生成的只是设计模型库,没有程序入口。
为何常见:工具链的“翻译”和“编译”两步在高级命令中被封装成一步,让人忽略了底层其实经历了两道工序。
误区有人听说 Verilator 是两态仿真,立刻担心:没有 X/Z 传播,仿真结果能信吗?于是认定它只是一个“快速但不可靠”的工具,只配做 smoke test。
正解两态是刻意的设计取舍。周期级功能验证本来就只关心时钟边沿处的逻辑值,X/Z 的精细传播属于门级时序范畴。Verilator 把 X 折叠成具体 0/1 值,换来的是内存减半、速度倍增。功能正确性靠的是约束和断言,而不是靠 X 来“提醒不对劲”。需要四态分析时,再回到商业事件驱动工具即可。
为何常见:四态仿真是学校教材的主流入门内容,学生天然以为“状态越全越正确”,忽略了仿真速度和仿真精度之间的工程权衡。
面试题
Q1. Verilator 把 Verilog 设计最终变成什么产物?
- A.可执行网表
- B.C++源程序← 正确答案
- C.VCD波形文件
- D.Python脚本
答案:B。Verilator 的核心动作是“翻译”:先把 Verilog 解析并优化,再生成等价的 C++ 源码,之后由 GCC/Clang 编译成可执行文件。它不生成网表,也不直接输出波形——波形是仿真运行后通过 --trace 才产生的副产品。
Q2. 传统事件驱动仿真器的主要性能瓶颈在哪里?
- A.编译优化过慢
- B.全局事件扫描← 正确答案
- C.波形文件太大
- D.硬件平台受限
答案:B。事件驱动仿真在运行时反复扫描“哪个信号变了、谁在等它”,这种全局扫描在电路规模变大时急剧膨胀。Verilator 把这些依赖关系在编译期算好,运行时只按顺序执行,所以把瓶颈从“运行期扫描”转移到了“编译期优化”。
Q3. 用 Verilator 生成波形调试,需要加哪个开关?
- A.编译模式 --cc
- B.追踪开关 --trace← 正确答案
- C.生成可执行 --exe
- D.自动构建 --build
答案:B。--trace 让生成的设计模型记录信号变化并输出 VCD/FST 波形。--cc 是编译成 C++,--exe 是链接测试台 main,--build 是自动调用 make/g++ 构建。四个开关各司其职,只有 --trace 是专门为“录像”服务的。
Q4. 周期级仿真的“周期”指什么?
- A.CPU 主频高低
- B.设计时钟周期← 正确答案
- C.编译耗时长短
- D.内存容量大小
答案:B。周期级仿真以被测设计的时钟周期为推进单位,仿真器不关心门级延迟和毛刺。Verilator 的 eval() 每调用一次就推进一个设计时钟周期,相当于每“翻一次勺”就检查一次出品。CPU 主频是宿主机属性,与仿真粒度无关。
Q5. Verilator 默认采用哪种信号状态模型?
- A.四态状态模型
- B.两态状态模型← 正确答案
- C.三态状态模型
- D.八态状态模型
答案:B。Verilator 默认只用 0 和 1 两态,把 Verilog 标准中的 X(未知)和 Z(高阻)折叠处理掉。这样每个信号只需 1 bit,内存开销减半,速度大增,但也因此不适合需要精细 X/Z 传播分析的场景。
Q6. 下列哪类设计最适合用 Verilator 做验证?
- A.门级时序收敛
- B.混合信号仿真
- C.处理器功能验证← 正确答案
- D.射频前端设计
答案:C。Verilator 的甜蜜点是“大规模数字逻辑的功能验证”,比如 RISC-V 处理器跑 Linux 启动。门级时序需要精确延迟和四态语义,混合信号和射频涉及模拟行为,这些都不是编译型周期级仿真的用武之地。
Q7. Verilator 生成的 eval() 函数主要作用是什么?
- A.生成波形文件
- B.执行一轮求值← 正确答案
- C.编译整个工程
- D.管理内存分配
答案:B。eval() 是 Verilator 生成的 C++ 模型中最重要的方法,每调用一次,设计就根据当前输入更新所有内部信号和输出,相当于一个时钟周期的状态推进。波形由 trace 对象管理,编译由 make 完成,内存由 C++ 的 new/delete 管理,与 eval() 无关。
Q8. Verilator 擅长哪些典型场景?
- A.处理器核验证← 正确答案
- B.大规模回归← 正确答案
- C.门级时序签核
- D.数据通路验证← 正确答案
答案:ABD。处理器核验证、大规模回归测试、数据通路验证都是“长仿真、大批量、重功能”的场景,正是编译型仿真的主战场。门级时序签核需要精确延迟和四态传播,属于事件驱动商业工具的职责范围,Verilator 并不胜任。
Q9. 关于 Verilator 编译流程,下列说法正确的是?
- A.解析成中间表示← 正确答案
- B.生成C++源码← 正确答案
- C.直接执行Verilog
- D.GCC链接成程序← 正确答案
答案:ABD。Verilator 先解析 Verilog 生成 AST 中间表示,优化后生成 C++ 源码,再由 GCC/Clang 编译链接成可执行程序。它不做“边解释边执行”,C 选项正好说反了,Verilator 从不直接执行 Verilog 文本。
Q10. 关于 Verilator 两态仿真的特点,说法正确的是?
- A.资源占用更少← 正确答案
- B.不区分 X 态← 正确答案
- C.保留完整 Z 态
- D.仿真速度提升← 正确答案
答案:ABD。两态模型把信号值压缩为 0/1,资源占用少、仿真速度快,但代价是 X 和 Z 被折叠处理,不再保留完整四态语义。C 说“保留完整 Z 态”正好相反——Verilator 对 Z 的处理同样是近似折叠,这也是它不用于门级仿真的一大原因。