3
\$\begingroup\$

I have a Verilog program for a memory module that looks really simple but is behaving oddly, and I cannot see why. If anyone can find my mistake, I would be eternally grateful. This is the memory program:

module mem_WidthxDepth ( 
 clk_i,
 wr_addr_i,
 rd_addr_i,
 wr_i,
 data_in_i,
 data_out_o
);
parameter Width = 8; 
parameter Depth = 8; 
//AW = Address Width
localparam AW = $clog2 (Depth);
//IO
input clk_i;
input [AW-1:0] wr_addr_i; 
input [AW-1:0] rd_addr_i;
input wr_i;
input [Width-1:0] data_in_i;
output [Width-1:0] data_out_o;
//Memory declaration. 
reg [Width-1:0] Mem [0:Depth-1];
//Write into the memory 
always @ (posedge clk_i) 
 if (wr_i) 
 Mem[wr_addr_i] <= data_in_i; 
//Read from the memory 
assign data_out_o = Mem [rd_addr_i]; 
endmodule

and this is the testbench code:

module mem_tb;
 reg clk_i;
 reg [2:0] wr_addr_i;
 reg [2:0] rd_addr_i;
 reg wr_i;
 reg [7:0] data_in_i;
 wire [7:0] data_out_o;
 // Instantiate the memory
 mem_WidthxDepth mem
 (
 clk_i,
 wr_addr_i,
 rd_addr_i,
 wr_i,
 data_in_i,
 data_out_o
 );
 // Clock generation
 always #5 clk_i = ~clk_i;
 initial begin
 clk_i = 0;
 wr_i = 0;
 rd_addr_i = 1;
 // Write data into FIFO
 for (integer i = 0; i < 8; i = i + 1) begin
 @(posedge clk_i);
 wr_i = 1'b1;
 wr_addr_i = i[2:0];
 data_in_i = i[7:0];
 $display("Write %d", data_in_i);
 end
 // Stop writing
 @(negedge clk_i);
 wr_i = 0;
 // Read data back
 for (integer i = 0; i < 8; i = i + 1) begin
 @(posedge clk_i);
 rd_addr_i = i[2:0];
 $display("Read %d", data_out_o);
 end
 // Finish simulation
 $finish;
 end
 // Waveform generation
 initial begin
 $dumpfile("mem_tb.vcd");
 $dumpvars(0, mem_tb);
 end
endmodule

so it should just write 0 to 7 into memory addresses 0 to 7 then read back the numbers. But, when I run it using iverilog (on Ubuntu) I get:

renniej@gramrat:/mnt/d/rhs/Students/Tejas/VLSI/L6$ iverilog -o mem_tb.vvp mem_Wi
dthxDepth.v mem_tb.v
renniej@gramrat:/mnt/d/rhs/Students/Tejas/VLSI/L6$ vvp mem_tb.vvp
VCD info: dumpfile mem_tb.vcd opened for output.
Write 0
Write 1
Write 2
Write 3
Write 4
Write 5
Write 6
Write 7
Read 0
Read x
Read 2
Read x
Read 4
Read x
Read 6
Read x
mem_tb.v:49: $finish called at 155 (1s)

For some reason, every second write and/or read appears to fail. If I look at the signals for the memory module in gtkwave I get:

gtkwave

which shows that data_out_o is undefined every second read, i.e. apparently it was never written. But, I just cannot see what is going wrong. This is such simple code that I cannot see where it is failing.

toolic
10.8k11 gold badges31 silver badges35 bronze badges
asked Nov 4, 2024 at 7:38
\$\endgroup\$

2 Answers 2

3
+100
\$\begingroup\$

I can not reproduce your result exactly. When I try to compile your code with iverilog, I get syntax errors, and I can not run a simulation. You and I are probably using different versions of iverilog, which would explain the discrepancy.

When I use a different simulator (Cadence), I get an unexpected result for the Read values. This is due to Verilog simulation race conditions in your code. Other simulators are available to you free on the EDA Playground site.

As you correctly deduced in your answer, you need to use nonblocking assignments (<=) when you drive all the synchronous inputs to the design from the testbench.

You should also be consistent and always drive at the posegde clk_i (you drive at negedge in one place).

By driving the inputs in the same way as you drive internal signals in the design, you are guaranteed to avoid these race conditions, namely:

  • @(posegde clk_i)
  • Nonblocking (<=) assignments

There is also a second type of race condition in the code. You should never $display a signal when it is changing because you will get indeterminate results. Since your signals change on posedge clk_i, it is better to display their value when they are stable on the negedge clk_i.

Here is a cleaner way to write the testbench code:

 initial begin
 clk_i = 0;
 wr_i = 0;
 rd_addr_i = 1;
 // Write data into FIFO
 for (integer i = 0; i < 8; i = i + 1) begin
 @(posedge clk_i);
 wr_i <= 1'b1;
 wr_addr_i <= i[2:0];
 data_in_i <= i[7:0];
 @(negedge clk_i);
 $display($time, , "Write %d", data_in_i);
 end
 // Stop writing
 @(posedge clk_i);
 wr_i <= 0;
 // Read data back
 for (integer i = 0; i < 8; i = i + 1) begin
 @(posedge clk_i);
 rd_addr_i <= i[2:0];
 @(negedge clk_i);
 $display($time, , "Read %d", data_out_o);
 end
 $finish;
 end

I was able to finally get the code to compile with iverilog. For some reason, the version I have does not support declaring a variable inside a for loop. I just moved the integer keyword outside the initial block:

integer i;
answered Nov 4, 2024 at 13:57
\$\endgroup\$
2
  • \$\begingroup\$ It works! :-) Thanks for taking the time to explain the problem in detail. A bounty will be on its way as soon as I can award one! \$\endgroup\$ Commented Nov 4, 2024 at 17:03
  • \$\begingroup\$ @JohnRennie: You're welcome, and thank you for the huge award! \$\endgroup\$ Commented Nov 7, 2024 at 12:35
0
\$\begingroup\$

And of course it was a silly mistake on my part. I was using blocking assignments in the loops in the testbench code - a habit that is going to be hard to shake after 40 years of writing C. When I change the testbench code to use non-blocking assignments the code works as expected.

However I don't understand why using blocking assignments was causing the problem. If anyone can add an answer explaining this I'd accept that and award a bonus. I'm guessing it's some timing issue e.g. I was assuming that when I write:

 @(posedge clk_i);
 wr_i = 1'b1;
 wr_addr_i = i[2:0];
 data_in_i = i[7:0];

everything happens at the same time e.g. wr_i has the value 1 at the moment of the rising edge for clk_i. Is this the case? I have to confess I do not understand how the timings work in Verilog.

answered Nov 4, 2024 at 8:08
\$\endgroup\$
1
  • 2
    \$\begingroup\$ My answer explains why. \$\endgroup\$ Commented Nov 4, 2024 at 16:10

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.