FPGA测试文件testbench模块

测试文件testbench:是写输入激励的。模拟实际环境,从软件角度仿真进行分析与验证。

一、测试文件的书写流程:
定义时间标尺——定义信号类型——例化.v文件——编写输入驱动

二、固定写法:
1、时间标尺的格式:`timescale 仿真时间单位/时间精度,时间单位>=时间精度

`timescale 1ns/1ps 
#100//延迟100ns,跟时间单位有关
#100.01//延迟100.01ns,若改为`timescale 1ns/1ns 则错误,因为精度还是ns无法达到小数点后面

2、信号类型的定义:原模块输入在testbench中为wire,输出在testbench中为reg。
3、几种固定的书写:

//时钟激励,这种写法可以在例化的时候修改参数大小,而不改变代码本身,减少了错误率。
parameter CYCLE = 20;		//默认50MHz的信号,则脉冲周期T=1/f=20ns
initial begin
	clk = 0;
	forever
	#(CYCLE/2)
	clk = ~clk;
end
//修改参数例化例子,其中A和B分别为原程序中参数名,2与10为在例化模块中最终参数值
modulename #(.A(2),.B(10)) modulename1(
	例化语句;
);
//时钟激励另一种书写形式
`define clock_period 20 		//在时间标尺下面预编译——宏定义
initial begin
	clk = 0;
	always #(·clock_period /2)
	clk = ~clk;
end
//复位信号
initial begin
	rst_n = 1;
	#2;
	rst_n = 0;
	#(CYCLE*RST_TIME)		//延迟RST_TIME个脉冲周期
	rst_n = 1;
end
//输入激励赋值
initial begin
	#1;		//每个输入信号赋值前需延迟1个时间单位,由D触发器决定
	din0 = 0;
	#TIME;
	din0 = 1;
end

三、仿真激励常见写法

1、一种激励方式:

initial begin
a = 0;b = 0;
#200
a = 0;b = 1;
#200
a = 1;b = 0;
#200
a = 1;b = 1;
#200
$stop;	//停止仿真
end

2、repeat造30次占空比为1:5的激励:

//在使用repeat时,需要注意前面必须加initial begin…end ,除此在任务函数的设定时需要有 task myname;begin repeat() …end endtask

initial begin

repeat(30)begin
  cin = 1'b1;
  #`clock_period;
  cin = 1'b0;
  #(`clock_period*5);
  end
end

3、随机任务函数

<1> 随机函数:$ random 这一系统函数可以产生一个有符号的32bit随机整数。一般的用法是$ random%b,其中 b>0,这样就会生成一个范围在(-b+1):(b-1)中的随机数。如果只得到正数的随机数,可采用{$ random}%b。
tip:这里的32bit随机整数指的是$random为32位大小,而非b为32位大小。

//eg1:产生一个随机的数c
c=$random;

//eg2:产生一个[min,max]区间的随机数
 reg	[31:0]	rand;
 rand = min+{$random}%(max-min+1);

<2> 任务函数
task <任务名>;
<端口及数据 类型声明语句>
<语句 1>
<语句 2>
<语句 n>
endtask
tip:task只要里面的语句是可综合的,那么task就可综合!

//eg:后面7.8要调用这里的任务函数

reg [5:0]myrand;

task press_key;				//建任务函数press_key(相当于module用于testbench的调用)
begin		
repeat(50) begin 			// 重复五十次随机时间
	myrand = {$random}%25; 		//取一次myrand为-24~24的一个随机数
	#myrand key_in = ~key_in; 	// 延迟随机数myrand后,输入激励取反
	end
end  
endtask 

initial begin
	Rst_n = 1'b0;
	key_in = 1'b1;
	#(`clk_period*10) Rst_n = 1'b1;
	#30000;
	press_key; #10000;	//调用任务函数press_key
	press_key; #10000;
	$stop;
end 

4、一种仿真写法,内部信号产生输出

`timescale 1ns/1ns
module key_model(key);

output reg key;
reg [15:0]myrand;

initial begin
	key = 1'b1;
	press_key;
	#10000;
	press_key;
	#10000;
	press_key;
	$stop;
	end

task press_key;
	begin
	// 此部分同6(上面设计)
	end
endtask

endmodule

5、一种仿真写法——内部信号产生输入输出

`timescale 1ns/1ns
module key_model(press,key);
input press;
output reg key;

reg [15:0]myrand;

initial begin
	key = 1'b1;
	end
	
always@(posedge press)
	press_key;
	
task press_key;
	begin
	// 此部分同6(上面设计)
	end
	
endtask

endmodule


6、当激励需要自动增加或者循环多次的时候,可以使用for循环在测试文件中
当要求发送的报文中报文长度随机(有上下限),报文之间间隔为随机(上下限),信号概率丢失,重复发多个报文时,使用随机函数$random和for循环来做。
FPGA测试文件testbench模块_第1张图片

/*要求:
	1、报文长度在1~128字节随机
	2、报文间隔0~3随机
	3、报文只发送1
	4、一共发送100个报文
	5、sop与eop有1/8概率丢失,din_err有1/32概率为1(即错误报文)
*/			
	reg		[31:0]		len_rand;
	reg		[3:0]		sop_rand;
	reg		[3:0]		eop_rand;
	reg		[5:0]		err_rand;
	reg		[3:0]		delay_rand;

			for(i=0;i<100;i=i+1)begin
			
				len_rand 	= MIN + {$random} % (MAX - MIN + 1);
				sop_rand 	= {$random} % 8;
				eop_rand 	= {$random} % 8;
				err_rand 	= {$random} % 32;
				delay_rand 	= {$random} % 4;
				
				for(j=0;j

完整的报文测试代码
tip:注意随机函数$ random的位置,重点理解每出现一次就会随机产生一个数。

7、一个串行输入的8bit数据
要点1:当我们输入英文字符的时候,一个字符代表8bit数据。
那么我们输入一串字符的时候,如 str = woshishei 那么定义[8*9-1:0](每个字符8bit,共9个,若中间有空格,感叹号等,也按照8bit算)

要点2:在仿真文件testbench中,语法比较随意,生成激励常用initial语句,但是也可以用组合逻辑和时序逻辑生成。

//在测试文件testbench中写入串行输入数据
	wire		[143:0]		data_temp;
	reg		[143:0]		reg_temp;
	assign	data_temp = "lssjlwjyhgggqsqqqa";		//有18个英文字符,8*18=144
	always @ (posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			reg_temp <= 0;
		end
		else if(din_vld)begin
			reg_temp <= {reg_temp [135:0],reg_temp [143:136]}; 		//向左移位寄存器,位数多的向哪个方向移就是什么方向的移位寄存。
		end
		else begin
			reg_temp <= data_temp ;
		end
	end
	assign din = reg_temp[143:136];		//最后把高位的8bit数据传给输入激励din

8、仿真文件的并转串
下面的例子中task函数可不用,只是为了引出9,用了task

	//16位并行数据转换为串行数据
		input [15:0]	vdata;
		reg	[4:0] cnt;
		begin
			cnt = 0;
			wait(condition);	//等待某个条件到来,不到来时则停滞在此
			while(cnt<16)begin
				@(negedge clk) dout = vdata[15-cnt];
				cnt = cnt + 1;
			end
		end

9、仿真测试文件造rom

`define sin_data_file "./document_name.txt"		
`timescale 1ns/1ps	//把PC上txt格式的文件document_name给测试文件的sin_data_file
.............
	reg		[11:0]	memory	[4095:0];	//存储空间,4096(address)个12位的数据
	reg		[11:0]	address;	//存储地址
	
	initial $readmemh(`sin_data_file,memory); //readmemh 中的h(16进制)可以用b(2进制)替换
	
	initial begin
		address = 0;
		forever begin
			for(address=0;address<4095;address=address+1)begin	//从地址0取到地址4095
				@(posedge clk);		//等待clk上升沿到来,上升沿不到就停滞在这
					memory[address];
			end
		end
	end

tip! 1:把8中的程序封装成task dout文件,引:9中用dout(memory[address])代替memory[address]可直接得到rom中并转串的数据
2:仿真文件中,用@(posedge/negedge signal)可表沿触发
用wait(signal)可表信号电平触发
用信号 module.signal 可表示在某个例化模块中的某个信号(这个信号不用接端口即可表达)
用$display(“打印的数据=%0h”,signal),在仿真界面打印signal的值。

你可能感兴趣的:(FPGA,笔记,Verilog)