Icarus Verilog(以下简称iverilog )号称“全球第四大”数字芯片仿真器,也是一个完全开源的仿真器。由于Synopsys、Cadence、Mentor版权的关系,国外很多高校在数字芯片设计的教学中都采用iverilog。
GTKWave是一个开源的波形文件察看工具,支持Verilog VCD/EVCD文件格式。因此,通过“iverilog gtkwave”的方式,可以很方便地实现商用仿真器的功能。
本文为我的学生整理自 iverilog 和 gtkwave 官方网站。
介绍
iverilog
Icarus Verilog是一个verilog仿真工具. 以编译器的形式工作, 将以verilog编写的源代码编译为某种目标格式. 如果要进行仿真的话, 它可以生成一个叫做vvp的中间格式. 这个格式可以由其所附带的vvp命令执行.
gtkwave
wave viewer. 可以用于查看标准的verilog VCD/EVCD, 以及其他的一些格式的波形文件。
安装
iverilog
- windows http://bleyer.org/icarus/(扫描下方二维码下载安装包)
- linux a. 使用自带的包管理器下载:sudo pacman -S iverilog b. 从github上clone源码然后编译: https://github.com/steveicarus/iverilog
gtkwave
- windows https://sourceforge.net/projects/gtkwave/files/
- linux a. 包管理器安装下载:sudo pacman -S gtkwave b. 从上面的链接下载源码, 然后编译
使用
1. 示例:
代码语言:javascript复制 1// adder_rtl.v
2module adder(clk, rst_n, a, b, c);
3 input [3:0] a;
4 input [3:0] b;
5 output [7:0] c;
6 input clk, rst_n;
7
8 wire [3:0] a;
9 wire [3:0] b;
10 wire [7:0] c;
11
12 always @(posedge clk or negedge rst_m) begin
13 if (rst_n == 1'b0)
14 c <= 8'b0;
15 else
16 c <= a b;
17 end
18endmodule
2. 编译:
代码语言:javascript复制1iverlog adder_rtl.v
抄错了. damn.
代码语言:javascript复制 1module adder(clk, rst_n, a, b, c);
2 input [3:0] a;
3 input [3:0] b;
4 output [7:0] c;
5 input clk, rst_n;
6
7 wire [3:0] a;
8 wire [3:0] b;
9 reg [7:0] c;
10
11 always @(posedge clk or negedge rst_n) begin
12 if (rst_n == 1'b0)
13 c <= 8'b0;
14 else
15 c <= a b;
16 end
17endmodule
3. 编译:
1iverlog adder_rtl.v
无事发生.
tb:
代码语言:javascript复制 1// adder_tb.v
2`timescale 1ns/1ns
3module adder_tb();
4 reg [3:0] a;
5 reg [3:0] b;
6 wire [7:0] c;
7
8 reg clk,rst_n;
9
10 adder DUT (
11 .clk(clk),
12 .rst_n(rst_n),
13 .a(a),
14 .b(b),
15 .c(c)
16 );
17
18 always begin
19 #10 clk = 0;
20 #10 clk = 1;
21 end
22
23 initial begin
24 rst_n = 1;
25 test(4'b1111, 4'b1111, 5'b11110);
26 $finish;
27 end
28 task test;
29 input [3:0] in;
30 input [3:0] in2;
31 input [7:0] e;
32 begin
33 a = in;
34 b = in2;
35 @(posedge clk);
36 @(negedge clk);
37 if (c == e) begin
38 $display("Itworks");
39 end else begin
40 $display("opps%d %d ~= %d, expect %d", in, in2, c, e);
41 end
42 end
43 endtask
44endmodule
4. 编译运行:
代码语言:javascript复制1iverilog adder_rtl.v adder_tb.v
5. 使用-o选项指定输出文件的名称
代码语言:javascript复制1iverilog adder_rtl.v adder_tb.v -oadder_test
6. 在tb中添加dump:
代码语言:javascript复制1 initial begin
2 $dumpfile("wave.vcd"); //指定用作dumpfile的文件
3 $dumpvars; //dump all vars
4 end
7. 重新编译运行一遍, 生成了一个vcd文件, 使用gtkwave查看.
iverilog的一些选项:
- -D: 定义宏
- -P: 覆盖root module中的一个参数的值
- -E: 只预处理(进行宏替换), 不编译
- -g1995, -g2001, -g2005 ...: 选择支持的verilog语言版本.
- -I includedir: 指定(添加)verilog中include指令的搜索路径
- -s topmodule : 指定要建立的顶层模块. 默认是没有被实例化的哪些module
VPI:
Verilog Prodecure Interface(VPI), 最开始也称作PLI 2.0, 一个主要面向C语言的接口. 可以让行为级别的Verilog代码调用C函数, 让C函数调用标准Verilog系统函数.
代码语言:javascript复制 1 // adder.c
2#include <vpi_user.h>
3
4static int sum_compiletf(char *user_data)
5{
6 fprintf(stderr, "Yes, youcompiled men");
7 return 0;
8}
9
10static int sum (char *user_data)
11{
12 vpiHandle systfref, args_iter,argh;
13 // typedef struct t_vpi_values_vpi_value
14 struct t_vpi_value argval;
15 unsigned int value, value2;
16 char res[1024];
17
18 systfref = vpi_handle(vpiSysTfCall,NULL);
19 args_iter =vpi_iterate(vpiArgument, systfref); // 迭代所有参数.
20
21 argh = vpi_scan(args_iter); // 获取下一个参数
22 argval.format = vpiIntVal; // 设定格式为int
23 vpi_get_value(argh, &argval); //获取参数值
24 value = argval.value.integer; // 读取获取到的参数值
25
26 argh = vpi_scan(args_iter); // 获取下一个参数
27 argval.format = vpiHexStrVal; // 以hex格式读入
28 vpi_get_value(argh, &argval);
29 sscanf(argval.value.str, "%x",&value2); // 将hex str格式读入的值转换为int
30
31 argh = vpi_scan(args_iter); // 获取第三个参数
32 argval.format = vpiHexStrVal; // 设置格式为hex str, verilog读取的时候会自动转换的.
33 sprintf(res, "%x", value value2); // 在C里计算两个值的和, 并将其转换为hex格式的字符串
34
35 argval.value.str = res;
36 vpi_put_value(argh, &argval, 0,vpiNoDelay); // 设置第三个参数的值
37
38 vpi_put_value(systfref,&argval, 0, vpiNoDelay);
39 vpi_free_object(args_iter);
40 return 0;
41}
42
43
44// 注册 $sum
45void sum_register() {
46 s_vpi_systf_data tf_data;
47
48 tf_data.type = vpiSysTask; // 类型. 还有一个是SysFunc
49 tf_data.tfname = "$sum"; // 在verilog中调用的名称
50 tf_data.calltf = sum; // 被verilog调用时调用的函数
51 tf_data.compiletf = sum_compiletf; //被编译时调用的函数
52 tf_data.sizetf = 0; // 不知道
53 tf_data.user_data = 0; // 不知道
54 vpi_register_systf(&tf_data); //注册
55}
56
57
58
59// 在这个函数数组里的函数会自动被调用.
60void (*vlog_startup_routines[])() = {
61 sum_register,
62 0
63};
修改tb来调用$sum:
代码语言:javascript复制 1 // adder_tb.v
2`timescale 1ns/1ns
3module adder_tb();
4 reg [3:0] a;
5 reg [3:0] b;
6 wire [7:0] c;
7
8 reg clk,rst_n;
9
10 integer i, n, nf;
11
12 adder DUT (
13 .clk(clk),
14 .rst_n(rst_n),
15 .a(a),
16 .b(b),
17 .c(c)
18 );
19
20 always begin
21 #10 clk = 0;
22 #10 clk = 1;
23 end
24
25 initial begin
26 $display("=============================");
27 $display("Tb start athere");
28 rst_n = 1;
29 n = 0;
30 nf = 0;
31 // $test($random%4,2);
32 for (i = 0; i < 20; i )
33 test($urandom%5'b10000,$urandom%5'b10000);
34 $display("%d total, �ail", n, nf);
35 $finish;
36 end
37 task test;
38 input [3:0] in;
39 input [3:0] in2;
40 begin: test
41 reg [7:0] e;
42 a = in;
43 b = in2;
44 $sum(a,b,e); // HERE
45 @(posedge clk);
46 @(negedge clk);
47 n = n 1;
48 if (c == e) begin
49 $display("Itworks, %d %d = %d", in, in2, e);
50 end else begin
51 nf = nf 1;
52 $display("opps%d %d ~= %d, expect %d", in, in2, c, e);
53 end
54 end
55 endtask
56
57 // initial begin
58 //$dumpfile("wave.vcd");
59 // $dumpvars;
60 // end
61endmodule
编译运行:
代码语言:javascript复制1iverilog adder_rtl.v adder_tb.v -oadder.vvp
2iverilog-vpi adder.c
3vvp -M. -madder adder.vvp
iverilog-vpi: 自带的帮助生成库的脚本 -M path: 将path加入定位VPI模块的路径, .: 当前路径 -m module: 告诉vvp在执行simulation之前加载指定的module.