Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations35
    • SystemVerilog Data Types
      • 4-State vs 2-State Types
      • Nets vs Variables
      • Integer Types & Casting
      • Enums, Structs & Unions
      • X/Z Propagation & Pessimism
      • Strings, Reals & Special Types
    • Arrays: Packed, Dynamic, Queues & Associative
    • Procedural Code & Scheduling
    • Operators, Expressions & Width Rules
    • Packages, Typedefs & Compilation
    • Interfaces, Modports & Clocking Blocks
  • Object-Oriented SystemVerilog for Verification35
  • Constraint Randomization — Interview-Critical Deep Dive66
  • SystemVerilog Assertions — Interview-Critical Deep Dive63
  • Functional Coverage in SystemVerilog28
  • Testbench Architecture in SystemVerilog38
  • Advanced Language & Integration30
  • Senior & Interview Preparation34
  • Principal-Level Verification28
Loading lesson

‹SystemVerilog Data Types
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Language Foundations
  4. SystemVerilog Data Types
  5. Nets vs Variables

Part 1 · Language Foundations · Intermediate

Nets vs Variables

Nets resolve many drivers while variables store one, with logic, tri-state, and var rules.

On this page

    Why this matters

    Two blocks of RTL both drove one logic signal, and the simulation still ran. Moving the signal to logic was supposed to catch exactly this; knowing which tools check it, and what a net does instead, saves the debug.

    After this lesson you can: say what a net resolves and a variable stores, pick wire or logic for a signal, and model a tri-state bus.

    Nets resolve, variables store

    A net (wire, tri, wand, wor) holds no value of its own. Its value is resolved continuously from everything driving it, which models real electrical nodes where several devices can connect.

    A variable (logic, bit, int, historically reg) stores the last value assigned to it. There is no resolution function, so IEEE 1800 allows a variable only one continuous driver and makes a second one an error. Not every simulator reports it, so lint for it too.

    Drivers on a plain wire

    Resolves to

    0 against 1

    X, the conflict stays visible

    0 against Z

    0, the driven value wins

    1 against Z

    1, the driven value wins

    Nothing drives

    Z, the net floats

    wand / wor nets

    0 wins / 1 wins instead of X

    design.sv
    1
    2
    3
    4
    // snippet: two hard drivers fighting on one net read back X
    wire w;
    assign w = 1'b1;
    assign w = 1'b0;   // w resolves to 1'bx

    One driver per variable

    In Verilog-2001 the choice of reg versus wire described how a signal was assigned. SystemVerilog logic erases that bookkeeping: one always block, one continuous assign, or one module output, and single-driver checking catches wiring bugs at compile time.

    The only place logic cannot go is a true multiply-driven net: tri-state buses, bidirectional pads, and wired-AND/OR buses still need wire or tri because they need resolution. A second continuous driver on a variable, or a second writer to one always_comb, always_ff, or always_latch variable, is illegal under IEEE 1800.

    Two plain always blocks writing one variable is legal but racy, so it stays a code-review catch rather than a compile error. Ports add one subtlety: plain input logic is a net underneath, and spelling input var logic forces true variable semantics with single-driver checking.

    two_drivers.sv
    1
    2
    3
    4
    5
    6
    // bad-example: a second continuous driver on a variable is illegal
    module two_drivers;
      logic a;
      assign a = 1'b1;
      assign a = 1'b0;   // illegal: variables have no resolution function
    endmodule
    m.sv
    1
    2
    3
    4
    5
    6
    // snippet: plain input ports are nets underneath; var forces a variable
    module m (
      input logic [7:0] plain,
      input var logic [7:0] strict
    );
    endmodule

    Buses that still need nets

    A tri-state bus is the canonical multiply-driven net. Each source drives its value or Z, and per-bit resolution picks the live driver, which no variable type can express.

    Each driver either drives or lets go with Z. The wire resolves what is left.
    Predict first Both enables are low, so neither driver is on. What does every bus bit read?

    Z. Every driver floats, and a net with nothing driving it resolves to high impedance on every bit.

    Modern RTL style is logic everywhere except true buses. Internal signals and outputs gain single-driver checking, while the shared bus stays a net.

    alu.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // Modern RTL style: logic everywhere except the shared bus
    module alu (
      input  logic [7:0] a, b,
      input  logic [1:0] op,
      output logic [7:0] y
    );
      logic [7:0] sum;
    
      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

    Testbench

    Single-driver values plus a tri-state bus tracking the live driver through an enable swap and a data change.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    module tb_drivers;
      timeunit 1ns; timeprecision 1ps;
      wire w2;
      assign w2 = 1'b1;                 // one driver: a plain known value
      wire [7:0] bus;
      logic en_a = 1'b1, en_b = 1'b0;
      logic [7:0] a_data = 8'hA5, b_data = 8'h3C;
      assign bus = en_a ? a_data : 8'bz;
      assign bus = en_b ? b_data : 8'bz;
      int errors = 0;
    
      initial begin
        #1;
        if (w2 !== 1'b1) errors++;
        if (bus !== 8'hA5) errors++;           // Z loses, the driven value wins
        en_a = 1'b0; en_b = 1'b1; #1;
        if (bus !== 8'h3C) errors++;
        a_data = 8'hFF; en_a = 1'b1; en_b = 1'b0; #1;
        if (bus !== 8'hFF) errors++;           // bus follows the live driver
        if (errors != 0) $fatal(1, "FAIL: driver resolution saw %0d unexpected results", errors);
        $display("PASS: single driver read clean and tri-state tracked the live driver");
        $finish;
      end
    endmodule
    Terminal
    PASS: single driver read clean and tri-state tracked the live driver

    Common pitfall

    Using logic for a tri-state bus removes the resolution the bus needs: Z from one driver can no longer combine with a value from another. Multi-driver structure always means a net type.

    Interview

    “Why can't logic have two drivers?” Variables have no resolution function to combine them, so the standard makes the second driver an error instead of a silent runtime X. That fail-fast rule is the point.

    Check yourself Two continuous assigns drive one wire with 1 and 0, and the same pair drives one logic. What happens in each case?

    The wire resolves to X at runtime. The logic is illegal, because a variable has no resolution function for two drivers. Not every simulator stops on it, so lint for it.

    Check yourself Why do the two tri-state assigns in the bench not break the single-driver rule?

    bus is a wire, a net, so it may have many drivers and resolves them. The single-driver rule is for variables such as logic.

    Remember

    • A net resolves all its drivers: 0 against 1 is X, a value against Z is the value, nothing is Z.

    • A variable stores the last assignment and may have only one continuous driver.

    • Use logic everywhere except true multi-driver buses, which stay wire or tri.

    Rule of thumb: more than one driver means a net; one driver means logic.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 (-g2012) runs them too, with the same output.

    The illegal two-driver listing is not run. Icarus 13 rejects it at elaboration; Verilator 5.048 accepts it without an error, so a lint step is the reliable check.

    Related topics

    • Next: Integer Types & Casting

    Previous

    8 / 363

    Next: Integer Types & Casting

    Up next

    Up nextInteger Types & Casting

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Enums, Structs & Unions
    • X/Z Propagation & Pessimism
    • Strings, Reals & Special Types