在 FPGA、ASIC 和数字芯片设计中,写完 Verilog 或 SystemVerilog 代码之后,并不能直接认为设计是正确的。在真正进行综合、布局布线甚至流片之前,工程师通常需要先回答一个最基本的问题:
这段硬件逻辑,行为到底对不对?
这就是数字电路仿真的任务。
在众多 HDL 仿真工具中,Verilator 是一个非常特殊的存在。它与 ModelSim、Questa、VCS 等传统仿真器最大的不同,是它并不主要依靠一个通用的事件驱动仿真引擎去“解释执行” HDL,而是采用一种更加接近编译器的技术路线:
把 Verilog/SystemVerilog 转换成 C++ 或 SystemC,再编译成本机可执行程序。
正是这一机制,使 Verilator 成为目前数字逻辑高速仿真、自动化测试和大规模回归测试中非常有代表性的工具。

一、Verilator 本质上做了什么?
假设有这样一段简单的 SystemVerilog:
always_ff @(posedge clk) begin
if (!rst_n)
count <= 0;
else
count <= count + 1;
end
从硬件角度看,它描述的是一个触发器组成的计数器:
时钟每出现一次上升沿,计数值增加一次。
传统 HDL 仿真器通常会建立完整的仿真时间和事件系统,例如:
10 ns:clk 变化
10 ns:检测到上升沿
10 ns:执行 always_ff
10 ns:登记非阻塞赋值
10 ns:更新 count
也就是说,仿真器不断维护时间、信号变化以及事件调度。
Verilator 的思路不同。
它首先分析 RTL,然后把这些逻辑转换成类似 C++ 程序的计算过程。概念上,可以把前面的逻辑理解成:
if (clock_rising_edge) {
if (!rst_n)
count = 0;
else
count = count + 1;
}
真正生成的代码当然要复杂得多,因为还需要正确处理组合逻辑、时序逻辑、非阻塞赋值、信号依赖、模块层次以及 SystemVerilog 调度语义。
但核心没有改变:
Verilog / SystemVerilog
↓
Verilator
↓
C++模型
↓
GCC / Clang
↓
本机机器码
↓
执行
因此,Verilator 可以理解为一种编译型 HDL 仿真技术。
二、为什么这种方法会非常快?
传统事件驱动仿真器需要不断回答:
哪个信号发生变化?
哪个 always 块需要执行?
当前仿真时间是多少?
某个赋值应该在哪个调度区域生效?
后面还有没有新的事件被触发?
这种机制非常灵活,也能够表达复杂的仿真语义,但代价是运行开销较大。
Verilator 会在编译阶段提前分析逻辑之间的依赖关系。
例如有:
A → B → C → D
传统仿真器可能把 A、B、C、D 的变化看成多个连续事件。
Verilator 则可以把相关计算组织成高效的 C++:
B = !A;
C = B & enable;
D = C ^ mode;
随后 GCC 或 Clang 还会继续进行常量传播、函数内联、死代码删除、寄存器分配等编译优化。
最终真正运行的是高度优化后的 CPU 机器码。
所以 Verilator 的性能优势,本质上并不是因为它“少算了电路逻辑”,而是因为:
大量原本需要仿真器在运行期间动态完成的工作,被提前放到了编译阶段。
这和解释型语言与编译型语言之间的差异有些相似。
三、Verilator 仿真由哪些部分组成?
一个典型仿真系统至少包括两个核心部分:
Testbench
↓
输入激励
↓
DUT
↓
输出结果
↓
检查与验证
其中 DUT 是 Device Under Test,即“被测设计”。
DUT 可以是:
一个计数器;
一个 FIFO;
一个 UART;
一个 CPU 内核;
一个 DMA 控制器;
也可以是一个完整 SoC。
Testbench 则负责制造输入条件,例如产生时钟、复位、总线访问、随机数据,并检查 DUT 输出是否符合预期。
Verilator 很经典的一种使用方式,是使用 C++ 编写 Testbench。
例如:
Vcounter dut;
dut.rst_n = 0;
dut.clk = 0;
dut.eval();
dut.clk = 1;
dut.eval();
这里最关键的是:
dut.eval();
它可以粗略理解为:
让数字电路根据当前输入状态重新计算一次。
假设:
clk:0 → 1
调用 eval() 后,Verilator 会识别出时钟上升沿,并执行与:
@(posedge clk)
有关的时序逻辑。
因此,软件中的一次函数调用,就对应了硬件模型的一次状态更新。
四、没有真实晶振,时钟从哪里来?
真实 FPGA 或 ASIC 中,时钟通常来自晶振、PLL 或其他时钟源。
软件仿真环境中不存在真实电信号,因此 Testbench 需要主动制造时钟。
最简单的方式就是:
clk = 0;
eval();
clk = 1;
eval();
clk = 0;
eval();
clk = 1;
eval();
于是形成:
┌─────┐ ┌─────┐
──────┘ └─────┘ └──────
↑ ↑
上升沿 上升沿
每出现一次上升沿,DUT 中相应的时序电路便会执行。
因此,从某种意义上说,Testbench 不仅是“测试代码”,它还承担了整个虚拟硬件世界的外部环境。
五、一次完整的 Verilator 仿真过程
典型流程可以概括为:
编写 RTL
↓
编写 Testbench
↓
Verilator 解析 HDL
↓
语法与静态检查
↓
生成 C++
↓
C++ 编译
↓
链接生成可执行文件
↓
运行仿真
↓
检查结果
↓
生成并查看波形
例如已有:
counter.sv
sim_main.cpp
可以执行:
verilator --cc counter.sv --exe sim_main.cpp --build
其中:
--cc
表示将 RTL 转换成 C++ 模型。
Verilator 通常会生成一个 obj_dir 目录,其中包含诸如:
Vcounter.cpp
Vcounter.h
Vcounter.mk
等文件。
其中 Vcounter 可以理解为 RTL 顶层模块对应的 C++ 类。
完成编译以后,就可以直接运行生成的仿真程序。
此时已经不再是 Verilator 在逐条“解释 HDL”,而是在执行真正的本机程序。
六、波形仍然非常重要
自动化测试能够判断 PASS 或 FAIL,但 FPGA 和数字芯片开发中,波形仍然是定位问题的重要工具。
Verilator 可以生成 VCD、FST 等波形文件,例如:
simulation.fst
再通过 GTKWave 等工具查看:
clk
rst_n
data_valid
state
address
data
这些内部信号。
于是整个过程变成:
Verilator
↓
VCD / FST
↓
GTKWave
↓
查看数字波形
当出现状态机跳转错误、握手时序异常、计数器偏差或者数据错位时,波形通常能够直观暴露原因。
七、Verilator 真正重要的价值:把硬件测试“软件工程化”
如果仅仅把 Verilator 当成“另一个波形仿真器”,实际上低估了它。
它最大的价值之一,是能够让 RTL 开发采用很多成熟的软件工程方法。
例如测试一个 FIFO,可以编写自动测试:
向 FIFO 写入一万个随机数据;
随机插入读操作;
建立一个软件参考队列;
实时比较 RTL 输出与软件模型;
任何一个数据不一致立即报错。
最终测试结果可能只有:
100000 tests passed
0 tests failed
而不是工程师盯着数十万周期的波形逐个检查。
更进一步,还可以把它接入:
Git
↓
CI
↓
Verilator
↓
自动仿真
↓
PASS / FAIL
每次提交 RTL 以后自动运行几百甚至几千个测试用例。
这样一来,数字硬件开发就拥有了与现代软件开发非常类似的:
单元测试、自动化测试、持续集成和回归测试能力。
八、与“黄金模型”结合尤其强大
复杂数字算法通常可以先建立一个参考实现,例如使用:
Python、C++ 或 MATLAB。
这个软件参考模型通常被称为 Golden Model,即“黄金模型”。
整个验证过程可以变成:
相同输入
│
┌────────┴────────┐
↓ ↓
Python/C++模型 RTL设计
↓ ↓
参考结果 Verilator
↓ ↓
└────────┬────────┘
↓
比较
↓
PASS / FAIL
这对于:
数字信号处理;
视频处理;
通信算法;
AI 加速器;
CPU 指令执行;
密码算法;
数据压缩;
网络协议处理
等设计特别有价值。
工程师不需要人工判断“这个结果看起来是不是差不多”,而可以进行严格的逐样本、逐周期甚至逐比特比较。
九、Verilator 与综合工具完全不是一回事
这是初学者非常容易混淆的一点。
Verilator 做的是:
SystemVerilog
↓
C++模型
↓
软件仿真
而 Vivado、Quartus 等 FPGA 工具做的是:
SystemVerilog
↓
综合
↓
逻辑网表
↓
布局布线
↓
配置文件
↓
FPGA
所以 Verilator 不负责把设计真正变成 FPGA 内部的 LUT、寄存器、RAM 和布线资源。
它主要回答:
逻辑功能是否正确?
综合和实现工具则进一步回答:
这段逻辑如何映射成真实硬件?
以及:
它能否在目标时钟频率下正确运行?
二者是互补关系,而不是替代关系。
十、Verilator 也不是万能的
Verilator 的优势集中在:
RTL 功能验证、高速仿真、大规模回归、软硬件协同验证以及自动化测试。
但如果问题变成:
布线延迟是多少?
某条路径是否违反建立时间?
实际 FPGA 内部的时钟偏斜是多少?
特定工艺库中的门延迟是多少?
那么这些已经进入综合后时序分析或者门级仿真的范畴。
这类问题通常需要 FPGA 厂商工具、静态时序分析工具或者专业 EDA 仿真器完成。
因此,更合理的工程流程不是“Verilator 还是传统仿真器二选一”,而是:
RTL设计
↓
Lint
↓
Verilator功能验证
↓
自动化回归测试
↓
综合
↓
静态时序分析
↓
布局布线
↓
必要时进行更精细仿真
↓
真实硬件验证
不同工具分别解决不同层面的问题。
十一、现代芯片开发为什么越来越重视 Verilator
数字设计规模不断扩大以后,传统的“写 RTL—跑一下仿真—人工看波形”已经越来越难以支撑复杂系统开发。
一个现代数字系统可能包含:
数百个模块;
数万乃至数百万行代码;
大量总线接口;
多个处理器;
复杂状态机;
大量并发逻辑。
此时真正重要的已经不是“能不能仿真”,而是:
能不能自动、快速、重复地验证。
Verilator 的价值正体现在这里。
它把 RTL 转换成高速软件模型,使硬件设计可以借用现代软件行业已经成熟几十年的工程体系,包括自动化构建、单元测试、持续集成、随机测试和大规模回归测试。
从这个角度来看,Verilator 并不仅仅是一款 HDL 仿真器。
它实际上代表了一种越来越重要的数字硬件开发理念:
把硬件描述语言当成可以持续编译、持续测试和持续验证的工程代码。
当数字系统规模越来越大,这种方法的重要性也会越来越高。