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).