A note on Verilog® assignments
By Paul Campbell
There's a lot of confusion about the use of the various types of
procedural assignments within Verilog® - this document is an
attempt to describe their differences and to explain when their
use is appropriate.
Types of procedural assignments
There are five basic types of assignment:
'a = b' - a simple assignment - the variable a is assigned the value
'b' immediately.
'a = #N b' - a copy of the value 'b' is made N time units are delayed
then a is assigned - this is exactly equivalent to 'tmp = b; #N; a = tmp;'.
This is often called a 'blocking assignment'. All the statements following
the assignment are blocked until it completes.
'a <= b' this is called a 'non-blocking assignment' - it is implemented
by taking a copy of the value 'b' in a temporary variable, the following statements
are then immediately executed, once all the
currently scheduled events (@(...) and #N waits) have been processed. At that time
the temporary value is assigned into a. If the same variable is scheduled to be
assigned at the same time from different always statements Verilog® does not
specify which order they will be assigned unless both the non-blocking assignments
are in the same always statement - in this case Verilog® defines that
the assignments are done in the same order that they are performed.
'a <= #N b' this is a delayed non-blocking assignment - in this case the value
b is copied into a temporary variable, the following statements are executed
immediately and then after N time units pass by the assignment is performed.
'force a = b' - this forces the value of a to be b no matter what else is assigned
to it - this assignment continues until a 'release a' is executed. 'force' statements
are relatively expensive to implement and IMHO should be avoided except for debugging.
Building pipelines
One of the main areas that people run into confusion is to build register
pipelines - for example a bunch of registers connected input to output which
pass data on every clock edge - in the real world we can do this because there's
a delay from the output of one flop to the input of the next that helps meet
the hold times on the following flop. But in the Verilog® world things
happen in 0 time unless we explicitly plan otherwise.
As a good example consider the problem of swapping the values in two registers
a and b. An obvious first attempt at this problem is:
always @(posedge clk) begin
a = b;
b = a;
end
Of course this doesn't work because a gets overwritten before it's read to be stored
in b.
Obviously we need to read the two values, put them in some temporary place and then later
place them in their new locations after all of them have been read.
There are 3 safe ways of doing this (but see the section on 'traps' below):
1) always @(posedge clk)
a = #N b;
always @(posedge clk)
b = #N a;
2) always @(posedge clk) begin
a <= b
b <= a;
end
3) always @(posedge clk) begin
a <= #N b
b <= #N a;
end
Example 1 uses multiple always statements with 1 blocking assignment per
statement - for this to work safely the assignment must be the last thing
in the always statement.
The second example depends on the non-blocking assignments temporary storage.
The third example does too - but also delays the assignment.
Notes:
Always statements that are NOT clocked (for example always statements
intended to represent combinatorial logic) should only use blocking
assignments without delays.
Assignments to nets/wires where the wire has a single driver are almost exactly
like a combinatorial always statement with a simple assignment:
wire a;
assign a = b&c;
is equivalent to
reg a
always @( b or c)
a = b&c;
for this reason you shouldn't include a delay in an assign statement unless you
have a good reason - in fact dropping delays into random places to fix problems
rather than having a careful model of storage is often an indication of other
problems in your simulation.
Traps
Remember that if you write a variable with a non-blocking write and then
read it later in the same always statement it wont yet have been updated
and you will pick up the previous value - for example:
always @(posedge clk) begin
....
a <= 1;
.....
if (a) ... // gets the previous value
....
b <= a; // gets previous value, not 1
....
end
This is not the normal behavior you would expect from a C-like program so
be careful.
Another possible trap involves UDPs - if you use sequential UDPs (or even if your
flop models use them) then you should realize that the outputs of UDPs are not
safe unless you put delays on them when you instantiate them - used this way they
work in a similar manner to the first pipeline example above.
A third trap involves delays - it has become almost traditional to use '#1'
as the standard pipe delay - but you can be caught if your clock period
gets close to this (or in some cases twice this if you are using latches), it's
better to use a define like #`D and `defining D to be a number much less than a
clock period.
Another trap involves nonblocking assignments '<=' without delays - when these
assignments get performed they can cause other events to occur as a result -
the semantics of Verilog's stratified event queue can be complex and can cause
unexpected results - especially if you have clocks generated from the outputs - using
a delay can stop this from happening.
Finally - if you are modeling storage you had better make sure you have either
a delay or a non-blocking assignment somewhere in the model for the storage element. I
once had to work with an extracted model that had modeled extracted flops as
pairs of latches:
assign m = (~clk ? d : m);
assign q = (clk ? m : q);
Now this happened to work in one simulation environment but not in any others
because the event ordering of the two assignments is undefined.
Rules
While there are many ways to make good verilog models here's a set of rules
that should help you avoid pipe races when coding synthesizable code:
- Create all storage (flops) using always statements on a clock edge
and one (and only one - don't mix them) of the following:
- '<='
- '<= #N' with N != 0
- '= #N' with N != 0 - and each assignment with a separate always statement
- If you have sequential UDPs ('primitives') modeling flops make sure they have a non-zero delay attached to each
place you instance them
- Use '=' everywhere else (including latches and combinatorial blocks)
Other sources of information
After I wrote this I discovered the excellent in depth coverage of this issue
here by Cliff Cummings.
While I mostly agree with his treatment a few things should be noted:
- He fails to point out that Verilog's stratified event queue can get
rerun in the same time if non-blocking events trigger always
statements (or in other words things can be even more complicated than he
describes - all the more reasons for following a careful
assignment regime)
- I believe that blocking assignments with delays, one per separate
always statement is a viable alternative (even a faster one in some
past Verilog® implementations)
- I think you can safely mix blocking assignments (without delays) that model combinatorial
logic (ie temporary variables) and non-blocking assignments that model flops
in the same edge triggered always statement - provided the flop assignments
are the last thing in the always loop (care however must be taken with synthesis
tools which can sometimes get confused and try to generate latches).
|