I am having a very difficult time to understand the logic behind the naming of "Blocking" and "Non-blocking" statements in Verilog.
By definition, Blocking assignment evaluates and assigns values immediate. Whereas, Non-blocking assignment deferred until all right-hand sides have been evaluated (See reference)
Here I have created two test modules to verify this definition in Vivado 2022:
// module to test non-blocking
module NonBlockingTest(D, Clk, Qa, Qb, Qc);
input D, Clk;
output reg Qa, Qb, Qc;
always @(posedge Clk)
begin
Qa <= D;
Qb <= Qa;
Qc <= Qb;
end
endmodule
// module to test blocking
module BlockingTest(D, Clk, Qd, Qe, Qf);
input D, Clk;
output reg Qd, Qe, Qf;
always @(posedge Clk)
begin
Qd = D;
Qe = Qd;
Qf = Qe;
end
endmodule
And here is the test bench:
module NonBlockingTest_TB();
reg D, Clk;
wire Qa, Qb, Qc;
NonBlockingTest DUT_NonBlocking(D, Clk, Qa, Qb, Qc);
BlockingTest DUT_Blocking(D, Clk, Qd, Qe, Qf);
initial
begin
Clk = 0;
D = 1;
#50;
Clk = 1;
#50;
Clk = 0;
#50;
Clk = 1;
#50;
Clk = 0;
#50;
Clk = 1;
#50;
Clk = 0;
end
endmodule
The result is not what I expected: enter image description here Qa, Qb, Qc come from non-blocking assignment, yet they are executed sequentially.
Qd, Qe, Qf come from blocking assignment, yet they are executed at simultaneously.
From the meaning of the English word itself, it appears that "=" is the non-blocking assignment, while "<=" is the blocking assignment.
Am I interpreting the word 'blocking' incorrectly?
4 Answers 4
It might help by referring to the following diagram in page 5 of the reference that you provided.
For blocking, you see x = a & b
is evaluated after a
changes from 1 to 0. And then y = x | c
is evaluated after x = a & b
has been evaluated. It is blocking in that sense.
For non-blocking, you see x = a & b
is evaluated, but not immediately assigned (is deferred). Notice that y = x | c
appears to be evaluated without waiting for x = a & b
result; otherwise y
would have been 0 instead of 1 because x = 0
. It is non-blocking in that sense.
To understand your result, below is the table showing the update progression for BlockingTest:
Clock Cycle | D | Qd | Qe | Qf | Evaluation |
---|---|---|---|---|---|
0 | 1 | 1 | 0 | 0 | Qd = D |
0 | 1 | 1 | 1 | 0 | Qe = Qd |
0 | 1 | 1 | 1 | 1 | Qf = Qe |
1 | 1 | 1 | 1 | 1 | Qd = D |
1 | 1 | 1 | 1 | 1 | Qe = Qd |
1 | 1 | 1 | 1 | 1 | Qf = Qe |
2 | 1 | 1 | 1 | 1 | Qd = D |
2 | 1 | 1 | 1 | 1 | Qe = Qd |
2 | 1 | 1 | 1 | 1 | Qf = Qe |
By the end of the clock cycle 0, you see Qd, Qe, and Qf have taken the value of D=1
. This is because Qe waits for Qd to be assigned with the value 'D=1' before it itself being updated. Similarly, Qf waits for Qe to be assigned with the value 'Qd=1' before it itself being updated.
The following table shows the update progression for NonBlockingTest:
Clock Cycle | D | Qa | Qb | Qc | Qa' | Qb' | Qc' | Evaluation |
---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | Qa <= D |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | Qb <= Qa' |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | Qc <= Qb' |
1 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | Qa <= D |
1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | Qb <= Qa' |
1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | Qc <= Qb' |
2 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | Qa <= D |
2 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | Qb <= Qa' |
2 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | Qc <= Qb' |
where Qa', Qb', and Qc' are the immediate previous values of Qa, Qb, and Qc, respectively. In each clock cycle, each variable is assigned with the immediate previous values of the variables on the RHS of the expressions because in non-blocking code, the evaluations do not wait for the others to complete. Hence it takes a number of clock cycles before every variable settles on the final values of 1's.
-
\$\begingroup\$ Thank you for creating the two tables. They really help illustrating the differences. \$\endgroup\$user97662– user976622024年03月23日 03:46:30 +00:00Commented Mar 23, 2024 at 3:46
-
\$\begingroup\$ For the NonBlocking Table, did you mean to use '<=' instead of '='? Like 'Qa <= D'? \$\endgroup\$user97662– user976622024年03月23日 03:55:36 +00:00Commented Mar 23, 2024 at 3:55
-
\$\begingroup\$ Yes, correct. I have amended it. \$\endgroup\$kaosad– kaosad2024年03月23日 08:55:00 +00:00Commented Mar 23, 2024 at 8:55
The term "blocking" was originally intended just to describe the potential to suspend a process. A clarifying description of this terminology was just added to the Annex P Glossary in the IEEE 1800-2023 SystemVerilog LRM:
blocking statement: A construct having the potential to suspend a process. This potential is determined through lexical analysis of the source syntax alone, not by execution semantics. For example, the statement wait(1) is considered a blocking statement even though evaluation of the expression '1' will be true at execution. All statements with procedural event controls (see 9.4) become blocking statements. A task enable is also a blocking statement because the task may itself contain a blocking statement. The task caller is not required to recursively investigate the task body.
A blocking assignment (see 10.4.1) is only considered a blocking statement when the syntax contains an optional intra-assignment delay. Without the delay, a blocking assignment is not a blocking statement. A nonblocking assignment (see 10.4.2) is never a blocking statement.
I would suggest never using a blocking assignment with an inter-assignment delay, and simply calling it a procedural assignment.
Qa, Qb, Qc come from non-blocking assignment, yet they are executed one after another,
You're seeing Qb
has its value changed on the 2nd clock edge and calling that "executing one after the other". This is not correct.
When we say the assignment is non-blocking, what it means is that on the first clock edge, all three of the assignment statements execute. But because the first assignment is non-blocking, the right-hand-side of the second assignment uses the "old" values of its signals. The process isn't "blocked" to wait for the first assignment to happen before the r.h.s. of the second assignment is evaluated. So since Qa
is initially X
, Qb
is assigned X
(the same value it had before) on this edge.
Only on the second clock edge will the Qb
assignment see that Qa
has changed to 1
and get that value.
-
\$\begingroup\$ I suppose for Qd, Qe, Qf, the process waits for all three assignment completes before "clocking in", hence, we are observing all three outputs go high on 1st clock's rising edge. It makes sense now. \$\endgroup\$user97662– user976622024年03月23日 03:44:17 +00:00Commented Mar 23, 2024 at 3:44
Non-blocking statements have the following meaning:
always @(posedge Clk)
begin
Qa_after_end <= D_before_begin;
Qb_after_end <= Qa_before_begin;
Qc_after_end <= Qb_begin_begin;
end
In effect, D, Qa, Qb, Qc form a shift register. The assignments are all executed in parallel, after the block end
. This is borne out in the simulation results.
always @(posedge Clk)
begin
Qd_below_this_line = D_above_this_line;
Qe_below_this_line = Qd_above_this_line;
Qf_below_this_line = Qe_above_this_line;
end
Qd, Qe, and Qf all take on the value of D, because the assignments are executed sequentially within the block. But note that this all happens within the same clock cycle, and takes zero time. The sequence of assignments only establishes how the values propagate. The above block simplifies to:
always @(posedge Clk)
begin
Qd_below_this_line = D_above_this_line;
Qe_below_this_line = Qd_above_this_line = D_above_this_line;
Qf_below_this_line = Qe_above_this_line = D_above_this_line;
end
Now, since the ultimate value referred to on the right side of the assignment (D) is never same as the destination on the left, the "above this line" and "below this line" distinctions are not necessary anymore, and the block is equivalent to:
always @(posedge Clk)
begin
Qd = D;
Qe = D;
Qf = D;
end
And, as you can see in the simulation results, after the positive edge of the clock, Qd, Qe and Qf do indeed all take the value of D.
non blocking
is two words and the symbol contains two characters<=
\$\endgroup\$