Modules, Signals, and Numbers
`wire` vs `reg` in Verilog
What wire and reg mean in classic Verilog, how each may be driven, and why inferred combinational logic, latches, and flip-flops depend on behavior rather than the keyword.
4 min read · Updated
In classic Verilog, wire declares a net and reg declares a procedural variable. Those keywords mainly control how a signal may be driven in the language. Neither keyword, by itself, proves whether synthesized hardware contains storage.
Use a wire for a connection driven by a continuous assignment, primitive output, or module output. Use a reg for a value assigned by procedural statements in an initial, always, task, or function context. For synthesizable RTL, the behavior and assignment coverage of that procedural code determine whether the hardware is combinational, a latch, or a flip-flop.
Quick comparison
| Question | wire net |
reg procedural variable |
|---|---|---|
| Typical driver | assign, primitive output, module output |
Assignment inside procedural code |
| Can the declaration alone imply storage? | No | No |
| Multiple writers/drivers | Multiple net drivers are legal and resolved; disagreement may produce x |
Multiple procedural writers can simulate with races but are poor RTL and generally not synthesizable |
| Typical synthesis role | A connection between driver and loads | Combinational result, latch, or flip-flop depending on the complete process |
| Does it keep an old value? | A net continuously reflects its resolved drivers | Only when the procedural behavior requires retention |
Special net types such as wand and wor change the resolution function. They are not required merely to have more than one driver on a net.
The behavior creates the hardware
These two descriptions both infer an AND function with no storage:
wire y_wire;
assign y_wire = a & b;reg y_comb;
always @(*) begin
y_comb = a & b;
endThe second output is a reg because it is assigned procedurally. Its assignment is complete on every evaluation, so it does not need to retain an older value.
An incomplete procedural assignment is different:
reg y_latched;
always @(*) begin
if (enable)
y_latched = data;
endWhen enable is low, the code requires y_latched to keep its previous value. Synthesis can therefore infer a level-sensitive latch even though the sensitivity list is @(*) and there is no clock edge.
An edge-triggered process commonly infers a flip-flop:
reg y_registered;
always @(posedge clk) begin
y_registered <= a & b;
endThe important questions are: what event activates the process, is every procedural value assigned on every relevant path, and what value must persist between activations?
Predict before running
The example compares a continuously driven net, a completely assigned combinational variable, and an edge-triggered variable. Input changes occur away from the rising edge so the sampling point is unambiguous.
Before running it, predict which outputs react between edges and which output changes only after a rising edge.
`timescale 1ns/1ps
module wire_example (
input wire a,
input wire b,
output wire y_out
);
assign y_out = a & b;
endmodule
module variable_combinational (
input wire a,
input wire b,
output reg y_out
);
always @(*) begin
y_out = a & b;
end
endmodule
module variable_registered (
input wire clk,
input wire a,
input wire b,
output reg y_out
);
always @(posedge clk) begin
y_out <= a & b;
end
endmodule
module tb;
reg a, b, clk;
wire y_wire, y_comb, y_registered;
wire_example u_wire(.a(a), .b(b), .y_out(y_wire));
variable_combinational u_comb(.a(a), .b(b), .y_out(y_comb));
variable_registered u_reg(.clk(clk), .a(a), .b(b), .y_out(y_registered));
always #5 clk = ~clk;
initial begin
clk = 1'b0; a = 1'b1; b = 1'b1;
#1;
$display("t=%0t a=%b y_wire=%b y_comb=%b y_registered=%b", $time, a, y_wire, y_comb, y_registered);
if (y_wire !== 1'b1 || y_comb !== 1'b1 || y_registered !== 1'bx) begin $display("FAIL T1"); $finish; end
a = 1'b0; #1;
$display("t=%0t a=%b y_wire=%b y_comb=%b y_registered=%b", $time, a, y_wire, y_comb, y_registered);
if (y_wire !== 1'b0 || y_comb !== 1'b0 || y_registered !== 1'bx) begin $display("FAIL T2"); $finish; end
@(posedge clk); #1;
$display("t=%0t a=%b y_wire=%b y_comb=%b y_registered=%b", $time, a, y_wire, y_comb, y_registered);
if (y_registered !== 1'b0) begin $display("FAIL T6"); $finish; end
@(negedge clk); a = 1'b1; #1;
$display("t=%0t a=%b y_wire=%b y_comb=%b y_registered=%b", $time, a, y_wire, y_comb, y_registered);
if (y_wire !== 1'b1 || y_comb !== 1'b1 || y_registered !== 1'b0) begin $display("FAIL T11"); $finish; end
@(posedge clk); #1;
$display("t=%0t a=%b y_wire=%b y_comb=%b y_registered=%b", $time, a, y_wire, y_comb, y_registered);
if (y_registered !== 1'b1) begin $display("FAIL T16"); $finish; end
$display("PASS");
$finish;
end
endmoduleExpected output:
t=1000 a=1 y_wire=1 y_comb=1 y_registered=x
t=2000 a=0 y_wire=0 y_comb=0 y_registered=x
t=6000 a=0 y_wire=0 y_comb=0 y_registered=0
t=11000 a=1 y_wire=1 y_comb=1 y_registered=0
t=16000 a=1 y_wire=1 y_comb=1 y_registered=1
PASSy_registered samples a & b on the rising edges at 5 ns and 15 ns. The testbench waits one additional nanosecond before observing it, so the lines printed at 6 ns and 16 ns are post-update observations, not the capture times themselves. Between those edges, y_wire and y_comb react to a, while y_registered holds its sampled value.
Practical rules
- Use
wirefor classic-Verilog connections driven continuously or by another module. - Use
regwhen classic Verilog requires a procedural assignment target. - Determine storage by reading the entire process: event control, assignment coverage, and hold behavior—not only the declaration or sensitivity list.
- Keep one intentional procedural writer per synthesizable variable. Multiple procedural writers create races and are normally rejected for synthesis.
- Use non-blocking assignment for state-holding variables in edge-triggered RTL and blocking assignment for combinational calculations.
SystemVerilog’s logic, always_comb, and always_ff make these intentions more explicit, but they do not remove the need to understand the inferred hardware.
Sources and verification
Example provenance: SkillLift Labs authored the example for this tutorial. tests/scripts/tutorial-verified-examples.test.ts compiles it in Verilog-2005 mode, reproduces the published output, and independently checks the expected combinational and edge-sampled values, including capture-versus-observation timing. The same test exercises the article-to-runner source split. Automation verifies those stated properties; human technical review for indexing remains a separate gate.