non-blocking assignment does not work as expected in Verilog

I have a very simple Verilog code and it does not seem to work as expected:

Here is my code:

always @ (posedge clk, negedge resetn) begin
if (resetn == 1'b0) begin
var <= 1'b0;
end else begin
if (valid == 1'b1) begin
var<= 1'b1;
end else begin
var <= 1'b0;
end
end
end


I expected that assuming resetn is H all along, when valid goes H the var becomes H in the next cycle. But in simulation, var becomes H in the same cycle. Why is that?

Here is a diagram as well:

next_state_d2 is my var

EDIT: Testbench code:

initial begin
gclk = 1'b1;
resetn = 1'b1;
div_valid=1'b0;
#40 div_valid = 1'b1;
#40 div_valid = 1'b0;
end

always
#20 gclk = ~ gclk;

• Can you share a timing diagram showing your simulation results? Include resetn, clk, valid, and var. – The Photon Oct 4 '13 at 16:30
• Since you only have one assignment per begin-end block, there would be no difference between blocking and non-blocking assignment in your code. Also, your whole if (valid ...) ... else ... block could be reduced to var <= valid. – The Photon Oct 4 '13 at 16:31
• The explanation must be in how you coded your testbench, please post it. You must be somehow setting valid right before the clock's edge. – apalopohapa Oct 4 '13 at 17:50
• Why don't you post the complete code of your testbench? Without that any answer you'll get is just a guess. – Vasiliy Oct 7 '13 at 9:41
• @Vasiliy I edited my answer. – ghostrider Oct 7 '13 at 9:53

This is actually quite similar to a question I answered previously, but I will try to build up a canonical answer for this somewhat common issue.

In a zero-delay simulation like this, the test flip-flop has a setup time and a hold time of zero:

$T_{setup} = T_{hold} = 0$

What this means is that the instant the sensitive clock edge occurs, the output is updated, regardless of what happened immediately before or after that instant. This is not like real hardware which would usually have a non-zero $T_{setup}$ and $T_{hold}$.

I ran your testbench, and the results are pretty clear. The valid signal changes at the same time the clock signal does. You have delayed them by precisely the same amount. So at the very edge when the clock is high, the valid signal has also changed:

Both the input (div_valid), and the clock (gclk) go high at the same time: 220 ns. Therefore, the DFF latches this new data, and the output changes instantly since there is also 0 propagation delay. This simulation would look less confusing if we just chose a different delay value for the input to the design:

In this case, we update the input on the falling edge of the clock (620 ns). It is much more clear now that the next clock edge (640 ns) will be when the DFF updates its output.

Here is the testbench code so you can see exactly how it works in your own simulator. Please update the design name as it wasn't clear what yours was named.

module scratch_tb;

reg gclk;
reg resetn;
reg div_valid;
wire data;

// instantiate design under test
scratch scratch (gclk, resetn, div_valid, data);

// generate stimulus
initial begin
gclk            = 1'b1;
resetn          = 1'b0;
div_valid       = 1'b0;

#80 resetn      = 1'b1;

// test 1: input switches on rising clock edge
#160 div_valid   = 1'b1;
#40 div_valid   = 1'b0;

#160 div_valid  = 1'b0;

// test 2: input switches on falling clock edge
#180 div_valid  = 1'b1;
#40 div_valid   = 1'b0;

#2000 $finish; end always begin #20 gclk = ~ gclk; end endmodule  • Ok I get and I will accept your answer. My question is this - in an FPGA for example (I do not have a spare right now), will it work as expected? I mean the div_valid comes at the start of a cycle (with some delay) and in the next cycle data will go H, as we did right now by imposing that delay on our testbench, right? – ghostrider Oct 7 '13 at 11:07 • Also, Why the propagation delay is 0? That's the whole meaning of the non-blocking assignment - to be able to schedule the output. – ghostrider Oct 7 '13 at 11:15 • The solution of changing testbench signals while there is no positive edge of the clock will work, but the explanation seems incorrect. What do you mean by $T_{setup}$ and $T_{hold}$? There are no timing notation in Verilog, unless you are referring to$setup and \$hold tasks... – Vasiliy Oct 7 '13 at 12:41
• @Vasiliy, in this context, Tsetup and Thold are simply being used with their universally accepted definitions, not as a Verilog construct. The approach to this answer was to define exactly what construct the RTL models. As I go through the answer, I reason that this RTL models a DFF with 0 setup time, 0 hold time and 0 propagation delay. From this, we find the simulation is exactly how we would expect such a DFF to behave. Let me know if you find an error. – travisbartley Oct 8 '13 at 1:02
• @ghostrider, in an FPGA, the DFF will have realistic timing, so the setup time, hold time and propagation delay will be non-zero. If the input does not violate setup time, or hold time, the DFF will have stable output after some delay after the next clock edge. Also, the propagation delay is 0 because there is nothing in the code which can add a delay (except the testbench stimulus). We can add delays to the design under test, but usually prefer not to do that. It is better to run gate-level simulation with annotated timing. – travisbartley Oct 8 '13 at 1:06

It is all about the scheduling rules of Verilog simulators, which may be confusing for both newcomers and experienced engineers.

If you read "Verilog Nonblocking Assignments With Delays, Myths & Mysteries" (here) carefully and make sure you understand everything it explains, you will not experience this kind of issues in the future, or, at least, you'll know what to look for when debugging.

What it shows is that evaluation of RHS of non-blocking assignments (<=), and evaluation and assignment of blocking assignments (=) have no ordering rules.

What it means is that in your case, where both posedge gclk and div_valid scheduled to the same time slot , both following outcomes can happen:

1. div_valid changes before the RHS of non-blocking assignment is evaluated
2. div_valid changes after the RHS of non-blocking assignment is evaluated

#1 above leads to a behavior observed by you - both div_valid and var seem to change on the same clock cycle.

#2 above leads to a delay of 1 clock cycle from div_valid to var change.

In practice, all the simulators I saw behaved according to #1 (seems that they place all the events triggered by @(posedge signal) at the end of the Active Events Queue). However, it is not advisable to rely on this consistency.

I believe you'd prefer to see the delay between these signals, right? It is more consistent with the way a real HW behaves. There are two ways to achieve this:

• Change the states of the signals driven with blocking assignments (=) from the testbench at time slots when there is no positive edge of the clock (see the answer by @Travis for waveform example)
• Use sequential always blocks with non-blocking assignments (<=) to drive signals from testbench (at positive edge of the clock).

If you want to understand why will the approaches I suggested work, you are welcome to read the paper I referenced above.

Hope this helps.

• Ah, this is a great point. I am not able to get the SNUG PDF, is there another link? – travisbartley Oct 8 '13 at 1:15

It's a function of when valid goes high. If it goes high before the clock edge (or for that matter before the setup/hold time requirements end) then you'll get var high in the same cycle. If it goes high after the clock edge (or after the hold time requirement) then you'll get valid high in the same clock cycle. What simulator are you using? Different simulators deal with setup/hold time slightly differently. Without knowing anything else about your system I'd guess that valid isn't a registered output and is in fact a combinational output, which results in the former timing case that I outlined. If you share how valid is being driven it should become clear pretty quickly what's actually happening.

• I've never seen a Verilog simulator which accounts for setup/hold times. Anyway, you should've posted this as a comment, not an answer. – Vasiliy Oct 5 '13 at 14:05
• @Doov can you see my edit? – ghostrider Oct 7 '13 at 9:37