Module 4: Introduction to Verilog HDL ยท 4.2 Verilog Language Basics

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).

What the always block infers
Figure 1: The sensitivity list, not the reg keyword, decides between flip-flop, logic and latch

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

wire or reg
Figure 2: Who drives it decides the type

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?

  1. carry, driven by assign carry = a[7] & b[7];
  2. count, incremented in always @(posedge clk)
  3. mux_sel, assigned in always @(*) begin mux_sel = a | b; end
  4. result, connected to an output port of a submodule instance
Solution
  1. wire: driven by an assign.
  2. reg: assigned in a block. The block is clocked, so it becomes flip-flops.
  3. reg: assigned in a block. The block is @(*), so it becomes an OR gate and no storage.
  4. wire: an instance output is a continuous driver. You cannot connect an instance output to a reg.

Next: signals wider than one bit. Vectors, part-selects, concatenation and memory arrays are the tools of bus-level design.

โ† Modules and Ports Vectors and Arrays โ†’