Skip to content

Verilog · Chapter 5 · Foundations

Variables & Data Types in Verilog

Every signal in a Verilog design has a type, and types split into exactly two families. Nets such as wire, tri, wand, wor, tri0, tri1, trireg, and supply model physical interconnect and have no storage of their own. Variables such as reg, integer, real, and time model procedural storage that holds a value between assignments. Getting this split right is the foundation of every design, because choosing the wrong family causes errors the compiler often cannot catch. This chapter is the working engineer's cheatsheet for both families. It walks the full type catalogue, the four-state value system that underlies both, signal strengths for net resolution, and the scalar, vector, and array vocabulary layered on top, along with the synthesis versus simulation cutline.

Foundation24 min readVerilogData TypesNetsVariablesVectors

Chapter 5 · Page 2.1 · Data & Variables

What "Data Type" Means in Verilog

A data type in Verilog answers three questions at once:

  1. Where does the value live? A wire is a physical net — its value is whatever the driver(s) put on it at any instant; no storage. A reg is procedural storage — it holds a value between assignments inside an always or initial block.
  2. What states can it carry? Verilog is a 4-state language at heart — every bit is one of 0, 1, x (unknown), z (high-impedance). integer and real are the exceptions — integer is a 32-bit 2-state signed register; real is IEEE 754 double-precision.
  3. What's its width / shape? A single-bit scalar, a packed N-bit vector, or an unpacked array of vectors. Width is fixed at declaration.
overview.v — one example from each family
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire        a;                  // 1-bit net, 4-state, driven by something external
wire [7:0]  bus;                // 8-bit packed net
reg  [7:0]  data_q;             // 8-bit packed variable; holds value between assignments
reg         valid_q;            // 1-bit variable
integer     i;                  // 32-bit 2-state signed; testbench loop counter
real        period_ns;          // IEEE 754 double; testbench-only
reg  [7:0]  mem [0:1023];       // unpacked array — 1024 bytes

The Two Families: Nets and Variables

The single most important data-types distinction in Verilog. Get this right and everything else falls into place.

PropertyNetsVariables
ModelsPhysical interconnect (wires between gates)Procedural storage (state holding a value)
Default keywordwirereg
Has storage?No — driven continuously by assign or by a gate / module outputYes — retains value until overwritten
Where you assign itassign, gate primitive, or module output portInside always / initial (procedural) blocks
Synthesises toA wire on the chip (or a tri-state buffer for tri)A flip-flop, a latch, or combinational logic (depending on the always block)
Default initial valuez (high-impedance) for wire; varies for the othersx (unknown) for reg
Keyword familywire, tri, wand, wor, trior, triand, tri0, tri1, trireg, supply0, supply1reg, integer, real, realtime, time

The first reflex check on any Verilog declaration is which family is this? — every typing rule downstream (where you can assign it, what it synthesises to, how the simulator schedules it) flows from that distinction.

The 4-State Value System

Every Verilog bit (in both families, except integer / real) carries one of four states:

StateMeansWhere it shows up
0Logic lowDriven asserted-low; default for most assignments
1Logic highDriven asserted-high
xUnknownUninitialised reg, conflicting drivers, propagation of x
zHigh-impedanceFloating net, tri-state buffer turned off, default for wire

The 4-state system distinguishes Verilog from C-like languages — it lets the simulator model real hardware behaviour like uninitialised flops (x), floating buses (z), and bus contention (x from conflicting drivers). The cost is that propagation rules apply at every operator (1 & x = x, not 0) — see Operator Usage §The 4-State X-Propagation Rule.

SystemVerilog adds a 2-state type family (bit, byte, int, shortint, longint, logic) that drops x and z. Pure Verilog (.v) lives in the 4-state world.

Net Types — The Full Catalogue

Pure Verilog defines 11 net types. The simple wire covers 95% of RTL; the rest are specialised for tri-state buses, multi-driver resolution, and power-supply modelling.

Net typeResolutionWhere used
wireDriver valueThe default. Single-driver point-to-point nets.
triSame as wireStyle alias — signals "this net intentionally has tri-state drivers".
wandWired-ANDBus where any 0 driver wins (open-drain emulation).
worWired-ORBus where any 1 driver wins.
triandSame as wandTri-state version.
triorSame as worTri-state version.
tri0Pulls to 0 when undrivenModels a pull-down resistor.
tri1Pulls to 1 when undrivenModels a pull-up resistor.
triregCharge storageModels capacitive net retention; simulation-only.
supply0Constant 0Power-net ties; synthesises to GND.
supply1Constant 1Power-net ties; synthesises to VDD.

For everyday RTL — wire. For tri-state buses with pull-ups / pull-downs — tri0 / tri1. The wired-logic types (wand / wor) are rarely synthesisable on modern processes and live mostly in legacy designs.

Sub-topics covering each net family will land as separate pages:

  • 5.1 Physical Data Types — overview of the net family.
  • 5.1.1 wire and tri Nets — the workhorse types.
  • 5.1.2 Signal Strengths — the strength resolution table.
  • 5.1.3 Wired Netswand / wor / triand / trior.
  • 5.1.4 trireg Nets — charge-storage modelling.
  • 5.1.5 tri0 and tri1 Nets — pull resistor models.
  • 5.1.6 supply0 and supply1 — power net modelling.

Variable Types — The Full Catalogue

The five variable types cover everything from synthesisable storage to testbench-only floating-point.

Variable typeBitsStatesSynthesisable?Where used
regdeclared4The workhorse — anything assigned inside an always. Synthesises to flops or latches or combinational logic depending on the block.
integer322 (no x/z)Usually no (testbench / loop counter)Loop counters, calculation variables. Signed by default.
real64 (IEEE 754)Testbench timing values, behavioural model parameters.
realtime64Real-typed time variable; $realtime returns this.
time644Time variables for $time, delay annotations.

The day-to-day naming-vs-type discipline:

  • reg for everything in RTL. Even if it's combinational, you declare reg in the procedural block and let the synthesis tool figure out whether to instantiate a flop. The keyword reg does not mean "register" in the synthesised sense — it means "procedurally assignable variable."
  • integer for loop counters, calculation in initial blocks. Don't use in RTL data paths (it's 32-bit signed; sizing surprises follow).
  • real only in testbenches. Synthesis tools either ignore real declarations or refuse compilation. See Number Representation §Real Numbers.
  • time only for testbench instrumentation — annotating elapsed cycles, $time snapshots, etc.

Sub-topics covering each variable family will land as separate pages:

  • 5.2 Register Data Types — overview.
  • 5.2.1 reg — the synthesisable workhorse.
  • 5.2.2 integer — 32-bit 2-state, testbench-focused.
  • 5.2.3 real — IEEE 754 double-precision, simulation-only.
  • 5.2.4 Scalar vs Vector vs Arrays — the dimensional vocabulary.

Scalar vs Vector vs Array

Three orthogonal dimensions every declaration combines:

TermShapeExample
ScalarSingle bitwire a; / reg b;
Vector (packed)Multi-bit contiguous rangewire [7:0] bus; / reg [DATA_WIDTH-1:0] data_q;
Array (unpacked)Repetition of scalars or vectorsreg [7:0] mem [0:1023]; — 1024 bytes; wire [31:0] busarr [0:3]; — 4 buses

Two examples that catch beginners:

dimensional-vocab.v — packed vs unpacked
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Packed: a single 8-bit value, all bits live together
reg [7:0] data_q;
data_q = 8'hA5;           // sets all 8 bits in one assignment
 
// Unpacked: 1024 independent 8-bit slots; each indexed separately
reg [7:0] mem [0:1023];
mem[42] = 8'hA5;          // sets one 8-bit slot
// mem = 8'hA5;           // ❌ illegal — can't assign whole array at once

The MSB:LSB declaration order ([7:0] vs [0:7]) is a project-wide convention; both are legal but mixing them across modules causes subtle byte-ordering bugs. Pick [N-1:0] for everything and lint for [0:N-1] exceptions.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Packed + unpacked combined
reg [31:0] regfile [0:31];      // 32 registers × 32 bits = a register file
regfile[5][7:0] = 8'h12;        // byte-select within a vector within an array

The dimensional vocabulary scales — packed and unpacked dimensions stack, giving Verilog 2D and 3D arrays. Synthesis tools handle one packed + one unpacked dimension natively; higher dimensions need rewriting as flat structures or generate-block expansion.

Default Initial Values

The simulator gives every signal a starting value before any initial or always runs:

FamilyDefault value at t = 0
wire (and most nets)z (high-impedance)
tri00
tri11
supply00 (held)
supply11 (held)
reg (4-state)x (unknown)
integer, time0 (or x in 4-state contexts)
real, realtime0.0

These defaults are why "did I forget to reset that flop?" RTL bugs show up as x propagation in simulation — the synthesised flop powers up at the silicon's natural state (random 0 or 1), but the simulator shows you x until something writes the variable. Always write a defined reset in synthesisable code; never rely on the default.

Signed vs Unsigned

A net or reg declaration may carry a signed modifier:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
wire signed [7:0] s_data;        // 8-bit signed (two's complement)
reg  signed [15:0] s_acc;        // 16-bit signed register
reg  [7:0]         u_byte;       // unsigned (default)

Signed-ness affects:

  • Width-extension on assignment to wider destinations (sign-extend vs zero-extend) — see Number Representation §Width-Extension Rules.
  • Relational and arithmetic operator behaviour<, >, >>> (arithmetic shift right) interpret the operand as signed.
  • Concatenation is always treated as unsigned; use $signed() cast to re-mark.

integer is signed by default. reg is unsigned by default. real is signed.

Verification Angle

Every data-type declaration carries a verification expectation too:

  • Default reset value — every reg in synthesisable RTL must have a defined reset path. Lint flags missing-reset registers; testbench should hit every reset condition.
  • x propagation — uninitialised regs propagate x through downstream logic. UVM testbenches typically assert "no x on output ports" as a coverage point.
  • Net contentionwand / wor and multi-driver wires can resolve to x on conflict. Lint and waveform inspection catch these.
  • Width-mismatch warnings — assignments where source ≠ destination width are usually bugs; treat lint warnings as failures.

Detail across the verification angle for each type will land in the sub-topic pages.

Common Mistakes

Five data-types bugs that ship.

  1. reg ≠ "register". The keyword names a language construct (procedurally assignable variable), not a hardware artefact. A reg in a combinational always @(*) synthesises to combinational logic, not a flop. The synthesis report's "Inferred latches" line tells you whether the tool agreed.
  2. x survives operators. 1 & 1'bx is 1'bx, not 0 (because the synthesiser couldn't see this until silicon). Comparison if (sig == 1'bx) does NOT do what beginners think — every operand promotes to a wider value in the equality check.
  3. wire cannot appear left of procedural =. Common porting bug — a reg used as a port output, then the surrounding logic was rewritten to drive it via assign. The fix is rename and re-declare.
  4. Default integer is 32-bit signed. Loop counters declared integer i; will surprise when assigned narrow values — see Number Representation §The 32-bit Default Trap.
  5. Array-of-arrays can't be assigned whole. mem = '{default: 8'h0}; is a SystemVerilog construct; in pure Verilog, every cell of an unpacked array must be assigned individually (or in a for loop / initial block).

Worked Example — A Tiny FIFO Frontend

A 4-deep × 8-bit FIFO front-end that exercises every data-types concept from this chapter: net for clock, reg for pointers, packed vector for data, unpacked array for storage, parameter for sizing.

rtl/fifo_frontend.v — every data type in one place
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module fifo_frontend #(
    parameter DEPTH       = 4,
    parameter DATA_WIDTH  = 8,
    parameter PTR_W       = 2                // = $clog2(DEPTH)
) (
    input  wire                  clk,            // net — clock from outside
    input  wire                  rst_n,          // net — async reset
    input  wire                  push,           // net — write strobe
    input  wire [DATA_WIDTH-1:0] data_in,        // packed vector net
    output reg  [DATA_WIDTH-1:0] data_q,         // packed vector reg
    output reg                   full_q          // scalar reg
);
 
    // unpacked array of packed vectors — the storage
    reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
 
    // pointer registers
    reg [PTR_W-1:0] wr_ptr_q;
    reg [PTR_W:0]   count_q;                     // PTR_W+1 wide for full detection
 
    integer i;                                    // testbench-style index for the reset loop
 
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_ptr_q <= {PTR_W{1'b0}};
            count_q  <= {(PTR_W+1){1'b0}};
            full_q   <= 1'b0;
            data_q   <= {DATA_WIDTH{1'b0}};
            for (i = 0; i < DEPTH; i = i + 1)
                mem[i] <= {DATA_WIDTH{1'b0}};
        end else if (push && !full_q) begin
            mem[wr_ptr_q] <= data_in;
            wr_ptr_q      <= wr_ptr_q + 1'b1;
            count_q       <= count_q  + 1'b1;
            full_q        <= (count_q == DEPTH-1);
            data_q        <= data_in;
        end
    end
 
endmodule

What every declaration teaches:

  • wire clk, rst_n, push, data_in — nets driven by the parent. The module reads them but cannot reassign them.
  • reg [DATA_WIDTH-1:0] data_q — packed-vector variable; the procedural block assigns it on each clock.
  • reg full_q — scalar variable; a one-bit flag.
  • reg [DATA_WIDTH-1:0] mem [0:DEPTH-1] — unpacked array of packed vectors; this is the storage. Cell-at-a-time assignment.
  • integer i — testbench-style loop variable, used by the reset clearing loop. Synthesisable inside an always block as the loop counter unrolls at elaboration.
  • {DATA_WIDTH{1'b0}} / {PTR_W{1'b0}} — parametric all-zero literals, the canonical way to size resets without hitting the 32-bit unsized default.

This is the level of type-discipline every code review at a working ASIC house expects.

Interview Insights

Summary

Verilog's data-types layer splits into nets (physical interconnect, no storage, wire + 10 specialised types) and variables (procedural storage, reg + 4 specialised types). Every bit in the language lives in a 4-state value system (0 / 1 / x / z), with integer and real as the 2-state / no-state exceptions. On top of the type family come the dimensional layers — scalar, packed vector, unpacked array — that compose into the register files, FIFOs, and memory arrays of real RTL. Get the family right (net vs variable) and every downstream rule about assignment direction, synthesis inference, and simulator scheduling follows. The chapter's sub-topics drill into each net type, each variable type, and the scalar/vector/array dimensional vocabulary — they ship as separate pages. Chapter 6 picks up with constant variables (parameter / localparam), and Chapter 7 with the compiler directives that hold a real Verilog project together.

Beat coverage notes: all required beats present. ⏱ Timing Observation, 🧠 Simulator Scheduling Insight (the mental-model callout) folded into the Nets vs Variables section. 🏗 Synthesis Concern documents the x-on-silicon resolution. Sub-topics carry deeper waveform / verification examples per type.