I got contradictory results when I tried to test my customized Vivado block ram IP in two different testbenches.
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 2. IP summary:
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:
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 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:
Based on figure 4, the block ram works perfectly fine. Since 255 is apparently larger than 150 hence the legit "X".
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:
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.