Verilog · Chapter 5 · Foundations
Variables & Data Types in Verilog
Chapter 4 taught you to read Verilog source as text. This chapter is about what the declarations in that text actually mean. Every signal you declare belongs to one of two families — a net, which is driven by something else, or a variable, which is assigned by procedural code — and that single distinction explains most of the rules you will meet. It also clears up the most damaging misconception in the language: the word reg does not mean register, and declaring something reg does not create a flip-flop. You will learn what a declaration tells you about width, values and signedness, and, just as importantly, what it deliberately leaves undecided about the hardware.
Foundation21 min readVerilogData TypesNetsVariablesVectors
Chapter 5 · Page 2.1 · Data & Variables
1. What a Declaration Actually Tells You
Here are two declarations:
wire ready;
reg [7:0] count;A reasonable first reading is: ready is a wire on the chip, and count is an 8-bit register.
The first half is roughly right. The second half is wrong, and it is the single most expensive misunderstanding a Verilog beginner can carry forward.
A declaration tells you three things:
- Which family the signal belongs to — net or variable.
- How wide it is.
- How its value may be given to it — driven, or assigned.
It does not, on its own, tell you what hardware you get. That comes from what the rest of the code does with the signal. Hold on to this, because the whole chapter is built on it:
A declaration says what kind of thing a signal is. It does not say what hardware it becomes.
2. The Two Families — Nets and Variables
Every Verilog signal is either a net or a variable.
wire y; // a net — something else drives it
reg q; // a variable — procedural code assigns itThe difference is about how a value gets there:
- A net is driven. It carries whatever its driver puts on it, continuously. A
wireon its own holds nothing — remove the driver and it floats. - A variable is assigned. Procedural code inside an
alwaysorinitialblock writes it, and it keeps that value until something writes it again.
That is the rule that decides which keyword you need:
| You want to… | Use | Because |
|---|---|---|
drive it with a continuous assign | a net (wire) | assign connects a driver to a net |
| drive it from a module or gate output | a net (wire) | the instance is the driver |
assign it inside always or initial | a variable (reg) | procedural assignment needs a variable |
And it explains the two errors every beginner meets once: you cannot put a wire on the left of an assignment inside an always block, and you cannot put a reg on the left of a continuous assign.
3. wire — Driven by Something Else
wire y;
assign y = a & b;y is a net. The assign statement is its driver: it connects y permanently to the value of a & b, so whenever a or b changes, y follows.
Two things worth being precise about.
The wire keyword does not create a piece of metal. It declares connectivity in the Verilog model. What a synthesis tool builds for it depends on the design — often it is just routing, but the expression driving it is logic, and the tool is free to implement the required behaviour however it can.
wire does not mean "combinational". It is true that assign y = a & b; describes combinational behaviour — but that comes from the continuous assignment and its expression, not from the word wire. The net is the destination, not the logic.
4. reg — Assigned by Procedural Code
This is the section that matters most in Chapter 5.
regdoes not mean register. Declaring a signalregdoes not create a flip-flop.
The confusion is historical and entirely understandable: IEEE calls reg, integer and real the register data types, and that name has misled generations of learners. What reg actually means is much narrower:
regmeans: this signal is assigned by procedural code — inside analwaysorinitialblock.
That is all. It says nothing about storage, nothing about clocks, and nothing about flip-flops. Whether the hardware ends up with storage in it depends entirely on how the procedural code assigns it — which is the subject of the next two sections.
5. The Same Combinational Logic, Written Two Ways
Here is the proof that reg is not a register. These two modules describe the same AND gate:
wire y;
assign y = a & b;reg y;
always @(*) begin
y = a & b;
endRead the second one carefully:
yis declaredregonly because it is assigned inside analwaysblock. Verilog requires a variable there. That is a language rule, not a hardware statement.always @(*)means re-evaluate whenever any input changes. There is no clock and no edge.y = a & b;assignsyevery time the block runs, for every possible input combination.- So
yalways reflects the currenta & b. It never has to remember anything.
That is combinational logic — an AND gate. No flip-flop, no latch, no storage, despite the word reg sitting right there in the declaration.
This is why Chapter 3 could write combinational always blocks without contradiction. Now you know the language reason behind it.
6. What Actually Makes a Flip-Flop
Change one line:
reg q;
always @(posedge clk) begin
q <= d;
endqis still just a variable assigned by procedural code — same asyabove.- What changed is
@(posedge clk): the block now runs only on a rising clock edge. - Between edges,
qmust hold its value, because nothing re-evaluates it.
Holding a value between clock edges is exactly what a flip-flop does. So this describes storage — and the reason is the edge-triggered timing control, not the declaration.
Put the whole thing together and you get the chain this chapter exists to teach:
DECLARATION HOW IT IS DRIVEN BEHAVIOUR DESCRIBED HARDWARE
----------- ---------------- ------------------- --------
wire y; → assign y = a & b; → always follows a & b → combinational
reg y; → always @(*) → always follows a & b → combinational
y = a & b;
reg q; → always @(posedge clk) → holds between edges → flip-flop
q <= d;Look down the left column: the declaration alone cannot distinguish rows two and three. Look at the middle column and it is obvious. The driver style decides, not the keyword.
Two details carried forward from Chapter 3 and used correctly above: combinational procedural logic uses blocking =, and edge-triggered sequential logic uses non-blocking <=. Blocking and Non-Blocking Assignments covers why.
7. wire vs reg in One Table
| Question | wire | reg |
|---|---|---|
| Verilog category | net | variable |
| Can be the target of a procedural assignment? | no | yes |
Can be the target of a continuous assign? | yes | no |
| Automatically means combinational hardware? | no | no |
| Automatically means a flip-flop? | no | no |
| What decides the hardware? | the driving expression | the procedural block |
The two "no" rows are the point of the table. Neither keyword decides the hardware.
8. Width — Scalar and Vector
A declaration with no range is one bit — a scalar:
wire a; // one bit
reg valid; // one bitAdd a range and you get a vector: several bits treated as one signal.
wire [7:0] data;Read [7:0] as "bit 7 down to bit 0":
bit index: 7 6 5 4 3 2 1 0
^ ^
| |
most significant bit least significant bit
total width = 8 bitsThe two numbers are the highest and lowest bit index, not a count — the width is the number of indices in the range, here eight.
Once declared, you can select one bit or a slice of bits:
assign msb = data[7]; // a single bit — a bit-select
assign lsb = data[0]; // the other end
assign upper = data[7:4]; // four bits — a part-selectdata[7] is one bit wide; data[7:4] is four bits wide. Widths matter here for the same reason they mattered for literals in Number Representation: a value and its destination should agree.
Which brings the two halves together:
A literal has a width. A signal has a width. RTL works cleanly when they match.
reg [7:0] count;
initial count = 8'd0; // an eight-bit value into an eight-bit variableAnd note what this declaration does not say. reg [7:0] count; means eight bits wide in the Verilog model. It does not mean "eight flip-flops" — whether any storage exists depends, as always, on how count is assigned.
9. Range Direction
You will almost always see descending ranges:
wire [7:0] data;Ascending ranges are also legal Verilog:
wire [0:7] data;Both declare eight bits. The difference is which end index counts as bit 0, and therefore how bit numbering lines up with the usual most-significant-first way of writing a value.
RTL convention is overwhelmingly [7:0], because it matches how numbers are written — most significant on the left. Use descending ranges unless you have a specific reason not to, and be careful when connecting to code that does the opposite.
10. Signed and Unsigned — Same Bits, Different Reading
A declaration may carry the signed modifier:
reg [7:0] a; // unsigned — the default
reg signed [7:0] b; // signedBoth are eight bits. The signed keyword adds no bits and changes no bits — it is exactly the bits-versus-interpretation idea from Number Representation §10, now applied to a declaration instead of a literal:
BITS 1111_1111
+
INTERPRETATION unsigned -> 255
signed -> -1Signedness starts to matter when the value takes part in something — a comparison, arithmetic, a shift, or a change of width. Those rules belong to Verilog Operators and Operands, Chapter 10, where they can be taught alongside the operators that apply them.
One fact worth remembering now: reg and wire are unsigned by default, and integer is signed.
11. Four Values, Not Two
Every bit of a net or a reg carries one of four values:
| Value | Means | Typically seen when |
|---|---|---|
0 | logic low | driven low |
1 | logic high | driven high |
x | unknown | a variable never assigned, or drivers in conflict |
z | high-impedance | nothing is driving the net |
This is the same four-state model you met for literals in Chapter 4.4 §9, and the same caution applies: x and z are values in Verilog's model, not voltages on silicon. An x in a waveform means the model does not know, which is useful precisely because it is visible.
integer is four-state too — it can hold x, and at time zero it does.
12. What Everything Starts As
Before any initial or always block runs, the simulator gives each signal a starting value:
| Family | Value at time zero |
|---|---|
wire and most nets | z — nothing driving yet |
tri0 / tri1 | 0 / 1 |
supply0 / supply1 | 0 / 1, held |
reg, integer, time | x — unknown |
real, realtime | 0.0 |
This is why a forgotten reset shows up as x spreading through a waveform. That is the model being honest: nothing has defined the value yet. Real silicon has no x — a flip-flop powers up at some definite 0 or 1 you cannot predict — so write a proper reset rather than relying on any starting value.
13. The Type Catalogue
You do not need to memorise these. Read them once for orientation; each has its own page.
Nets — driven, never procedurally assigned:
| Net type | Behaviour | Where used |
|---|---|---|
wire | takes its driver's value | the default; almost all RTL |
tri | same as wire | signals intentional tri-state drivers |
wand / triand | wired-AND of its drivers | any 0 driver wins |
wor / trior | wired-OR of its drivers | any 1 driver wins |
tri0 / tri1 | pulls to 0 / 1 when undriven | pull-down / pull-up models |
trireg | retains its last driven value | capacitive charge storage; modelling |
supply0 / supply1 | constant 0 / 1 | power and ground ties |
Variables — procedurally assigned:
| Variable type | Width | Notes |
|---|---|---|
reg | as declared | the workhorse; combinational or sequential, depending on the block |
integer | 32 bits | signed, four-state; loop counters and calculations |
real | floating-point | simulation and modelling, not ordinary RTL signals |
realtime | floating-point | a real-valued time variable |
time | 64 bits | time values from $time |
For everyday synthesisable RTL the working set is small: wire, reg, and a vector range. The rest are for tri-state buses, power modelling and testbenches.
14. Where to Go Next
The twelve sub-topics below are live and each drills one type properly.
5.1 Physical Data Types — the net family in full.
- 5.1.1
wireandtriNets — the workhorse types - 5.1.2 Signal Strengths — how conflicting drivers resolve
- 5.1.3 Wired Nets —
wand,wor,triand,trior - 5.1.4
triregNets — charge-storage modelling - 5.1.5
tri0andtri1Nets — pull-up and pull-down models - 5.1.6
supply0andsupply1— power-net modelling
5.2 Register Data Types — the variable family, and the deeper treatment of everything in §4–§6 above.
- 5.2.1
reg— every synthesis outcome aregcan produce - 5.2.2
integer— 32-bit signed, and whyreg [N-1:0]usually wins in RTL - 5.2.3
real— floating-point for simulation - 5.2.4 Scalar vs Vector vs Arrays — the dimensional vocabulary, including memory arrays
15. Common Mistakes
Reading reg as "register". It means procedurally assigned. A reg in always @(*) with every branch assigned is combinational logic.
Expecting the declaration to tell you the hardware. reg [7:0] count; does not say whether count is storage. Look at how it is assigned.
Putting a wire on the left inside always. Procedural assignment needs a variable. Declare it reg.
Putting a reg on the left of assign. A continuous assignment drives a net. Declare it wire.
Treating [7:0] as a count. It is a range of bit indices — highest to lowest. The width is how many indices that range covers.
Thinking signed changes the bits. It changes how the same bits are interpreted, nothing more.
16. Exercises
Work these out before reading the answers.
Exercise 1 — Which declaration?
always @(*) begin
y = a | b;
endMust y be declared wire or reg, and why?
Exercise 2 — Does this imply a flip-flop?
reg y;
always @(*) begin
y = a & b;
endDoes reg y; mean y becomes a flip-flop? Explain what hardware this describes.
Exercise 3 — Width and indexing
wire [7:0] data;How wide is data? Which bit does data[7] select? How wide is data[7:4]?
Exercise 4 — Spot the difference
// A
reg q;
always @(*) q = d;
// B
reg q;
always @(posedge clk) q <= d;Both declare q as reg. What hardware does each describe, and what makes them different?
Exercise 5 — Signed
reg [7:0] a;
reg signed [7:0] b;How many bits does each hold? What does signed change?
Answers
Exercise 1. reg. The assignment is inside an always block, which is procedural, and procedural assignment requires a variable. This is a language requirement — it says nothing about the hardware, which here is an OR gate.
Exercise 2. No. y is declared reg only because it is assigned procedurally. The block is always @(*) with no clock and no edge, and y is assigned on every execution, so y always reflects the current a & b and never has to remember anything. This describes combinational logic — an AND gate.
Exercise 3. data is 8 bits. data[7] selects the most significant bit — a single bit. data[7:4] is a part-select of the top four bits.
Exercise 4. Both are variables assigned by procedural code, which is why both are reg.
- A re-evaluates whenever
dchanges and always tracks it, so it describes combinational logic — a buffer, effectively. - B runs only on a rising clock edge, so
qmust hold its value between edges. That is a flip-flop.
The difference is entirely in the timing control — @(*) versus @(posedge clk) — not in the declaration, which is identical in both.
Exercise 5. Both hold eight bits. signed changes nothing about the bits; it says those bits should be read as a signed (two's complement) value rather than an unsigned one. The same pattern 1111_1111 reads as 255 unsigned and −1 signed.
17. Summary
- A declaration says what kind of thing a signal is, not what hardware it becomes.
- Nets are driven; variables are assigned.
wireneeds a driver — a continuousassignor an instance output.regis written by procedural code in analwaysorinitialblock. regdoes not mean register. It means procedurally assigned. IEEE's name for the family is historical and misleading.- The driver style decides the hardware.
always @(*)with complete assignment describes combinational logic;always @(posedge clk)describes storage. The samereg q;appears in both. - A range is bit indices, not a count.
[7:0]is eight bits, numbered 7 down to 0. signedchanges the reading, not the bits.- Four values, not two —
0,1,x,z, all of them model values rather than voltages.
Next, Physical Data Types takes the net family in full, and Register Data Types takes the variable family — including every synthesis outcome a reg can produce.
Related Tutorials
- Lexical Conventions — Chapter 4; the source-text layer underneath these declarations.
- Number Representation — Chapter 4.4; width, base and signedness for literals.
- Physical Data Types — Chapter 5.1; the net family.
- Register Data Types — Chapter 5.2; the variable family.
- Constant Variables — Chapter 6;
parameterandlocalparam. - Verilog Operators and Operands — Chapter 10; how width and signedness govern expressions.
- RTL Designing — Chapter 3; where these declarations do their work.
Standards & specifications
- Governing standard
- IEEE Std 1364 (Verilog)(opens IEEE in a new tab)
Defines the Verilog language and its simulation semantics, including the event scheduling model. Synthesis support is defined by tools, not by this standard.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the Verilog HDL curriculum.
