【教程】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

双击信号即可将其加入波形图中进行观察

posted @ 2024-03-22 16:32  小拳头呀  阅读(2777)  评论(0编辑  收藏  举报