Skip to content

VHDL · Chapter 5.6 · Sequential Statements & Processes

Loops — for, while, and loop

A loop in a process looks like software iteration, but for hardware it is something different. A for loop with static bounds unrolls into replicated logic, the process-side counterpart of the generate statement. Each iteration becomes a copy of the body, parameterised by the loop index, and all the copies are built at once and run in parallel. That single idea, that loops unroll rather than iterate over time, is what keeps you from writing logic that explodes in size or refuses to synthesise. This lesson covers the synthesizable for loop and how it builds reductions, bit-sliced logic, and array operations from one body, the while and plain loop forms with exit and next, and why static bounds matter for anything you intend to turn into hardware.

Foundation14 min readVHDLfor loopUnrollingReplicationReductionsSequential

1. Engineering intuition — a loop is replication, not iteration

In software a loop runs its body again and again over time. In synthesizable VHDL a for loop does not run over time at all — the tool unrolls it, stamping out one copy of the body per index value and wiring them together. A loop over 0 to 7 is eight parallel (or chained) copies of the body, not one block executed eight times. This is the same replication you met with generate (lesson 4.8), now available inside a process for sequential-style description. Keep "unroll, do not iterate" in mind and loops stop being mysterious.

2. Formal explanation — the three loop forms

loop_forms.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- FOR loop: static range, loop variable is a per-iteration constant. Synthesizable when bounds are static.
for i in 0 to N-1 loop
  -- body, parameterised by i (i is read-only, declared implicitly)
end loop;
 
-- WHILE loop: repeats while a condition holds. Synthesizable only if statically bounded.
while count < LIMIT loop
  -- body
end loop;
 
-- Plain LOOP with exit/next: general loop; 'exit' leaves it, 'next' skips to the next iteration.
loop
  -- body
  exit when done;        -- leave the loop
end loop;

The for loop's range must be static (constants/generics) to synthesise — the tool needs to know how many copies to build. The loop variable is an implicitly-declared, read-only per-iteration constant. while and plain loop are general but synthesise only when statically bounded; they are common in testbenches. exit leaves a loop; next skips to the next iteration.

3. Production RTL — reductions and bit-sliced logic

loop_patterns.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
 
-- (A) Population count (number of '1' bits) — a for loop unrolled into an adder tree/chain.
popcount : process (all)
  variable cnt : unsigned(3 downto 0);
begin
  cnt := (others => '0');
  for i in data'range loop
    if data(i) = '1' then cnt := cnt + 1; end if;   -- one comparator+add per bit, unrolled
  end loop;
  ones <= std_logic_vector(cnt);
end process;
 
-- (B) Initialise / transform an array with a for loop (unrolled wiring).
rev : process (all)
begin
  for i in din'range loop
    dout(din'high - i) <= din(i);     -- bit-reverse: pure routing, one assignment per bit
  end loop;
end process;

What hardware does this become? (A) the loop unrolls into eight conditional increments — an adder chain/tree counting set bits, combinational logic with no clock; (B) the loop unrolls into wiring that reverses the bus — no logic at all, just routing. Neither "runs" eight times; each is one piece of combinational hardware built from the unrolled body. (cnt is a variable so each iteration sees the running total immediately — the anchor's immediate-update rule.)

4. Hardware interpretation — unrolled copies

for loop unrolled into replicated body copies parameterised by the indexunrollchain (e.g.running sum)chainloop body (index i)described oncecopy i=0body for bit 0copy i=1body for bit 1copy i=N-1body for bit N-112
A for loop with static bounds unrolls into replicated hardware: the body is copied once per index value, parameterised by the loop variable, and the copies are wired together (chained, like the popcount increments, or independent, like the bit-reverse routing). This is the process-side analog of a generate statement. The loop does not execute over time — it is fully expanded at elaboration into parallel/iterative logic, so the size of the hardware is the loop count times the body.

5. Simulation interpretation — unrolled, then run in zero time

By the execution model, when the process wakes the (unrolled) loop body runs in zero simulated time — the iterations are sequential within that single wake-up, so a variable accumulated across iterations (like cnt) carries its running value from one iteration to the next immediately. Real time does not pass between iterations; the whole loop is part of one process run.

for loop versus generate replicationfor loop (in aprocess)replicates sequentialstatementsvariable carriesrunning valueacross iterations, zerotimegenerate (inarchitecture)replicates concurrentstatements/instancesstructural instancearrayswired by index12
A for loop versus a generate statement. Both unroll at elaboration into replicated hardware. A generate replicates CONCURRENT statements/instances directly in the architecture; a for loop replicates SEQUENTIAL statements inside a process, where a variable can carry a running value across iterations within one zero-time wake-up. Use generate for structural instance arrays, a for loop for sequential per-element computation inside a process.

Combinational popcount: the for loop output tracks the input bit count

8 cycles
Combinational popcount: the for loop output tracks the input bit countdata=0x03 (two '1' bits) → ones=2data=0x03 (two '1' bit…data=0xFF (eight '1' bits) → ones=8data=0xFF (eight '1' b…data000103070F1F3FFFones01234568t0t1t2t3t4t5t6t7
The unrolled for loop is pure combinational logic: 'ones' continuously equals the number of set bits in 'data', updating as the input changes. The loop did not iterate over time — it became an adder network that computes the count in one combinational cone. The waveform shows that logic responding to its input.

6. Debugging example — the non-static loop bound

Expected: a for loop synthesises. Observed: the synthesiser errors or refuses, complaining the loop bound is not static (or it builds enormous logic). Root cause: the loop range depended on a signal (a runtime value) instead of a constant/generic, so the tool cannot know how many copies to build; or the static bound was very large, unrolling into huge hardware. Fix: make the range a static constant/generic, and keep the count modest (large reductions may need a clocked, multi-cycle implementation instead of a single unrolled cone). Engineering takeaway: synthesizable loops have static bounds and unroll fully — if a loop count must vary at runtime, that is sequential (multi-cycle) behaviour, not a single unrolled loop.

static_bound.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- WRONG: loop bound from a runtime signal → not statically unrollable → synthesis error.
-- for i in 0 to to_integer(unsigned(len)) loop ... end loop;
-- RIGHT: static range (constant/generic); process only the bits you need with a guard.
for i in 0 to N-1 loop
  if i < to_integer(unsigned(len)) then acc := acc + data(i); end if;  -- static range, runtime guard
end loop;

7. Common mistakes & what to watch for

  • Non-static for/while bounds in synthesizable code. Synthesis needs static counts; use a constant/generic range with a runtime guard inside.
  • Expecting time to pass per iteration. A loop unrolls into one wake-up's worth of logic; it does not consume clock cycles. For multi-cycle behaviour use a clocked process + counter.
  • Huge unroll. A large static loop builds large combinational logic (and long critical paths); consider a registered, iterative design instead.
  • Accumulating in a signal across iterations. Use a variable for a running value within a loop (immediate update); a signal would read its old value each iteration.
  • Forgetting exit/next semantics. exit leaves the loop entirely; next skips to the next iteration — mixing them up changes the unrolled logic.

8. Engineering insight

Loops are the sequential face of replication. A for loop lets you describe per-element computation — reductions, bit-slicing, encoders, array transforms — once, and the tool unrolls it into the parallel or chained hardware it implies. The discipline is to keep bounds static and counts sane, and to remember that "iteration" here is spatial (copies of logic) for combinational loops, not temporal (clock cycles). When you genuinely need iteration over time, that is a clocked process with a counter, not a single unrolled loop — a distinction the execution model makes precise.

9. Summary

A for loop with static bounds unrolls at elaboration into replicated hardware — the process-side analog of generate — so it describes spatial replication, not runtime iteration. while and plain loop (with exit/next) are general but synthesise only when statically bounded. Use variables to carry running values across iterations (immediate update), keep bounds static, and reach for a clocked counter when you truly need multi-cycle iteration.

10. Learning continuity

Loops, if, and case all leaned on variables to carry intermediate and running values immediately. The next lesson, Variables Inside Processes, examines them in full: their process-local scope and immediate-update semantics, when a variable is just combinational wiring versus when read-before-write infers a register, and the disciplined patterns (accumulators, intermediates) that make variables a precise, predictable tool rather than a source of surprise storage.