Skip to content

Generate statement in Verilog

Introduction

Verilog’s generate statements provide a powerful way to create repetitive or conditional hardware structures efficiently. It is part of behavioral modeling. They are a key feature for parameterized, scalable, and modular RTL design, allowing you to describe hardware that changes based on compile-time parameters. When designing hardware, it’s common to instantiate multiple identical modules or logic blocks. Instead of writing the same code multiple times, Verilog provides generate constructs to automate repetitive structures and conditionally include code based on parameters.

Typical use cases include:

  • Repeating identical hardware blocks (e.g., adders, flip-flops)
  • Creating parameterized bus widths
  • Conditionally instantiating modules
  • Configuring hierarchical designs

Basic Syntax

The generate statement comes in two major forms:

  • For-generate — for repetitive instantiation
  • If-generate / Case-generate — for conditional instantiation

The general structure is:

generate
  // code or loop
endgenerate

The generate and endgenerate keywords are optional in Verilog-2001 and later, but including them improves readability and compatibility.

For-Generate: Creating Repeated Structures

The for-generate loop replicates hardware elements based on a compile-time loop variable.

Syntax

genvar i;
generate
  for (i = 0; i < N; i = i + 1) begin : block_name
    // repeated hardware
  end
endgenerate
  • genvar declares a special generate-time variable
  • block_name provides a named generate block (required for hierarchical referencing)
  • The loop executes during elaboration (not runtime)

Example: 4-bit Ripple Carry Adder

module ripple_adder #(parameter N = 4)(
  input  [N-1:0] a, b,
  input  cin,
  output [N-1:0] sum,
  output cout
);
  wire [N:0] c;
  assign c[0] = cin;

  genvar i;
  generate
    for (i = 0; i < N; i = i + 1) begin : add_stage
      full_adder FA (
        .a(a[i]),
        .b(b[i]),
        .cin(c[i]),
        .sum(sum[i]),
        .cout(c[i+1])
      );
    end
  endgenerate

  assign cout = c[N];
endmodule

Here, a for-generate loop creates N instances of the full_adder module. Changing the parameter N automatically scales the design.

Hierarchical Naming in Generate Blocks

Each iteration in a generate block forms a unique hierarchical name that can be referenced for debugging or simulation.

Example:

ripple_adder.add_stage[0].FA
ripple_adder.add_stage[1].FA
ripple_adder.add_stage[2].FA

This naming convention helps in waveform analysis and instance-level tracing during simulation.

If-Generate: Conditional Instantiation

if-generate statements allow conditional inclusion of hardware blocks based on parameters or constants known at compile time.

Syntax

generate
  if (PARAM == 1) begin : gen_case1
    // hardware block A
  end else begin : gen_case2
    // hardware block B
  end
endgenerate

Example: Parameterized Adder/Subtractor

module add_sub #(parameter MODE = 0)(
  input [7:0] a, b,
  output [7:0] y
);
  generate
    if (MODE == 0) begin : ADD
      assign y = a + b;
    end else begin : SUB
      assign y = a - b;
    end
  endgenerate
endmodule
  • If MODE=0, the module behaves as an adder
  • If MODE=1, it becomes a subtractor

This feature enables design-time configuration — useful for reusing the same module with different functionality.

Case-Generate Statement

For multiple conditional branches, Verilog supports a case-generate block.

Example: Case-Based Design Selection

module alu #(parameter MODE = 2)(
  input [7:0] a, b,
  output reg [7:0] y
);
  generate
    case (MODE)
      0: begin : ADD_MODE
        always @(*) y = a + b;
      end
      1: begin : SUB_MODE
        always @(*) y = a - b;
      end
      2: begin : AND_MODE
        always @(*) y = a & b;
      end
      3: begin : OR_MODE
        always @(*) y = a | b;
      end
      default: begin : DEFAULT_MODE
        always @(*) y = 8'h00;
      end
    endcase
  endgenerate
endmodule

This design chooses the operation based on the parameter MODE, allowing the same module to act as multiple ALU configurations.

Combining Generate Types

You can combine for-generate and if-generate for complex hierarchical designs.

Example: Conditional Instantiation with Loops

module multi_block #(parameter N = 8, parameter USE_AND = 1)(
  input [N-1:0] a, b,
  output [N-1:0] y
);
  genvar i;
  generate
    for (i = 0; i < N; i = i + 1) begin : gen_logic
      if (USE_AND)
        assign y[i] = a[i] & b[i];
      else
        assign y[i] = a[i] | b[i];
    end
  endgenerate
endmodule

Here:

  • Each bit position is processed individually
  • The same generate block chooses between AND or OR gates based on a parameter

Generate Inside Hierarchical Designs

Generate constructs are often used for instantiating submodules conditionally or repetitively within larger designs.

Example: Multiple Submodule Instantiation

module top #(parameter N = 4)(
  input [N-1:0] in,
  output [N-1:0] out
);
  genvar i;
  generate
    for (i = 0; i < N; i = i + 1) begin : STAGE
      submodule sm (
        .a(in[i]),
        .y(out[i])
      );
    end
  endgenerate
endmodule

The top-level module now automatically instantiates N identical submodules, simplifying design scaling.

Example: Parameterized Shift Register

module shift_register #(parameter N = 8)(
  input clk, rst, din,
  output dout
);
  wire [N-1:0] q;

  genvar i;
  generate
    for (i = 0; i < N; i = i + 1) begin : shift
      if (i == 0)
        dff d0 (.clk(clk), .rst(rst), .d(din), .q(q[i]));
      else
        dff dN (.clk(clk), .rst(rst), .d(q[i-1]), .q(q[i]));
    end
  endgenerate

  assign dout = q[N-1];
endmodule

This parameterized shift register can scale to any width N — by modifying just one parameter.

Generate vs. Procedural Loops

It’s important to understand that generate loops are compile-time constructs, not runtime loops.

Aspect Generate Loop Procedural Loop (for)
Execute At compile/elaboration time At simulation/runtime
Purpose Replicate hardware Iterate operations
Location Outside always/initial blocks Inside procedural blocks
Creates Multiple instances Single block of hardware

Example:

// Generate loop creates multiple hardware instances

genvar i;
generate
  for (i=0; i<4; i=i+1) begin
    and_gate g (.a(a[i]), .b(b[i]), .y(y[i]));
  end
endgenerate

// Procedural loop executes sequentially
integer j;
always @(*) begin
  for (j=0; j<4; j=j+1)
    y[j] = a[j] & b[j];
end

Both produce the same logical behavior, but their synthesis and elaboration differ fundamentally.

Synthesizability and Tool Support

Synthesizable:

  • for-generate, if-generate, and case-generate
  • Conditional instantiation and parameterized blocks

Not synthesizable:

  • Generate statements based on runtime variables
  • Non-constant loop bounds or parameters

Modern synthesis tools (Synopsys, Cadence, Xilinx Vivado, Intel Quartus) fully support generate constructs for parameterized RTL.

Real-World Use Cases

Use Case Description
Bus width scaling Automatically generate N-bit logic
Pipelined datapaths Replicate register stages
Modular DSP units Instantiate arithmetic blocks
Configurable IP cores Enable/disable submodules using parameters
FIFOs and arrays Generate multiple queue elements

Best Practices

  • Always declare genvar for loop variables
  • Use parameters for scalable hardware
  • Keep generate logic synthesizable
  • Use named blocks for clarity
  • Avoid nested generates unless necessary
  • Use consistent indentation for readability
  • In simulation waveforms, expand each generate block instance for verification
  • Use $display and $monitor to verify generated logic

Summary

Generate statements in Verilog allow designers to:

  • Create repetitive structures efficiently
  • Implement parameterized, scalable modules
  • Conditionally include or exclude hardware blocks
  • Improve readability and maintainability of large designs

Key takeaways:

  • for-generate -> repetition
  • if-generate-> conditional instantiation
  • case-generate -> multiple design variants
  • Generate blocks operate at elaboration time, not runtime

Generate constructs bridge the gap between parameterized modeling and synthesizable RTL, enabling flexible, reusable hardware architectures.