Race Conditions in verilog

L

leon

Guest
Please consider this piece of code:

I have 2 always (*) combinatorial blocks and one sequnetial block. c_to
from the first combinatorial loop feeds into the second always(*) block
which produces c_ld which triggers the first. Although this is
synthesizable code the NC simulator I use freezes on this code as it is
toggling between the 2 always(*) blocks. However if I place #1 to the
first c_to assignment then it works or if I comment it out and use an
else clause it works as well.

Does this work with other simulators or is this a bug in NC or a
violation of verilog LRM. Please view c_to as a nor gate connected to
cnt but this is a race in the simulator... Any comments anyone!!


module test
(
input clk,
input rst_n,
input a,
input [3:0] b,
output wire [6:1] c
);

reg [8:0] cnt;
reg [2:0] fsm_state;

reg [8:0] c_cnt;
reg [2:0] c_fsm_state;
reg c_to;
reg c_ld;
reg [6:1] c_c;

assign c = c_c;

always @(*)
begin
c_cnt = cnt;
c_to = 0; // Comment this line

if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;

if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)

always @(*)
begin
c_fsm_state = fsm_state;
c_ld = 0;
c_c = 0;

case (fsm_state)
3'd0:
begin
if (a)
begin
c_ld = 1'b1;
c_fsm_state = 3'd1;
end // if (gain_change)
c_c = 0;
end // case: 3'd0
3'd1:
begin
if (c_to)
begin
c_fsm_state = 3'd2;
c_ld = 1'b1;
end
c_c = 6'b100000;
end
default:
begin
c_fsm_state = 0;
c_ld = 0;
c_c = 0;
end
endcase // case(fsm_state)
end // always @ (*)


always @(posedge clk or negedge rst_n)
if (~rst_n)
begin
fsm_state <= 0;
cnt <= 0;
end
else
begin
fsm_state <= c_fsm_state;
cnt <= c_cnt;
end

endmodule
 
Hi,

both always are connected with the net c_ld.
if the second is evaluated, c_ld goes low, so the first is evaluated.
as c_ld changed, c_cnt is modifed, c_to is modifed, so the second always
is evaluated.
1. the code is not clean.
2. perhaps changing assignemnt = by <= will make the simulation works.



leon a écrit :
Please consider this piece of code:

I have 2 always (*) combinatorial blocks and one sequnetial block. c_to
from the first combinatorial loop feeds into the second always(*) block
which produces c_ld which triggers the first. Although this is
synthesizable code the NC simulator I use freezes on this code as it is
toggling between the 2 always(*) blocks. However if I place #1 to the
first c_to assignment then it works or if I comment it out and use an
else clause it works as well.

Does this work with other simulators or is this a bug in NC or a
violation of verilog LRM. Please view c_to as a nor gate connected to
cnt but this is a race in the simulator... Any comments anyone!!


module test
(
input clk,
input rst_n,
input a,
input [3:0] b,
output wire [6:1] c
);

reg [8:0] cnt;
reg [2:0] fsm_state;

reg [8:0] c_cnt;
reg [2:0] c_fsm_state;
reg c_to;
reg c_ld;
reg [6:1] c_c;

assign c = c_c;

always @(*)
begin
c_cnt = cnt;
c_to = 0; // Comment this line

if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;

if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)

always @(*)
begin
c_fsm_state = fsm_state;
c_ld = 0;
c_c = 0;

case (fsm_state)
3'd0:
begin
if (a)
begin
c_ld = 1'b1;
c_fsm_state = 3'd1;
end // if (gain_change)
c_c = 0;
end // case: 3'd0
3'd1:
begin
if (c_to)
begin
c_fsm_state = 3'd2;
c_ld = 1'b1;
end
c_c = 6'b100000;
end
default:
begin
c_fsm_state = 0;
c_ld = 0;
c_c = 0;
end
endcase // case(fsm_state)
end // always @ (*)


always @(posedge clk or negedge rst_n)
if (~rst_n)
begin
fsm_state <= 0;
cnt <= 0;
end
else
begin
fsm_state <= c_fsm_state;
cnt <= c_cnt;
end

endmodule
 
Hi,

Nope, changing to non-blocking doesnt work. The 2 always(*) block are
combinatorial and I have used blocking here. Its just the way
simulators interpret the code. This is a case of synthesizable code
that wont work. If I change c_to to a wire and use continous assign
outside this always block as below it works.

wire c_to = (cnt == 0)? 1'b1: 1'b0;

Because cnt is a flip-flop there is no combinatorial loop.


Jerome wrote:
Hi,

both always are connected with the net c_ld.
if the second is evaluated, c_ld goes low, so the first is evaluated.
as c_ld changed, c_cnt is modifed, c_to is modifed, so the second always
is evaluated.
1. the code is not clean.
2. perhaps changing assignemnt = by <= will make the simulation works.



leon a écrit :
Please consider this piece of code:

I have 2 always (*) combinatorial blocks and one sequnetial block. c_to
from the first combinatorial loop feeds into the second always(*) block
which produces c_ld which triggers the first. Although this is
synthesizable code the NC simulator I use freezes on this code as it is
toggling between the 2 always(*) blocks. However if I place #1 to the
first c_to assignment then it works or if I comment it out and use an
else clause it works as well.

Does this work with other simulators or is this a bug in NC or a
violation of verilog LRM. Please view c_to as a nor gate connected to
cnt but this is a race in the simulator... Any comments anyone!!


module test
(
input clk,
input rst_n,
input a,
input [3:0] b,
output wire [6:1] c
);

reg [8:0] cnt;
reg [2:0] fsm_state;

reg [8:0] c_cnt;
reg [2:0] c_fsm_state;
reg c_to;
reg c_ld;
reg [6:1] c_c;

assign c = c_c;

always @(*)
begin
c_cnt = cnt;
c_to = 0; // Comment this line

if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;

if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)

always @(*)
begin
c_fsm_state = fsm_state;
c_ld = 0;
c_c = 0;

case (fsm_state)
3'd0:
begin
if (a)
begin
c_ld = 1'b1;
c_fsm_state = 3'd1;
end // if (gain_change)
c_c = 0;
end // case: 3'd0
3'd1:
begin
if (c_to)
begin
c_fsm_state = 3'd2;
c_ld = 1'b1;
end
c_c = 6'b100000;
end
default:
begin
c_fsm_state = 0;
c_ld = 0;
c_c = 0;
end
endcase // case(fsm_state)
end // always @ (*)


always @(posedge clk or negedge rst_n)
if (~rst_n)
begin
fsm_state <= 0;
cnt <= 0;
end
else
begin
fsm_state <= c_fsm_state;
cnt <= c_cnt;
end

endmodule
 
"leon" <noel.vargese@gmail.com> writes:
Nope, changing to non-blocking doesnt work. The 2 always(*) block are
combinatorial and I have used blocking here. Its just the way
simulators interpret the code. This is a case of synthesizable code
that wont work. If I change c_to to a wire and use continous assign
outside this always block as below it works.

wire c_to = (cnt == 0)? 1'b1: 1'b0;

Because cnt is a flip-flop there is no combinatorial loop.
....
always @(*)
begin
c_cnt = cnt;
c_to = 0; // Comment this line

if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;

if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)
....

Yes, changing c_to to a wire breaks the combinatorial loop, so does
simply splitting the always block. The "problem" with always @* is
that it looks at all things which might trigger a change and runs the
entire block if it does. Doing that causes something to change that
causes the other always @* block (that you wrote I omitted) to run,
and the loop continues on. Beaking the first always @* block up,
should break the loop, because c_to doesn't really depend on c_ld, it
just looks like it does to the simple logic implied by always @*.

always @(*)
begin
c_cnt = cnt;
if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;
end // always @ (*)

always @(*)
begin
c_to = 0; // Comment this line
if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)

Hope this helps,
-Chris

*****************************************************************************
Chris Clark Internet : compres@world.std.com
Compiler Resources, Inc. Web Site : http://world.std.com/~compres
23 Bailey Rd voice : (508) 435-5016
Berlin, MA 01503 USA fax : (978) 838-0263 (24 hours)
------------------------------------------------------------------------------
 
There is a combinatorial loop. The first always@* block uses "c_ld" and
produces "c_to". The second always@* block uses "c_to" and produces
"c_ld". Assume the first always block executes. This produces an update
event for variable "c_to". The second always block sees this so the
always block is executed. Executing the second always block produces an
update event for "c_ld" which causes the first always block to get
evaluated again, etc. etc. etc.

The best way to eliminate this would be to put both combinatorial
blocks in the same always@* statement.

David Walker
leon wrote:
Hi,

Nope, changing to non-blocking doesnt work. The 2 always(*) block are
combinatorial and I have used blocking here. Its just the way
simulators interpret the code. This is a case of synthesizable code
that wont work. If I change c_to to a wire and use continous assign
outside this always block as below it works.

wire c_to = (cnt == 0)? 1'b1: 1'b0;

Because cnt is a flip-flop there is no combinatorial loop.


Jerome wrote:
Hi,

both always are connected with the net c_ld.
if the second is evaluated, c_ld goes low, so the first is evaluated.
as c_ld changed, c_cnt is modifed, c_to is modifed, so the second always
is evaluated.
1. the code is not clean.
2. perhaps changing assignemnt = by <= will make the simulation works.



leon a écrit :
Please consider this piece of code:

I have 2 always (*) combinatorial blocks and one sequnetial block. c_to
from the first combinatorial loop feeds into the second always(*) block
which produces c_ld which triggers the first. Although this is
synthesizable code the NC simulator I use freezes on this code as it is
toggling between the 2 always(*) blocks. However if I place #1 to the
first c_to assignment then it works or if I comment it out and use an
else clause it works as well.

Does this work with other simulators or is this a bug in NC or a
violation of verilog LRM. Please view c_to as a nor gate connected to
cnt but this is a race in the simulator... Any comments anyone!!


module test
(
input clk,
input rst_n,
input a,
input [3:0] b,
output wire [6:1] c
);

reg [8:0] cnt;
reg [2:0] fsm_state;

reg [8:0] c_cnt;
reg [2:0] c_fsm_state;
reg c_to;
reg c_ld;
reg [6:1] c_c;

assign c = c_c;

always @(*)
begin
c_cnt = cnt;
c_to = 0; // Comment this line

if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;

if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)

always @(*)
begin
c_fsm_state = fsm_state;
c_ld = 0;
c_c = 0;

case (fsm_state)
3'd0:
begin
if (a)
begin
c_ld = 1'b1;
c_fsm_state = 3'd1;
end // if (gain_change)
c_c = 0;
end // case: 3'd0
3'd1:
begin
if (c_to)
begin
c_fsm_state = 3'd2;
c_ld = 1'b1;
end
c_c = 6'b100000;
end
default:
begin
c_fsm_state = 0;
c_ld = 0;
c_c = 0;
end
endcase // case(fsm_state)
end // always @ (*)


always @(posedge clk or negedge rst_n)
if (~rst_n)
begin
fsm_state <= 0;
cnt <= 0;
end
else
begin
fsm_state <= c_fsm_state;
cnt <= c_cnt;
end

endmodule
 
leon wrote:
Does this work with other simulators or is this a bug in NC or a
violation of verilog LRM. Please view c_to as a nor gate connected to
cnt but this is a race in the simulator... Any comments anyone!!
This is perfectly legitimate behavior from the simulator for the code
you have written. Other simulators may behave the same way, or they
may not. It largely depends on how they process zero-width glitching
of the values of variables.

You have created an apparent combinational loop here, that passes
through both always blocks. As Chris pointed out, this is only
apparent, as there is not an actual loop between c_to and c_ld. It is
only their clumping together with other logic in the same always blocks
that creates the apparent loop. But as long as they are together, the
language requires recalculating the entire always block as a unit, not
a subset of it. So there is a loop in the required calculations.

A combinational loop alone would not cause this problem, as long as it
is not oscillatory. It would settle into a stable state and stop
calculating as soon as recalculating one of the always blocks did not
cause any change in its outputs. But the way you have written it,
calculating the outputs causes a glitch in the outputs, even if the
final value does not change. You are assigning a default value of 0 to
c_to and to c_ld, and then conditionally changing it back to the value
that it started with. So the final value is the same, but you created
a zero-width glitch, and the other always block responds to that value
change by re-evaluating. Assigning the default value in an else clause
or in the default of a case will accomplish the same thing, but without
creating a glitch.

You can see these glitches in a waveform viewer in the simulator,
though you may have to set something special to tell it you want to see
zero-width glitches. You could also see them by putting in an 'always
@(posedge c_to) $display("positive edge on c_to");' into your design.
From the viewpoint of simulation, these narrow glitches are quite real,
even if you consider them to be just an artifact of your coding style.

If these outputs passed through nets before reaching the other always
block (as they would if they were passed through module ports), then
these zero-width glitches would get filtered out by the scheduling onto
the net. But your always blocks are waiting directly on the variables,
so they see the glitches. Nonblocking assignments do not filter them
out, because nonblocking assignments do not do "inertial descheduling"
(or "event cancellation"). The glitch still occurs.

There is a tool option that can be used in NC to cause it to filter out
zero-width glitches on regs which happen faster than an always block
can respond. However, I don't believe this option can be used with @*,
because it conflicts with the ability to support references to memories
in the @*.

Again, this is legitimate behavior. The simulator is just doing what
your Verilog code says to do. There have been a number of suggestions
for avoiding the problem. If either of the always blocks is modified
to stop glitching its output when it is evaluated, then the evaluation
will stabilize, as you found when you changed to using an else clause.
If you break up the always blocks into smaller pieces that calculate
separate outputs, then you can eliminate the closed loop in the
evaluation (at least when there is not an actual combinational loop).
If you put any kind of delay in (as you did with the #1, or even with
the delta cycle of delay that you get when scheduling a net in NC), you
will filter out the zero-width glitch. You could also assign these
internediate calculated values to a termporary variable, and only
assign that to the actual output when you have the actual value. That
would also eliminate the glitch.
 
This really comes down to what is a combinatorial loop. If you think in
terms of the netlist that will be produced c_to is a nor gate taking in
all the bits of cnt which is a flip-flop. I can see this as a
simulation loop depending on how things are updated in a time slice by
the simulator. If I remove c_to from the first always(*) block and use
continous assign wire c_to = ~(|cnt); - the design intent is preserved
and c_to is still a gate and has the same logic as it had when it was a
procedural assign within the first always(*) block. By doing this yes I
have broken the simulation loop caused by the simulator but the
combinatorial logic has not changed.

Is this just a misunderstanding of the terminology combinatorial
loop!!!!

Leon

dbwalker0min@gmail.com wrote:
There is a combinatorial loop. The first always@* block uses "c_ld" and
produces "c_to". The second always@* block uses "c_to" and produces
"c_ld". Assume the first always block executes. This produces an update
event for variable "c_to". The second always block sees this so the
always block is executed. Executing the second always block produces an
update event for "c_ld" which causes the first always block to get
evaluated again, etc. etc. etc.

The best way to eliminate this would be to put both combinatorial
blocks in the same always@* statement.

David Walker
leon wrote:
Hi,

Nope, changing to non-blocking doesnt work. The 2 always(*) block are
combinatorial and I have used blocking here. Its just the way
simulators interpret the code. This is a case of synthesizable code
that wont work. If I change c_to to a wire and use continous assign
outside this always block as below it works.

wire c_to = (cnt == 0)? 1'b1: 1'b0;

Because cnt is a flip-flop there is no combinatorial loop.


Jerome wrote:
Hi,

both always are connected with the net c_ld.
if the second is evaluated, c_ld goes low, so the first is evaluated.
as c_ld changed, c_cnt is modifed, c_to is modifed, so the second always
is evaluated.
1. the code is not clean.
2. perhaps changing assignemnt = by <= will make the simulation works.



leon a écrit :
Please consider this piece of code:

I have 2 always (*) combinatorial blocks and one sequnetial block. c_to
from the first combinatorial loop feeds into the second always(*) block
which produces c_ld which triggers the first. Although this is
synthesizable code the NC simulator I use freezes on this code as it is
toggling between the 2 always(*) blocks. However if I place #1 to the
first c_to assignment then it works or if I comment it out and use an
else clause it works as well.

Does this work with other simulators or is this a bug in NC or a
violation of verilog LRM. Please view c_to as a nor gate connected to
cnt but this is a race in the simulator... Any comments anyone!!


module test
(
input clk,
input rst_n,
input a,
input [3:0] b,
output wire [6:1] c
);

reg [8:0] cnt;
reg [2:0] fsm_state;

reg [8:0] c_cnt;
reg [2:0] c_fsm_state;
reg c_to;
reg c_ld;
reg [6:1] c_c;

assign c = c_c;

always @(*)
begin
c_cnt = cnt;
c_to = 0; // Comment this line

if (c_ld)
c_cnt = {b, 5'b0};
else
c_cnt = cnt - 1;

if (cnt == 0)
c_to = 1'b1;
/*else // Uncomment this line and the next
c_to = 0;*/
end // always @ (*)

always @(*)
begin
c_fsm_state = fsm_state;
c_ld = 0;
c_c = 0;

case (fsm_state)
3'd0:
begin
if (a)
begin
c_ld = 1'b1;
c_fsm_state = 3'd1;
end // if (gain_change)
c_c = 0;
end // case: 3'd0
3'd1:
begin
if (c_to)
begin
c_fsm_state = 3'd2;
c_ld = 1'b1;
end
c_c = 6'b100000;
end
default:
begin
c_fsm_state = 0;
c_ld = 0;
c_c = 0;
end
endcase // case(fsm_state)
end // always @ (*)


always @(posedge clk or negedge rst_n)
if (~rst_n)
begin
fsm_state <= 0;
cnt <= 0;
end
else
begin
fsm_state <= c_fsm_state;
cnt <= c_cnt;
end

endmodule
 

Welcome to EDABoard.com

Sponsor

Back
Top