Intricacies of Blocking vs Non-Blocking Assignment Statements in SystemVerilog
Have you really understood the nuances of Blocking vs Non-Blocking Assignment statements in SystemVerilog?
If I were to venture a guess, almost 90% of interviewees are able to answer the basic question about this topic. But go one level deeper, and the success rate drops to less than 10%. Again, these are not "official" statistics, but, I am assuming you get the point.
Let's get the simple stuff out of the way
Verilog and SystemVerilog have two types of assignment statements -
- Blocking assignments (e.g.
a = b
) - Non-blocking assignments (e.g.
a <= b
)*
Blocking assignments model the behavior of combinatorial logic in hardware. The order of operations is important.
Non-blocking assignments model the behavior of sequential elements in hardware, with clock-to-Q propagation delay.
An example of combinatorial logic.
always_comb
begin
a = b + c;
d = a + b;
end
Combinatorial Logic Block
We have two blocking assignments. First, the sum of b
and c
is computed and assigned to a
. Then next statement in the sequence is executed. d
gets the value of a
that was computed in the previous statement, plus the value of b
. So in short, the value of d
is 2*b+c
.

Now let's take a look how things differ when we use non-blocking assignments.
always_ff @(posedge clk)
begin
a <= b + c;
d <= a + b;
end
Sequential Logic Block
The above snippet of code models two separate adders followed by a flip-flop each. Since non-blocking assignments are used, both statements get executed in parallel. The current values of b
and c
are added and assigned to a
, which will only be updated on the next clock edge (after the clock-to-Q delay). At the same time, the current value of a
is added to the current value of b
and assigned to d
at the next clock edge. In this case, the value of d
is not equal to 2*b+c
, as in the earlier example.

Now for the more tricky stuff
I have asked this question to many candidates, right from recent graduates to some more experienced candidates.
"What happens if you use a blocking assignment inside an always_ff
block?"
Here are some of the answers that I have come across. Think through and figure out which one of these is correct.
- [Answer 1] That's a syntax error. The compiler will not allow it.
- [Answer 2] Why would you ever want to do that? (By the way, that wasn't the essence of the question)
- [Answer 3] It makes no difference. I use it all the time.
By the way, the answers are either incorrect or trying to skirt the essence of the question completely.
It is not syntactically incorrect to have a blocking statement inside a sequential block.
It will just not produce the output you are looking for.
Let's take an example of a simple shift register, written incorrectly with blocking assignments.
always_ff (posedge clk) // This is **NOT** how you write a Shift Register
begin
ff0_q = d; // Load d into ff0_q without clock-to-Q delay
ff1_q = ff0_q; // Load ff0_q into ff1_q without clock-to-Q delay
end
Shift Register Example (with an error)
If we were to describe a shift register properly, what we really want is for ff0_q
and ff1_q
to be positive edge triggered flip-flops. On the appropriate clock edge, d
should get transferred to ff0_q
and the previous value of ff0_q
should get transferred to ff1_q
.
Since we have used blocking assignments, the described hardware doesn't quite work like a shift register. The blocking assignment of d
to ff0_q
blocks the evaluation of the statement that follows, until the value of ff0_q
has been updated. Thinking through this, you can see that the value of d
gets passed on to ff1_q
on the first clock edge, rather than getting shifted through in two clock cycles (like a shift register).
The synthesis tool will recognize that ff0_q
is just a temporary variable and optimize it out.
The question then is,
Why are blocking statements even allowed inside a sequential block?
The practical reason is to allow the use of a temporary variable within the sequential block. Let's say a complex math equation has to be computed and assigned to a sequential output. For code readability purposes, it might be practical to break down the equation into multiple parts. That is when the sequential block should use a blocking assignment to compute intermediate values. In synthesis, the extra assignment will be optimized out and the blocking feature will be removed.
A secondary and a more obscure, philosophical reason is that a Hardware Description Language used for synthesis and verification purposes must correctly model the hardware and must be able to prove what will work (and not work) correctly in hardware.
Takeaways
Get into good coding habits. In most cases, you should use blocking assignments only to describe combinatorial logic (always_comb
blocks), and non-blocking assignments to describe sequential logic (always_ff
blocks). There are always exceptions to the rule, but I have not really ever come across the need to use these somewhat arcane exceptions.