verilog module-1module-2

assign vs. always: When to Use Each

A quick reference for choosing between continuous assignment (assign) and procedural blocks (always) in synthesizable RTL.

Updated 2026-04-30

The core rule

Use assign for combinational wires that are driven by an expression. Use always @(*) (or always_comb in SystemVerilog) for combinational logic that needs sequential statements (if, case). Use always @(posedge clk) for registered (sequential) logic.

assign — continuous assignment

wire y;
assign y = a & b;   // y is always (a AND b)
  • The right-hand side is re-evaluated whenever any input changes.
  • Left-hand side must be a wire (or logic in SV).
  • Cannot be used inside a procedural block.
  • Good for: simple combinational gates, muxes, arithmetic.

always @(*) — combinational procedural block

reg y;
always @(*) begin
    if (sel)
        y = a;
    else
        y = b;
end
  • @(*) means "re-execute whenever any signal in the sensitivity list changes".
  • Left-hand side must be a reg (or logic in SV).
  • Enables if/else, case, loops.
  • Good for: priority encoders, decoders, complex muxes.

always @(posedge clk) — sequential (registered) logic

always @(posedge clk or posedge rst) begin
    if (rst)
        q <= 0;
    else
        q <= d;
end
  • Captures state on the rising edge of the clock.
  • Use non-blocking assignments (<=) to avoid race conditions.
  • Good for: flip-flops, registers, state machines.

Summary table

Construct Sensitivity Target type Use for
assign continuous wire simple combinational expressions
always @(*) combinational reg conditional combinational logic
always @(posedge clk) clock edge reg registers, state machines

Common mistakes

  • Driving a wire with always: always must target reg; use assign for wire.
  • Missing signals in @(*): Use @(*) instead of listing signals manually — the synthesizer will detect all inputs automatically.
  • Mixing <= and = in a clocked block: Use <= (non-blocking) in always @(posedge clk) and = (blocking) in always @(*).