SV ‘always_comb’ safer than Verilog ‘assign’

I wrote here that

It’s not uncommon for the right-hand side of a continuous assignment statement to be a function call, or to contain a function call. If the function depends on a global net or variable, then it’s more accurate to use always_comb, because it “is sensitive to changes within the contents of a function”.

For example,

module test(input in, output logic out1, out2);

  function f(); return in; endfunction

  assign out1 = f();

  always_comb out2 = f();

  initial begin
    #1 $display(out1, , out2);
    force in = 1'b1;
    #1 $display(out1, , out2);
    force in = 1'b0;
    #1 $display(out1, , out2);
  end

endmodule

Additionally, if you want to be warned about latches, use always_comb instead of assign. For example, the following Verilog uses latched logic

module test(input in0, in1, sel_in, output out0, out1, sel_out);
reg side0, side1;

assign sel_out = f(sel_in);
assign {out0, out1} = {side0, side1};

function automatic f(input sel_in);
begin
  case (sel_in)
    1'b0: side0 = in0;
    1'b1: side1 = in1;
  endcase
  f = sel_in;
end
endfunction

initial begin
  force in0 = 1'b1;
  force in1 = 1'b0;
  force sel_in = 1'b0;
  #1 $displayb({out0, out1, sel_out});
  force in0 = 1'b0;
  force in1 = 1'b1;
  force sel_in = 1'b1;
  #1 $displayb({out0, out1, sel_out});
end

endmodule

But rewriting with a SystemVerilog always_comb will cause a warning to be issued

module test(input in0, in1, sel_in, output out0, out1, sel_out);
reg side0, side1;

logic temp;
assign sel_out = temp;
always_comb begin
  temp = f(sel_in);
end
assign {out0, out1} = {side0, side1};

function automatic f(input sel_in);
begin
  case (sel_in)
    1'b0: side0 = in0;
    1'b1: side1 = in1;
  endcase
  f = sel_in;
end
endfunction

initial begin
  force in0 = 1'b1;
  force in1 = 1'b0;
  force sel_in = 1'b0;
  #1 $displayb({out0, out1, sel_out});
  force in0 = 1'b0;
  force in1 = 1'b1;
  force sel_in = 1'b1;
  #1 $displayb({out0, out1, sel_out});
end

endmodule

Or, if you really intend to use latched logic, then you can indicate that clearly by rewriting with always_latch instead.

3 Comments

  1. the hardware modelled by the synthesizer should functionally be the same regardless if we use always_comb or assign ,
    would you think that is a correct or complete statement….

    BTW I like the topic you have raised in this blog 🙂

    thanks

    Like

    • Yes, gate-level simulations of the synthesized results should be the same if derived from either style of RTL. But with assign, the RTL-level simulation is less likely to match the gate-level simulation.

      Like

Tell me (anonymous OK)