Wires, registers, and vectors
wire vs reg, and multi-bit buses
In Verilog a wire carries a continuously-driven value while a reg holds a value assigned inside a procedural block; either can be a single bit or a vector such as [7:0] for a multi-bit bus, and every bit is four-state (0, 1, x for unknown, z for high-impedance).
So far the signals in your Verilog have been single bits. Real designs move buses and need to know *what kind* of thing each name is. Verilog has two: a wire (driven continuously, like the output of an assign) and a reg (assigned inside a procedural block, like the
always block of a flip-flop). Getting these right, and writing multi-bit values, is most of day-to-day Verilog.wire versus reg
A wire is exactly what it sounds like: a connection driven by something else, an
assign, a module output, a gate. You cannot assign a wire inside an always block. A reg is a name that *holds* its value between assignments and is written inside procedural blocks (always, initial). The rule of thumb: if a signal is driven by assign or a port connection, it is a wire; if it is assigned inside an always block, it must be a reg.The single most misleading name in Verilog: a **
reg is not necessarily a register**. A reg assigned in always @(posedge clk) becomes a flip-flop, but a reg assigned in always @(*) (combinational) is just a wire with a procedural style, no memory at all. reg only means "assigned procedurally," not "stored in hardware." What makes it a real register is a clock edge, not the keyword.Vectors: a bus in one name
A vector is a multi-bit signal, written with a range:
wire [7:0] bus; is an 8-bit bus. bus[7] is the most-significant bit, bus[0] the least; bus[3:0] is the low nibble, a slice. This is how you carry a whole byte on one name instead of eight separate wires, exactly the A[7:0] notation from the hardware. An 8-bit register is just the flip-flop widened to a vector:module reg8(
input clk,
input [7:0] D,
output reg [7:0] Q
);
always @(posedge clk)
Q <= D;
endmoduleNumber literals name a width and base:
8'hFF is eight bits of hex FF (all ones), 8'b1010_0101 is a binary byte (underscores are just for readability), and 4'd5 is four bits of decimal 5. The width before the quote keeps you honest about how many wires a value really occupies.Parametric modules: one width, any size
You wrote
reg8 at exactly eight bits. Tomorrow you need the same thing at 5 bits, or 16, or 32. Copy-pasting the module and hand-editing every [7:0] is how off-by-one bugs get in. A parameter is a named constant you set when you *instantiate* a module, so one source describes a whole family of widths. Declare it in the module header with #(...) and use it in the port ranges, exactly the 8-bit adder you built, but width-generic:module adder #(parameter WIDTH = 8) ( input [WIDTH-1:0] A, input [WIDTH-1:0] B, output [WIDTH:0] sum ); assign sum = A + B; endmodule
To use it, override the parameter with
#(...) before the instance name. The one adder source becomes both a 5-bit and an 8-bit adder in the same design:// 5-bit adder: A, B are [4:0], sum is [5:0] adder #(.WIDTH(5)) small (.A(a5), .B(b5), .sum(s5)); // 8-bit adder from the SAME module: A, B are [7:0], sum is [8:0] adder #(.WIDTH(8)) big (.A(a8), .B(b8), .sum(s8));
A
parameter is an elaboration-time constant, fixed the moment the design is built, one value per instance. It is not a signal that changes as the clock ticks. So a parameter can size a bus or count loop iterations, but you cannot drive it from logic at run time. Think of it as filling in a blank on the blueprint before the part is manufactured, not flipping a switch on the running board.Arrays: many vectors, a memory
A vector
[7:0] is a row of 8 bits. Stack many such rows and you get an array, Verilog's model of a memory. Write the element width first (the packed range, [7:0], before the name) and the number of elements after the name (the unpacked range, [0:255]):reg [7:0] mem [0:255]; // 256 locations, each one byte mem[0] = 8'h00; // whole byte at address 0 mem[255] = 8'hFF; // whole byte at address 255 data = mem[addr]; // read the byte selected at runtime by addr
The two ranges do different jobs: the packed
[7:0] says each element is 8 bits wide, and the unpacked [0:255] says there are 256 of them. To reach a single bit you index both: mem[addr][0] is the least-significant bit of the byte at addr. Putting it together, a byte-addressable RAM is one clocked write plus one continuous read:module ram256(
input clk,
input we, // write enable
input [7:0] addr,
input [7:0] din,
output [7:0] dout
);
reg [7:0] mem [0:255]; // 256 bytes of storage
always @(posedge clk)
if (we) mem[addr] <= din;
assign dout = mem[addr];
endmoduleYou can only touch an array one element (or slice) at a time:
mem[addr] is a value, but mem alone is not, so you cannot assign or wire up a whole memory by name. And an unpacked array is not a wide vector: reg [7:0] mem [0:255] is 256 addressable bytes (you reach one with mem[addr]), while reg [2047:0] flat is one 2048-bit number (you reach a byte only by slicing, flat[8*addr +: 8]). Same bit count, completely different thing.Four-state values
Like the signals on your canvas, each Verilog bit is four-state:
0 and 1 (driven low and high), x (unknown, the simulator does not know), and z (high-impedance, driven by nothing, a floating or released tri-state line). An uninitialised reg starts as x; a released bus reads z. Seeing x or z in a simulation is the same debugging signal as X or Z on the canvas: something is uninitialised, contended, or floating.Why this matters:
wire/reg and vectors are the vocabulary for every real Verilog module. A datapath is buses (wire [7:0]) between blocks; a register or the accumulator of your CPU is a reg [7:0] clocked on an edge; and x/z in simulation are how you catch an uninitialised or floating signal before it reaches silicon. Master these and you can read production HDL.Try it
You write
always @(*) y = a & b; where y is combinational (no clock). Should y be declared wire or reg, and does it create a stored register?Answer
y must be declared **reg**, because it is assigned inside an always block (only regs can be). But it creates no stored register: with always @(*) and no clock edge, it is pure combinational logic, an AND gate, that re-evaluates whenever a or b changes. This is the classic point that reg means "assigned procedurally," not "a hardware register"; only a clock edge makes real storage.Frequently asked
What is the difference between wire and reg in Verilog?
A wire is driven continuously by something else (an assign, a gate, a module output) and cannot be assigned inside a procedural block. A reg holds its value and is assigned inside always or initial blocks. Rule of thumb: assign-driven signals are wires; signals assigned in an always block must be regs.
Does reg in Verilog mean a hardware register?
No, that is the classic trap. A reg is just a signal assigned procedurally. A reg in always @(posedge clk) becomes a flip-flop, but a reg in always @(*) is plain combinational logic with no memory. Only a clock edge makes real storage; the keyword does not.
What is a vector in Verilog?
A vector is a multi-bit signal declared with a range, like wire [7:0] bus for an 8-bit bus. bus[7] is the top bit, bus[0] the bottom, and bus[3:0] is a slice. It lets one name carry a whole byte instead of eight separate wires.
What are the four states of a Verilog signal?
0 (driven low), 1 (driven high), x (unknown, the simulator cannot tell), and z (high-impedance, driven by nothing). These match the 0/1/X/Z states of a wire in hardware: x usually means uninitialised or contended, z means floating or a released tri-state line.
What is a parameter in Verilog?
A parameter is a named constant set when a module is instantiated, so one source can describe many sizes. You declare it in the module header, for example module adder #(parameter WIDTH = 8), use it in the port ranges like input [WIDTH-1:0] A, and override it per instance with adder #(.WIDTH(5)). It is fixed when the design is built, not a signal that changes over time.
How do you declare a memory or array in Verilog?
Put the element width before the name and the number of elements after it: reg [7:0] mem [0:255] is 256 bytes. The packed range [7:0] is each element's bit width, the unpacked range [0:255] is how many elements there are. Index it with mem[addr] to read or write one location at run time, and mem[addr][0] to reach a single bit.
Every lesson here builds toward one thing: a working CPU, from the transistor up.
Open the free lab →Builds towardTestbenches