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,ifandcase, and the role ofgenvar - Why generate blocks are evaluated at elaboration, before simulation or synthesis
- How a generate loop differs from a
forloop inside analwaysblock - 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.

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
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 for | for inside always | |
|---|---|---|
| Location | Module body | Inside an always or initial block |
| Loop variable | genvar | integer or reg |
| Can create | Module instances, assign statements, always blocks, wires | Procedural statements only |
| Exists in simulation | No, only the generated hardware remains | Yes, 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
// 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
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
forloop uses agenvarto replicate instances,assignstatements andalwaysblocks - Generate
ifandcasebuild 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
forloop creates statements; a generate loop can also create module instances - Alternative implementations selected by generate must behave identically, so test each parameter value