仿真是FPGA验证很重要的一环,但是开始的时候由于我们的电路比较简单所以仿真文件的设计也相对简单。那么一旦系统复杂起来,输入的数据也复杂的话,可能就需要用到系统函数$readmemh和$readmemb了,它们可以读取文本的文件然后用来做激励信号等等,或者会用到task函数来循环操作某些赋值。下面给出一个带有$readmemh和task的简单仿真文件来具体说明下:
module read_test(
input clk,
input rst,
input [7:0] data_in,
output reg [7:0] data_out
);
always@(posedge clk)
if(!rst)
data_out<=8'd0;
else
data_out<=data_in;
endmodule
module read_test_tb();
reg clk;
reg rst;
reg [7:0] data_in;
wire [7:0] data_out;
reg [7:0] memory [3:0];
reg [1:0] n;
initial begin
$readmemh("./data.txt",memory);
clk=0;
rst=0;
#100 rst=1;
end
initial begin
#100;
ass_data();
end
always #10 clk=~clk;
always@(posedge clk)
if(!rst)
n<=2'd0;
else if(n==2'd3)
n<=2'd0;
else
n<=n+1'b1;
integer i;
task ass_data();
begin
for(i=0;i<100;i=i+1)begin
@ (posedge clk);
data_in=memory[n];
end
end
endtask
read_test read_test(
.clk(clk),
.rst(rst),
.data_in(data_in),
.data_out(data_out)
);
endmodule
来说明下这两个文件,read_test是一个简单的将输入信号直接进过触发器传递给输出,然后read_test_tb是读取txt文本中的数据,将其循环赋值给输入信号。直接来看modelsim的输出结果:
输入数据延时了一个周期到达了输出。
这里主要用到了$readmemh函数和task。
$readmemh主要用来读取文本数据,相应的数据为16进制的,数据和数据之间通过空格或者换行隔开,如下所示:
规定好数据的格式后,由于我们输入信号是8位的,那么对应文本的数据也是8位的,如果你的信号是4位或者16位等等,可以相应增加或者减少你的数据位宽。$readmemh的语法如下:
reg [7:0] memory [3:0];
$readmemh("./data.txt",memory);
两个参数,第一个是你文件目录的位置,modelsim中我用的相对目录,用绝对目录报错了,但是vivado我是用的绝对目录,相对目录似乎不行,这个可以注意下。第二个是你数据存放的一个存储器,前一个参数是你数据的位宽,后一个参数是你存储的深度。这样如果你想调用哪个数据就可以按照c语音中数组的调用来进行,例如:
memory[n];
有了数据我们就可以给输入信号赋值了,在某些仿真中,我们可以需要重复进行某个固定的操作,例如这里我们想在每个时钟上升沿进行赋值操作,那么task就可以很好的完成这个任务了,假设我们想要在时钟上升沿给出入信号循环赋一个数据,循环100次可以如下:
integer i;
task ass_data();
begin
for(i=0;i<100;i=i+1)begin
@ (posedge clk);
data_in=memory[n];
end
end
endtask
initial begin
#100;
ass_data();
end
这样是不是在initial语句里疯狂延迟一个周期然后复制再如此往复要来的简单呢
当然这只是一个很简单的仿真示例,但是通过它可以明白一个复杂输入信号应该怎么激励以及如何重复一些简单的操作等等。。。
示例虽小,五脏俱全