Mixed Blocking/Nonblocking assignments

On Tue, 20 May 2008 11:40:23 -0800, glen herrmannsfeldt wrote:

Looking at Sternheim & Singh, all the example code uses blocking
assignment. I don't see any discussion that this could be
a problem.
Aaaaaarggggh. Another one to pulp.

Do you have any examples of legal verilog code with multiple
clocked always blocks that evaluate in the wrong order?
Plenty. I don't even need to appeal to nondeterminism;
this one is _guaranteed_ to be broken in any single-threaded
simulator, but gives correct simulation results if nonblocking
assignment is used.

module broken_ring_counter (
input clock,
input reset,
output [1:0] q
);

reg a, b;

always @(posedge clock)
if (reset)
a = 1'b0;
else
a = b;

always @(posedge clock)
if (reset)
b = 1'b0;
else
b = ~a;

assign q = {a, b};

endmodule

Why, oh why, does this argument rage on endlessly?
Why can't people just do it right, and move on to the
real problems?
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
Glen,

Your example and Jonathan's example are fundamentally the same. They
both suffer from a race condition that cannot be deterministically
resolved by the language. Sure, it may run just fine on one particular
compiler, and it may run just fine on 100 different compilers, but you
are never sure if it will run correctly on the next compiler.

The feature of the language does not give specific instructions on how
race conditions are to be resolved. Using blocking assignments in
synchronous logic creates race conditions that can be evaluated either
way.

In Jonathan's example:

always @(posedge clock)
if (reset)
a = 1'b0;
else
a = b;

always @(posedge clock)
if (reset)
b = 1'b0;
else
b = ~a;

Suppose that initially, a is 0 and b is 1. At the clock edge, if the
first block is evaluated first, a will be 1, and b will be 0. If the
second block is evaluated first, a will be 1 and b will be 1.

Now if we convert that example into non-blocking code. No matter which
block is evaluated first, the result is still a=1 and b=1.

~Jason Zheng

On Tue, 20 May 2008 15:24:59 -0800 glen herrmannsfeldt
<gah@ugcs.caltech.edu> wrote:

Jonathan Bromley wrote:
(I wrote)

Do you have any examples of legal verilog code with multiple
clocked always blocks that evaluate in the wrong order?

Plenty. I don't even need to appeal to nondeterminism;
this one is _guaranteed_ to be broken in any single-threaded
simulator, but gives correct simulation results if nonblocking
assignment is used.
(snip)

Why, oh why, does this argument rage on endlessly?
Why can't people just do it right, and move on to the
real problems?

If one wants to understand the features of the language,
one should understand this one. Tested with veriwell it
seems that yours doesn't work, as you say. This one,
more like the way I used to write them, does work.
(and even more, yours works with blocking assignment
for reset, and non-blocking for a and b.)

module ring_counter ( clock, reset, q );
input clock;
input reset;
output [1:0] q;

wire a, b;

register r1(clock,a,b,reset);
register r2(clock,b,~a,reset);

assign q = {a, b};

endmodule

module register(clk, q, in, reset);
input clk, in, reset;
output q;
reg q;
always @(posedge clk)
if(reset) q=0;
else q=in;
endmodule

module test;

integer i;
reg clock,reset;
wire [1:0] q;

ring_counter one(clock,reset,q);

initial begin
#1 clock= 1'b0;
#1 clock=~clock;
#1 clock=~clock;
#1 reset= 1'b1;
#1 clock=~clock;
#1 clock=~clock;
#1 reset= ~reset;
#1 clock=~clock;
#1 clock=~clock;

$display("reset=%h",reset);
for(i=0;i<40;i=i+1) begin
#1 clock=~clock;
$display ("%h %h %h", i,q,clock);
#1 clock=~clock;
$display ("%h %h %h", i,q,clock);
end
end
endmodule

--
Outside of a dog, a book is a man's best friend. Inside a dog it's too
dark to read.
-- Groucho Marx
 
Jonathan Bromley wrote:
(I wrote)

Do you have any examples of legal verilog code with multiple
clocked always blocks that evaluate in the wrong order?

Plenty. I don't even need to appeal to nondeterminism;
this one is _guaranteed_ to be broken in any single-threaded
simulator, but gives correct simulation results if nonblocking
assignment is used.
(snip)

Why, oh why, does this argument rage on endlessly?
Why can't people just do it right, and move on to the
real problems?
If one wants to understand the features of the language,
one should understand this one. Tested with veriwell it
seems that yours doesn't work, as you say. This one,
more like the way I used to write them, does work.
(and even more, yours works with blocking assignment
for reset, and non-blocking for a and b.)

module ring_counter ( clock, reset, q );
input clock;
input reset;
output [1:0] q;

wire a, b;

register r1(clock,a,b,reset);
register r2(clock,b,~a,reset);

assign q = {a, b};

endmodule

module register(clk, q, in, reset);
input clk, in, reset;
output q;
reg q;
always @(posedge clk)
if(reset) q=0;
else q=in;
endmodule

module test;

integer i;
reg clock,reset;
wire [1:0] q;

ring_counter one(clock,reset,q);

initial begin
#1 clock= 1'b0;
#1 clock=~clock;
#1 clock=~clock;
#1 reset= 1'b1;
#1 clock=~clock;
#1 clock=~clock;
#1 reset= ~reset;
#1 clock=~clock;
#1 clock=~clock;

$display("reset=%h",reset);
for(i=0;i<40;i=i+1) begin
#1 clock=~clock;
$display ("%h %h %h", i,q,clock);
#1 clock=~clock;
$display ("%h %h %h", i,q,clock);
end
end
endmodule
 
Jason Zheng wrote:

Your example and Jonathan's example are fundamentally the same. They
both suffer from a race condition that cannot be deterministically
resolved by the language. Sure, it may run just fine on one particular
compiler, and it may run just fine on 100 different compilers, but you
are never sure if it will run correctly on the next compiler.

The feature of the language does not give specific instructions on how
race conditions are to be resolved. Using blocking assignments in
synchronous logic creates race conditions that can be evaluated either
way.
Yes, I was trying to understand if block boundaries or module
boundaries are significant as far as assignment. It does seem
interesting that the result is different with the register
in its own module.

-- glen
 
On Tue, 20 May 2008 17:22:04 -0800, glen herrmannsfeldt wrote:

I was trying to understand if block boundaries or module
boundaries are significant as far as assignment. It does seem
interesting that the result is different with the register
in its own module.
In the bad old days before nonblocking assignments were
introduced in the Verilog language, continuous assign
statements introduced "just enough" delay to deal with
the problem. Since a port connection is effectively
a continuous assign across the port boundary, you could
make blocking update to a flop work "correctly" by
burying it inside a module. I wasn't writing Verilog
in anger myself at that time, but I understand that
this approach was actively promoted by some vendors.

This was never a good idea. Although (as far as I know)
all current simulators reproduce this particular event
ordering behaviour, it is not guaranteed by the language
standard. So I would expect to see Glen's example
(two flops, each in their own module) work in practice,
but I would never tolerate it in any design over which
I had any control.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Tue, 20 May 2008 15:24:59 -0800, glen herrmannsfeldt
<gah@ugcs.caltech.edu> wrote:
yours works with blocking assignment
for reset, and non-blocking for a and b.
Yes, and I think this is what started off the whole thread.
Someone had found a typo in a published example, where
NBA was used in the reset branch and blocking assignment
in the clocked branch of an asynchronously-reset flop model.

In simulation you'll typically get "correct" (i.e. as expected
for the intended hardware design) behaviour even if you use
the wrong kind of assignment in the reset branch. For
asynchronous resets that's entirely understandable, because
the behaviour is asynchronous and there is no concern
about read/write races around a clock edge. For synchronous
resets, your comment (that my ring counter "works" when you
mix the assignment types) is true, but purely an accident of
the design; if another clocked always block were to use the
value of one of those flops, there would be a race between
its being reset synchronously, and its value being used
in the other block.

Synthesis tools (without exception, to the best of my knowledge)
outlaw the mixing of blocking and nonblocking assignment to
any given variable in any always block. There are certainly
a few situations where mixed blocking/nonblocking assignment
to the same variable gives reasonable behaviour, but in
general it's a completely reasonable synthesis restriction
because such a mixture could easily give rise to unrealisable
behaviour. The one situation where people might feel moved
to complain about the restriction is this:

always @(posedge clock or posedge reset)
if (reset)
Q = 0; // asynch reset, blocking!
else
Q <= D; // clocked, must be NBA

I can't find any meaningful cases where this code would
give rise to a race condition in practice. However,
you lose absolutely nothing by using nonblocking assignment
in both branches, and it makes life much easier for synthesis
tools to enforce the "uniform assignment" rule even here.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So, for the last time:

- Keep synthesis happy by avoiding any mixture of blocking
and nonblocking assignment *to the same variable* in any
code that models synthesisable logic. It's easy to find
examples where this rule is strictly unnecessary, but
synthesis tools enforce it and you lose nothing by
observing it.
- In a clocked always block, with or without asynch reset,
NEVER, NEVER, NEVER use blocking assignment to any variable
whose value will be used outside that same always block.
Failure to observe this rule WILL give rise to code that
can suffer races in simulation, and therefore mismatches
between simulation and synthesis.
- In a combinational always block, nonblocking assignments
are always unnecessary and often wrong; stick to blocking
assignments throughout.

That's it.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
Jonathan Bromley wrote:
(snip)

In the bad old days before nonblocking assignments were
introduced in the Verilog language, continuous assign
statements introduced "just enough" delay to deal with
the problem. Since a port connection is effectively
a continuous assign across the port boundary, you could
make blocking update to a flop work "correctly" by
burying it inside a module. I wasn't writing Verilog
in anger myself at that time, but I understand that
this approach was actively promoted by some vendors.
Maybe I was lucky. Most of my early verilog put them
in a module because it followed the modularity of the
design. (Systolic array processors.)

Looking back at some of the old code, I also put
a delay in.

module reg16(q,data,clock);
output [15:0] q;
reg [15:0] q;
input clock;
input [15:0] data;
always @(negedge clock) #10 q=data;
endmodule

It seems likely that is what the sample code I had at
the time did, either from reference books or Xilinx examples.

This was never a good idea. Although (as far as I know)
all current simulators reproduce this particular event
ordering behaviour, it is not guaranteed by the language
standard. So I would expect to see Glen's example
(two flops, each in their own module) work in practice,
but I would never tolerate it in any design over which
I had any control.
It seems that even after it was added, the old methods
stayed around. The Sternheim and Singh book from 1993
has non-blocking assignment in the reference sections,
but not in any of the examples. Some examples use #0
for the delay value.

-- glen
 
Jonathan Bromley wrote:

On Tue, 20 May 2008 15:24:59 -0800, glen herrmannsfeldt
gah@ugcs.caltech.edu> wrote:

yours works with blocking assignment
for reset, and non-blocking for a and b.

Yes, and I think this is what started off the whole thread.
Someone had found a typo in a published example, where
NBA was used in the reset branch and blocking assignment
in the clocked branch of an asynchronously-reset flop model.
Though your example seems to have synchronous reset...

-- glen
 
Jonathan Bromley wrote:

In the bad old days before nonblocking assignments were
introduced in the Verilog language, continuous assign
statements introduced "just enough" delay to deal with
the problem. Since a port connection is effectively
a continuous assign across the port boundary, you could
make blocking update to a flop work "correctly" by
burying it inside a module. I wasn't writing Verilog
in anger myself at that time, but I understand that
this approach was actively promoted by some vendors.

This was never a good idea.
All very well, but serious chips had to be designed
in those days also. I dare to suggest that the backbone
of the Internet has been designed without non-blocking
assignments!

Sometimes I get the impression that those "early"
Verilog designers (I was one) are considered idiots
for not using a feature that wasn't available to them :)

If anyone is to blame, it's the designers of Verilog itself.
I've called this absurd story "horrible" in the past and
I stand by those words.

Jan

--
Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com
Kaboutermansstraat 97, B-3000 Leuven, Belgium
From Python to silicon:
http://myhdl.jandecaluwe.com
 
Jonathan Bromley wrote:

So, for the last time:

- Keep synthesis happy by avoiding any mixture of blocking
and nonblocking assignment *to the same variable* in any
code that models synthesisable logic. It's easy to find
examples where this rule is strictly unnecessary, but
synthesis tools enforce it and you lose nothing by
observing it.
- In a clocked always block, with or without asynch reset,
NEVER, NEVER, NEVER use blocking assignment to any variable
whose value will be used outside that same always block.
Failure to observe this rule WILL give rise to code that
can suffer races in simulation, and therefore mismatches
between simulation and synthesis.
- In a combinational always block, nonblocking assignments
are always unnecessary and often wrong; stick to blocking
assignments throughout.

That's it.
I can't help it, but I find those rules one more example of
how too much Verilog exposure can corrupt even the finest minds :)

We want to avoid races at the HDL level. Then why should the
kind of logic I'm describing influence which types of assignment
I should use to accomplish that? This is a mix of HDL concepts
with implementation considerations - often a source of suboptimal
HDL design.

Moreover, there is a much simpler, conceptually clean alternative:
- use non-blocking for communication (between always blocks)
- use blocking for computation (inside always blocks)

This works always, regardless of the kind of logic I'm describing,
and regardless of which HDL I'm using (terminology aside.)

Jan

--
Jan Decaluwe - Resources bvba - http://www.jandecaluwe.com
Kaboutermansstraat 97, B-3000 Leuven, Belgium
From Python to silicon:
http://myhdl.jandecaluwe.com
 
On Wed, 21 May 2008 02:42:02 -0800, glen herrmannsfeldt wrote:

Though your example seems to have synchronous reset...
Yes; I was trying to decouple the real issue (races at the
clock edge) from the question of whether it's OK to mix
BA and NBA to the same variable, so I thought it best to
have a synch-only design. And if it lacked a reset, it
would have been broken for a different reason :)
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Wed, 21 May 2008 14:15:04 +0200, Jan Decaluwe wrote:

- Keep synthesis happy by avoiding any mixture of blocking
and nonblocking assignment *to the same variable* in any
code that models synthesisable logic. It's easy to find
examples where this rule is strictly unnecessary, but
synthesis tools enforce it and you lose nothing by
observing it.
- In a clocked always block, with or without asynch reset,
NEVER, NEVER, NEVER use blocking assignment to any variable
whose value will be used outside that same always block.
Failure to observe this rule WILL give rise to code that
can suffer races in simulation, and therefore mismatches
between simulation and synthesis.
- In a combinational always block, nonblocking assignments
are always unnecessary and often wrong; stick to blocking
assignments throughout.

I can't help it, but I find those rules one more example of
how too much Verilog exposure can corrupt even the finest minds :)
Although I'm probably guilty as charged of corruption, I think
you may perhaps be overstating the case!

[...]
Moreover, there is a much simpler, conceptually clean alternative:
- use non-blocking for communication (between always blocks)
- use blocking for computation (inside always blocks)
The first two of my three "rules" are totally consistent with
this and, indeed, the second of them follows directly from
your alternative - as you are perfectly well aware.

My third "rule" is less clean, I agree, but it is worth noting
that nonblocking variable update (i.e. VHDL signal assignment)
is not at all necessary to build race-free *combinational* logic.
The sensitivity list on every combinational block assures safe
execution ordering among the various blocks. This is just as
well, because continuous assignment in Verilog has semantics
much closer to blocking than to nonblocking assignment, and it
is commonly and correctly used to model combinational logic.
I agree, though, that it drives a wedge between VHDL and
Verilog coding of combinational logic.

The real problem with coding combinational logic comes when
you are trying to describe clock gating or buffering. In this
case, real physical hardware can easily suffer the same races
that you can get in simulation. Use of blocking update in
combinational logic can help to work around that and make
your clock gating work in RTL simulation the same way as you
will finally make it work in the real hardware, with all those
nasty extra delays that you or your P&R tool will introduce
to fix the hold violations....
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Wed, 21 May 2008 14:04:36 +0200, Jan Decaluwe wrote:

[using port boundaries to fix simulation races]
was never a good idea.

All very well, but serious chips had to be designed
in those days also.

Sometimes I get the impression that those "early"
Verilog designers (I was one) are considered idiots
for not using a feature that wasn't available to them :)
No, no, no. I hope you don't believe I'm so arrogant
or so ignorant as to suggest that. However, I *would*
consider you an idiot if you failed to use NBA in your
clocked logic today :)

If anyone is to blame, it's the designers of Verilog itself.
I've called this absurd story "horrible" in the past and
I stand by those words.
Absurd it may be, but (as you are better aware than I)
we have little choice but to live with it and make the
best of it. For what it's worth, I totally agree with
you about the "horrible" part.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
On Wed, 21 May 2008 01:06:15 -0800, glen herrmannsfeldt wrote:

Looking back at some of the old code, I also put
a delay in.

[...]
always @(negedge clock) #10 q=data;
Perfect, until the simulated clock period gets
reduced to #10 or shorter...

See Jan Decaluwe's post. The lack of NBA in early
Verilog led to all manner of hackery. It had to be
done at the time, but it's inexcusable today.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
Jan wrote:

Moreover, there is a much simpler, conceptually clean alternative:
- use non-blocking for communication (between always blocks)
- use blocking for computation (inside always blocks)
Jonathan wrote:

- In a combinational always block, nonblocking assignments
are always unnecessary and often wrong; stick to blocking
assignments throughout.
Thanks to Jan and Jonathan
for another pass at the variable assignment rules.

Since I have already adopted an unrelated style rule that says:
"- don't use combinational always blocks"
I intend to adopt Jan's two line formulation as
sufficient for me, and easy to remember.

-- Mike Treseler
 
On May 8, 3:24 pm, Jonathan Bromley <jonathan.brom...@MYCOMPANY.com>
wrote:
b) It is easier to reason about your clocked logic if
you use only nonblocking assignments in it. Registers
thus assigned-to are sure to imply flops. Registers
written using blocking assignment may or may not imply
flops depending on how you used them. You can exploit
this to do various creative things, and ALL credible
synthesis tools support such use, but it takes a good
level of understanding of synthesis to be able to do it
with confidence.
While it is easier to understand the structure of the logic implied by
the description, it is often more difficult to understand its behavior
if you use only non-blocking assignments for registers. With
increasing use of re-timing optimizations that move registers around/
between/through combinatorial logic clouds, the exact location/
contents of registers in the synthesized circuit will not match what
was neatly described in non-blocking assignments anyway. The only
thing that will be preserved in circuitry is the cycle-accurate
behavior of the description. Therefore it is preferable to focus on a
descriptive style that best illustrates the cycle-accurate behavior,
including blocking assignments that may result in registers.

Obvious exceptions to this preference include synchronization
boundaries that rely on the explicit location/contents of registers,
which should be excluded from re-timing optimizations anyway.

As to the level of understanding of synthesis required to use blocking
assignments with confidence, I agree whole heartedly. However, if we
are ever going to evolve our design process past writing fancy
netlists of registers and combinatorial functionality, we will have to
reach that level of understanding.

Andy
 

Welcome to EDABoard.com

Sponsor

Back
Top