verilog testbench中initial块和实例化模块的执行顺序问题

   在编写testbench做简单的模块功能验证时,initial初始化的过程块和模块的实例化是常见的两个块,这两者的执行顺序一般来说认为是并行执行的,但是笔者在实际仿真过程中发现并不是这样的,现举例记录和说明如下。

实例

功能仿真环境
  • Modelsim SE-64 2020.4
仿真代码

   如下所示,待验证模块的功能为一个输入尺寸为384,单个输入数据的位宽为7的循环左移器,通过其他高级语言生成对应的输入输出数据存入txt文件中,在testbench中读入以进行验证,为纯组合逻辑。

`timescale 1 ns / 1ns
module barrel_shifter_tb;
    parameter LIFT_SIZE     = 384;
    parameter STAGE_NUM     = $clog2(LIFT_SIZE) + 1;  // ceil(log2(384)) = 9, 9 + 1 = 10
    parameter SHIFT_WIDTH   = STAGE_NUM - 1;  // 9
    parameter MSG_WIDTH     = 7;
    parameter DATA_WIDTH    = LIFT_SIZE * MSG_WIDTH;  // 384 * 7 = 2688
    parameter MAX_APP_VALUE = 2 ** MSG_WIDTH - 1;

    parameter MAX_TEST_CASES = 20;
    
    
    reg  [DATA_WIDTH - 1 : 0]  app_msg_in;
    reg  [SHIFT_WIDTH - 1 : 0] shift_factor;
    wire [DATA_WIDTH - 1 : 0]  app_msg_out;
    reg  [DATA_WIDTH - 1 : 0]  app_msg_out_valid;

    reg  [DATA_WIDTH - 1 : 0]  dataInMem[0:(MAX_TEST_CASES - 1)];
    reg  [DATA_WIDTH - 1 : 0]  dataOutMem[0:(MAX_TEST_CASES - 1)];
    reg  [SHIFT_WIDTH - 1 : 0] shiftFacMem[0:(MAX_TEST_CASES - 1)];
    
    integer i;
    integer errCnt;
    
    barrel_shifter u_barrel_shifter(
        .app_msg_in(app_msg_in),
        .shift_factor(shift_factor),
        .app_msg_out(app_msg_out)
    );    
    
    initial begin
        errCnt = 0;
        $readmemb("./sim/dataIn.txt", dataInMem);
        $readmemb("./sim/dataOut.txt", dataOutMem);
        $readmemb("./sim/shiftFactors.txt", shiftFacMem);

        for (i = 0; i < MAX_TEST_CASES; i = i + 1) begin
            app_msg_in = dataInMem[i];
            shift_factor = shiftFacMem[i];
            app_msg_out_valid = dataOutMem[i];

            checkOutputs;
            #10;
        end

        if (errCnt) begin
            $display("********** TOTAL ERRORS: %d **********\n", errCnt);
        end
        else begin
            $display("********** NO ERRORS! **********\n");
        end
        $stop;
    end // inital

    task checkOutputs;
        begin
            if (app_msg_out != app_msg_out_valid) begin
                error;
            end
        end
    endtask

    task error;
        begin
            errCnt = errCnt + 1;
            $display("ERROR AT %d: expected -> %h, get -> %h", i, app_msg_out_valid, app_msg_out);
        end
    endtask
    
endmodule
仿真结果

  功能仿真的波形结果如下所示。不难看出,波形的结果是与预期相符合的。
在这里插入图片描述

但是,从上图中的红色方框中可以看出,testbench中是检测到了错误存在的,并且观察打印的模块输出和预期输出,会发现存在“错位”的现象,如检查第3个输出时,预期输出是读取到的第三个输出,但是testbench中比较的模块输出却是第二个输出,从结果上就像是纯组合逻辑的循环移位器的输出有“一级寄存器”,但这显然是不可能的,这是怎么一回事儿呢?
   询问一些大佬之后,最终得到的结论(猜测)是,initial块和模块实例化在EDA工具仿真执行时其实还是存在一些逻辑上的先后顺序的,不能完完全全地当作并行来看。在这一假设的前提下,前面的现象就可以解释了,即相当于initial块中的输入用存储器中的数据初始化后,实例化的模块“经过一段时间”后才产生输出,而不是initial块中的输入一改变实例化模块的输出就立马发生变化并传递到了initial块中,因此在实例化模块产生输出“迟滞”的这一段时间内,initial块进行了输出检查,自然就是检查的实例化模块上一次的输出结果了,但根据initial块中顺序执行的规则,这时候的预期结果是本次的预期输出结果,这细微的差异就导致了仿真结果的异常。

解决方案

   由于这是EDA仿真原理的问题,因此需要咱们在平时仿真时自行注意并采取对应的救急方案,笔者这里给出一种解决方案,即类似于寄存器的保持时间和建立时间,既然输出有“迟滞”,那就等一会儿,如下所示,就可以得到预期结果了~

……
for (i = 0; i < MAX_TEST_CASES; i = i + 1) begin
            app_msg_in = dataInMem[i];
            shift_factor = shiftFacMem[i];
            app_msg_out_valid = dataOutMem[i];

            #5 checkOutputs;
            #10;
end
        ……
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值