Nonblocking assignments are very common in VHDL. They have some pitfalls to look out for. VHDL is different from Verilog in that it forces the use of nonblocking assignments for signals. This means a design will use nonblocking assignments somewhere.
When a process is evaluated, multiple nonblocking assigns can be encountered. Each assignment is evaluated, but the value isn’t assigned until the end of the process. Where multiple non-blocking assignments to the same signal are encountered, the one the is encountered last is the one that has priority.
-- inside a process if (x = y) then z <= '1'; else z <= '0' end if; -- more code z_d <= z;
Above is a simple example of non-blocking assignments. This block of code, when inside a process, will result in two registers, “z” and “z_d”. “z” will have (x=y) as the input, and “z_d” will have “z” as its input.
The important thing to notice is that “z_d” is getting the value that “z” had upon entering the process, not the value it would have after the if-else block. This is because that value will not be assigned to “z” until the end of the process. In fact, the line assigning “z_d” can be moved above the if-else block with no change in the code’s meaning.
-- inside a process z <= '0'; -- further down in the process if (x = y) then z <= '1'; end if; --more code z_d <= z;
This is another example of nonblocking assignments. The code synthesizes the same as the code in the previous example. The difference is that “z” is given a default at the top of the process. When the code is evaluated and (x /= y), the z <= ‘0’ assignment is the only one seen. When (x = y), both assignments are seen. In such a case, the last one encountered has priority.
--inside a process. if (x = y) then z <= '1'; end if if (x /= y) then z <= '0'; end if; -- more code z_d <= z;
The above code will also synthesize to the same thing. While the two previous styles are generally accepted, this third style is more dangerous. Its usually best to avoid it. There are several issues with this style:
- It hides the complexity of the code. There is an inferred priority structure, and this structure is spread out across the entire process.
- It can make code difficult to read. The assignment of a signal is now spread across the entire process. This makes it difficult to determine the logic that assigns a value to the signal.
- It makes the code difficult to write/modify. This is because the code now has extra position dependence. Code inside a process can no longer be re-ordered to appear near other, related code.
In general, its best to use one of the first two coding styles. The first style is the most direct in showing the complexity of the logic. The second style is common for state machines, or places where a default assignment occurs in several cases. The last style is sometimes used for slower running code.
One final note, this article states that the value is assigned at the end of the process. It is actually assigned after all processes for the given event have been processed. Thus, two clocked processes can use non-blocking assignments for signals and not worry about which is evaluated first.