Skip to content

Verilog · Chapter 14.6.1 · Behavioural Modeling

For Loop in Verilog — The Synthesizable Count Loop

The for loop is the synthesizable workhorse of Verilog iteration, a count-controlled loop with an initialization, a condition, and an update. When its bound is static, meaning a constant or a parameter, it unrolls at elaboration into replicated parallel hardware. Each iteration becomes a copy of the body, and all of them exist at once. This makes it the compact way to describe bit-sliced and regular logic, such as a per-bit operation across a vector, a reduction across many inputs, or a small array of identical computations. This page drills the loop, covering its syntax, how it unrolls, the bit-sliced patterns it expresses, and the rule that its bound must be static. A loop whose limit is a runtime signal cannot be unrolled and will not synthesize. It is the loop you reach for in synthesizable RTL.

Foundation12 min readVerilogfor loopUnrollingBit-SlicedRTL Design

Chapter 14 · Section 14.6.1 · Behavioural Modeling

1. The Engineering Problem

The for loop is the one loop you write in synthesizable RTL, and using it correctly means holding the unrolling model and the static-bound rule:

A for loop with a static bound unrolls into replicated parallel hardware; a for loop bounded by a runtime signal cannot unroll and will not synthesize.

This page drills the for loop and the bit-sliced patterns it builds.

2. Mental Model — Count, Then Unroll

3. Syntax and Unrolling

for-unroll.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // bit-wise AND of two vectors — unrolls into WIDTH AND gates
   always @(*)
       for (i = 0; i < WIDTH; i = i + 1)
           y[i] = a[i] & b[i];
 
   // equivalent unrolled form (for WIDTH = 4):
   //   y[0] = a[0] & b[0];
   //   y[1] = a[1] & b[1];
   //   y[2] = a[2] & b[2];
   //   y[3] = a[3] & b[3];

The loop describes WIDTH parallel AND operations; the synthesizer unrolls it into WIDTH AND gates. The index i parameterizes the slice. (A single assign y = a & b; does the same in dataflow, 10.4 — the loop generalizes to per-bit operations that aren't a single operator.)

4. Bit-Sliced and Accumulation Patterns

The common synthesizable for patterns:

for-patterns.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // bit reversal — y is a reversed
   always @(*)
       for (i = 0; i < WIDTH; i = i + 1)
           y[i] = a[WIDTH-1-i];
 
   // count set bits (population count) — an adder tree, unrolled
   always @(*) begin
       count = 0;
       for (i = 0; i < WIDTH; i = i + 1)
           count = count + a[i];        // accumulate (blocking, combinational)
   end
 
   // per-element operation across a small array
   always @(*)
       for (i = 0; i < N; i = i + 1)
           out[i] = in[i] + offset;

Bit reversal, population count (an unrolled adder tree), and per-element array operations are all for loops with static bounds. Note the accumulation uses blocking = (combinational, 14.3) and a default (count = 0) before the loop. These unroll into parallel combinational hardware.

5. The Static-Bound Rule

A for loop's bound must be known at elaboration:

static-bound.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // SYNTHESIZABLE — static bound:
   for (i = 0; i < WIDTH; i = i + 1) ...     // WIDTH is a parameter
 
   // NOT SYNTHESIZABLE — runtime bound:
   for (i = 0; i < n; i = i + 1) ...          // n is a runtime signal → can't unroll

The synthesizer must know how many copies to make, so the count must be a constant or parameter. A loop limited by a runtime signal (n) has an unknown iteration count at elaboration and cannot be unrolled — it does not synthesize. To do something a runtime-variable number of times, use a clocked state machine with a counter (over cycles), not an unrollable loop (in space).

6. Common Mistakes

  1. Runtime-bounded for — can't unroll; not synthesizable; use a static bound (§5, DebugLab 1).
  2. Thinking the loop runs over time — it unrolls into parallel hardware (§2).
  3. Non-blocking in a combinational loop accumulation — use blocking = (14.3).
  4. Missing default before an accumulation loop — initialize the accumulator (e.g. count = 0) first (§4).

7. Debugging Lab

One for-loop debug post-mortem

Pitfall — runtime-bounded for loop won't synthesize
Buggy Code
module count_to_n (input [3:0] n, input [15:0] data, output reg [4:0] cnt);
  integer i;
  // Intent: count set bits in the low 'n' bits of data. But the loop bound
  // 'n' is a RUNTIME signal.
  always @(*) begin
      cnt = 0;
      for (i = 0; i < n; i = i + 1)       // n is a runtime input → can't unroll
          cnt = cnt + data[i];
  end
endmodule

// Synthesis cannot unroll a loop whose bound is a runtime value — it doesn't
// know how many copies to make. The loop is rejected as non-synthesizable
// (or mis-synthesized).
Symptom

A module with a for loop fails to synthesize with an error about a non-constant loop bound, even though it simulates fine. The loop limit is a module input.

Root Cause

Runtime-bounded loop. A synthesizable for loop unrolls at elaboration into replicated hardware, which requires the iteration count to be KNOWN at elaboration (a constant or parameter). Here the bound 'n' is a runtime signal, so the number of iterations — and thus the amount of hardware — is unknown at build time, and the loop cannot be unrolled. Simulation handles it (it just iterates at runtime), but synthesis cannot.

The fix is to make the loop bound STATIC (iterate over the full width) and use the runtime value differently — e.g. mask each bit by whether its index is below 'n', so the count reflects only the low 'n' bits while the loop itself is statically bounded.

Fix
module count_to_n (input [3:0] n, input [15:0] data, output reg [4:0] cnt);
  integer i;
  always @(*) begin
      cnt = 0;
      for (i = 0; i < 16; i = i + 1)      // STATIC bound (full width)
          if (i < n) cnt = cnt + data[i]; // runtime 'n' gates the term
  end
endmodule

// The loop is statically bounded (unrolls to 16 conditional adds); the
// runtime 'n' gates each term. Static bound + runtime condition, not a
// runtime bound.

8. Interview Q&A

9. Exercises

Exercise 1 — Unroll the loop

Write the unrolled form of for (i = 0; i < 4; i = i + 1) y[i] = a[i] ^ b[i];.

Exercise 2 — Static or runtime?

Which of these synthesize: (a) for (i = 0; i < WIDTH; ...) with WIDTH a parameter; (b) for (i = 0; i < cnt; ...) with cnt an input?

Exercise 3 — Fix the bound

Rewrite a for loop bounded by a runtime input m to be statically bounded with m gating each iteration.

10. Summary

The for loop is the synthesizable count loop:

  • Unrolls at elaboration into replicated parallel hardware (one body copy per iteration).
  • Static bound required — constant/parameter; runtime-bounded loops don't synthesize.
  • Bit-sliced patterns — per-bit operations, bit reversal, population count, per-element array ops (blocking = for combinational accumulation).
  • vs runtime iteration — use a clocked counter for over-time behaviour, not an unrolled loop.

The next sub-topic is the condition loop: Chapter 14.6.2 While Loop drills while — synthesizable only when statically bounded, otherwise a testbench construct.