Verilog LRM Nondeterminism - verilog

I am facing some doubts regarding the nondeterminism in Verilog Scheduling Semantics mentioned in the Verilog LRM. Below is the excerpt which I am unable to understand:
"Another source of nondeterminism is that statements without time-control constructs in behavioral blocks do not have to be
executed as one event. Time control statements are the # expression
and # expression constructs (see 9.7). At any time while evaluating a
behavioral statement, the simulator may suspend execution and place
the partially completed event as a pending active event on the event
queue. The effect of this is to allow the interleaving of process
execution. Note that the order of interleaved execution is
non-deterministic and not under control of the user."
The only inference I could make was that statements in a behavioral block may be paused for the execution of other behavioral blocks (which are active in the same timestep) so as to interleave process execution though I am not sure.
Also, I don't understand the line "statements without time-control constructs in behavioral
blocks do not have to be executed as one event". What does the LRM mean by saying it doesn't execute as one event and what would happen if a behavioral block would contain all time-controlled statements?
Can anyone please explain this with the help of some examples? Thanks in advance.

The only thing which simulation guarantees is that all statements in the always blocks will be executed sequentially. Say, as in the following block:
always #(b,c,e,f) begin
a = b | c;
d = e & f;
g = a ^ d ^ x;
...
end
However, the simulator can decide to execute first 2 statements in a row, but then stop execution of this block before the last statement and let other blocks to continue. Then it will return to the last statement. In this sense you have a non-deterministic order of the execution of the statements.
Guess what?! Value of x can definitely change while it is waiting. a and d can potentially also change while other statements are executed. So, result of g could be non-deterministic. Good programming will help to remove this type of non-determinism: list all events in the sensitivity list, do not multiply-drive signals, ... The simulator then will do the best to avoid those situations.
The need for this interleave simulation is to allow simulators to do better optimization for performance reasons and allow other blocks to progress in case of the very long and loopy statements.
Answering the comment about time and event controls
In the above block a simulator can decide when to break the execution. From the point of view of the programmer, it is non-deterministic. You do not know when it can happen. The only thing that is known that it would happen in the same simulation (time) tick. A good simulator will try its best to avoid any side effect of this.
Timing and delay controls provide deterministic stops. For example,
always #(b,c,e,f) begin
a = b | c;
d = e & f;
#1 g = a ^ d ^ x;
...
end
In the above statement with #1 you actually tell the simulator to stop execution of the statements and wait till the next time tick.
always #(b,c,e,f) begin
a = b | c;
d = e & f;
#(posedge clk)
g = a ^ d ^ x;
...
end
Here it will stop execution and wait for the posedge clk event.
Note that above examples are not synthesizable and should be used in behavioral code (test bench) only. For synthesis you have to stick with the very first example and make sure that your code is written in accordance to the good Verilog coding practices.

Related

What is the difference between begin end and fork join with respect to non-blocking statements?

We know that the difference between blocking statements and non-blocking statements is: blocking statements executes sequentially (execution of next statement is blocked until present one completes) and are used to perform in combinational circuits.
Example:
always#(*) begin
c = A & B;
D = C/A;
end
Here all the statements executes sequentially (blocking statement)
Whereas non-blocking statements execute parallelly (execution of next statement is not blocked) and are used to perform in sequential circuits.
Example: Take an example of shift register
always#posedge(clk) begin
A<=B;
B<=C;
C<=D;
end
Here all the statements executes parallely because it is non-blocking and we have used posedge clk
Now if you see the difference between begin end and fork join, the difference is: in begin end, statements are executed in the order they are listed (i.e. sequentially), whereas in fork join, statements are executed parallelly.
My question here is, in the above example of non-blocking statement, we have used begin end but the statements are been executed parallelly not sequentially, but if you see in the difference between begin end and fork join it says begin end executes the statements one after another.
Could someone explain with a clear answer to this?
in the above example of non-blocking statement, we have used begin end
but the statements are been executed parallelly not sequentially
That is not true. In the above example of non-blocking statement, we have used begin-end and the statements are executed sequentially. However, in spite of that, the order of execution doesn't matter. A subtle but important distinction.
When a line of code containing a non-blocking assignment is executed, it is executed immediately, but the left-hand-side of the assignment (the target) does not get its new value immediately. So, any other statements in the same begin-end block that read a variable assigned to using a non-blocking assignment will use the old value of that variable. As a consequence, the order of statements using non-blocking assignments inside begin-end blocks often does not matter. That is the case with your example.
So, when does the left-hand-side of a non-blocking assignment get updated?
System-Verilog has 9 so-called scheduler regions:
from prev time step
|
PREPONED
|
ACTIVE
|
INACTIVE
|
NBA
|
OBSERVED
|
RE-ACTIVE
|
RE-INACTIVE
|
RE-NBA
|
POSTPONED
|
V to next time step
and Verilog has 4:
from prev time step
|
ACTIVE
|
INACTIVE
|
NBA
|
POSTPONED
|
V to next time step
Think of this like a flow chart that gets executed at every timestep. (A timestep is a particular simulation time - 12345ns or whatever.) The lines of code in your begin-end block get executed in the ACTIVE region (ie early), however, the left-hand-side of your non-blocking assignments doen't get updated until the NBA region, ie later.
your description of non-blocking assignments is not correct. All statements inside procedural blocks (always, initial, final) are always executed sequentially, including statements with non-blocking assignments. Multiple statements are enclosed within begin/end pairs.
The difference between blocking and non-blocking assignments is when the value gets assigned to a left hand side variable. Non-blocking assignment cause delayed assignment. It is done sequentially as well but in the delayed scheduling region. This is a simulation artifact.
The fork/join pair causes all statements inside to be executed in parallel. They belong to test-bench and are not synthesizable. Usually they are used to run multiple test-bench tasks in parallel in simulation.
Serge is correct. You can think of the fork .. join as splitting each expression into its own action block. So if you recoded your eample:
always#posedge(clk) begin
A<=B;
B<=C;
C<=D;
end
as
always#posedge(clk) fork
A<=B;
B<=C;
C<=D;
join
A simulator would interpret the above as if it had been written as:
always#posedge(clk) A<=B;
always#posedge(clk) B<=C;
always#posedge(clk) C<=D;
Which in this case would happen to behave exactly as if it were in the begin .. end pair.
As others have noted, a synthesis tool would typically reject the fork .. join construct, as while you are not using them here, the fork join provides additional features that are designed for efficient test bench coding. As an example, code execution stops at the join until each of the the forked blocks has completed execution.

Verilog execution parallel or sequential if block along with another statement

The following is a verilog code
I'm trying to understand the working of statements inside an always block.
I know the rule that blocking statements are written using = and non blocking use <= in verilog.I know the working of these.
What will happen if an if statement(with blocking statements) come in between blocking statements?
always(#posedge clk)
begin
if(en1)
begin
out1=c; //statement 1
out2=c+1; //statement 2
if(out2>5)
begin
out3=out1+out2;//statement 3
end
else
out3=0;
out4=out1-out2;
end
end
The 'if statement' was synthesized into a multiplexer. Will it use the values updated in the previous statement1 ???Requirement is -The output should be such that the three statements were executed sequentially.
But when this is implemented in hardware the mux for ' if ' will be separate from the other adders.So I thought the if statement3 in between will work in parallel with statement 1 and 3.
I checked in simulation and the value taken is the latest value-ie as if it works sequentially.
My questions are
1.Is it correct to use 'if statements' in between other assignments.
or should I use another method of programming.Will it run sequentially when blocking statements are used?
2.Will this work in all conditions as if sequentially?
The story here is very simple. In simulation all statements inside an always block are always executed sequentially, as in any programming language. The if statement will be executed after the assignment, as in the order given below.
1> out1=c; //statement 1
2> out2=c+1; //statement 2
3> if(out2>5)
begin
3a> out3=out1+out2;//statement 3
end
else
3b> out3=0;
4> out4=out1-out2;
The above will work as in any generic programming language. The if will be taken if out1 > 5.
The confusion is in in the blocking/non-blocking assignments. Verilog has a very special semantic for the <=, non-blocking assignment. Actually it is just a simulation artifact. It means that the rhs value will not be assigned to the lhs inside this particular always block. It will be done later, after all always blocks are evaluated.
So, consider the following:
out1=c; //statement 1
out2 <= c+1; //<<<< non blocking, assignment here, statement 2
if(out2>5)
begin
Now, if c is 4 and the result of the rhs c+1 is 5, the if statement will still be taken, because out2 is not yet updated in this block. It will still have the previous value of 4. But the value of 5 will be assigned to it at the very end, after all the statements are evaluated. It will not be seen by the if statement till the next posedge of your clock.
1.Is it correct to use 'if statements' in between other assignments. or should I use another method of programming.Will it run sequentially when blocking statements are used?
it depend on what you are trying to achieve here. Could be correct or not
Will this work in all conditions as if sequentially?
As i explained above. You have to understand one thing though. Verilog is not a hardware description language. It is a language for describing desired behavior of hardware and gets interpreted by synthesis tools. Therefore you can run into a situation when your simulation results will not match behavior of the synthesized schematic. To minimize this possibility you should use verilog programming practices accepted in the industry. One of them: use non-blocking assignments for the real outputs of the edge-controlled blocks. There are compelling reasons for doing so. From this perspective your example is completely wrong.

Question about triggering of always blocks

I know that an always block will be trigger on a change in any of the elements in its sensitivity list however, my question is what happens if a change in sensitivity list happens while the statements inside the always blocks are still being executed (due to a previous trigger). Does the always block begin execution again in a parallel thread or is triggering blocked until execution finishes first?
Many people do not realize that # is a statement modifier, not construct by itself. It says to delay the statement that follows until there is an event. #(A or B) means wait until there is a change in the value of A or B (not to be confused with a change in the result of A|B). #* means look at the statement that follows, and build an implicit sensitivity list of signals to wait for a change.
always #(A or B) C = A + B;
always begin
#(A or B) C = A + B;
end
always begin
#* C = A + B;
end
always_comb
begin
C = A + B;
end
These 4 always blocks have identical behavior with the exception of the last always_comb also triggers at time 0 regardless of any change on A or B.
If you look at the code that follows the always as a procedural sequence of executing statements, it might be easier to see the # construct as part of that procedural sequence, and the change has to occur while you are executing the construct. Just adding another delay will show this concept (not synthesizable)
always #(A or B) begin
#10 C = A + B;
end
This says "wait for a change on A or B, then wait 10 more time units, then make an assignment to C with the current values of A + B". If A or B changes during the 10 time unit wait, that change is missed. You have to wait for another change on A or B after those 10 time units.
As soon as an always block is triggered, it will be executed from the beginning to the end. Verilog is a single-thread simulation engine, therefore only one block can be executed at a time. Nothing else could happen while an always block is executed, unless it contains delay statements or waits on events. In the latter case it will just yield
to allow other blocks to being executed, then it continues.
If an always block changes its inputs by the end of the execution, then next simulation behavior depends on the type of the block:
all v95/v2k always blocks (e.g. always #*) will be re-triggered,
system verilog blocks (e.g, always_comb) will not be re-triggered in the current delta cycle.
With v2k/v95 always blocks you can end up with a zero-delay loop if you are not careful. In other words simulation can hang.
With SystemVerilog blocks you can get an interesting read-before-write condition (when a variable is read before it is written in the same block). This can potentially create simulation/synthesis mismatch.
Each always block is a single thread (unless it contains a fork-join). So, if it's executing, it won't retrigger if something in the sensitivity list changes. (If it does contain a fork-join then it splits into multiple threads once triggered, but again won't retrigger).

Why do we use Blocking statement in Combinatorial Circuits designed using Always Block in Verilog/Systemverilog ? Why not Nonblocking?

Everywhere it is mentioned this as a guideline, but after lot of thought i want to know what harm will it cause if we use Nonblocking statement inside Always Block for Combinatorial also. I won't be mixing the two together. But what i feel is when we use Nonblocking for Combinatorial statements in Always Block it represents the hardware more accurately. Does it not...?
For Example, if we take the following circuit:
In this diagram when the inputs a,b,c are supplied the outputs x1 and x will not be available instantly. There will be gate delays. first x1 will be available and then x will be available. If we use blocking statements both are available instantly. If we use nonblocking, it resembles the hardware more accurately.
For Example, if we take the following code based on the above diagram
module block_nonblock(output logic x,x1,y,y1,input logic a,b,c);
always#* begin : BLOCKING
x1 = a & b;
x = x1 & c;
end
always#* begin : NONBLOCKING
y1 <= a & b;
y <= y1 & c;
end
endmodule
This synthesizes as:
Both are synthesized as And gates, and give same simulation results but when we check for the changes in output in delta time, i feel the Non blocking matches the hardware more accurately as compared to the Blocking.
Also i went through : IEEE P1364.1 / D1.6 Draft Standard for VerilogĀ® Register Transfer Level Synthesis,
which specifies the use of non blocking for Sequential modeling but doesn't specify specifically using blocking for Combinational modeling using Always
Block. It says don't mix the two(Blocking and Nonblocking) in Combinational statements.
So, shouldn't we use nonblocking for combinational statements in always blocks which are dealing with pure combi logic (non sequential/ no clocks involved)
The harm is in simulation; in performance, and in race conditions.
Your NONBLOCKING code executes twice for every change in a or b. Non-blocking assignment updates are scheduled into a later queue of events, and this causes a much bigger rippling effect where blocks get repeatedly executed.
When you simulate RTL code, you are doing so in the absence of physical delays and synthesis tools understand how the logic is going to be implemented. But simulation tools cannot do this and also need to work with non-synthesizable code. They have to execute the code exactly as written. And they also have to deal with massive amounts of concurrency executing code on a processor with a single or limited number of threads. So simulation introduces race condition in software that would not exist in real hardware. Non-blocking assignments are there to prevent those race conditions when writing sequential logic. But they can have the opposite affect if you use them in combinational logic, especially when used in the combinational logic involved in the generation of clocks.
You ask "So, shouldn't we use nonblocking for combinational statemnts in Sequential?" The answer is No.
If you use non-blocking assignments for combinational logic in clocked always blocks, you will get more flip-flops than you expect. Basically, non-blocking assignments in clocked always blocks will behave like flip-flops when you simulate and infer flip-flops when you synthesise.
So,
1 - use blocking assignments for gates and
2 - use non-blocking assignments for flip-flops.
Your very own description of the behaviour of the circuit suggest actually the use of blocking operations.
You say: (emphasys mine)
In this diagram when the inputs a,b,c are supplied the outputs x1 and
x will not be available instantly. There will be gate delays. first x1
will be available and then x will be available. If we use blocking
statements both are available instantly. If we use nonblocking, it
resembles the hardware more accurately.
So you need x1 to be available before x. So your always block must use a blocking assignment, so...
always#* begin : BLOCKING
x1 = a & b;
x = x1 & c;
end
x1 first will have a known value, and then, and only then, x will have a value. If you use non blocking...
always#* begin : NON BLOCKING
x1 <= a & b;
x <= x1 & c;
end
You are telling the simulator that x1 and x will be evaluated at the same time.
Although for synthesis this may work, in simulation this won't, and you want to make sure your simulated circuit works as intended before going over the synthesis phase.
We should not use non-blocking assignments in Combinational block, because if we use non-blocking it will infer the transport delays in the design due to this our results will never come what we expected, but if we use blocking , this blocking will able to suppress the transport delays in the design and we can safely said that there is no glitches in the waveform. So it is recommended that we should use blocking statements in combinational designs.
I have found a satisfactory answer and need input for it. I feel we should use Nonblocking statements for both combinational and sequential statements.
For sequential it is pretty clear y we should use.
I will describe the reason for Combi Blocks.
For combinational segments we will use Nonblocking Statements because, when we use Blocking or NonBlocking statements, even though it gives us the same hardware or RTL in the end; it is the nonblocking statements which shows us the glitches in simulation. Theses glitches will be there in hardware as well (because of Gate Delays), so we can rectify them when we see them in Simulation so that they will cause less harm at a later stage of the design/development cycle.
In the circuit I originally mentioned in the question if we give the inputs together as (A = 1,B = 1,C = 0) and then change them together after say 10ns as (A=1,B=0,C=1) then we can see that there is a glitch. This glitch will be there in actual Hardware as well. But this is only shown in simulation by Nonblocking Statements Output (Y) and not by Blocking Statements Output (X). Once we see a glitch we van take additional measures to prevent this from happening, so that it doesnt happen in hardware.
Hence i feel it is safe to conclude that we must use Nonblocking statements for combi blocks.

nonblocking statements and fork-join in verilog and/or system verilog

Can anyone please tell me the difference between non-blocking begin-end in a procedural block and fork-join. One of my friends told me that the latter in non-synthesizable and are used only in testbenches. Is it true? Are there other differences?
There is no such thing as a non-blocking begin/end construct in Verilog/SystemVerilog. A begin/end block executes each statement in serial order, blocking execution of the next statement until the previous statement completes. The end of the block is reached after the last statement completes. A fork/join executes each statement simultaneously at the same time, and the join of the block is reach after all statements complete.
If there is only one statement in each kind of block, there is no behavioral difference between the two, but basic synthesis tools will not accept fork/join constructs. And there are many other ways of using fork/join that you could write that are behaviorally equivalent to writing the same thing with just begin/end inside of multiple always blocks, but are not considered synthesizable. This is because synthesis tools rely on recognizing templates of coding styles, and have limited resources to support multiple styles of writing code.
Yes your friend is telling the truth. Fork-join blocks are not synthesizable while begin-end blocks are synthesizable.
There is also a difference between these two blocks. Let's write two snippets of code.
initial
begin
begin
A <= 0;
A <= #5 1;
A <= #10 2;
end
$display($time);
end
initial
begin
fork
A = 0;
A = #5 1;
A = #10 2;
join
$display($time);
end
In the first block of code, the begin-end block is executed and completed at time 0 and display function will display 0. However, A will change at time 5 and 15.
In the second block of code, the fork-join block is completed at time 10 and display function will display 10 this time.
There are similarities but they are not equivalent due to the nature of blocking and non-blocking assignments.

Resources