A Verilog generate block creates hardware at compile time: it can replicate module instances and statements a parameterized number of times, or include one piece of hardware and leave out another based on a parameter. There are two kinds of generate constructs: generate loops (for) and generate conditionals (if and case).

12 min read | Beginner Level

What You'll Learn

  • The syntax of generate for, if and case, and the role of genvar
  • Why generate blocks are evaluated at elaboration, before simulation or synthesis
  • How a generate loop differs from a for loop inside an always block
  • How to avoid genvar, naming and non-constant condition mistakes

Syntax

Generate constructs are written directly in the module body, outside any always or initial block. The generate and endgenerate keywords around them are optional since Verilog-2005, but they make the code easier to read.


// 1. Generate for loop: replicate hardware
genvar i;                               // Loop variable that exists only at elaboration
generate
  for (i = 0; i < N; i = i + 1) begin : gen_name   // Named block, one copy per i
    // Module instances, assign statements, always blocks
  end
endgenerate

// 2. Generate if: include one of two pieces of hardware
generate
  if (PARAM == 1) begin : gen_a
    // Hardware built when the condition is true
  end else begin : gen_b
    // Hardware built when it is false
  end
endgenerate

// 3. Generate case: include one of several pieces of hardware
generate
  case (PARAM)
    0       : begin : gen_0  /* ... */ end
    1       : begin : gen_1  /* ... */ end
    default : begin : gen_d  /* ... */ end
  endcase
endgenerate

Components:

  • genvar - The loop variable of a generate loop. It takes only non-negative integer values and exists only while the design is being built, not during simulation (required for loops)
  • Loop bounds and conditions - Must be constant expressions built from numbers and parameters (required)
  • begin : name - A named block. Its name becomes part of the hierarchical path of everything inside it (strongly recommended)

When are Generate Blocks Evaluated?

A generate block is resolved during elaboration, the step in which the simulator or synthesis tool builds the design hierarchy from the source code and parameter values. The loop is expanded and the conditions are decided once, before simulation time 0. Only the selected or replicated hardware exists afterwards. A generate block never adds logic of its own: there is no mux that chooses between the branches of a generate if, because the unselected branch is simply not built.

Generate for loop

This example builds an N-bit design from N separate half adders. Each loop iteration creates one instance of ha, connected to one bit of each port.


// Design for a half-adder
module ha ( input   a, b,
            output  sum, cout);

  assign sum  = a ^ b;
  assign cout = a & b;
endmodule

// A top level design that contains N instances of half adder
module my_design
	#(parameter N=4)
		(	input [N-1:0] a, b,
			output [N-1:0] sum, cout);

	// Declare a temporary loop variable to be used during
	// generation and won't be available during simulation
	genvar i;

	// Generate for loop to instantiate N times
	generate
		for (i = 0; i < N; i = i + 1) begin : gen_ha    // Named block: instances are gen_ha[0].u0, gen_ha[1].u0, ...
          ha u0 (a[i], b[i], sum[i], cout[i]);       // Each copy connects to bit i of every port
		end
	endgenerate
endmodule

Elaboration result: With N = 4, the loop creates four instances of ha, with hierarchical names gen_ha[0].u0 through gen_ha[3].u0. Every instance has the same name u0, but each sits inside a different copy of the named block, so the full paths are unique. Synthesis then builds four independent XOR and AND gate pairs.

Elaborated schematic of my_design with N=2, showing two half-adder instances, each connected to one bit of a, b, sum and cout

Testbench

The testbench sets N = 2, so my_design contains two half adders, one for each bit of the 2-bit inputs.


module tb;
	parameter N = 2;
  reg  [N-1:0] a, b;
  wire [N-1:0] sum, cout;

  // Instantiate top level design with N=2 so that it will have 2
  // separate instances of half adders and both are given two separate
  // inputs
  my_design #(.N(N)) md( .a(a), .b(b), .sum(sum), .cout(cout));

  initial begin
    a <= 0;
    b <= 0;

    $monitor ("a=0x%0h b=0x%0h sum=0x%0h cout=0x%0h", a, b, sum, cout);

    #10 a <= 'h2;
    		b <= 'h3;
    #20 b <= 'h4;
    #10 a <= 'h5;
  end
endmodule
Output
ncsim> run
a=0x0 b=0x0 sum=0x0 cout=0x0
a=0x2 b=0x3 sum=0x1 cout=0x2
a=0x2 b=0x0 sum=0x2 cout=0x0
a=0x1 b=0x0 sum=0x1 cout=0x0
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

Each bit is added independently. With a = 2'b10 and b = 2'b11, bit 0 gives sum 1, carry 0, and bit 1 gives sum 0, carry 1, so sum = 2'b01 and cout = 2'b10. In the last two lines, the values 'h4 and 'h5 are wider than the 2-bit inputs, so only their lower two bits are kept: 'h4 becomes 0 and 'h5 becomes 1.

Where are Generate Loops Used?

Network switches and multi-channel controllers repeat the same logic for every port or channel: an 8-port switch has eight identical ingress buffers, and a 16-channel DMA engine has sixteen identical channel state machines. A generate loop instantiates one copy per port, with the port count set by a parameter, so a 4-port and a 32-port version come from the same source file.

Generate Loop vs Procedural for Loop

Both loops are unrolled at compile time, but they work in different places and can create different things. See Verilog for Loop for the procedural version.

Generate forfor inside always
LocationModule bodyInside an always or initial block
Loop variablegenvarinteger or reg
Can createModule instances, assign statements, always blocks, wiresProcedural statements only
Exists in simulationNo, only the generated hardware remainsYes, the loop runs each time the block runs

Quick Check

Q: Can you instantiate a module inside a for loop that is inside an always block?

Show Answer

No. Module instances can only be placed in the module body, never inside procedural blocks. To replicate instances, use a generate for loop with a genvar.

Generate if

A generate if chooses which hardware to build from a parameter. Here, USE_CASE selects one of two multiplexer implementations. Each mux module prints a message from an initial block, so the simulation log shows which one was built.


// Design #1: Multiplexer design uses an "assign" statement to assign
// out signal
module mux_assign ( input a, b, sel,
                   output out);
  assign out = sel ? a : b;

  // The initial display statement is used so that
  // we know which design got instantiated from simulation
  // logs
  initial
  	$display ("mux_assign is instantiated");
endmodule

// Design #2: Multiplexer design uses a "case" statement to drive
// out signal
module mux_case (input a, b, sel,
                 output reg out);
  always @ (a or b or sel) begin
  	case (sel)
    	0 : out = a;
   	 	1 : out = b;
  	endcase
  end

  // The initial display statement is used so that
  // we know which design got instantiated from simulation
  // logs
  initial
    $display ("mux_case is instantiated");
endmodule

// Top Level Design: Use a parameter to choose either one
module my_design (	input a, b, sel,
         			output out);
  parameter USE_CASE = 0;

  // Use a "generate" block to instantiate either mux_case
  // or mux_assign using an if else construct with generate
  generate
  	if (USE_CASE)
      mux_case mc (.a(a), .b(b), .sel(sel), .out(out));
    else
      mux_assign ma (.a(a), .b(b), .sel(sel), .out(out));
  endgenerate

endmodule

Elaboration result: Only one mux exists in the built design. With USE_CASE = 1, the hierarchy contains mc and no ma; with USE_CASE = 0, it is the reverse. Synthesis sees a single 2 to 1 mux either way.

Testbench


module tb;
	// Declare testbench variables
  reg a, b, sel;
  wire out;
  integer i;

  // Instantiate top level design and set USE_CASE parameter to 1 so that
  // the design using case statement is instantiated
  my_design #(.USE_CASE(1)) u0 ( .a(a), .b(b), .sel(sel), .out(out));

  initial begin
  	// Initialize testbench variables
  	a <= 0;
    b <= 0;
    sel <= 0;

    // Assign random values to DUT inputs with some delay
    for (i = 0; i < 5; i = i + 1) begin
      #10 a <= $random;
      	  b <= $random;
          sel <= $random;
      $display ("i=%0d a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out);
    end
  end
endmodule
Output
// When USE_CASE = 1
ncsim> run
mux_case is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
i=1 a=0x0 b=0x1 sel=0x1 out=0x1
i=2 a=0x1 b=0x1 sel=0x1 out=0x1
i=3 a=0x1 b=0x0 sel=0x1 out=0x0
i=4 a=0x1 b=0x0 sel=0x1 out=0x0
ncsim: *W,RNQUIE: Simulation is complete.

// When USE_CASE = 0
ncsim> run
mux_assign is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
i=1 a=0x0 b=0x1 sel=0x1 out=0x0
i=2 a=0x1 b=0x1 sel=0x1 out=0x1
i=3 a=0x1 b=0x0 sel=0x1 out=0x1
i=4 a=0x1 b=0x0 sel=0x1 out=0x1
ncsim: *W,RNQUIE: Simulation is complete.

The first line of each run confirms which mux was built. The $display runs in the same time step as the non-blocking assignments, before they take effect, so each line shows the inputs from the previous iteration together with the output they produced.

The two mux designs are not equivalent.

Compare lines i=1 and i=3 of the two runs: with the same inputs, the outputs differ. mux_assign passes a when sel is 1 (sel ? a : b), while mux_case passes a when sel is 0. When a generate if chooses between two implementations of the same function, both must behave identically. Running the same testbench with each parameter value, as done here, is how such a mismatch is caught.

Target-Specific Implementations

IP blocks often have to run on both FPGAs and ASICs. A parameter such as TARGET drives a generate if that instantiates a vendor's FPGA block RAM primitive in one case and a foundry memory macro in the other. The rest of the design uses the same ports either way, so a single RTL source supports both targets.

Generate Case

A generate case works like a generate if, but chooses among several alternatives. Here ADDER_TYPE selects a half adder (0) or a full adder (1).


// Design #1: Half adder
module ha (input a, b,
           output reg sum, cout);
  always @ (a or b)
  {cout, sum} = a + b;

  initial
    $display ("Half adder instantiation");
endmodule

// Design #2: Full adder
module fa (input a, b, cin,
           output reg sum, cout);
  always @ (a or b or cin)
  {cout, sum} = a + b + cin;

    initial
      $display ("Full adder instantiation");
endmodule

// Top level design: Choose between half adder and full adder
module my_adder (input a, b, cin,
                 output sum, cout);
  parameter ADDER_TYPE = 1;

  generate
    case(ADDER_TYPE)
      0 : ha u0 (.a(a), .b(b), .sum(sum), .cout(cout));
      1 : fa u1 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
    endcase
  endgenerate
endmodule

Elaboration result: Exactly one adder is built. With ADDER_TYPE = 0, the cin port of my_adder is left unconnected inside the module, and synthesis removes it from the logic. Note that this case has no default: any other value of ADDER_TYPE builds no adder at all, and sum and cout are left undriven.

Testbench


module tb;
  reg a, b, cin;
  wire sum, cout;
  integer i;

  my_adder #(.ADDER_TYPE(0)) u0 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));

  initial begin
    a <= 0;
    b <= 0;
    cin <= 0;

    $monitor("a=0x%0h b=0x%0h cin=0x%0h cout=0%0h sum=0x%0h",
             a, b, cin, cout, sum);

    for (i = 0; i < 5; i = i + 1) begin
      #10 a <= $random;
      b <= $random;
      cin <= $random;
    end
  end
endmodule
Output
ncsim> run
Half adder instantiation
a=0x0 b=0x0 cin=0x0 cout=00 sum=0x0
a=0x0 b=0x1 cin=0x1 cout=00 sum=0x1
a=0x1 b=0x1 cin=0x1 cout=01 sum=0x0
a=0x1 b=0x0 cin=0x1 cout=00 sum=0x1
ncsim: *W,RNQUIE: Simulation is complete.

Because a half adder is instantiated, cin does not have any effect on the outputs sum and cout. In the third line, a = b = cin = 1 gives sum 0 and cout 1, which is the half adder result for 1 + 1. A full adder would give sum 1 and cout 1. $monitor prints only when a value changes, so the initial values and five random iterations produce just four lines.

Quick Check

Q: Can the condition of a generate if be an input port, such as if (mode) ... else ...?

Show Answer

No. The condition is evaluated once during elaboration, before any input has a value, so it must be a constant made from parameters and numbers. To choose between two results while the hardware runs, build both and select between them with a mux, for example an assign with the conditional operator.

Configurable Arithmetic Units

Processor and DSP IP often lets the integrator choose the multiplier implementation by parameter: a small iterative multiplier for area-limited microcontrollers, or a fast pipelined array multiplier for performance designs. A generate case on that parameter builds only the chosen version, so unused options cost no area.

Common Beginner Mistakes

Mistake #1: Using an integer as the generate loop variable

Wrong: integer i; generate for (i = 0; i < N; i = i + 1) ...
Right: genvar i; generate for (i = 0; i < N; i = i + 1) ...

Why? A generate loop must use a genvar, which exists only during elaboration. An integer is a simulation variable, and the compiler reports an error. The reverse also fails: a genvar cannot be used as the loop variable of a for loop inside an always block.

Mistake #2: Using a signal in a generate condition or loop bound

Wrong: if (mode) mux_case mc (...); where mode is an input port
Right: Use a parameter: if (USE_CASE) mux_case mc (...);

Why? Generate blocks are resolved before simulation starts, when signals have no values yet. Every generate condition and loop bound must be a constant expression. The compiler reports an error for a non-constant expression.

Mistake #3: Leaving generate blocks unnamed

Wrong: for (i = 0; i < N; i = i + 1) begin ha u0 (...); end
Right: for (i = 0; i < N; i = i + 1) begin : gen_ha ha u0 (...); end

Why? Without a name, the tool invents one, such as genblk1, and the number can change when another generate block is added above it. Testbench probes, waveform setups and synthesis constraints that use hierarchical paths then break. Verilog-2001 tools may also reject an unnamed generate loop. Name every generate block.

Mistake #4: Leaving outputs undriven when no branch is selected

Wrong: A generate case with items 0 and 1 only, while ADDER_TYPE is set to 2
Right: Add a default branch that builds a valid design, or that triggers a compile error for unsupported values

Why? If no branch matches, nothing is built and the outputs float at z. Elaboration succeeds, so the problem appears only as z values in simulation or as unconnected nets after synthesis. A default branch makes every parameter value produce working hardware.

Practice Exercise

Challenge: N-bit Ripple-Carry Adder

Using the full adder fa from the Generate Case example, write a module rca with a parameter N, N-bit inputs a and b, a carry input cin, an N-bit output sum and a carry output cout. Use a generate for loop to chain N full adders, where each adder's carry out feeds the next adder's carry in.

Hint

Declare an internal carry bus wire [N:0] c;. Connect c[0] to cin and c[N] to cout. Full adder i takes c[i] as carry in and drives c[i+1].

Solution

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

  wire [N:0] c;                 // Carry chain: c[0] in, c[N] out
  assign c[0] = cin;
  assign cout = c[N];

  genvar i;
  generate
    for (i = 0; i < N; i = i + 1) begin : gen_fa
      fa u_fa (.a    (a[i]),
               .b    (b[i]),
               .cin  (c[i]),     // Carry in from the previous stage
               .sum  (sum[i]),
               .cout (c[i+1]));  // Carry out to the next stage
    end
  endgenerate

endmodule

Elaboration builds N full adders named gen_fa[0].u_fa to gen_fa[N-1].u_fa, connected in a chain. Unlike the half-adder example, the stages depend on each other, so the carry ripples through all N adders and the delay grows with N. This is why wide adders in fast designs use carry-lookahead structures instead.

Summary

In this tutorial, you learned:

  • Generate blocks build hardware at elaboration, before simulation or synthesis, and add no logic of their own
  • A generate for loop uses a genvar to replicate instances, assign statements and always blocks
  • Generate if and case build only the selected branch, based on parameters
  • All generate conditions and loop bounds must be constant expressions
  • Named generate blocks give stable hierarchical paths such as gen_ha[0].u0
  • A procedural for loop creates statements; a generate loop can also create module instances
  • Alternative implementations selected by generate must behave identically, so test each parameter value
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5