iverilog+gtkwave+mingw64安装流水账
iverilog+gtkwave+mingw64安装流水账

iverilog+gtkwave+mingw64安装流水账

纯流水记录安装过程。

Step1. 下载iverilog+gtkwave的安装包。

链接:http://bleyer.org/icarus/

安装包会自动设置环境变量,安装过程点同意即可。

这一步搞完,iverilog.exe/vvp.exe/gtkwave.exe都可以使用了,但是iverilog-vpi暂时还不可用,下一步需要正确设置gcc编译器。

Step2. 下载mingw64免安装版本。

链接:https://sourceforge.net/projects/mingw-w64/files/

解压到D盘,然后手动添加环境变量PATH=”D:\mingw64\bin”.

通过命令窗口”gcc -v”确认安装状态,有信息则成功。

3. 建一个sample目录,准备仿真脚本sim.bat如下:

iverilog -o sim.vvp -s adder_tb adder.v adder_tb.v
iverilog-vpi -mingw=D:\mingw64 adder.c
vvp -M. -madder sim.vvp
echo "sim done"
pause
exit

查看波形脚本seewave.bat:

gtkwave wave.vcd

清除脚本clr.bat:

del *.vvp *.vcd *.out *.o *.vpi

准备c代码如下:

// adder.c
#include <vpi_user.h>

static int sum_compiletf(char *user_data)
{
	fprintf(stderr, "Yes, you compiled me\n");
	return 0;
}

static int sum (char *user_data)
{
	vpiHandle systfref, args_iter, argh;
	// typedef struct t_vpi_value s_vpi_value
	struct t_vpi_value argval;
	unsigned int value, value2;
	char res[1024];

	systfref = vpi_handle(vpiSysTfCall, NULL);
	args_iter = vpi_iterate(vpiArgument, systfref); // 迭代所有参数.

	argh = vpi_scan(args_iter); // 获取下一个参数
	argval.format = vpiIntVal; // 设定格式为int
	vpi_get_value(argh, &argval); // 获取参数值
	value = argval.value.integer;  // 读取获取到的参数值

	argh = vpi_scan(args_iter); // 获取下一个参数
	argval.format = vpiHexStrVal; // 以hex格式读入
	vpi_get_value(argh, &argval);
	sscanf(argval.value.str, "%x", &value2); // 将hex str格式读入的值转换为int

	argh = vpi_scan(args_iter); // 获取第三个参数
	argval.format = vpiHexStrVal; // 设置格式为hex str, verilog读取的时候会自动转换的.
	sprintf(res, "%x", value + value2); // 在C里计算两个值的和, 并将其转换为hex格式的字符串

	argval.value.str = res;
	vpi_put_value(argh, &argval, 0, vpiNoDelay); // 设置第三个参数的值

	vpi_put_value(systfref, &argval, 0, vpiNoDelay);
	vpi_free_object(args_iter);
	return 0;
}

// 注册 $sum
void sum_register() {
	s_vpi_systf_data tf_data;

	tf_data.type      = vpiSysTask; // 类型. 还有一个是SysFunc
	tf_data.tfname    = "$sum"; // 在verilog中调用的名称
	tf_data.calltf    = sum; // 被verilog调用时调用的函数
	tf_data.compiletf = sum_compiletf; // 被编译时调用的函数
	tf_data.sizetf    = 0; // 不知道
	tf_data.user_data = 0; // 不知道
	vpi_register_systf(&tf_data); // 注册
}

// 在这个函数数组里的函数会自动被调用.
void (*vlog_startup_routines[])() = {
	sum_register,
	0
};

tb代码如下:

// adder_tb.v
`timescale 1ns/1ns
module adder_tb();
	reg [3:0] a;
	reg [3:0] b;
	wire [7:0] c;

	reg clk,rst_n;

	integer i, n, nf;

	adder DUT (
		.clk(clk),
		.rst_n(rst_n),
		.a(a),
		.b(b),
		.c(c)
	);

	always begin
		#10 clk = 0;
		#10 clk = 1;
	end

	initial begin
		$display("=============================");
		$display("Tb start at here");
		rst_n = 1;
		n = 0;
		nf = 0;
		// $test($random%4,2);
		for (i = 0; i < 20; i++)
			test($urandom%5'b10000,$urandom%5'b10000);
		$display("%d total, %d fail", n, nf);
		$finish;
	end

	task test;
		input [3:0] in;
		input [3:0] in2;
		begin: test
			reg [7:0] e;
			a = in;
			b = in2;
            $sum(a,b,e); // HERE
			@(posedge clk);
			@(negedge clk);
			n = n + 1;
			if (c == e) begin
				$display("It works, %d + %d = %d", in, in2, e);
			end else begin
				nf = nf + 1;
				$display("opps %d + %d ~= %d, expect %d", in, in2, c, e);
			end
		end
	endtask

	initial begin
		$dumpfile("wave.vcd");
		$dumpvars;
	end
endmodule

dut代码如下:

//adder.v
module adder(clk, rst_n, a, b, c);
	input [3:0] a;
	input [3:0] b;
	output [7:0] c;
	input clk, rst_n;

	wire [3:0] a;
	wire [3:0] b;
	reg [7:0] c;

	always @(posedge clk or negedge rst_n) begin
		if (rst_n == 1'b0)
			c <= 8'b0;
		else
			c <= a+b;
	end
endmodule

最终可以看到的运行结果和波形如下:

附上过程中起到帮助的三则参考链接:

https://www.jb51.net/softjc/696089.html

https://zhuanlan.zhihu.com/p/148795858

https://www.pcyo.cn/linux/20181212/216.html

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注