My question is how exactly does mixing blocking assigns and non-blocking assigns work? and how do they trigger reevaluation?
Here is my understanding of these assigns -
Blocking
always@(posedge clk)
begin
a = 1+d;
b = 2;
c = 3
end
each statement in the above block evaluates rhs and assigns to the lhs before processing the next statement
Non-Blocking
always@(posedge clk)
begin
a <= x;
b <= y;
c <= z;
end
All statements in the above evaluate rhs parallelly(non-deterministically) and assigns to the lhs at the same time.
Would the three LHS assignments be scheduled in the order of the original statements. I believe that is what the LRM says correct?
Now what happens when we mix the two?
always@(posedge clk)
begin
a = x+1;
x <= y;
end
Would this evaluate rhs on the first blocking statement and assign to the lhs and then proceed with evaluating rhs on the non-blocking assign and finally update the lhs on the non blocking assign? or does the evaluation of rhs-y and a=x+1 happen at the same time (non-deterministically)?
Would this then retrigger the first blocking assign a second time in the same simulation time step?
What if the order is reversed?
always@(posedge clk)
begin
x <= y;
a = x+1;
end
Does y evaluate before x+1 and the updating of a?
I know we are not supposed to mix these two assigns, but in uvm testbench writing i do see some necessary mixing of these two assigns, such as when developing a driver. I have tried reading the lrm but the only thing I could get clarity on is that non blocking assigns update the lhs in the order of their appearance in the block.
Also if someone has any good recommendations that dive into the details of the scheduling semantics please let me know.
Thanks
You have a few misunderstandings.
Within a single begin/end
group of statements, each statement gets executed in order, Regardless of whether the assignments are blocking or nonblocking, the RHS gets evaluated in the same order of execution. This is what’s known as the active region. Concurrency (or non-deterministic parallelism) happens between multiple processes synchronized to the same event (multiple always @(posedge clk)...
)
With blocking assignments, the LHS gets updated as part of the execution of the statement after evaluating the RHS, but before proceeding to the next statement.
With nonblocking assignments, the LHS does not get updated as part of the execution of the statement. Instead, the update gets scheduled into a later NBA event region. This NBA event region does not perform the updates until the active region is completed. The updates happen in the same order they were scheduled to execute in the active region.
There are volumes of sites dedicated to explaining the difference between the two different Verilog assignment operators.
1 Like
I am aware of the difference between the operators but I couldn’t find anything on how retriggering works.
There is no retriggering involved here.
Just to be clear. Are you saying that in the following case -
always@(posedge clk)
begin
a = x+1;
x <= y;
end
non blocking assign to x would not retrigger a=x+1 blocking assign in the same simulation time step? If not, then could you explain when non blocking assigns or blocking assigns can retrigger other blocking assigns?
The code you show waits for the a rising edge of clk
, then executes the statements between the begin/end
keywords in order. An always
construct is an endlessly looping process, it then waits for the next rising edge of clk
.
If instead you used always_comb
or always @(*)
, thats when the nonblocking assignment update occurs after the begin/end statements have finished and is already waiting for the next event. If the NBA assignment changes the value the LHS and needs to read it on the RHS, the begin/end executes again. That is considered a poor modeling style. You should never use nonblocking assignments to model combinational RTL logic.