A for generate unrolls at elaboration time. Each iteration builds real logic under its own block name.
Lane slicing uses it: iteration i owns bits [i*WIDTH +: WIDTH]. The block name (GEN_LANE) shows up in netlists and error messages.
// snippet: the generate body of gen_lanes (full module in the testbench below)
genvar i;
generate
for (i = 0; i < LANES; i++) begin : GEN_LANE // one named block per lane
if (INVERT == 1) begin : G_INV // if-generate inside the loop
assign stage[i*WIDTH +: WIDTH] = ~in_flat[i*WIDTH +: WIDTH];
end else begin : G_PAS
assign stage[i*WIDTH +: WIDTH] = in_flat[i*WIDTH +: WIDTH];
end
end
if (PIPE == 1) begin : G_PIPE // registered output
always_ff @(posedge clk) out_flat <= stage;
end else begin : G_COMB // combinational output
always_comb out_flat = stage;
end
endgenerateThe loop builds LANES copies of a lane. Inside each copy, the INVERT flag picks one of two bodies. After the loop, PIPE picks a register or a wire for the whole output.
An if generate picks between two structures with a constant condition. A case generate picks among many with a constant selector.
Both decide at elaboration and cannot depend on a signal. A choice that depends on a signal is a mux, not a generate.
Construct | Decides on | Use it for |
|---|---|---|
| A constant count | Replicating lanes, banks, pipeline stages |
| A constant flag | Including a register stage or a debug tap |
| A constant mode | Selecting add versus subtract at build time |
Read each branch as the hierarchy a simulator or netlist shows after elaboration. A path such as u_pipe.GEN_LANE[1].G_INV names one real block.
Same source, different trees. u_comb has four lanes and no register; u_pipe has two inverting lanes and a register.
Unchosen branches are gone. u_comb has no G_INV and no G_PIPE, so a probe of u_comb.G_PIPE fails to resolve.
PIPE is 0 in the lesson module. Which branch builds, and how does the output behave?The G_COMB branch builds and G_PIPE never exists. The output follows the input combinationally, with no clock delay.
MODE defaults to add, but the build subtracts. Where do you look first?The instantiation override at that site: MODE was set to 1 there. Generate follows the parameter it receives, not the default.
Lane replication with polarity and pipeline options, an add/subtract pair, and a finite testbench over both widths. After a known pipeline capture, change inputs between rising edges: the combinational instance must respond before the next edge while the registered instance must hold, then update after NBA settlement.
module gen_lanes #(
parameter int LANES = 4,
parameter int WIDTH = 8,
parameter int INVERT = 0, // if-generate: per-lane polarity
parameter int PIPE = 1 // if-generate: registered or combinational output
) (
input logic clk,
input logic [LANES*WIDTH-1:0] in_flat,
output logic [LANES*WIDTH-1:0] out_flat
);
logic [LANES*WIDTH-1:0] stage;
genvar i;
generate
for (i = 0; i < LANES; i++) begin : GEN_LANE
if (INVERT == 1) begin : G_INV
assign stage[i*WIDTH +: WIDTH] = ~in_flat[i*WIDTH +: WIDTH];
end else begin : G_PAS
assign stage[i*WIDTH +: WIDTH] = in_flat[i*WIDTH +: WIDTH];
end
end
if (PIPE == 1) begin : G_PIPE
always_ff @(posedge clk) out_flat <= stage;
end else begin : G_COMB
always_comb out_flat = stage;
end
endgenerate
endmodule
// case-generate: MODE picks the operation at elaboration time.
module gen_op #(
parameter int MODE = 0, // 0 = add, 1 = subtract
parameter int WIDTH = 8
) (
input logic [WIDTH-1:0] a, b,
output logic [WIDTH-1:0] y
);
generate
case (MODE)
0: begin : G_ADD
assign y = a + b;
end
1: begin : G_SUB
assign y = a - b;
end
default: begin : G_DEF
assign y = a + b;
end
endcase
endgenerate
endmodule
Testbench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It covers both widths, and checks that the combinational instance responds before the next edge while the registered instance holds and then updates after NBA settlement. This sensitivity case is argued from the source; no replacement mutation was run. The checks are finite digital checks, not physical signoff.