verilogsimulationramvivadotest-bench

Simulation contradiction using the same Vivado block ram IP


I got contradictory results when I tried to test my customized Vivado block ram IP in two different testbenches.

Ram Configuration

The generated ram is a piece of 16x255 True Dual Port Ram (Port A and Port B) with native interface. Primitives Output Registers and RST signals are available to both ports. In addition, there are no change in the operating modes. The port configurations and an IP summary are provided respectively in figure 1 & 2.

Figure 1. Port configurations:

Figure 1. Port configurations.

Figure 2. IP summary:

Figure 2. IP summary.

Testbench A

Testbench A is a simple and intuitive testbench where everything (control signals, input data and I/O addresses) is provided manually.

module tb_blk_mem_1();
    reg rst, clk, clkb;
    reg blk_mem_wen, blk_mem_ren;
    wire wrst_busy, rrst_busy;
    reg [7:0] waddr_0, raddr_0;
    reg [15:0] din_0;
    wire [15:0] dout_0;
    reg ena;
    
    always begin
        #10 clk = ~clk;
    end
    
    always begin
        #10 clkb = ~clkb;
    end 
    
    initial begin
        clk = 1; rst = 1; blk_mem_wen = 0; blk_mem_ren = 0; waddr_0 = 255; raddr_0 = 3; din_0 = 65535;clkb = 1; ena = 1;
        #40 rst = 0;
        #180 blk_mem_wen = 1; 
        #20 din_0 = 1; waddr_0 = 0;
        #20 blk_mem_wen = 1; waddr_0 = 1; din_0 = 2;
        #20 blk_mem_wen = 1; waddr_0 = 2; din_0 = 3;
        #20 blk_mem_wen = 1; waddr_0 = 3; din_0 = 4;
        #20 blk_mem_wen = 0;ena = 0;
        #200 blk_mem_ren = 1; raddr_0 = 0;
        #20 blk_mem_ren = 1; raddr_0 = 1;
        #20 blk_mem_ren = 1; raddr_0 = 2;
        #20 blk_mem_ren = 1; raddr_0 = 3;
        #20 raddr_0 = 0;
        #20 raddr_0 = 1;

    end
    
    blk_mem_gen_1 blk_mem_gen_1(
        .clka(clk),
        .wea(blk_mem_wen),
        .addra(waddr_0),
        .dina(din_0),
        .douta(),
        .ena(ena),
        .addrb(raddr_0),
        .dinb(),
        .rsta(rst),
        .doutb(dout_0),
        .rstb(rst),
        .enb(blk_mem_ren),
        .web(1'b0),
        .clkb(clkb),
        .rsta_busy(wrst_busy),
        .rstb_busy(rrst_busy)
    );



endmodule

Figure 3. Testbench A result:

Figure 3. Testbench A

According to figure 3, it can be realized that the block ram fails to return the data for the first time when an address is provided. Moreover, the further returned data is only delayed by 1 clock cycle rather than 2.

Testbench B

Testbench B is a lazier testing procedure which both addresses and data are provided automatically to the block ram. The addresses and data are generated in an accumulative manner.

module tb_blk_mem();
    wire [15:0] douta, doutb;
    reg clka, clkb;
    reg ena, enb;
    reg wea;
    reg rst;
    wire rsta_busy, rstb_busy;


    wire [15:0] dina;
    wire [7:0] addra, addrb;

    blk_mem_wr blk_mem_wr(
        .wr_clk(clka),
        .rst_busy(rsta_busy),
        .wr_data(dina),
        .wr_addr(addra),
        .wen(wea)
    );

    blk_mem_rd blk_mem_rd(
        .rd_clk(clkb),
        .ren(enb),
        .rst_busy(rstb_busy),
        .rd_addr(addrb)
    );


    blk_mem_gen_1 blk_mem_gen_1(
        .addra(addra),
        .addrb(addrb),
        .dina(dina),
        .dinb(),
        .clka(clka),
        .clkb(clkb),
        .ena(ena),
        .enb(enb),
        .wea(wea),
        .web(),
        .rsta(rst),
        .rstb(rst),
        .douta(),
        .doutb(doutb),
        .rsta_busy(rsta_busy),
        .rstb_busy(rstb_busy)
    );

    always begin
        #10 clka = !clka;
    end
    
    always begin
        #10 clkb = !clkb;
    end
    
    initial begin
        clka = 1; clkb = 1;rst = 1;ena = 1; enb = 0; wea = 0;
        #10 rst = 0; 
        #300 ena = 1'b1; wea = 1'b1;
        #400 wea = 1'b0;ena = 1'b0;
        #50 enb = 1;



        #400 $finish;
    end
endmodule

Figure 4. Testbench B result:

Figure 4. Testbench B: result.

Based on figure 4, the block ram works perfectly fine. Since 255 is apparently larger than 150 hence the legit "X".

Conclusion

In conclusion, I have two testbenches, which A) the IP ram is not working normally, and the testing result is against the IP information, while B) the IP ram is working well.

I tried to use a different block ram, but the problem remains. In addition, by comparing the two waveform diagrams (figure 3 & 4), the control logics are almost the same. Therefore, I expect that there are some mistakes in my testbenches which I clearly have not realized.

Comment related Figures:

  1. Corrected Image for Testbench A results corrected image for figure 3

  2. Requested Image: initial raddr replaced requested image with initial raddr replaced with 255


Solution

  • I am able to reproduce your issue. The issue is caused by the input that are being provided by the test bench such that at rising edge of clock you are toggles all the signals, which is creating a race condition. In short the race condition is being created because the inputs in the testbench are being driven asynchronously. To get into more details about why this race condition is occurring you can check this answer. Change the values of clk = 1; to clk = 0; and clkb = 1; to clkb = 0; in your test bench, this will prevent the occurrence of race condition. In other word, move the edge of the clock slightly from the transitions of the data inputs.

    Your testbench B is generating the correct result exactly due to the same reason, because in test bench B you are not driving the input from the test bench. Instead in testbench B, you are deriving the inputs from two other modules and they are providing inputs synchronously to the Block Ram.