1、前言
笔者最近在进行DDR3方面的仿真验证工作,为了验证DDR是否读写正常,激励中产生八个通道的视频源数据,通过DDR初始信号、视频源场信号控制输入。
仿真时看起来输入的数据及相关波形都是正常的,但是这种验证只看输入是没有用处的,还是得看具体读数据的内容和时序。
由于采用了3片DDR,所以读取数据的位宽是384位,在modelsim对应的波形一个一个去比对,实在是过于费眼,也不好对比数据是否正常,效率过低。
所以采用 $fopen $fwrite $readmemh 这些系统函数,进行激励中的数据写入以及读出到文本中,再执行数据比对。
2、问题描述
笔者最初的思路是,定义两个文本,分布存放前一次和当次的读数据,在DDR每次读取数据完成后,进行对比。
具体操作方法是,利用DDR的 o_mem_rd_end 信号同时执行 两次$readmemh 操作,但是每一次读取出来的数据会出现一个384位宽的数据错误,百思不得其解。
reg [383:0] data_read0 [0:511];
reg [383:0] data_read1 [0:511];
always @(posedge app_clk) begin
if (o_mem_rd_end) begin
$readmemh ("read_data0.txt",data_read0,0,511);
$readmemh ("read_data1.txt",data_read1,0,511);
end
end
$readmemh 可用于 initial 以及always 中,乍一看真就是一句简单的从文件中读取十六进制的数据到仿真中而已,语法方面不会出错。
3、解决思路
由于这个 $readmemh 的错误,导致我所写的自动对比数据的逻辑中,用于比对两次数据的flag一直异常拉低,不好判断数据是否正常,还是得人工手动去放大波形比对数据。对于我这种追(lan)求(de)效(yi)率(pi)的人,是不可以接受的。
经过定位问题,反复验证后,发现最后还是上面一个代码块的问题,还是 $readmemh 的问题。
补充一点 $readmemh 的简单用法:
(1)$readmemh("<数据文件名>",<存储器名>);
(2)$readmemh("<数据文件名>",<存储器名>,<起始地址>);
(3)$readmemh("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);
经过反复的思考后,笔者判断可能是 o_mem_rd_end 这个触发信号多次使用,或者是一个时钟内同时执行两次 $readmemh 出现问题。
最后笔者将 o_mem_rd_end 信号打了两拍进行使用,并且先后进行读操作, $readmemh 数据则正常了。
always @(posedge app_clk) begin
if (o_mem_rd_end0_r) begin //使用打拍后的信号做判定条件
$readmemh ("read_data0.txt",data_read0,0,511);
end
else if (o_mem_rd_end0_rr) begin
$readmemh ("read_data1.txt",data_read1,0,511);
end
4、相关逻辑代码
由于将DDR中的数据读取出来,写入文本中,再从文本中读出来进行比对的逻辑不易于直接定位DDR读取数据错误的位置。
所以更换了逻辑进行测试,同样还是利用两个文本,每次读取数据时交替使用 $fwrite 存储数据。在第一次读取数据时,实时比对第二个文本里面存储的数据,输出比对结果;在第二次读取数据时,实时比对第一个文本里面存储的数据,输出比对结果;以此类推,通过乒乓操作的形式进行实时数据比对。
芜湖~舒坦!
就是在跑仿真时还得手动Zoom Full ,还有DDR3的官方ip核模型跑仿真实在是太慢了。
贴上相关的逻辑代码:
//将modelsim仿真的数据存入文本
wire rd_req_vio_r;
wire app_clk;
wire o_mem_rd_vld0;
wire o_mem_rd_end0;
wire [383:0] o_mem_rd_dat0;
reg [11:0 ] R_data_cnt;
reg rd_req_vio_r_r;
wire op_f;
reg op_count = 0;
reg op_r = 0;
wire oprr;
integer f_id;
always @(posedge app_clk) begin
rd_req_vio_r_r <= rd_req_vio_r;
end
assign op_f = rd_req_vio_r && ~rd_req_vio_r_r;
always @(posedge app_clk) begin
if (op_f && !op_count) begin
op_r <= 1'b1; //选择不同存储位置
op_count <= 1'b1;
end
else if (op_f && op_count) begin
op_r <= 1'b0; //选择不同存储位置
op_count <= 1'b0;
end
end
assign oprr = op_r;
//$fopen的具体路径我已经省去了,注意在这里路径需用反斜杠/ 而不是正常路径的\
always @(op_r) begin
if (op_r) begin
f_id = $fopen("/read_data0.txt","w"); //read_data0.txt
end
else if (!op_r) begin
f_id = $fopen("/read_data1.txt","w"); //read_data1.txt
end
end
always @(posedge app_clk)
begin
if (rd_req_vio_r) begin
if (o_mem_rd_vld0) begin
$fwrite(f_id,"%h\n",o_mem_rd_dat0[383:0]); //写入数据到f_id中
end
else if (o_mem_rd_end0) begin
$fclose(f_id);
end
end
end
//缓存后 判定跟前一次数据是否相等
reg [383:0] data_read0 [0:511];
reg [383:0] data_read1 [0:511];
wire [383:0] data_read0r;
wire [383:0] data_read1r;
reg [9:0] count_data = 0;
reg data_flag;
reg [7:0] data_count = 0;
reg o_mem_rd_end0_r;
reg o_mem_rd_end0_rr;
wire o_mem_rd_end0_rrr;
wire op_a;//打开第一个文本
wire op_b;//打开第二个文本
reg op_rr;
always @(posedge app_clk) begin
op_rr <= op_r;
end
assign op_b = ~op_rr && op_r;
assign op_a = op_rr && ~op_r;
always @(posedge app_clk) begin
if (op_b) begin
$readmemh ("read_data1.txt",data_read1,0,447); //读取文件夹中 read_data1 的数据内容
end
else if (op_a) begin
$readmemh ("read_data0.txt",data_read0,0,447); //读取文件夹中 read_data0 的数据内容
end
end
always @(posedge app_clk) begin
if (o_mem_rd_vld0 && op_r) begin
if (data_read1[count_data] == o_mem_rd_dat0) begin //对比数据是否相同
data_flag <= 1'b1;
end
else begin
data_flag <= 1'b0;
end
end
else if (o_mem_rd_vld0 && !op_r) begin
if (data_read0[count_data] == o_mem_rd_dat0) begin //对比数据是否相同
data_flag <= 1'b1;
end
else begin
data_flag <= 1'b0;
end
end
end
always @(posedge app_clk) begin
if (op_a || op_b) begin //每次数据读取完毕后开始判定
count_data <= 1'd0;
end
else if (count_data == 447) begin //计数器 判定448个384的数据内容是否相同
count_data <= count_data;
end
else if (o_mem_rd_vld0 && (count_data >= 0) && (count_data < 447)) begin
count_data <= count_data + 1'd1;
end
end
always @(posedge app_clk) begin
if (op_a || op_b) begin
data_count <= data_count + 1'd1;
end
else begin
data_count <= data_count;
end
end
assign data_read0r = data_read0[count_data];
assign data_read1r = data_read1[count_data];
5、后言
FPGA实在是太细了,笔者也只是半只鞋丢进了入门的门槛。记录下每个工程中碰到的问题,以供分析及复盘。如有问题,恳请各位批评指正。如有其它疑问可以留言询问。文章来源:https://www.toymoban.com/news/detail-429134.html
注:具体是什么原理导致的问题,我还没有思考出来,以后有想法后再进行补充。文章来源地址https://www.toymoban.com/news/detail-429134.html
到了这里,关于Testbench关于$readmemh读取数据异常问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!