Non-blocking assignments were created to help model sequential logic. They help distinguish between the old and new value of a signal. They should be avoided in combinatorial logic as they can create problems with simulation if there are multiple clock domains. It also creates a lot of unnecessary signal rippling. Synthesis tools don’t care.
For a sequential block, you can mix combinatorial and sequential logic in the same block. Which assignment type you choose makes a difference in synthesis/simulation when making an assignment before reading it in the same block.
always_ff @(posdege clk) begin
a = b & c;
z <= a & w; // same as b & c & w;
end
always_ff @(posdege clk) begin
a <= b & c;
z <= a & w; // uses previous cycle value of b & c.
end
You must use a non-blocking assignment when one process reads, and another process write to the same variable and they are both synchronized to the same clock.
An extra question popped out:
In the case of functions and tasks what would the the right assignment?
To my understanding:
Functions are executed in a sequence, they are synthesized as combinatorial, and in execution time they are instantaneous (do not consume time).
Tasks on the other hand, are executed also in a sequence but can have timing controls, so they could be synthesized both sequential (if they wait for a clock) and combinatorial (if they wait for any other signal).
So, my guess is:
all functions should always use blocking assignments: =, because of their sequence, combinatorial, and instantaneous nature
all tasks should also always use the blocking assignments: =, because of their sequence execution nature
Are my understanding/guess correct?
or please clarify, :) thank you
Your conclusions are mainly correct, but not for the reasons mentioned.
The body of code inside tasks and functions are inlined/expanded in the place where they are called for synthesis. After expansion, that code follows the same synthesis coding guidelines for blocking and nonblocking assignments in any procedural code block.
However, assignments to the function’s return value, and task/function output arguments must use blocking assignments because they get copied out upon returning from the task/function. If you used non-blocking assignment, the argument values will not have been updated before being copied out.
Please let me to open the topic again. I have understood that for RTL design, in tasks and functions blocking assignments are preferred. In Functional Verification with SystemVerilog, there is the same situation? It is preferred to use blocking assignments rather than non-blocking ones inside tasks and functions?
It makes no difference for RTL or verification. This is the rule you need to remember:
It’s unlikely the arguments or variables declared within a task or function would be read and written by different processes. Also, you cannot make a non-blocking assignments to variables with automatic lifetimes
So your driver which is “writing” to interface and design “reading” from the interface should drive using NBA and monitor should BA as it is only “reading”. Using NBA in monitor might lead to 1 cycle delay in signal observation?