Part 1 · Language Foundations · Intermediate

Nets vs Variables

wire vs logic, driver resolution, multiple-driver rules, when logic replaces reg/wire, and the var keyword.

Two fundamentally different kinds of storage

A net (wire, tri, wand, wor) does not store a value of its own. Its value is resolved continuously from everything driving it: continuous assigns, module output ports, gate primitives. Two drivers fighting (one drives 0, one drives 1) resolve to X on a plain wire; no driver at all resolves to Z. This models real electrical nodes where multiple devices can connect.

A variable (logic, bit, int, historically reg) stores the last value procedurally or continuously assigned to it. There is no resolution function — which is exactly why the LRM forbids a variable from having more than one continuous driver. The simulator enforces the single-driver rule at elaboration, turning a class of silent wiring bugs into compile errors. Interviewers love asking "why can't logic have two drivers?" — the answer is: variables have no resolution function to combine them.

diagram
Legend: [TYPE]

  NET (wire)                         VARIABLE (logic)
  ──────────                         ────────────────
  driver A ──┐                        single source:
             ├──► resolution ──► val    assign  OR
  driver B ──┘    function              one always block OR
                                        one module output
  0 vs 1   X (conflict)
  0 vs Z   0                         second driver?
  1 vs Z   1                          ELABORATION ERROR
  Z vs Z   Z (floating)              (caught at compile,
                                       not a silent X at runtime)
  wand: 0 wins (wired-AND)
  wor : 1 wins (wired-OR)

When logic replaces reg and wire

In Verilog-2001 you needed reg for anything assigned in an always block and wire for anything driven by assign or a port — a distinction about how it is assigned , not what it is. SystemVerilog's logic erases that bookkeeping: it is a 4-state variable that can be assigned by one always block, one continuous assign, or one module output. The only place logic cannot go is a true multiply-driven net: tri-state buses, bidirectional pads, wired-AND/OR buses. Those still require wire/tri because they need resolution.

systemverilog
module driver_rules;
  logic a;
  wire  w;

  assign a = 1'b1;          // OK: one continuous driver on a variable
  // always_comb a = 1'b0;  // ERROR: second driver on variable 'a'

  assign w = 1'b1;          // legal: nets accept multiple drivers
  assign w = 1'b0;          // also legal — w resolves to X (conflict)

  // Tri-state bus: MUST be a net, needs Z resolution
  wire [7:0] data_bus;
  assign data_bus = en_a ? a_data : 8'bz;
  assign data_bus = en_b ? b_data : 8'bz;   // resolved per bit
endmodule
systemverilog
// Modern RTL style: logic everywhere except true buses
module alu (
  input  logic [7:0] a, b,
  input  logic [1:0] op,
  output logic [7:0] y          // 'output logic' — no reg/wire dance
);
  logic [7:0] sum;              // assigned in always_comb: fine

  always_comb begin
    sum = a + b;
    unique case (op)
      2'b00: y = sum;
      2'b01: y = a & b;
      2'b10: y = a | b;
      2'b11: y = a ^ b;
    endcase
  end
endmodule

The var keyword and port defaults

Ports have a subtlety: an input logic [7:0] d port is, per the LRM, actually a net of type wire with data type logic unless you say var explicitly — logic in a port context describes the value set, not the storage kind. This rarely bites in practice, but it explains why simulators accept hierarchical multi-driving of input ports, and it is a sharp interview question: "input logic — net or variable?" The var keyword forces variable semantics anywhere a net would otherwise be inferred: input var logic [7:0] d is a true variable port and gains single-driver checking.

Choosing in practice

  • Internal RTL signals and outputs: logic. Single-driver checking catches wiring bugs at compile time.

  • Tri-state, bidirectional, or multi-driver buses: wire/tri — resolution is the point.

  • Inputs: plain 'input logic' is a wire underneath; use 'input var logic' if you want strict variable semantics.

  • Testbench class members: always variables — nets cannot exist inside classes.

Key takeaways

  • Nets are resolved from N drivers; variables store the last assignment and allow exactly one continuous driver.

  • logic replaces both reg and wire for single-driver signals — multi-driver buses still need wire/tri.

  • Driver conflicts on wire become X at runtime; on logic they become elaboration errors — fail-fast is a feature.

  • input logic ports are nets by default; var forces variable semantics.

Common pitfalls

  • Two always blocks assigning the same logic variable — elaboration error that surprises Verilog veterans expecting silent last-write-wins.

  • Using logic for a tri-state bus — Z from one driver cannot be resolved against another driver's value.

  • Believing logic ≡ reg — logic also accepts a single continuous assign, which reg-era rules forbade.

  • Forgetting wand/wor exist — reading legacy netlists, 0-wins/1-wins resolution explains 'impossible' values.