2
votes

I have read "Nonblocking Assignments in Verilog Synthesis, Coding Styles that Kill!" by Clifford Cummings. He says that the following code (page 12, simplified) is a correct implementation of a flip-flop often used in textbooks, even if not exactly the kind that anyone should use. The document won a best paper award, so I assume the claim is true.

module ff (q, d, clk)
  output q;
  input d, clk;
  reg q;

  always @(posedge clk)
    q = d;
endmodule

I would like to know why this would continue to work correctly if two or more of these flip-flops were connected in series. Say

module two_ffs (q, d, clk)
  input d, clk;
  output q;

  wire tmp;

  ff firstff (tmp, d, clk);
  ff secondff (q, tmp, clk);
endmodule

The way I see it, it's possible that the value of tmp is updated before it is used by secondff, thus resulting in one flip-flop rather than two. Can someone please tell me what part of the standard says that cannot happen? Many thanks.

[not that I would ever contemplate writing code like that, I just want to understand the blocking/nonblocking behavior even in cases when poor coding style makes the meaning non-obvious]

Added later:

I now think the paper is unlikely to be correct. Section 5 "Scheduling Semantics" of the 1364-2201 Verilog standard explains what happens. In particular, section 5.6.6 "Port connections" on page 68 says that unidirectional ports are just like continuous assignments. In turn, a continuous assignment is just an always block sensitive to everything. So the bottom line is that that the two instantiations of an ff in my example below are equivalent to a module with multiple always clauses, which everyone would agree is broken.

Added after Clive Cummings answered the question:

I am grateful to CC for pointing out that that the statements above taken out of section 5 of the standard only refer to the timing of update events, and do not imply literal equivalence between e.g. some continuous assignments and always blocks. Nevertheless, I think they explain why some simulators (e.g. Icarus Verilog) will produce different simulation results with a blocking and a non-blocking assignment in the "flip-flop". [On a larger example, I got 2 apparent ffs with a blocking assignment, and the correct five with a non-blocking one.] Other simulators (e.g. Modelsim with default options or Cver) seem to produce the same result no matter which form of assignment is used.

2
Are you sure that this is an example of a correct implementation? That looks like a textbook example of an incorrect design to me. I think your understanding is correct.Tim
I actually don't see that first section anywhere in this paper . I think you're probably misreading it, or can you point to exactly where you got that code sample from? I don't see it on page 12 (I may have a different version than you).Tim
Tim: sorry about not providing a link (added now). I refer to rev 1.3 of the document whereas you found rev 1.2. Sorry again. It is in section 9 in both documents. "Example 13 shows a flipflop model that appears in most Verilog test books". Then its caption is "Simple flawed ..... but it works!".user1002059
Ok, I see now. Hmm, I can't think of why he would make that claim. Anyway: "Readers are encouraged to send email to Cliff Cummings ( redacted ) any time they find potential mistakes or if they would like to suggest improvements." Maybe you can ask the author :)Tim
If you have access to a university library, you may want to consider getting IEEE 1364.1 Standard for Verilog Register Transfer Level Synthesis.user597225

2 Answers

6
votes

All -

A few corrections and updates. Section 5.6.6 of the 2001 Verilog Standard does not say that "unidirectional ports are just like continuous assignments," it says "Ports connect processes through implicit continuous assignment statements." There is a difference that I will note below.

Second, "a continuous assignment is just an always block sensitive to everything," is not true. Continuous assignments Drive values onto nets that can be driven by other sources with pre-defined resolution functions as described in the Verilog Standard. Always blocks Change values of variables and last procedural change wins (no resolution).

Regarding my description of the 1-always block flip-flop, my description in the paper is not 100% accurate (but is usually accurate). The 2-instantiated flip-flop model in theory does have a race condition, though it is rarely seen. The race is rarely seen because when you make an always block assignment to a variable that is declared as an output, Verilog compilers automatically throw in an "implicit continuous assignment statement" (IEEE-1364-2001, Section 5.6.6, 1st paragraph) to convert the procedural variable into a net-Driving assignment (you never see this happen!) This conversion is typically sufficient to introduce the equivalent of a nonblocking assignment delay on the port, so the simulation works. I have experimented in the past with compiler optimization switches that effectively remove the module ports between the flip-flops and have observed the unwanted race conditions, so technically, my description of an okay 1-always, blocking-assignment flip-flop is not 100% correct; hence, you should still use the nonblocking assignments described in the paper.

The 2-always blocking-assignment example in the same module has a definite race condition. As written, it will probably work because most compilers execute the code top-down, but if you reverse the order of the always blocks, you will probably see a race.

Regards - Cliff Cummings - Verilog & SystemVerilog Guru

0
votes

Reading Version 1.3 of the paper, Section 9 Example 13. The text under it explains that it is OK if the module only contains a single always block. My current understanding is that it is not an issue between separate modules. Allowing your example to work. However if a module contained multiple always blocks then the order of execution is undefined and will lead to the race conditions talked about in section 2 of the paper.

The example below is almost the same as the 2 flop example in the question, except it is in 1 module and so has an undefined order of execution, this will likely not work.

module ff (q, d, clk)
  output reg q;
  input d, clk;
  reg d_delay ;

  always @(posedge clk)
    d_delay = d;

  always @(posedge clk)
    q = d_delay;

endmodule