In short: Verilog splits its data objects into two families — nets (like wire, driven continuously by hardware) and variables (like reg, integer, real, which hold a value until reassigned) — and only the subset you write in a synthesizable style actually maps to gates, flip-flops, or wires on silicon.
Choosing the wrong data type is the single most common reason a Verilog design simulates perfectly but synthesizes into something unexpected — an unwanted latch, a truncated bus, or nothing at all. This guide walks through every core Verilog data type, explains the hardware each one represents, and shows exactly where the simulation model and the synthesized gate-level netlist diverge. Everything here maps to IEEE 1364 (classic Verilog) and carries straight into SystemVerilog.
The Two Families: Nets vs Variables
Verilog objects fall into two fundamentally different categories, and conflating them is where most beginner bugs start.
- Nets model a physical connection. A net has no storage of its own — it continuously reflects whatever is driving it, exactly like a metal trace between cells.
wireis the one you will use 95% of the time. Other net types (tri,wand,wor,supply0,supply1) exist for buses, tri-state, and power rails. - Variables (the LRM renamed them from "registers" to "variables" to stop the confusion) hold their last assigned value.
reg,integer,real,time, andrealtimeare all variables. Crucially, aregis not automatically a hardware register.
A reg only becomes a flip-flop or latch if you assign it inside an edge- or level-sensitive always block that stores state. In pure combinational logic, a reg synthesizes to wires and gates — no storage element at all.
This distinction is the whole reason the reg vs wire question dominates Verilog interviews. The rule of thumb: assign with assign (continuous) → the target must be a net; assign inside always/initial → the target must be a variable.
Vectors: Multi-Bit Buses
Any net or variable becomes a vector by adding a range: wire [7:0] data; declares an 8-bit bus. The left index is the most-significant bit by convention. Vectors synthesize to parallel wires or a bank of flip-flops — one per bit.
Two sizing traps bite everyone:
- Truncation. Assigning a 10-bit result to an 8-bit vector silently drops the top 2 bits. Synopsys Design Compiler and Cadence Genus both issue a width-mismatch warning (e.g.
TRUNC), but simulation won't error — so trust the lint, not the waveform. - Part-selects.
data[3:0]picks a slice;data[i+:4]is an indexed part-select that synthesizes to a mux. Variable bit-selects with a non-constant index become multiplexers, which can be surprisingly large.
integer, time, and real
These are the convenience variables. They are legal in synthesizable code only in restricted ways:
integeris a signed 32-bit variable. It's perfect forfor-loop counters that the synthesizer unrolls at elaboration — the loop variable never becomes hardware. If you drive a real output with aninteger, it maps to a 32-bit bus.timeis a 64-bit unsigned variable meant for storing simulation timestamps from$time. It is a simulation-only construct — no synthesizer builds hardware from it.real(andrealtime) are IEEE-754 double-precision floating point. They exist for testbenches and behavioral models. No RTL synthesis tool turnsrealinto gates — if you need fractional arithmetic in hardware you build fixed-point on vectors yourself.
Rule to memorize: real and time are testbench-only. integer is synthesizable but behaves as a 32-bit signed vector, so use a sized reg when you care about exact width.
How Each Type Synthesizes
| Data type | Family | Simulation meaning | Synthesizes to |
|---|---|---|---|
wire | Net | Continuous value from driver | Interconnect wire / combinational node |
reg (clocked) | Variable | Holds value, updated on edge | Flip-flop(s) |
reg (comb, full assign) | Variable | Holds value, updated on any input | Gates / LUTs, no storage |
reg (comb, incomplete) | Variable | Keeps old value | Unintended latch ⚠️ |
integer | Variable | 32-bit signed counter | 32-bit bus / loop unrolled away |
real / time | Variable | Float / 64-bit timestamp | Not synthesizable |
tri / wand | Net | Resolved multi-driver | Tri-state buffer / wired logic |
Animated: A Clocked reg Becoming a Counter
The clearest way to see "variable becomes flip-flop" is to watch a clocked reg accumulate. Below, each rising clock edge latches the next count value — exactly what always @(posedge clk) q <= q + 1; builds.
q updates — the behavioral essence of a synthesized flip-flop bank.The Latch Trap
If you write a combinational always block but forget to assign the output on every path — a missing else, or a case without default — the reg must "remember" its old value. The synthesizer honours this by inferring a transparent latch. Latches complicate timing closure and are flagged by every tool (Design Compiler's LATCH report, Genus warnings). The fix is to give every variable a default assignment at the top of the block. For a deeper treatment of avoiding inferred storage, see our guide to avoiding inferred latches.
If your work touches real tapeout flows rather than classroom examples, the design-services team at Avecas runs these same synthesis checks on production RTL every day — a useful reality check on how type discipline affects QoR.
Want to go from "it simulates" to "it synthesizes cleanly"? The CourseTron RTL & FPGA track drills data-type discipline, latch avoidance, and real Synopsys/Cadence lint on hands-on labs.
Frequently Asked Questions
Is a Verilog reg always a hardware register?
No. A reg is just a variable that can hold a value in simulation. It becomes a flip-flop only when assigned on a clock edge, a latch when combinational logic is incompletely specified, and plain gates when it drives fully-specified combinational logic.
Can I synthesize real or floating-point in Verilog?
Not directly. real is IEEE-754 double precision meant for testbenches and behavioral models. For hardware fractional math you implement fixed-point arithmetic on sized vectors, or instantiate a vendor floating-point IP core.
When do I use wire versus reg?
Use a wire (net) for anything driven by a continuous assign or a module output port connection. Use a reg (variable) for any signal assigned inside an always or initial block. SystemVerilog's logic type removes the choice in most cases.
What happens if I assign a 12-bit value to an 8-bit vector?
The upper 4 bits are silently truncated. Simulation will not error, but synthesis and lint tools issue a width-mismatch warning. Always size your left-hand side deliberately.