Home Download Documentation FAQ Report a Bug SourceForge page

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:
  1. 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
  2. If you have sequential UDPs ('primitives') modeling flops make sure they have a non-zero delay attached to each place you instance them
  3. 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).