Skip to content

VHDL · Chapter 4.8 · Concurrent Statements

Generate Statements

Regular hardware, such as a wide adder, a chain of registers, or a bank of identical slices, should come from one description, not many copy-pasted blocks. Generate statements provide exactly that. A for-generate unrolls a range into an array of concurrent statements or instances, and an if-generate conditionally includes hardware based on a static condition. The key idea is that a generate is an elaboration-time construct, so the loop is unrolled into parallel hardware before simulation starts and is never a runtime loop that executes step by step. This lesson shows how generate builds bit-sliced datapaths, ripple chains, and register files from a single parametric source, and how the generate index and labels name each replicated slice in the design hierarchy for debugging.

Foundation15 min readVHDLgenerateReplicationBit-slicedStructuralParametric

1. Engineering intuition — describe one slice, build many

A 32-bit datapath is 32 near-identical bit slices; a shift register is a chain of identical flops; a crossbar is a grid of identical cells. Writing each by hand is error-prone and unmaintainable. A generate statement lets you describe one slice and tell the tool "make N of these, wired in this regular pattern." It is replication of structure, evaluated when the design is built, so the result is N real, parallel pieces of hardware — not a loop that runs over time.

2. Formal explanation — for-generate and if-generate

generate_forms.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- FOR..GENERATE: replicate over a static range; 'i' is the generate index (a constant per slice).
gen_slice : for i in 0 to N-1 generate
  -- concurrent statements / instances here, parameterised by 'i'
  u_fa : entity work.full_adder
    port map ( a => a(i), b => b(i), cin => carry(i), sum => s(i), cout => carry(i+1) );
end generate;
 
-- IF..GENERATE: conditionally include hardware (condition is static/elaboration-time).
gen_opt : if HAS_PARITY generate
  parity <= xor data;        -- only built when HAS_PARITY is true
end generate;
-- VHDL-2008 also allows elsif/else generate branches.

The range and conditions are static (constants, generics) — resolved at elaboration, not at run time. for..generate produces one copy of its body per index value, with the index i available as a constant inside each copy; if..generate includes its body only when the condition holds. Each generate has a label, and replicated instances are named hierarchically by it.

3. Production-quality RTL — a ripple-carry adder from one slice

ripple_carry_generate.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all;
-- N full adders chained by the carry — described once, generated N times.
signal carry : std_logic_vector(N downto 0);
begin
  carry(0) <= cin;
  gen_adder : for i in 0 to N-1 generate
    u_fa : entity work.full_adder
      port map ( a => a(i), b => b(i), cin => carry(i),
                 sum => sum(i), cout => carry(i+1) );
  end generate;
  cout <= carry(N);

What hardware does this become? N full-adder instances placed side by side, each bit slice wired to the next by the carry chain — a ripple-carry adder. The generate did not "run"; it was unrolled at elaboration into N parallel instances. (For wide adders you would not ripple in practice, but the pattern shows replication-with-connection cleanly.)

4. Hardware interpretation — unrolled into parallel slices

for-generate unrolled into N parallel slices wired by a carry chainunroll i=0carry(1)carryone described slicefull_adder body, index islice 0a(0),b(0)→sum(0)slice 1a(1),b(1)→sum(1)slice N-1a(N-1)…→sum(N-1)12
A for..generate is unrolled at elaboration into an array of parallel slices. The single described body becomes N copies, one per index value, each parameterised by the generate index i and wired in the regular pattern (here a carry chain linking slice i to slice i+1). The result is N real, concurrent hardware blocks — not a loop executed over time. if..generate similarly includes or omits a block based on a static condition.

5. Simulation interpretation — built at elaboration, then concurrent

The generate is resolved during elaboration (lesson 4.6): the elaborator evaluates the static range/condition and instantiates the bodies, producing the replicated hardware before time zero. After that, the generated slices are ordinary concurrent hardware running in the event-driven engine. Because a generated chain does have time behaviour worth seeing — data moving slice to slice — a waveform is appropriate here, unlike the pure-connectivity lessons before it.

elaboration unrolling a generate range into instantiated bodiesStatic range /condition0 to N-1 / HAS_PARITYUnroll at elaborationone body per index /include if trueBodies instantiated +wiredN parallel slicesConcurrent at runtimeno loop executes during sim12
Elaboration of a generate. The elaborator evaluates the static range (or condition), then instantiates the body once per index value (or includes it if the condition holds), creating the replicated, wired hardware. From time zero onward those slices are concurrent hardware. The loop never 'runs' during simulation — it was fully unrolled into structure beforehand.

A generated 4-stage shift register: data ripples one stage per clock

10 cycles
A generated 4-stage shift register: data ripples one stage per clocka '1' enters dina '1' enters dinq0 captures it; each generated stage feeds the nextq0 captures it; each g…after 4 clocks it reaches q3 — one identical stage per indexafter 4 clocks it reac…clkdin0100000000q00011000000q10000110000q30000000011t0t1t2t3t4t5t6t7t8t9
The four flip-flop stages were produced by a single for..generate (one stage described, four built and chained). The '1' advances one stage per clock — proof the generate created a real chain of identical, concurrently-running registers. The replication is structural; the rippling you see is the generated hardware behaving.

6. Debugging example — the off-by-one in the generate range

Expected: an N-bit structure wired correctly. Observed: an elaboration error indexing out of range, or the top/bottom slice mis-wired (a dangling carry, a missing bit). Root cause: the generate range or an index expression overran an array bound — commonly carry(i+1) with the range 0 to N-1 requires carry to be N downto 0 (N+1 bits), and forgetting the extra bit indexes past the end; or the range was 0 to N instead of 0 to N-1. Fix: size the chained signals for the boundary (one extra carry bit) and make the range exactly cover the slices. Engineering takeaway: generate bugs are usually range/boundary bugs — check the index arithmetic at the first and last slice, and size chained nets (carries, links) for the +1 boundary.

generate_boundary.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- carry must hold N+1 values because slice i reads carry(i) and drives carry(i+1).
signal carry : std_logic_vector(N downto 0);     -- N+1 bits — NOT (N-1 downto 0)
gen : for i in 0 to N-1 generate                 -- exactly N slices
  u : entity work.full_adder
    port map ( a=>a(i), b=>b(i), cin=>carry(i), sum=>sum(i), cout=>carry(i+1) );
end generate;
-- WRONG would be 'carry : std_logic_vector(N-1 downto 0)' → carry(N) out of range at the top slice.

7. Common mistakes & what to watch for

  • Range/boundary off-by-one. Chained signals (carries, links) usually need one extra bit; check the first and last index expressions.
  • Treating generate as a runtime loop. It is unrolled at elaboration into parallel hardware; the index is a per-slice constant, not a loop counter that "runs."
  • Using non-static range/condition. Generate ranges and if conditions must be elaboration-time constants (generics/constants), not runtime signals.
  • Forgetting labels. Each generate (and the instances inside) needs a label; it names the slices in the hierarchy for debug.
  • Replicating with hidden cross-slice dependencies. Keep the per-slice body genuinely local plus explicit chaining; accidental shared state breaks the regular structure.

8. Engineering insight

Generate is the tool for regularity: any structure that is "N of the same, wired in a pattern" — datapath bit slices, register files, FIFO depth, systolic arrays, conditional feature blocks — comes from one parametric description that scales with a generic. That single-source property is a huge maintenance and correctness win: fix or verify the one slice and the whole array follows, and a width change is a generic change rather than an edit to N blocks. Keep the mental model precise — generate builds hardware at elaboration, it does not run — and pair for..generate for replication with if..generate for optional features to express scalable, configurable designs cleanly.

9. Summary

Generate statements replicate hardware from one description: for..generate unrolls a static range into an array of concurrent statements/instances (each parameterised by the generate index), and if..generate conditionally includes logic. It is an elaboration-time unrolling into parallel hardware, not a runtime loop, ideal for bit-sliced datapaths, chains, and register files. Watch the range/boundary arithmetic on chained signals.

10. Learning continuity

You now have the full set of concurrent statements — assignments (plain, conditional, selected), operators, instantiation, port maps, and generate. The module's final lesson, Concurrent vs Sequential Statements, draws the line between everything here (concurrent, always-active, in the architecture body) and the sequential world inside a process — the boundary that opens Module 5, where processes and sequential statements describe behaviour step by step.