文章目录
-
- 前言
- 准备
- [安装 Visual Studio Code](#安装 Visual Studio Code)
- [安装 Icarus Verilog](#安装 Icarus Verilog)
- [安装 Digital IDE 扩展](#安装 Digital IDE 扩展)
- [配置 Digital IDE 扩展](#配置 Digital IDE 扩展)
- 在代码中建立信号转储
- 执行编译和仿真
- 常见问题
前言
我校计算机组成原理课程的实验会用到 Verilog 语言,学校建议使用 Modelsim 软件作为实验工具。但是在实践中我们发现,Modelsim 占用空间大,UI不太友好,且难以上手。一个更重要的原因是,除非是电子信息工程专业,或者打算将来从事计算机底层研究的,很多人通过这门课之后,未来很长一段时间不会再使用这一工具,不值得对一个完全陌生的新工具花费大量学习成本,部分人希望能在 Visual Studio Code 中集成 Verilog 支持,实现一个相对轻量化的 Verilog 编译和仿真环境。
本教程基于 Windows 平台(x64),工具下载链接均可在国内网络环境下访问。**如果你不了解本教程提到的一些工具,请不要轻易跳过某些步骤,这些很可能是未知错误和危险的根源。因过度自信而不遵照本教程所造成的损害,教程制作者概不负责。**作为一个教程,我无法直接再分发本教程中使用到的安装包等工具,因为它们通常具有商业许可证,不允许公开再分发。但我可以提供下载链接,你可以点击链接前往官网下载,或联系学校请求内部分发(如果有)。
准备
- Visual Studio Code(代码编辑器,以下简称VSCode)
- Digital IDE(VSCode的一个扩展,以下简称DIDE)
- Icarus Verilog(编译和仿真工具,以下简称iVerilog)
- GTKWave(信号波形图渲染工具,以下简称gwave)
值得注意的是,Digital IDE 它不仅支持 iVerilog,它还支持更高级的软件如 Vivado,Modelsim 等,我们的初衷是构建一个轻量化的 iVerilog 环境,所以我们当然是不会使用其他工具的。不过,如果你仅仅是不满 Modelsim 的 UI 和操作,但承认 Modelsim 的仿真实力的话,你确实可以尝试使用 VSCode + DIDE + Modelsim 的组合。本教程不涉及它们,但是让 DIDE 调用 Modelsim ,和 DIDE 调用 iVerilog 的配置方法是差不多的,你可以参考。
安装 Visual Studio Code
(已经完成的可以直接跳转到安装DIDE(#安装 Digital IDE 扩展))
- 前往官网下载 VSCode 安装包。
VSCode 官网下载 - 打开安装包,依照提示安装 VSCode。
安装 Icarus Verilog
-
前往官网,下载最新版 iVerilog 安装包。
-
运行安装包,指定安装路径。
请注意,不要把 iVerilog 安装在含有空格的目录下,否则 DIDE 扩展将无法调用 iVerilog !

-
在安装组件的页面,确保你选择了 "Full Installation",保证依赖环境和 gwave 都装上。

-
最后一个要强调的是,强烈建议勾选 Add executable folder(s) to the user PATH ,即将可执行文件所在文件夹加入到用户 PATH 环境变量,这使得你可以在终端(命令行)的任何路径下都可以直接调用 iverilog 和 gtkwave 这两个命令。虽然实际使用中你不需要亲自调用它们,但是 DIDE,它需要调用 iverilog 和 gtkwave 这两个命令,如果你这步照着做了,在配置 DIDE 扩展的时候你将获得极大的便利。

安装 Digital IDE 扩展
- 打开 Visual Studio Code,在左边栏找到"扩展"

- 搜索 Digital IDE 并安装,作者为sterben

配置 Digital IDE 扩展
- 找到 DIDE 扩展,点击齿轮,进入该扩展的设置

- 如果你完整地执行了前述安装注意事项,你只需要做一个最小更改,找到 Digital-ide › Function › Lsp › Linter › Verilog: Diagnostor 设置项,点击下拉菜单将其设置为 iverilog

- 至此,DIDE 扩展也配置好了。
在代码中建立信号转储
安装完成工具不等于我们就能够对 Verilog 程序进行波形仿真,我们需要在代码中将我们的各个元件的信号转储出去,让它们能够被工具所发现,我们的工具才能够制作波形图。
我们观察以下示例代码:
verilog
// adder1
module adder1(
input a,
input b,
input cin,
output sum,
output cout
);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
// tb_adder1
module tb_adder1;
reg a;
reg b;
reg cin;
wire sum;
wire cout;
adder1 uut (
.a(a),
.b(b),
.cin(cin),
.sum(sum),
.cout(cout)
);
initial begin
$dumpfile("tb_adder1.vcd"); // 定义转储文件名
$dumpvars(0, tb_adder1); // 选择转储信号来源
a = 0; b = 0; cin = 0; #10;
a = 0; b = 0; cin = 1; #10;
a = 0; b = 1; cin = 0; #10;
a = 0; b = 1; cin = 1; #10;
a = 1; b = 0; cin = 0; #10;
a = 1; b = 0; cin = 1; #10;
a = 1; b = 1; cin = 0; #10;
a = 1; b = 1; cin = 1; #10;
$finish; // 结束仿真信号转储
end
endmodule
以上是 1 位全加器及其测试模块(testbench),1 位全加器非常简单,我们重点观察 testbench 中的 initial 块:
verilog
initial begin
$dumpfile("tb_adder1.vcd"); // 定义转储文件名
$dumpvars(0, tb_adder1); // 选择转储信号来源
a = 0; b = 0; cin = 0; #10;
a = 0; b = 0; cin = 1; #10;
a = 0; b = 1; cin = 0; #10;
a = 0; b = 1; cin = 1; #10;
a = 1; b = 0; cin = 0; #10;
a = 1; b = 0; cin = 1; #10;
a = 1; b = 1; cin = 0; #10;
a = 1; b = 1; cin = 1; #10;
$finish; // 结束仿真信号转储
end
initial 块做了什么事情呢?它首先执行了一个系统命令 dumpfile ,它告诉解释器,从现在开始,我计划将信号输出到当前目录下的 tb_adder_1.vcd 的文件中,下一行告诉解释器,我期望解释器记录 tb_adder1 模块中所有声明的信号,通常我们进行波形仿真,我们都是使用 0 作为第一个参数来表示全部信号,第二个参数一般选择顶层模块,我们编写 verilog 的时候习惯将其命名为 top_module 或 tb_xxx,在本例中,我们的信号序列都编写在 tb_adder1 的模块中,所以我们写的就是 tb_adder1。随后每隔 10 个单位时间,a、b 两个加数和外来进位 cin 都会变化,1 位全加器只需要 8 例就能测试完全部功能,结束之后,我们调用系统命令 finish,结束信号转储,保存相应的 vcd 文件。
执行编译和仿真
当你打开一个 verilog 文件时,你或许会注意到有些模块上出现了内联的文字按钮(官方称之为 CodeLens )

我们刚刚的 tb_adder1 代码就是可以直接仿真的,点击 Simulate,你预期看到的画面是:

什么都没有,这是因为,虽然我们将所有信号都转储了,但是 DIDE 允许你只渲染你感兴趣的信号的波形,现在你尚未选择要渲染的波形,那当然什么都没渲染出来。你可以点击右侧的加号,选择顶层模块中的输入和输出,观察是否符合预期。

如果模块是由多个模块构成的,你甚至可以观察内部所有的中间信号的值,看到所有信号变化的过程。如果你认为波形的间隔不符合你的预期,你可以在 verilog 代码的开头中添加一句:
verilog
`timescale 1ps/1ps
// 它的含义是设定 1 单位的时间间隔为 1 ps,仿真最小精度为 1 ps
另外我们可以看到,我们的文件夹中出现了一些新的文件,其中 vcd 文件存储了信号波形数据,view 文件则是存储了一些华丽的渲染。

你可以自由探索扩展的其它功能。
常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| 无法仿真,没有产生相应的 vcd 文件 | 你的 verilog 代码中没有 $dumpfile 指令 | 在 initial begin 之后,仿真模拟之前,加上 $dumpfile("<文件名>.vcd"); |
| 已经修改过代码,但是波形还是旧的 | gtkwave 使用了缓存中的数据,且缓存没有被更新 | 删除旧的 vcd 和 view 文件重新点击仿真 |
| 仿真界面打开了,但是右侧信号栏也没有找到任何东西,没有我想要的信号 | 你的 verilog 代码中没有 $dumpvars 指令或者有但是漏掉了你想要的信号 | 确保 $dumpvars 指令的第一个参数为 0,第二个参数为元件的最顶层的模块 |
| 直接卡死,毫无输出 | 你的 verilog 代码中没有 $finish 指令,这会导致仿真一直在进行,且没有停止 | 在完成所有信号变化结束后,执行 $finish 指令 |
| 波形不完整 | 有些测试用例可能没被写入 vcd 文件 | 确保你的测试用例包裹在两个 dump 指令和 finish 指令之间 |
| 无法编译且终端有个奇怪的前缀和乱码 | 你大概率把 iVerilog 装在有空格的路径下了 | 重装iverilog |
| 无法编译,提示 Digital IDE 找不到 iverilog 或 gtkwave | 你大概率在安装 iVerilog 时忘记点击 Add to PATH 了 | 将 gtkwave 和 iVerilog 安装目录下的 bin 目录手动加入环境变量 PATH 或重装一遍 |