Testbench关于$readmemh读取数据异常问题

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实在是太细了,笔者也只是半只鞋丢进了入门的门槛。记录下每个工程中碰到的问题,以供分析及复盘。如有问题,恳请各位批评指正。如有其它疑问可以留言询问。

        注:具体是什么原理导致的问题,我还没有思考出来,以后有想法后再进行补充。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值