I'm implementing a simple serializer in Verilog, but I do not understand the nuances of when blocking assigns can cause problems. I'm specifically having trouble understanding part of this answer. "However, you should never use blocking assignments for synchronous communication, as this is nondeterministic."
I'm building a block that takes, as an input:
- A bit clock
- A 5-bit parallel data input (the value to be serialized)
- A "Data valid" signal that indicates valid 5-bit data is present
As an output, I have:
- Serial data out
- A "Complete" signal that indicates it's time for a new 5-bit value
- A "Transmitting" signal that's high whenever there's valid serial data going out on the bus
Whenever data valid goes high, the block starts outputting the 5-bit value, one bit a time, starting at the next rising edge of the bit clock. When the last bit is out on the wire, the block signals "complete" so a new 5-bit value can be made available.
Omitting some of the reset logic, the code to do this looks like this:
Now, I can write the block with all non-blocking assigns, but I feel that it hurts readability. That would look something like this:
Both appear to do what I want in simulation, and I favor the 1st one because it's easier for me to read but since I don't understand why using blocking assignments for synchronous communication is nondeterministic, I'm worried that I've coded up a ticking time bomb
The Question: Am I doing something wrong in the 1st code that's going to blow up when I try to synthesize this? Is the 2nd code preferable despite being a bit harder (for me anyway) to read? Is there some 3rd thing I should be doing?
Blocking vs. Nonblocking in Verilog
The concept of Blocking vs. Nonblocking signal assignments is a unique one to hardware description languages. The main reason to use either Blocking or Nonblocking assignments is to generate either combinational or sequential logic. In software, all assignments work one at a time. So for example in the C code below:LED_on = 0; count = count + 1; LED_on = 1;
The second line is only allowed to be executed once the first line is complete. Although you probably didn't know it, this is an example of a blocking assignment. One assignment blocks the next from executing until it is done. In a hardware description language such as Verilog there is logic that can execute concurrently or at the same time as opposed to one-line-at-a-time and there needs to be a way to tell which logic is which.
<= Nonblocking Assignment
= Blocking Assignment
always @(posedge i_clock) begin r_Test_1 <= 1'b1; r_Test_2 <= r_Test_1; r_Test_3 <= r_Test_2; end
The always block in the Verilog code above uses the Nonblocking Assignment, which means that it will take 3 clock cycles for the value 1 to propagate from r_Test_1 to r_Test_3. Now consider this code:always @(posedge i_clock) begin r_Test_1 = 1'b1; r_Test_2 = r_Test_1; r_Test_3 = r_Test_2; end
See the difference? In the always block above, the Blocking Assignment is used. In this example, the value 1 will immediately propagate to r_Test_3. The Blocking assignment immediately takes the value in the right-hand-side and assigns it to the left hand side. Here's a good rule of thumb for Verilog:
In Verilog, if you want to create sequential logic use a clocked always block with Nonblocking assignments. If you want to create combinational logic use an always block with Blocking assignments. Try not to mix the two in the same always block.
Nonblocking and Blocking Assignments can be mixed in the same always block. However you must be careful when doing this! It's actually up to the synthesis tools to determine whether a blocking assignment within a clocked always block will infer a Flip-Flop or not. If it is possible that the signal will be read before being assigned, the tools will infer sequential logic. If not, then the tools will generate combinational logic. For this reason it's best just to separate your combinational and sequential code as much as possible.
One last point: you should also understand the semantics of Verilog. When talking about Blocking and Nonblocking Assignments we are referring to Assignments that are exclusively used in Procedures (always, initial, task, function). You are only allowed to assign the reg data type in procedures. This is different from a Continuous Assignment. Continuous Assignments are everything that's not a Procedure, and only allow for updating the wire data type.