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.
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.
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// 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
endmoduleThe 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.