Using a continous assignment in a Verilog procedure?

It is called procedural continuous assignment. It is the use of an assign or force (and their corresponding counterparts deassign and release) within procedural block. A new continuous assignment process is created when the line is reached in the procedural block. assign can be applied to register types such as reg, integer, and real. force can be applied to registers and nets (i.e. wires). It has been part of the LRM since 1364-1995.

  • IEEE Std 1364-1995 § 9.3
  • IEEE Std 1364-2001 § 9.3
  • IEEE Std 1364-2005 § 9.3
  • IEEE Std 1800-2005 § 25.3
  • IEEE Std 1800-2009 § 10.6
  • IEEE Std 1800-2012 § 10.6

Procedural continuous assignments are synthesizable, by most tools. However it is recommend to limit the use to behavior modeling of an analog block, test bench files, or fixing RTL<->gate functional mismatches.

  • always @* assign data_in = Data;
    is functional the same as
    always @* data_in = Data;

  • always @(posedge clk) assign data_in = Data;
    is functional the same as:
    always @(posedge clk)
      enable = 1;
    always @*
      if (enable==1) data_in = Data;

A valid use of procedural continuous assignment would be should be applited to the following:

always @(posedge clk or negedge rst_n, negedge set_n) begin
   if (!rst_n)      q <= 1'b0;
   else if (!set_n) q <= 1'b1;
   else             q <= d;
end

It will synthesize to a flop with an asynchronous set and reset with priority to reset. In simulation however the model is inaccurate if rst_n and set_n are both low then rst_n goes high. q should go to 1 the the asynchronous set is still enabled, but nothing to trigger in in the sensitivity list. This is a well documented issue with Verilog. It is the one case procedural continuous assignment are allowed in RTL when used with the translate off keyword your synthesizer. The release/deassign allows the the register/wire to be assigned in the usual manner.

// translate_off
always @(rst_n or set_n)
  if (rst_n && !set_n) force q = 1'b1;
  else                 release q;
// translate_on

OR (currently valid but discouraged)

// translate_off
always @(rst_n or set_n)
  if (rst_n && !set_n) assign q = 1'b1;
  else                 deassign q;
// translate_on

Using assign/deassign in this manner is being considered to be depreciated in future IEEE 1800 release. IEEE Std 1800-2005 § 25.3, IEEE Std 1800-2009 § C.4.2 and IEEE Std 1800-2012 § C.4.2 recognizes assign used this way causes confusion and is the source of errors. Use force/release if procedural continuous assignment as needed.

In generate using procedural continuous assignment (with force/release) should only be used if absolutely necessary. Alternative approaches are more reliable.

Misuse of procedural continuous assignment and solutions:

  • Combinational logic on reg:

    always @(sel)
      if (sel) assign reg1 = func1(x,y,z);
      else     assign reg1 = func2(a,b,c);
    
    • Solution:

      always @* // <- IEEE Std 1364-2001 construct
        if (sel) reg1 = func1(x,y,z);
        else     reg1 = func2(a,b,c);
      
  • Combinational logic on wire:

    always @(sel)
      if (sel) force wire1 = func1(x,y,z);
      else     force wire1 = func2(a,b,c);
    
    • Solution:

      assign wire1 = sel ? func1(x,y,z) : func2(a,b,c);
      
  • Sequential logic:

    always @(posedge clk)
      if (sel) assign reg2 = func1(x,y,z);
      else     assign reg2 = func2(a,b,c);
    
    • Solution (assuming original functionality is wrong):

      always @(posedge clk)
        if (sel) reg2 <= func1(x,y,z); // Non-blocking assignment !!!
        else     reg2 <= func2(a,b,c);
      
    • Solution (assuming original functionality is correct):

      reg flop_sel;
      always @(posedge clk)
        flop_sel <= sel; // Non-blocking assignment !!!
      always @*
        if (flop_sel) reg2 = func1(x,y,z); // Blocking assignment !!!
        else          reg2 = func2(a,b,c);