Skip to article

SystemVerilog Bridge

logic, always_comb, and always_ff

How SystemVerilog logic, always_comb, and always_ff map onto the Verilog wire, reg, always @(*), and always @(posedge clk) you already know.

2 min read · Updated


SystemVerilog's logic type replaces the wire-versus-reg distinction Course 1 spent real time teaching, for the common case: a signal with exactly one driver. Lesson 2.2's rule tied the type to the driver (assign drives a wire, an always block needs a reg); a logic signal accepts either. Its limit is the number of drivers: it can take one assign or be written by procedural code, but not several continuous drivers and not a mix of the two. That limitation is why plain wire has not gone away. Nets that genuinely need more than one driver (a shared bus, a tri-state line) still use wire, so "just use logic everywhere" is a useful starting habit, not an absolute rule.

Two always variants replace the general-purpose ones:

  • always_comb is the explicit form of always @(*). It exists only to describe combinational logic. Under always @(*), a path that leaves an output unassigned is legal Verilog that infers a latch; a tool may warn, as in Lesson 6.6, but it cannot know you wanted no storage. Under always_comb you have declared that you did not, so the SystemVerilog standard asks tools to warn about the latch.
  • always_ff is the explicit form of always @(posedge clk). It exists only to describe clocked logic, so a tool warns when the block has no clock edge to act on (for example always_ff @(*)).

Both keywords add one more rule: a variable written inside an always_comb or always_ff block may not be written by any other block, so two blocks fighting over one signal becomes a compile error.

Nothing about the discipline changes: default-at-top inside always_comb, non-blocking assignment inside always_ff. Only the keywords get more specific about what each block is allowed to contain.