why is this so bad?

M

mksuth

Guest
I'm busy transitioning from vhdl to verilog and I've read several
verilog guidelines which say
"do not mix blocking and non-blocking assignments in the same always
block". What exactly is the problem here?

For example, I would find it convenient to do something like this:

// ....
reg [ADDR_WIDTH-1:0] read_addr,
write_addr;
reg signed [ADDR_WIDTH :0] read_addr_tmp;

always @(posedge clk)
if (reset) begin
write_addr <= 0;
read_addr <= 0;
end else if (enable) begin

// write address
if (write_addr == RAM_DEPTH-1)
write_addr <= 0;
else
write_addr <= write_addr + 1;

// read address
read_addr_tmp = $signed({1'b0,write_addr}) + 2 - $signed
({1'b0,size});
if (read_addr_tmp < 0)
read_addr_tmp = read_addr_tmp + DEPTH;
else if (read_addr_tmp >= DEPTH)
read_addr_tmp = read_addr_tmp - DEPTH;

read_addr <= read_addr_tmp[ADDR_WIDTH-1:0];

end

Is there anything wrong with using blocking assignments to perform
intermediate calculations within sequential processes?
 
On Tue, 25 Aug 2009 06:58:10 -0700 (PDT), mksuth
<mksutherland5@gmail.com> wrote:

I'm busy transitioning from vhdl to verilog and I've read several
verilog guidelines which say
"do not mix blocking and non-blocking assignments in the same always
block". What exactly is the problem here?

For example, I would find it convenient to do something like this:

// ....
reg [ADDR_WIDTH-1:0] read_addr,
write_addr;
reg signed [ADDR_WIDTH :0] read_addr_tmp;

always @(posedge clk)
if (reset) begin
write_addr <= 0;
read_addr <= 0;
end else if (enable) begin

// write address
if (write_addr == RAM_DEPTH-1)
write_addr <= 0;
else
write_addr <= write_addr + 1;

// read address
read_addr_tmp = $signed({1'b0,write_addr}) + 2 - $signed
({1'b0,size});
if (read_addr_tmp < 0)
read_addr_tmp = read_addr_tmp + DEPTH;
else if (read_addr_tmp >= DEPTH)
read_addr_tmp = read_addr_tmp - DEPTH;

read_addr <= read_addr_tmp[ADDR_WIDTH-1:0];

end

Is there anything wrong with using blocking assignments to perform
intermediate calculations within sequential processes?
The only potential problem with this method is the danger of some
other process using read_addr_tmp. It is perfectly ok if you hide that
reg inside its own block by adding a begin to always and declaring it
inside.
--
Muzaffer Kal

DSPIA INC.
ASIC/FPGA Design Services

http://www.dspia.com
 
mksuth <mksutherland5@gmail.com> wrote:
< I'm busy transitioning from vhdl to verilog and I've read several
< verilog guidelines which say
< "do not mix blocking and non-blocking assignments in the same always
< block". What exactly is the problem here?

Personally, I believe in using continuous assignment, and minimal
behavioral verilog, and others that build real systems agree.

Getting into the thought process for digital hardware is hard
enough for some people, and making it harder by mixing them
is probably too much. It might be that you can read it and
understand it, but many others won't be able to.

As I understand it, in the beginning there was only blocking
assignment. The fix was then to add a small delay (any delay)
such that the block would complete before the actual assignment
was done. Non blocking assignment is a nice solution to that.
(Synthesis tools ignore the delay values, but anything non-zero
will get around the blocking assignment.)

< For example, I would find it convenient to do something like this:

(snip)
< Is there anything wrong with using blocking assignments to perform
< intermediate calculations within sequential processes?

I usually try to write assignments in the order such that
the right result is obtained even with blocking. That started
when I was writing software for hardware emulation.

-- glen
 
I agree with Muzaffer on this. A blocking assignment to a locally
declared reg is akin to using variables in vhdl processes, and is 100%
safe.

Plenty of folks "that build real systems" use this method, and it
works great.

Andy
 
On Tue, 25 Aug 2009 06:58:10 -0700 (PDT), mksuth wrote:

I'm busy transitioning from vhdl to verilog and I've read several
verilog guidelines which say
"do not mix blocking and non-blocking assignments in the same always
block". What exactly is the problem here?
Gnashes teeth, swallows another load of beta-blockers....
The problem is some badly-written advice that has, by
some strange alchemy, been transmuted into lettering
chiselled on gold tablets.

Muzaffer and Andy were entirely right; your blocking
assignments represent intermediate combinational logic
in your clocked process, and are fine, PROVIDED you don't
allow the target reg to leak out of the clocked
process. The easy and safe way to achieve that is
by declaring it locally in a named begin...end block
so that you get semantics equivalent to that of a
VHDL variable.

However, just as with VHDL variables you need to be
careful to avoid creating excessively long and complex
logic paths....

read_addr_tmp = $signed({1'b0,write_addr}) + 2 - $signed
({1'b0,size});
if (read_addr_tmp < 0)
read_addr_tmp = read_addr_tmp + DEPTH;
else if (read_addr_tmp >= DEPTH)
read_addr_tmp = read_addr_tmp - DEPTH;
That's a nontrivial piece of logic; at least three adders
and a couple of muxes, I suspect. Your two addresses are
chasing each other around the buffer, and it would surely
be cheaper to build two modulo-DEPTH counters that get
incremented together, and are initialized to be (size-2)
apart. If (size) is a constant, that's really easy.
If not, you'll need to think carefully about what happens
when (size) changes; but in any case it's not obvious that
your arithmetic-intensive implementation is the best one.
--
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.
 
Thank you all for your replies, its good to know this is an acceptable
coding style. I'll be sure to wrap "local regs" within labeled begin
and end blocks as you suggest.

That's a nontrivial piece of logic; at least three adders
and a couple of muxes, I suspect. Your two addresses are
chasing each other around the buffer, and it would surely
be cheaper to build two modulo-DEPTH counters that get
incremented together, and are initialized to be (size-2)
apart. If (size) is a constant, that's really easy.
If not, you'll need to think carefully about what happens
when (size) changes; but in any case it's not obvious that
your arithmetic-intensive implementation is the best one.
The issue here is that size is not a constant (this is a configurable
shifter) and DEPTH, which is the physical depth of the RAM is not
always going to be a power of 2, so a better solution isn't jumping
out at me right now... You do of course raise a good point about the
combinational path and I'll need to ensure the timing requirements on
this are met.
 
On Fri, 28 Aug 2009 06:30:09 -0700 (PDT), mksuth wrote:

The issue here is that size is not a constant (this is a configurable
shifter)
OK, that indeed makes things harder. And if your rather
complicated address calculation logic is small and fast
enough, then that's just fine.

However, it's surely worth fighting just a little
bit with the arithmetic to make the logic path
faster.

read_addr_tmp = $signed({1'b0,write_addr})
+ 2 - $signed({1'b0,size});
if (read_addr_tmp < 0)
read_addr_tmp = read_addr_tmp + DEPTH;
else if (read_addr_tmp >= DEPTH)
read_addr_tmp = read_addr_tmp - DEPTH;

Seems to me that the second overflow (read_addr_tmp >= DEPTH)
can happen only if (size<2), which I suspect is not possible
(surely the +2 is to soak up your memory read and write
latency, so there's no way for size to be meaningfully
less than 2?) - so the "else if" can perhaps be removed.
And then as a further improvement you could perhaps
precalculate both versions of the sum, and use whichever
is appropriate; that might save one adder delay:

read_addr_A = $signed({1'b0,write_addr})
+ 2 - $signed({1'b0,size});
read_addr_B = $signed({1'b0,write_addr})
+ (2-DEPTH) - $signed({1'b0,size});
read_addr <= (read_addr_A < 0) ? read_addr_B : read_addr_A;

It's just a thought. Any such attempts at optimization
are always at the mercy of the synthesis tool anyhow.
And, as usual: if it's small/fast enough, you're done
and there's no point in trying to optimize further.

Worse still, there's probably some really neat trick for
doing this that is well known to everyone except you and me :)
--
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.
 
Thanks for the tip. You're right about removing the second overflow
condition.
 
On Aug 25, 3:15 pm, Andy <jonesa...@comcast.net> wrote:
I agree with Muzaffer on this. A blocking assignment to a locally
declared reg is akin to using variables in vhdl processes, and is 100%
safe.

Plenty of folks "that build real systems" use this method, and it
works great.

Andy
Note that the locally declared reg is still a static; so if you don't
write it before reading it you have created a state holding reg.
Depending on your synthesizer/options etc. this may become a state
element or not.

This in turn could lead to mismatches between simulation and
synthesis.

Declaring the local reg as automatic (puts you into SystemVerilog) is
a way to make sure that your locally declared regs (that you intend to
use as temporary variables) don't hold state in simulation. If your
code has a read before write of the automatic it will always read a X.
 
Ramesh wrote:

Note that the locally declared reg is still a static; so if you don't
write it before reading it you have created a state holding reg.
If this were not true, simulation would not match synthesis.
To avoid confusion, start with simulation.

Depending on your synthesizer/options etc. this may become a state
element or not.
No. It depends only on the code.
Synthesis makes wires, gates and regs
as needed to match the simulation.
Nothing more, nothing less.

-- Mike Treseler
 
On Sep 5, 12:35 pm, Mike Treseler <mtrese...@gmail.com> wrote:
Ramesh wrote:
Note that the locally declared reg is still a static; so if you don't
write it before reading it you have created a state holding reg.

If this were not true, simulation would not match synthesis.
To avoid confusion, start with simulation.

Depending on your synthesizer/options etc. this may become a state
element or not.

No. It depends only on the code.
Synthesis makes wires, gates and regs
as needed to match the simulation.
Nothing more, nothing less.

   -- Mike Treseler
There is nothing wrong with inferring state holding registers with
blocking assignments in verilog or variables in vhdl.

Either way, like Mike said, the synthesis tool will create hardware
that matches the cycle based behavior of your code. So make sure it
simulates the way you want it, and let the synthesis tool worry about
where to put the registers to match the behavior. If you apply re-
timing optimizations in synthesis, the register locations won't match
the code anyway, but the behavior still will.

Andy
 
On Sep 8, 9:34 am, Andy <jonesa...@comcast.net> wrote:
On Sep 5, 12:35 pm, Mike Treseler <mtrese...@gmail.com> wrote:
....
Ramesh wrote:
Note that the locally declared reg is still a static; so if you don't
write it before reading it you have created a state holding reg.

If this were not true, simulation would not match synthesis.
To avoid confusion, start with simulation.

Depending on your synthesizer/options etc. this may become a state
element or not.

No. It depends only on the code.
Synthesis makes wires, gates and regs
as needed to match the simulation.
Nothing more, nothing less.

   -- Mike Treseler
The synthesis semantics / implementation is derived from a independent
analysis of the RTL. There is no particular attempt to "match"
simulation in all cases.

Specifically in the case of local registers there is an option in
Design Compiler to have local registers be inferred as flops or not.

There are many other cases of simulation vs synthesis mismatches; a
common one is "always @(a or b) y = a & b & c;" may be turned into a
And gate by the synthesizer with a warning. Clearly that doesn't match
the simulator. To match the simulation model in this case the
synthesizer would require cell types that don't exist in typical
technology libraries.

(At least in ASIC space synthesis vs simulation mismatches are common
enough problem that there is a sub-industry of RTL rule checker
tools).

As to the comment below on it being ok to inferring state holding
registers with blocking assignments, my comment is specific to the
original poster's expectation of using blocking assigns to do
temporary calculations. If he intends to use local registers to do
temporary calculations i presume he doesn't want flops inferred for
his temporaries.


There is nothing wrong with inferring state holding registers with
blocking assignments in verilog or variables in vhdl.

Either way, like Mike said, the synthesis tool will create hardware
that matches the cycle based behavior of your code.
....
Andy
 
Ramesh wrote:

There are many other cases of simulation vs synthesis mismatches; a
common one is "always @(a or b) y = a & b & c;" may be turned into a
And gate by the synthesizer with a warning. Clearly that doesn't match
the simulator.
I would expect and error for code that bad, but
as long as there is a warning, that is fair enough.
Sounds like a good reason to use synchronous blocks.

(At least in ASIC space synthesis vs simulation mismatches are common
enough problem that there is a sub-industry of RTL rule checker
tools).
That is fortunate for the sub-industry.

As to the comment below on it being ok to inferring state holding
registers with blocking assignments, my comment is specific to the
original poster's expectation of using blocking assigns to do
temporary calculations. If he intends to use local registers to do
temporary calculations i presume he doesn't want flops inferred for
his temporaries.
If flops are unexpectedly inferred, the code is wrong.

-- Mike Treseler
 

Welcome to EDABoard.com

Sponsor

Back
Top