【教程】windows环境下使用iverilog及qtkwave进行仿真
一、介绍
1.1 iverilog
Icarus Verilog是一个轻量、免费、开源的Verilog编译器,基于C++实现,开发者是 Stephen Williams ,遵循 GNU GPL license 许可证。简称iVerilog,是比较著名的开源HDL仿真工具。
iVerilog以编译器的形式工作,将以verilog编写的源代码编译为某种目标格式。如果要进行仿真的话,,它可以生成一个叫做vvp的中间格式,这个格式可以由其所附带的vvp命令执行。
iVerilog的安装文件中已经包含GTKWave支持Verilog/VHDL文件的编译和仿真,命令行操作方式,类似gcc编译器,通过testbench文件可以生成对应的仿真波形数据文件,通过自带的GTKWave可以查看仿真波形图,支持将Verilog转换为VHDL文件。
1.2 gtkwave
wave viewer,可以用于查看标准的verilog VCD/EVCD,以及其他的一些格式的波形文件。
二、下载与安装
打开iVerilog官网下载:http://bleyer.org/icarus/
傻瓜式安装,一直下一步且勾选添加环境变量即可
三、基本参数介绍
启动cmd
输入以下命令测试是否安装成功,成功则输出对应可执行文件所在路径
where iverilog
where vvp
where gtkwave
Icarus Verilog编译器主要包含3个工具:
iverilog:用于编译verilog和vhdl文件,进行语法检查,生成可执行文件
vvp:根据可执行文件,生成仿真波形文件
gtkwave:用于打开仿真波形文件,图形化显示波形
在cmd终端输入iverilog回车,可以看到常用参数使用方法的简单介绍,如下图所示:
-
参数 -o
- 较常用的一个参数,和GCC中-o的使用几乎一样,用于指定生成文件的名称;
- 如果不指定,默认生成文件名为a.out;
iverilog -o test test.v
-
参数 -y
- 用于指定包含文件夹;
- 如果verilog中调用了其他模块,直接编译会提示 Unknown module
-
如test_led.v在E:\xilinx\data\unsim下,且需要该目录下的其他module,则:
-
iverilog -y E:\xilinx\data\unsim test_led.v
-
详细的使用方法可以查看官方用户指南:https://iverilog.fandom.com/wiki/User_Guide
-
参数 -I
- 如果程序使用include语句包含了头文件路径,可以通过-I参数指定文件路径,使用方法和-y参数一致。
-
参数-tvhdl
- iverilog还支持把verilog文件转换为VHDL文件,如:
iverilog -tvhdl -o out_file.vhd test_led.v
四、快速上手
这里用一个使用差分输入连接IN_FIFO控制流水灯的示例做演示。
4.1 所需文件
设计文件 test_led.v
module led_demo (
input sys_clk_p, //system clock positive
input sys_clk_n, //system clock negative
input rst_n,
output [3:0] o_led
);
wire clk;
reg [7:0] ram_out;
IBUFDS ibufds_inst (
.I (sys_clk_p ), // 连接到差分正端
.IB (sys_clk_n ), // 连接到差分负端
.O (clk ) // 输出单端信号
);
////////////////////////////////////////////////////// led output
// reg [25:0] cnt_led;
reg [4:0] cnt_led; // for sim
reg [7:0] led;
wire flag_cnt_led;
assign flag_cnt_led = (cnt_led == 0);
always @ (posedge clk or negedge rst_n)
if (!rst_n)
begin
cnt_led <= 1'b0;
led <= 8'd1;
end
else
begin
cnt_led <= cnt_led + 1'b1;
if ((led==8'd1) || (led==8'd2) || (led==8'd4) || (led==8'd8) || (led==8'd16) || (led==8'd32) || (led==8'd64) || (led==8'd128))
led <= flag_cnt_led ? {led[6:0], led[7]} : led;
else
led <= 8'd1;
end
assign o_led = ~{|ram_out[7:6], |ram_out[5:4], |ram_out[3:2], |ram_out[1:0]};
////////////////////////////////////////////////////// ram control
// reg [5:0] addr;
reg wren, rden;
reg [3:0] wrdata;
reg [7:0] wrdata_buf, rddata_buf;
wire [7:0] rddata;
wire [7:0] q0, q1, q2, q3, q4, q5, q6, q7, q8, q9;
wire almostempty, almostfull;
wire empty, full;
wire [3:0] d0, d1, d2, d3, d4, d7, d8, d9;
wire [7:0] d5, d6;
assign d1 = d0;
assign d2 = d0;
assign d3 = d0;
assign d4 = d0;
assign d5 = {~d0[3:0], d0[3:0]};
assign d6 = {~d0[3:0], d0[3:0]};
assign d7 = d0;
assign d8 = d0;
assign d9 = d0;
assign d0 = wrdata;
assign rddata = q9;
reg [31:0] rst_n_d = 32'b0;
always @ (posedge clk) rst_n_d <= {rst_n_d[30:0], rst_n};
always @ (posedge clk or negedge rst_n_d[31])
if (!rst_n_d[31])
begin
wren <= 1'b0;
rden <= 1'b0;
wrdata_buf <= 8'd0;
wrdata <= 4'b0;
rddata_buf <= 8'd0;
ram_out <= 8'd0;
end
else
case (cnt_led)
0 :
begin
wren <= 1'b1;
rden <= 1'b0;
wrdata_buf <= led;
wrdata <= led[3:0];
ram_out <= rddata_buf;
end
1 :
begin
wren <= 1'b1;
rden <= 1'b0;
wrdata <= wrdata_buf[7:4];
end
2,3,4,5,6 :
begin
wren <= 1'b0;
rden <= 1'b0;
end
7 :
begin
wren <= 1'b0;
rden <= 1'b1;
end
8 :
begin
wren <= 1'b0;
rden <= 1'b0;
rddata_buf <= rddata;
end
default :
begin
wren <= 1'b0;
rden <= 1'b0;
ram_out <= rddata_buf;
end
endcase
IN_FIFO #(
.ALMOST_EMPTY_VALUE(1), // Almost empty offset (1-2)
.ALMOST_FULL_VALUE(1), // Almost full offset (1-2)
.ARRAY_MODE("ARRAY_MODE_4_X_8"), // ARRAY_MODE_4_X_8, ARRAY_MODE_4_X_4
.SYNCHRONOUS_MODE("FALSE") // Clock synchronous (FALSE)
) IN_FIFO_inst (
// FIFO Status Flags: 1-bit (each) output: Flags and other FIFO status outputs
.ALMOSTEMPTY(almostempty), // 1-bit output: Almost empty
.ALMOSTFULL(almostfull), // 1-bit output: Almost full
.EMPTY(empty), // 1-bit output: Empty
.FULL(full), // 1-bit output: Full
// Q0-Q9: 8-bit (each) output: FIFO Outputs
.Q0(q0), // 8-bit output: Channel 0
.Q1(q1), // 8-bit output: Channel 1
.Q2(q2), // 8-bit output: Channel 2
.Q3(q3), // 8-bit output: Channel 3
.Q4(q4), // 8-bit output: Channel 4
.Q5(q5), // 8-bit output: Channel 5
.Q6(q6), // 8-bit output: Channel 6
.Q7(q7), // 8-bit output: Channel 7
.Q8(q8), // 8-bit output: Channel 8
.Q9(q9), // 8-bit output: Channel 9
// D0-D9: 4-bit (each) input: FIFO inputs
.D0(d0), // 4-bit input: Channel 0
.D1(d1), // 4-bit input: Channel 1
.D2(d2), // 4-bit input: Channel 2
.D3(d3), // 4-bit input: Channel 3
.D4(d4), // 4-bit input: Channel 4
.D5(d5), // 8-bit input: Channel 5
.D6(d6), // 8-bit input: Channel 6
.D7(d7), // 4-bit input: Channel 7
.D8(d8), // 4-bit input: Channel 8
.D9(d9), // 4-bit input: Channel 9
// FIFO Control Signals: 1-bit (each) input: Clocks, Resets and Enables
.RDCLK(clk), // 1-bit input: Read clock
.RDEN(rden), // 1-bit input: Read enable
.RESET(~rst_n), // 1-bit input: Reset
.WRCLK(clk), // 1-bit input: Write clock
.WREN(wren) // 1-bit input: Write enable
);
endmodule
仿真testbench文件 test_led_tb.v:
module top_tb;
reg sys_clk_p, rst_n;
wire sys_clk_n;
wire [3:0] o_led;
led_demo DUT (.sys_clk_p(sys_clk_p), .sys_clk_n(sys_clk_n), .rst_n(rst_n), .o_led(o_led));
/*iverilog */
initial
begin
$dumpfile("top_tb.vcd"); //生成的vcd文件名称
$dumpvars(0, top_tb); //tb模块名称
end
/*iverilog */
initial
begin
sys_clk_p = 1;
rst_n = 0;
#200.1
rst_n = 1;
#15_000 $stop;
end
always #2.5 sys_clk_p = ~sys_clk_p;
assign sys_clk_n = ~sys_clk_p;
endmodule
这里需要注意一下,正常的testbench是不需要手动写出vcd文件的,这里为了能够使用gtkwave可视化波形图,需要加入一下代码:
/*iverilog */
initial
begin
$dumpfile("top_tb.vcd"); //生成的vcd文件名称
$dumpvars(0, top_tb); //tb模块名称
end
/*iverilog */
4.2 编译
这里由于verilog中引入了IBUFDS与IN_FIFO,因此需要导入库文件。
我将库文件放在了C:\Users\DELL\Desktop\test_iverilog\techmap下
执行命令,生成可执行文件led
iverilog -y C:\Users\DELL\Desktop\test_iverilog\techmap -o led test_led_tb.v test_led.v
4.3 生成波形文件
确保编译成功后,输入一下命令生成.vcd格式的波形文件
vvp -n led -lxt2
4.4 打开波形文件
波形文件输出成功后,使用gtkwave可视化
gtkwave top_tb.vcd
双击信号即可将其加入波形图中进行观察