Skip to content

Verilog Syntax and Data Types

Introduction

In Verilog, everything is built on signals — the connections and storage elements that represent digital logic.
Understanding how these signals are declared, connected, and behave is the foundation of writing correct hardware description language (HDL) code.

This guide explains Verilog syntax and data types, focusing on the most used objects in digital design:
wire, reg, net, and variables — what they mean, when to use them, and how synthesis interprets them.

Basic Syntax in Verilog

Before we dive into data types, let’s review the basic structure of a Verilog design.

Example of a simple module

module and_gate (
  input  a, b,
  output y
);
  assign y = a & b;
endmodule

Every Verilog file consists of one or more modules, each describing a piece of hardware. Each module has:

  • Ports: Inputs, outputs, or inouts.
  • Internal signals: Declared as wire, reg, etc.
  • Assignments or procedural blocks: Define how signals interact.

Two Main Classes of Data Types

Verilog has two broad categories of data types:

Type Represents Example Synthesizable?
Nets (like wire) Physical connections between hardware elements wire a; Yes
Variables (like reg, integer, etc.) Storage elements holding values until next assignment reg q; Yes (if used properly)

Nets: Modeling Connections

Nets are used to represent physical connections between components in hardware — wires that transmit signals from one point to another. They do not store values. A net continuously reflects the value driven onto it by some source.

Net Type Description Example
wire Most common; carries combinational logic wire sum;
tri Tri-state wire (can be high-impedance 'z') tri bus;
wand Wired-AND connection wand signal;
wor Wired-OR connection wor signal;
wire a, b, y;
assign y = a & b;

Here, y is a wire that carries the AND of a and b. If a or b changes, y automatically updates — no storage involved.

Variables: Modeling Storage

Variables hold values until they are reassigned. They appear in procedural blocks such as always or initial. The most common variable type is reg.

Note

Despite its name, "reg" does not necessarily mean a hardware register — it can synthesize as either combinational or sequential logic, depending on context.

Example 1 — Combinational variable

reg y;
always @(*) begin
  y = a & b;  // Blocking assignment
end

Here y behaves like a combinational output, not a flip-flop.

Example 2 — Sequential variable

reg [7:0] q;
always @(posedge clk or posedge rst) begin
  if (rst)
    q <= 0;
  else
    q <= d;   // Non-blocking assignment
end
Here q is a flip-flop, updated only on a clock edge — storage element.

Declaring Data Types

You can declare nets and variables with or without bit widths. If no bit-width is given, the signal is 1-bit wide.

Declaration Meaning
wire a; Single-bit wire
reg [3:0] count; 4-bit register (bits 3 down to 0)
wire [7:0] data_bus; 8-bit wire bus
reg signed [15:0] value; Signed 16-bit register

The Difference Between wire and reg

One of the most confusing parts of Verilog for starters is understanding when to use wire or reg.

Feature wire reg
Stores value? No Yes
Requires continuous assignment? Yes (assign) No
Used in procedural blocks? No Yes
Default value Unknown (x) until driven Unknown (x) until assigned
Hardware equivalent Wire (connection) Latch/FF or combinational node
module example (
  input a, b, clk,
  output reg q,       // variable
  output wire y       // net
);

  assign y = a & b;  // continuous assignment

  always @(posedge clk)
    q <= a | b;      // procedural assignment

endmodule

In the above code, y is a wire: continuously driven by assign. y cannot be used inside procedural block (always @). q is a reg: updated only on a clock edge.

Other Variable Types

Verilog supports several variable types besides reg, though they’re mainly for simulation or specific uses.

Type Width Use Case Synthesizable?
integer 32-bit signed Loop indices, counters Sometimes
real Floating point Simulation only No
time 64-bit Simulation timing No
realtime Floating-point time Simulation only No
integer i;
initial begin
  for (i = 0; i < 8; i = i + 1)
    $display("i=%0d", i);
end

integer here is fine for simulation; not meant for hardware logic storage.

Continuous Assignments (for Nets)

Nets are driven by continuous assignments, which use the keyword assign.

assign y = a & b;

The expression on the right of assign continuously drives the wire y. If a or b changes, y updates instantly. Multiple drivers can drive the same wire, but the simulator resolves values (e.g., with wired-OR, wired-AND).

Procedural Assignments (for Variables)

Variables (e.g., reg) are updated using procedural assignments inside an always or initial block.

Two types of assignments:

Type Symbol Behavior
Blocking = Executes immediately (like software)
Non-blocking <= Executes concurrently at end of time step (used for sequential logic)
always @(*) begin
  y = a & b;       // Blocking
end

always @(posedge clk) begin
  q <= d;          // Non-blocking
end

Vectors and Bit Slicing

Verilog allows you to create vectors/bus (multi-bit signals or multi-width wires).

wire [7:0] data_bus;
reg  [3:0] nibble;

Bit and part selection

data_bus[0]       // LSB
data_bus[7:4]     // Upper nibble

Concatenation

assign full_byte = {upper, lower};  // Join two nibbles

Replication

assign pattern = {4{a}}; // Repeat 'a' 4 times

Nets and Variables in Hierarchical Designs

When connecting modules, you’ll often use nets (wire) between them.

module adder(input [3:0] a, b, output [4:0] sum);
  assign sum = a + b;
endmodule

module top;
  wire [3:0] x, y;
  wire [4:0] result;

  adder u1 (.a(x), .b(y), .sum(result));
endmodule

In the above code, x, y, and result are wires connecting the submodule adder to the top module. You cannot connect reg directly across modules; use wire for that.

Signed vs Unsigned Numbers

By default, Verilog treats numbers as unsigned. To handle negative values or signed arithmetic, declare signals as signed.

reg signed [7:0] a, b;
wire signed [8:0] sum;
assign sum = a + b;

This ensures proper sign extension and arithmetic interpretation.

Default Values and Initialization

In simulation:

  • Uninitialized wire → z (high-impedance)
  • Uninitialized reg → x (unknown)

In synthesis:

  • Uninitialized registers don’t have defined power-on values (unless FPGA supports it).

Use reset logic for predictable hardware behavior.

Data Type Best Practices

Do

  • Use wire for combinational interconnections.
  • Use reg in procedural blocks.
  • Define bit widths explicitly ([n:0]).
  • Use signed if negative arithmetic is required.
  • Add reset for all sequential logic.

Don't

  • Assign to wire inside an always block.
  • Drive a reg with multiple procedural blocks.
  • Mix blocking and non-blocking in same block.
  • Omit bit widths in large designs.

Example: Full Design with Data Types

module counter_4bit (
  input clk, rst,
  output [3:0] count
);
  reg [3:0] q;       // variable (storage)
  assign count = q;  // wire output

  always @(posedge clk or posedge rst) begin
    if (rst)
      q <= 0;
    else
      q <= q + 1;
  end
endmodule

In the above code, q is a reg — holds count value. count is a wire — continuously reflects q.

Quick Comparison Table between wire, reg, integer and real

Feature wire reg integer real
Represents Physical connection Variable/storage Loop variable Floating point
Used in Continuous assign Procedural blocks Testbenches, counters Simulation
Default width 1 bit 1 bit 32 bits 64 bits
Synthesizable Yes Yes Limited No
Holds value? No Yes Yes Yes
Multiple drivers Yes No No No