Verilog sequence of non blocking assignments
A would be 2 in simulation, the last defined value takes effect. If they are not in the same block then there could be a race condition depending on the simulator scheduler as to which was defined last in simulation.
I have seen this technique used quite a lot and never seen any unexpected results after synthesis.
From Verilog IEEE 1364-2005 Section 11.4.1 Determinism
Statements within a begin-end block shall be executed in the order in which they appear in that begin-end block. Execution of statements in a particular begin-end block can be suspended in favor of other processes in the model; however, in no case shall the statements in a begin-end block be executed in any order other than that in which they appear in the source.
This is also in SystemVerilog-IEEE1800 2012 as section 4.6 Determinism
Usage of this might be a FSM which sparsely defines its outputs:
always @(posedge clk) begin
out_one <= 1'b0;
out_two <= 1'b0;
out_thr <= 1'b0;
case (state)
2'd1 : out_one <= 1'b1;
2'd2 : out_two <= 1'b1;
2'd3 : out_thr <= 1'b1;
endcase
end
According to the "Determinism" section in the IEEE Std (1800-2009, for example), if those statements are in a begin-end block, A will always be assigned the value 2 in simulation.
However, the Std does not guarantee how the code will be synthesized. The resultig gates probably depend on the synthesis tool. But, a good RTL linting tool will identify such bad coding. Cadence's Hal lint tool issues a warning.
There is nothing nondeterministic about the final value of A
in your code, not for simulation, not for synthesis.
However, to be absolutely exact, there is a possible simulation-synthesis-mismatch if the design contains a trigger on A
. Consider the following example:
module test(input clk, output reg a, b);
always @(posedge clk) begin
a <= 0;
a <= 1;
end
initial b = 0;
always @(posedge a) begin
b <= !b;
end
endmodule
And a test bench:
module tb;
reg clk = 0;
always #5 clk = ~clk;
wire a, b;
test uut (clk, a, b);
initial begin
$monitor("clk=%b a=%b b=%b", clk, a, b);
repeat (100) @(posedge clk);
$finish;
end
endmodule
During simulation both updates a <= 0
and a <= 1
are pushed to the NBA events region and are executed in-order, so a
always ends up being set. However, as the a <= 0
is executed as well, there is a negative pulse with zero width on a
for every clock cycle. This pulse triggers the 2nd always block. This is the simulation output (tested with Icarus Verilog and Modelsim):
clk=0 a=x b=0
clk=1 a=1 b=1
clk=0 a=1 b=1
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=1
clk=0 a=1 b=1
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=1
clk=0 a=1 b=1
clk=1 a=1 b=0
clk=0 a=1 b=0
...
However, in synthesis this will simply assign a
the constant value 1 and b
the constant value zero. (Tested with Yosys and Xilinx Vivado.) So the post-synthesis simulation output looks like this:
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
(Theoretically the first line could still say a=x
, but every decent synthesis tool would optimize the a
-flip-flop away, as both tools in the test did.)
Other than that there is no potential issue with that code, and as @Morgan pointed out correctly in his answer, this is a very usual coding technique for defining the "default values" of output signals before encoding the special cases using conditional assignments (using if
and/or case
).