Data Types, Operators and Primitives in Verilog

Data Types

Nets

  • Purpose: represent physical connections (wires).
  • Declared with keywords: wire, tri, wand, wor, supply0, supply1, etc.
  • Driven by: continuous assignments or module/gate outputs.
  • Cannot hold value unless driven.
  • Default initialization = z (high-impedance).
wire a, b, sum;
assign sum = a ^ b;   // continuous drive

Corner case: multiple drivers → resolved by net type (wired-AND, wired-OR, etc.). If conflict without wired type → x.

Net Types

NetWhat it models / resolvesNotes
wireplain resolved wiredefault for ports (unless declared otherwise)
tritri-state wiresame as wire (historical name)
tri0tri-state with weak pull-down when undrivenhandy for default 0 on bus
tri1tri-state with weak pull-up when undrivenhandy for default 1 on bus
wandwired-AND resolutionmultiple drivers ANDed bitwise
worwired-OR resolutionmultiple drivers ORed bitwise
triandtri-state wired-ANDz participants ignored; else ANDed
triortri-state wired-ORz participants ignored; else ORed
triregcharge-storage netholds last driven value when all drivers go z (decay optional via strengths/delays)
supply0power net tied to logic 0 (strong0)cannot be driven by logic
supply1power net tied to logic 1 (strong1)cannot be driven by logic
uwireunresolved wire (Verilog-2005)compile error if multiply driven (great to catch fan-in mistakes)

Registers

  • Represent variables/storage elements.
  • Declared with reg.
  • Hold last assigned value until updated.
  • Used in procedural blocks (initial, always).
  • Default initialization = x (unknown).
reg q;
always @(posedge clk) q <= d;   // storage

⚠️ reg does not imply flip-flop; inference depends on procedural style.

Register Types

TypeWidth / SignednessTypical use
reguser-defined width (default 1-bit, unsigned unless declared signed)general procedural variable (flops/latches/comb as coded)
integer32-bit signedloop counters, arithmetic temporaries
time64-bit unsignedsimulation time storage
realdouble-precision floating-pointtestbench math (unsynthesizable)
realtimealias of realtestbench timing calcs
eventevent handletestbench synchronization

Parameters

  • Compile-time constants.
  • Used for configurability & readability.
parameter WIDTH = 8;
reg [WIDTH-1:0] data;
  • Can be overridden:
    • By name: my_module #(.WIDTH(16)) u1 (...);
    • By defparam: defparam u1.WIDTH = 16; (discouraged).
  • localparam = constant that cannot be overridden.

Examples:

// Parameter declaration
module m #(parameter WIDTH=8, parameter SIGNED=0) (
  input  [WIDTH-1:0] a, b,
  output [WIDTH-1:0] y
);
  localparam HALF = WIDTH/2;  // cannot be overridden
  assign y = a + b;
endmodule


// Overriding parameters

// 1) By name (preferred)
m #(.WIDTH(16), .SIGNED(1)) u1 (...);

// 2) By position (use with care; order matters as declared)
m #(16, 1) u2 (...);

Choosing Data Types for Ports

  • input/output/inout must be declared as wire (default) or reg explicitly.
  • Rules:
    • Combinational outputswire.
    • Sequential outputs (from always) → reg.
    • Bidirectional busesinout wire.
module example(input wire clk,
               input [7:0] a, b,
               output reg [7:0] sum,
               inout wire bus);

Continuous Assignment

  • Syntax: assign <net> = <expr>;
  • Drives nets only.
  • Updates whenever RHS changes.
assign y = a & b;        // combinational logic
assign #5 y = a ^ b;     // with delay

⚠️ Cannot assign to reg with assign.

Procedural Constructs

Initial Block

  • Executes once at time 0.
  • Used in testbenches (stimulus, init values).
initial begin
  clk = 0;
  forever #5 clk = ~clk;
end

Always Block

  • Executes repeatedly whenever trigger occurs.
  • Syntax:

    always @(<sensitivity_list>) <statements>
    

Examples:

  • Combinational:

    always @(*) y = a & b;   // implied sensitivity
    
  • Sequential:

    always @(posedge clk or posedge rst)
      if (rst) q <= 0;
      else    q <= d;
    

Named Blocks

  • Group statements with a name → can be disabled.
always begin : block1
  a = b + c;
  if (stop) disable block1;
end

Verilog Primitives or Inbuilt gates & switches

n_input gatesn_output_gatesThree-state gatesPull gatesMOS switchesBidirectional switches
andbufbufif0pulldowncmosrtran
nandnotbufif1pullupnmosrtranif0
nor notif0 pmosrtranif1
or notif1 rcmostran
xnor   rnmostranif0
xor   rpmostranif1

n-input Gates

Basic combinational logic gates with n inputs (2 or more).

  • and, nand, or, nor, xor, xnor
  • Used to build basic Boolean functions directly.
  • Synthesizable (mapped to standard logic cells).

n-output Gates

Single-input, multiple-output primitives.

  • buf → buffer (copies input to output(s)).
  • not → inverter (logical NOT).
  • Multiple outputs can be driven by a single source.

Three-State Gates

Output can be 0, 1, or high-impedance (Z).

  • bufif0, bufif1 → buffer with active-low or active-high enable.
  • notif0, notif1 → inverter with active-low or active-high enable.
  • Commonly used for tri-state buses.

Pull Gates

Provide weak constant logic values.

  • pullup → weak ‘1’.
  • pulldown → weak ‘0’.
  • Often used for default bus values or test benches.
  • Synthesizers usually replace them with explicit tie cells.

MOS Switches

Model ideal MOS transistors (digital switch-level).

  • nmos, pmos → NMOS/PMOS transistors.
  • cmos → CMOS transmission gate (p- and n-MOS pair).
  • rnmos, rpmos, rcmos → resistive versions (weak drive strength).
  • Mostly for switch-level simulation, not synthesizable in RTL.

Bidirectional Switches

Transmission gates passing signals both ways.

  • tran → bidirectional wire connection.
  • rtran → resistive bidirectional.
  • tranif0/tranif1 → conditional pass switch, controlled by enable (low/high).
  • rtranif0/rtranif1 → resistive conditional pass switch.
  • Used in switch-level modeling, not synthesizable.

👉 Rule of thumb for RTL design:

  • Use n-input gates and n-output gates sparingly (structural design).
  • Use three-state gates for I/O buffers (top-level ports).
  • Avoid MOS and bidirectional switches in RTL (simulation-only).

2’s Complement

  • Negative numbers stored in two’s complement.
  • Example: 4'b1011 = −5 if treated signed.

Declare signed explicitly:

reg signed [7:0] a, b;

Operand

  • Can be constant, variable, net, part-select, concatenation, function call, etc.
  • Bit-select: a[3]
  • Part-select: a[7:4]
  • Concatenation: {a, b, c}
  • Replication: {4{1’b1}} → 1111

Operators

Blocking vs. Non-Blocking Assignments

  • Blocking (=): Executes immediately, sequential.

    a = b; c = a; // c gets new a
    
  • Non-blocking (<=): Updates scheduled, parallel.

    a <= b; c <= a; // c gets old a
    
  • Guideline:
    • Use = in combinational always blocks.
    • Use <= in sequential (clocked) always blocks.

Arithmetic

+, -, *, /, %

  • Operands default to unsigned unless signed.
  • Division by zero → x.
reg signed [3:0] x = -3, y = 2;
assign z = x / y;   // result = -1

Sign Operators

  • Unary +, -
  • Applies sign extension when needed.

Relational

<, <=, >, >=

  • Result: 1 or 0.
  • If any operand has x/z → result = x.

Equality/Inequality

  • ==, != → logical compare, x/zx.
  • ===, !== → case equality (checks x/z too).
if (a === 1'bx) $display("a is unknown");

Logical Comparison

&&, ||, !

  • Treats any x/z as unknown (x).

Bitwise Logical

&, |, ^, ~^

  • Operates bit-by-bit.
assign y = a ^ b;   // XOR

Shift

  • <<, >> → logical shift.
  • <<<, >>> → arithmetic shift (preserve sign).
reg signed [7:0] a = -4;
assign b = a >>> 1;   // arithmetic shift right → -2

Concatenation / Replication

assign x = {a, b};       // concat
assign y = {4{1’b1}};    // replicate → 1111

Reduction

Applies operator across all bits: &a, |a, ^a, ~&a, etc.

assign parity = ^data;   // XOR reduction (parity bit)

Conditional (?:)

assign out = sel ? a : b;
  • If sel = x/z → result = x (unless both branches same value).

Signed expressions

$signed(expression)

  • Converts expression into a signed expression of the same bit-width.
  • Interprets MSB as the sign bit (two’s complement).
  • Example:

    reg [3:0] a = 4'b1000;   // 8 (unsigned)
    $display("%0d", $signed(a));  // -8
    

$unsigned(expression)

  • Converts expression into an unsigned expression of the same bit-width.
  • MSB is treated as a normal bit, not a sign bit.
  • Example:

    reg signed [3:0] b = 4'b1000;  // -8 (signed)
    $display("%0d", $unsigned(b)); // 8
    

💡Remember

  • Uninitialised reg = x.
  • Unconnected wire = z.
  • Multiple drivers on wire without resolution = x.
  • Division by 0 = x.
  • == vs === → don’t confuse (case equality is safer for X/Z detection).
  • Procedural assignments:
    • Blocking (=) → sequential.
    • Non-blocking (<=) → parallel, preferred in sequential logic.
  • Declared nets/regs are unsigned by default unless explicitly marked signed.
  • $signed() / $unsigned() do not resize — width is unchanged.

Concept understood? Let's apply and learn for real

Practice now