wire and reg
wire and reg are the most confusing thing in classic Verilog, and the confusion is earned: the names are misleading and the rules do not follow from them. Get it wrong and you get anything from a compile error to hardware that quietly does the wrong thing. This article gives the rules, the reason for them, and a procedure you can apply without thinking. If you are wondering when SystemVerilog’s logic makes this go away, the answer is Module 9; until then the classic types are the foundation.
wire
A wire is what it says: a conductor between two points. It has no memory, and at every instant it carries whatever its driver is putting on it. It is driven by an assign statement or by an output port of an instance, and it cannot be assigned inside an always or initial block; that is a compile error. Undriven, it floats at z. A port declared without a type is a wire.
`timescale 1ns / 1ps
`default_nettype none
module wire_examples (
input wire [7:0] a,
input wire [7:0] b,
output wire [7:0] sum,
output wire [7:0] and_result
);
wire [7:0] internal_node;
assign internal_node = a ^ b;
assign sum = a + b;
assign and_result = internal_node & 8'hF0;
endmodule
Change an input and every wire downstream changes, after a gate delay in silicon and instantly in an ideal simulation. No clock, no memory. Each assign is one piece of combinational logic in the netlist.
reg
Here is the fact that the textbooks bury: reg does not mean register. It means “this signal is assigned inside a procedural block”, an always or an initial. That is the whole of it. It cannot be driven by an assign; that is a compile error too. Its default value is x, unknown, until something assigns it. And whether it turns into a flip-flop or a gate is not decided by the keyword at all.
`timescale 1ns / 1ps
`default_nettype none
module reg_examples (
input wire clk,
input wire rst_n,
input wire [7:0] d,
output reg [7:0] q_ff, // a flip-flop, because of the block below
output reg [7:0] q_comb // combinational, because of the block below
);
// clocked block: flip-flops with asynchronous reset
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q_ff <= 8'h00;
else
q_ff <= d;
end
// combinational block: an incrementer, no storage
always @(*) begin
q_comb = d + 8'd1;
end
endmodule
The one rule
| Type | Driven by | Cannot be driven by |
|---|---|---|
wire |
assign, or an instance output |
always, initial |
reg |
always, initial |
assign |
The compiler enforces it, so the choice is mechanical: ask who drives the signal. An assign or a port, wire. A block, reg. If you write assign x = ... for a reg, the fix is to change the declaration or move the logic into a block, whichever matches what you meant.
What actually gets built
The always block’s sensitivity list decides the hardware, not the type (Figure 1).
always @(posedge clk) gives edge-triggered flip-flops. always @(*) gives combinational logic, exactly what an assign would give. And a block whose body does not assign the output on every path, an if with no else in a combinational block, gives a latch, which is almost never what you wanted:
`timescale 1ns / 1ps
`default_nettype none
module synthesis_inference (
input wire clk,
input wire en,
input wire [3:0] d,
output reg [3:0] ff_out, // flip-flops
output reg [3:0] comb_out, // logic
output reg [3:0] latch_out // a latch, almost certainly a bug
);
always @(posedge clk) begin
ff_out <= d;
end
always @(*) begin
comb_out = d + 4'd1;
end
// no else: when en is 0 the output must hold, and holding means a latch
always @(*) begin
if (en)
latch_out = d;
end
endmodule
Latches are hard to time and the synthesiser will warn about them. Assign every output on every path, or give it a default at the top of the block.
The other net types
| Type | Meaning |
|---|---|
wire |
one driver, the normal case |
tri |
same as wire, spelled to say tri-state is intended |
wand, wor |
several drivers ANDed or ORed |
supply0, supply1 |
tied to ground or supply |
tri0, tri1 |
pull down or up when undriven |
integer |
32-bit signed, for loop indices and simulation |
real |
floating point, simulation only |
In RTL wire covers nearly everything. The supply types appear in cell libraries; the rest are simulation or legacy.
The decision
Who drives the signal? An assign or an instance output, wire. An always block, reg. Then, separately, what does the block look like? Clocked, flip-flops; @(*) with complete assignment, logic; @(*) with a missing path, a latch.
logic
SystemVerilog’s logic can be driven by either an assign or a block, so the declaration question disappears, and the synthesiser still decides flip-flop against logic from the block. It is the right choice for new code and the course moves to it in Module 9. Understanding wire and reg first is what makes it clear why logic was needed, and the legacy code you will read all your career uses them.
Practice Exercise
For each signal, wire or reg, and why?
carry, driven byassign carry = a[7] & b[7];count, incremented inalways @(posedge clk)mux_sel, assigned inalways @(*) begin mux_sel = a | b; endresult, connected to an output port of a submodule instance
Solution
wire: driven by anassign.reg: assigned in a block. The block is clocked, so it becomes flip-flops.reg: assigned in a block. The block is@(*), so it becomes an OR gate and no storage.wire: an instance output is a continuous driver. You cannot connect an instance output to areg.
Next: signals wider than one bit. Vectors, part-selects, concatenation and memory arrays are the tools of bus-level design.