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
genvardeclares a special generate-time variableblock_nameprovides 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, andcase-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
genvarfor 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
$displayand$monitorto 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-> repetitionif-generate-> conditional instantiationcase-generate-> multiple design variantsGenerateblocks operate at elaboration time, not runtime
Generate constructs bridge the gap between parameterized modeling and synthesizable RTL, enabling flexible, reusable hardware architectures.