Module 5: Behavioral Verilog for Synthesis ยท 5.4 Loops in Verilog

generate for and Procedural for

Could the for loop of the last three articles instantiate ten full adders? No. A procedural for lives inside an always block, and a module instance is not a statement that can appear there. Repeating structure needs the other loop, generate for, which section 4.4 introduced and which is worth setting beside its namesake.

Two jobs

A procedural for, inside always or initial, unrolls repetition within one expression: a parity chain, a popcount, a comparator. Everything it produces stays inside that one block.

A generate for, at module level outside any block, stamps out structure: instances, wires, assign statements. Each iteration is a separate piece of hardware with its own name.

The test: are you repeating a calculation or a component? A calculation is procedural. A component is generate.

The adder again

genvar i;
generate
    for (i = 0; i < WIDTH; i = i + 1) begin : gen_fa
        full_adder u_fa (
            .a   (a[i]),
            .b   (b[i]),
            .cin (carry[i]),
            .sum (sum[i]),
            .cout(carry[i+1])
        );
    end
endgenerate

One parameter gives a 4-bit or a 64-bit adder, WIDTH full adders each wired to the next. The index is a genvar, not an integer: it exists while the design is elaborated and never as a signal.

The label

: gen_fa after the begin is not decoration. It names each iteration, so that a particular instance can be reached from a testbench:

tb.u_dut.gen_fa[2].u_fa.cin

is the carry-in of the third full adder. Without the label there is no clean way to address one of ten identical instances, and the first time an adder gives a wrong sum you will want to know which bit.

Not only instances

genvar j;
generate
    for (j = 0; j < N; j = j + 1) begin : gen_xor
        assign out[j] = in[j] ^ mask[j];
    end
endgenerate

$N$ separate XOR gates. For this uniform case assign out = in ^ mask; says the same in one line; the generate form earns its place when each iteration is wired a little differently, or needs its own instance.

The difference in what comes out is in Figure 1: a procedural loop becomes one piece of logic, a generate loop becomes separate, individually named instances.

Procedural against generate
Figure 1: One block of logic, or N addressable instances

Practice Exercise

An array of $N$ independent 2-to-1 muxes with a shared select. Procedural or generate, and write it with a testbench.

Solution

Each mux is an independent piece of hardware rather than one expression unrolled, so generate, with an assign per iteration since a 2-to-1 mux does not need a module of its own:

`timescale 1ns / 1ps
`default_nettype none

module mux_array #(
    parameter N = 8
) (
    input  wire [N-1:0] a, b,
    input  wire         sel,
    output wire [N-1:0] y
);
    genvar i;
    generate
        for (i = 0; i < N; i = i + 1) begin : gen_mux
            assign y[i] = sel ? b[i] : a[i];
        end
    endgenerate
endmodule

module tb_mux_array;
    parameter N = 8;
    reg  [N-1:0] a, b;
    reg          sel;
    wire [N-1:0] y;

    mux_array #(.N(N)) dut (.a(a), .b(b), .sel(sel), .y(y));

    initial begin
        $dumpfile("tb.vcd");
        $dumpvars(0, tb_mux_array);

        a = 8'hAA; b = 8'h55;
        sel = 0; #5; $display("sel=0: y=%h (expect %h)", y, a);
        sel = 1; #5; $display("sel=1: y=%h (expect %h)", y, b);

        $finish;
    end
endmodule

Since every bit is wired the same way, assign y = sel ? b : a; on its own is simpler still, and for this exact case it is the better answer. The generate earns its keep as soon as the bits differ.

That is section 5.4. Section 5.5 turns to the sequential building blocks: flip-flops, resets, enables, the register patterns every design is made of, and pipelines.

โ† Loops as Hardware Generators Inferring Flip-Flops โ†’