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
forloop with a static bound unrolls into replicated parallel hardware; aforloop 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
// 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:
// 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:
// 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 unrollThe 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
- Runtime-bounded
for— can't unroll; not synthesizable; use a static bound (§5, DebugLab 1). - Thinking the loop runs over time — it unrolls into parallel hardware (§2).
- Non-blocking in a combinational loop accumulation — use blocking
=(14.3). - 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
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).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.
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.
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.
Related Tutorials
- Loops — Chapter 14.6; the loop overview and unrolling concept.
- Reduction Operators — Chapter 10.5; the one-line alternative to a bit-wise loop.
- Generate Block — Chapter 14.7; structural replication (vs a procedural
for). - Blocking & Non-Blocking Assignments — Chapter 14.3; blocking
=in combinational loop bodies.