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:
- Where does the value live? A
wireis a physical net — its value is whatever the driver(s) put on it at any instant; no storage. Aregis procedural storage — it holds a value between assignments inside analwaysorinitialblock. - 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).integerandrealare the exceptions —integeris a 32-bit 2-state signed register;realis IEEE 754 double-precision. - 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.
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 bytesThe Two Families: Nets and Variables
The single most important data-types distinction in Verilog. Get this right and everything else falls into place.
| Property | Nets | Variables |
|---|---|---|
| Models | Physical interconnect (wires between gates) | Procedural storage (state holding a value) |
| Default keyword | wire | reg |
| Has storage? | No — driven continuously by assign or by a gate / module output | Yes — retains value until overwritten |
| Where you assign it | assign, gate primitive, or module output port | Inside always / initial (procedural) blocks |
| Synthesises to | A 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 value | z (high-impedance) for wire; varies for the others | x (unknown) for reg |
| Keyword family | wire, tri, wand, wor, trior, triand, tri0, tri1, trireg, supply0, supply1 | reg, 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:
| State | Means | Where it shows up |
|---|---|---|
0 | Logic low | Driven asserted-low; default for most assignments |
1 | Logic high | Driven asserted-high |
x | Unknown | Uninitialised reg, conflicting drivers, propagation of x |
z | High-impedance | Floating 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 type | Resolution | Where used |
|---|---|---|
wire | Driver value | The default. Single-driver point-to-point nets. |
tri | Same as wire | Style alias — signals "this net intentionally has tri-state drivers". |
wand | Wired-AND | Bus where any 0 driver wins (open-drain emulation). |
wor | Wired-OR | Bus where any 1 driver wins. |
triand | Same as wand | Tri-state version. |
trior | Same as wor | Tri-state version. |
tri0 | Pulls to 0 when undriven | Models a pull-down resistor. |
tri1 | Pulls to 1 when undriven | Models a pull-up resistor. |
trireg | Charge storage | Models capacitive net retention; simulation-only. |
supply0 | Constant 0 | Power-net ties; synthesises to GND. |
supply1 | Constant 1 | Power-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
wireandtriNets — the workhorse types. - 5.1.2 Signal Strengths — the strength resolution table.
- 5.1.3 Wired Nets —
wand/wor/triand/trior. - 5.1.4
triregNets — charge-storage modelling. - 5.1.5
tri0andtri1Nets — pull resistor models. - 5.1.6
supply0andsupply1— power net modelling.
Variable Types — The Full Catalogue
The five variable types cover everything from synthesisable storage to testbench-only floating-point.
| Variable type | Bits | States | Synthesisable? | Where used |
|---|---|---|---|---|
reg | declared | 4 | ✅ | The workhorse — anything assigned inside an always. Synthesises to flops or latches or combinational logic depending on the block. |
integer | 32 | 2 (no x/z) | Usually no (testbench / loop counter) | Loop counters, calculation variables. Signed by default. |
real | 64 (IEEE 754) | — | ❌ | Testbench timing values, behavioural model parameters. |
realtime | 64 | — | ❌ | Real-typed time variable; $realtime returns this. |
time | 64 | 4 | ❌ | Time variables for $time, delay annotations. |
The day-to-day naming-vs-type discipline:
regfor everything in RTL. Even if it's combinational, you declareregin the procedural block and let the synthesis tool figure out whether to instantiate a flop. The keywordregdoes not mean "register" in the synthesised sense — it means "procedurally assignable variable."integerfor loop counters, calculation ininitialblocks. Don't use in RTL data paths (it's 32-bit signed; sizing surprises follow).realonly in testbenches. Synthesis tools either ignore real declarations or refuse compilation. See Number Representation §Real Numbers.timeonly for testbench instrumentation — annotating elapsed cycles,$timesnapshots, 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:
| Term | Shape | Example |
|---|---|---|
| Scalar | Single bit | wire a; / reg b; |
| Vector (packed) | Multi-bit contiguous range | wire [7:0] bus; / reg [DATA_WIDTH-1:0] data_q; |
| Array (unpacked) | Repetition of scalars or vectors | reg [7:0] mem [0:1023]; — 1024 bytes; wire [31:0] busarr [0:3]; — 4 buses |
Two examples that catch beginners:
// 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 onceThe 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.
// 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 arrayThe 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:
| Family | Default value at t = 0 |
|---|---|
wire (and most nets) | z (high-impedance) |
tri0 | 0 |
tri1 | 1 |
supply0 | 0 (held) |
supply1 | 1 (held) |
reg (4-state) | x (unknown) |
integer, time | 0 (or x in 4-state contexts) |
real, realtime | 0.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:
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
regin synthesisable RTL must have a defined reset path. Lint flags missing-reset registers; testbench should hit every reset condition. xpropagation — uninitialised regs propagatexthrough downstream logic. UVM testbenches typically assert "noxon output ports" as a coverage point.- Net contention —
wand/worand multi-driverwires can resolve toxon 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.
reg≠ "register". The keyword names a language construct (procedurally assignable variable), not a hardware artefact. Aregin a combinationalalways @(*)synthesises to combinational logic, not a flop. The synthesis report's "Inferred latches" line tells you whether the tool agreed.xsurvives operators.1 & 1'bxis1'bx, not0(because the synthesiser couldn't see this until silicon). Comparisonif (sig == 1'bx)does NOT do what beginners think — every operand promotes to a wider value in the equality check.wirecannot appear left of procedural=. Common porting bug — aregused as a port output, then the surrounding logic was rewritten to drive it viaassign. The fix is rename and re-declare.- 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. - 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 aforloop /initialblock).
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.
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
endmoduleWhat 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 analwaysblock 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.
Related Tutorials
- Lexical Conventions — Chapter 4; the lexer layer underneath these type declarations.
- Identifier Declaration — Chapter 4.6; the naming layer for every signal declared here.
- Number Representation — Chapter 4.4; the literal-syntax that initialises these variables.
- Operator Usage — Chapter 4.3; the operators that consume these typed signals.
- RTL Designing — Chapter 3; where data-types-discipline earns its keep across a full module.