Icarus Verilog¶
简单了解Verilog语法基础和部分写法后, 我们就可以尝试编写一些简单的 Verilog 代码了.
前面我们也了解到, Verilog 是一种硬件描述语言, 因此其不像普通的编程语言, 可以直接通过编译或解释直接执行并验证程序行为是否符合预期, 虽然二者有共通之处, 但其还是有一套自己的验证方法, 我们一般称其为仿真.
Abstract
工具¶
| 工具 | 作用 |
|---|---|
iverilog | 读入 .v, 预处理 / 编译 / 精化 (elaboration), 默认生成 vvp 可执行中间文件 |
vvp | Verilog Virtual Processor, 执行仿真 |
gtkwave / surfer | 打开 VCD / FST 等波形文件, 对照信号时序排查错误 |
安装¶
# Arch Linux
sudo pacman -S iverilog gtkwave
# Debian / Ubuntu
sudo apt install iverilog gtkwave
# macOS (Homebrew)
brew install icarus-verilog gtkwave
仿真示例¶
以HDLBits 7458为例, 对应芯片逻辑: 两组「与门 + 或门」组合.
Verilog 代码如下:
module top_module (
input p1a, p1b, p1c, p1d, p1e, p1f,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y
);
assign p1y = (p1a & p1b & p1c) | (p1d & p1e & p1f);
assign p2y = (p2a & p2b) | (p2c & p2d);
endmodule
仅有DUT(Design Under Test, 被测设计)时, iverilog 能编译, 但没有激励与观察, 无法验证正确性. 需要编写旁路的testbench进行仿真验证.
Testbench¶
Testbench 通常具有以下特点:
-
无端口 (顶层仿真壳).
-
用
reg驱动 DUT 输入,wire接 DUT 输出. -
例化 DUT, 施加向量, 打印或自动比对.
-
(可选)
$dumpfile/$dumpvars导出波形.
以上面的 7458.v 为例, 尝试编写 testbench 7458_tb.v, 核心结构如下:
module tb_7458;
reg p1a, p1b, p1c, p1d, p1e, p1f;
reg p2a, p2b, p2c, p2d;
wire p1y, p2y;
integer errors;
top_module dut (
.p1a(p1a), .p1b(p1b), .p1c(p1c),
.p1d(p1d), .p1e(p1e), .p1f(p1f), .p1y(p1y),
.p2a(p2a), .p2b(p2b), .p2c(p2c), .p2d(p2d), .p2y(p2y)
);
initial begin
$dumpfile("7458.vcd");
$dumpvars(0, tb_7458); // 0 = 本模块及以下全部信号
end
// check task: 驱动输入 → #1 等待组合稳定 → 与 golden 比对
// ... 若干 check(...) 向量 ...
// $finish;
endmodule
Tip
组合逻辑在输入变化后可能有仿真延迟或同一时刻调度顺序问题. 施加输入后用 #1 (或更长) 再采样输出, 比紧贴赋值立刻读更稳妥. 四值比较用 !== / ===, 以便区分 x / z.
编译与运行¶
编译与运行命令如下:
# -g2012: 启用较新的 Verilog / SystemVerilog 子集 (本 TB 用了 automatic function/task)
# -Wall: 打开更多警告
iverilog -g2012 -Wall -o 7458.vvp 7458.v 7458_tb.v
vvp 7458.vvp
期望输出类似:
VCD info: dumpfile 7458.vcd opened for output.
PASS t=1 p1=000000 p2=0000 -> p1y=0 p2y=0
...
ALL TESTS PASSED
同时生成 7458.vcd (波形) 与 7458.vvp (编译产物). 二者属构建产物, 一般不进入版本控制系统.
多文件也可写进命令文件再使用 -c 参数引用:
# file_list.txt
7458.v
7458_tb.v
iverilog -g2012 -Wall -o 7458.vvp -c file_list.txt
若存在多个未被例化的模块 (多个候选 root), 用 -s 指定仿真顶层, 例如 -s tb_7458, 避免多余 root 一起被精化.
查看波形¶
gtkwave 7458.vcd &
操作要点:
-
左侧 SST 选中
tb_7458, 再进子 scopedut. -
将
p1a…p1y,p2a…p2y拖到 Signals. -
时间轴缩放, 对照某次
PASS/FAIL的$time看输入输出是否一致.
默认 VCD 兼容性最好. 若主要用 GTKWave, 可用更紧凑的 FST:
# testbench 里可写 $dumpfile("7458.fst");
vvp -fst 7458.vvp
gtkwave 7458.fst &
官方说明见 Viewing Waveforms.
调试思路¶
| 现象 | 常见原因 / 做法 |
|---|---|
| 编译报错找不到模块 | 源文件未列入 iverilog 命令行; 或模块名与例化不一致 |
| 多个 root / 仿真异常 | 用 -s tb_xxx 固定 testbench 为顶层 |
输出一直为 x | 输入未驱动; 或时序电路未复位 / 时钟未翻转 |
| 功能与预期不符 | 先看 $display 自检; 再在 GTKWave 对拍输入沿与输出变化 |
| 想看预处理结果 | iverilog -E -o out.v 7458.v 展开 `define / `include |
常用命令¶
iverilog -g2012 -Wall -o sim.vvp dut.v tb.v # 编译
vvp sim.vvp # 运行
vvp -fst sim.vvp # FST 波形
gtkwave dump.vcd # 看波形
iverilog -E -o preprocessed.v dut.v # 仅预处理
iverilog -s tb_7458 -o sim.vvp dut.v tb.v # 指定 root