A Verilog for loop repeats a group of statements while a condition holds, with a loop variable that steps from a start value to an end value. In software, a loop runs its iterations one after another in time. In synthesizable Verilog, a for loop is a shorthand for replicating hardware: the synthesis tool unrolls every iteration into its own copy of the logic.
12 min read | Beginner Level
What You'll Learn
- The syntax of the
forloop and how it differs from a software loop - How synthesis unrolls a loop into parallel hardware
- Write compact, parameterized designs with
forloops in clocked and combinational blocks - Avoid loop bugs: non-constant bounds, narrow loop variables and wrong assignment types
Syntax
A for loop has three parts in its header: an initial assignment, a condition checked before each iteration, and a step assignment that runs after each iteration. Like other procedural statements, it must be inside an always or initial block.
for (<initial_condition>; <condition>; <step_assignment>) begin
// Statements
end
<initial_condition>- Sets the loop variable's starting value, such asi = 0(required)<condition>- The loop runs while this is true, such asi < 10(required)<step_assignment>- Updates the loop variable after each iteration, such asi = i + 1(required)begin ... end- Groups several statements in the loop body; optional for a single statement
The loop variable is usually declared as an integer in the module. A while loop is more general, since it only needs a condition. A for loop is the better fit when there is a clear start, end and step, which is almost always the case in RTL.
module my_design;
integer i;
initial begin
// Note that ++ operator does not exist in Verilog !
for (i = 0; i < 10; i = i + 1) begin
$display ("Current loop#%0d ", i);
end
end
endmodule
ncsim> run Current loop#0 Current loop#1 Current loop#2 Current loop#3 Current loop#4 Current loop#5 Current loop#6 Current loop#7 Current loop#8 Current loop#9 ncsim: *W,RNQUIE: Simulation is complete.
The loop starts at i = 0 and stops when i reaches 10, so the body runs 10 times. Verilog has no ++ operator, so the step is written as i = i + 1. The ++ operator was added in SystemVerilog.
How Synthesis Handles a for Loop
Hardware has no notion of "running a loop". A synthesis tool unrolls the loop: it calculates every value the loop variable takes, then writes out the loop body once for each value, as if you had typed every iteration by hand. All the copies exist side by side in the hardware and work at the same time.
This has two consequences:
- The loop bounds must be constants at compile time, such as numbers or parameters. The tool must know how many copies to build.
- The loop takes no time. All iterations happen within one evaluation of the
alwaysblock, so a loop inside a clocked block finishes in a single clock cycle, not one iteration per clock.
For loops are also used inside generate blocks to replicate module instances and assign statements, using a genvar loop variable. That is a different construct; see Verilog generate block.
Design Example
This 8-bit register clears on reset, loads load_val when load_en is 1, and otherwise moves every bit one position to the left at each clock edge. Bit 7 wraps around into bit 0, so the bits rotate rather than shift out. First, the design without a loop:
module lshift_reg (input clk, // Clock input
input rstn, // Active low reset input
input [7:0] load_val, // Load value
input load_en, // Load enable
output reg [7:0] op); // Output register value
// At posedge of clock, if reset is low set output to 0
// If reset is high, load new value to op if load_en=1
// If reset is high, and load_en=0 shift register to left
always @ (posedge clk) begin
if (!rstn) begin
op <= 0;
end else begin
if (load_en) begin
op <= load_val;
end else begin
op[0] <= op[7];
op[1] <= op[0];
op[2] <= op[1];
op[3] <= op[2];
op[4] <= op[3];
op[5] <= op[4];
op[6] <= op[5];
op[7] <= op[6];
end
end
end
endmodule
The eight assignments follow one pattern: op[i+1] <= op[i]. A for loop writes that pattern once:
module lshift_reg (input clk, // Clock input
input rstn, // Active low reset input
input [7:0] load_val, // Load value
input load_en, // Load enable
output reg [7:0] op); // Output register value
integer i;
// At posedge of clock, if reset is low set output to 0
// If reset is high, load new value to op if load_en=1
// If reset is high, and load_en=0 shift register to left
always @ (posedge clk) begin
if (!rstn) begin
op <= 0;
end else begin
// If load_en is 1, load the value to op
// else keep shifting for every clock
if (load_en) begin
op <= load_val;
end else begin
for (i = 0; i < 7; i = i + 1) begin // i = 0..6 covers op[1] to op[7]
op[i+1] <= op[i];
end
op[0] <= op[7]; // Wrap bit 7 around to bit 0
end
end
end
endmodule
Synthesis result: The loop unrolls into exactly the eight assignments of the first version, so both designs build identical hardware: eight flip-flops with synchronous reset, and in front of each, a 2 to 1 mux that selects load_val or the neighboring bit. The rotate itself is only wiring between flip-flops and costs no logic. Non-blocking assignments (<=) make every bit capture its neighbor's old value, which is what a shift needs.

Parameterized Version
The main benefit of the loop is that the width can become a parameter. Without a loop, a 64-bit version would need 64 hand-written assignments. With a loop, the same code works for any width:
module lshift_reg_n #(parameter N = 8) ( // N sets the register width
input clk,
input rstn,
input [N-1:0] load_val,
input load_en,
output reg [N-1:0] op
);
integer i;
always @(posedge clk) begin
if (!rstn)
op <= 0;
else if (load_en)
op <= load_val;
else begin
for (i = 0; i < N-1; i = i + 1) // Bound uses the parameter: still a constant
op[i+1] <= op[i];
op[0] <= op[N-1]; // Wrap the MSB around to bit 0
end
end
endmodule
Instantiating lshift_reg_n #(.N(32)) gives a 32-bit rotating register with no code changes. The loop bound N-1 is still a constant, because parameters are fixed at compile time.
Where is a for Loop Used?
Parameterized IP blocks, such as FIFOs, bus width converters and CRC generators, are delivered as one RTL file that each project configures through parameters. Inside them, for loops handle the per-bit and per-byte logic, for example reversing byte order or masking bytes with a byte-enable signal, so the same code works for 32, 64 or 128-bit buses.
Quick Check
Q: In lshift_reg, how many clock cycles does the for loop take to move all eight bits?
Show Answer
One. The loop is unrolled into eight parallel assignments, and all eight flip-flops update at the same clock edge. The loop describes structure, not a sequence in time.
Testbench
The testbench holds reset for two clocks, runs for five more clocks with op at 0, loads 8'h01 for one clock, then lets the register rotate for 20 clocks. See Verilog Testbench for testbench structure.
module tb;
reg clk;
reg rstn;
reg [7:0] load_val;
reg load_en;
wire [7:0] op;
// Setup DUT clock
always #10 clk = ~clk;
// Instantiate the design
lshift_reg u0 ( .clk(clk),
.rstn (rstn),
.load_val (load_val),
.load_en (load_en),
.op (op));
initial begin
// 1. Initialize testbench variables
clk <= 0;
rstn <= 0;
load_val <= 8'h01;
load_en <= 0;
// 2. Apply reset to the design
repeat (2) @ (posedge clk);
rstn <= 1;
repeat (5) @ (posedge clk);
// 3. Set load_en for 1 clk so that load_val is loaded
load_en <= 1;
repeat(1) @ (posedge clk);
load_en <= 0;
// 4. Let design run for 20 clocks and then finish
repeat (20) @ (posedge clk);
$finish;
end
endmodule

In the waveform, op stays 0 through reset and the following five clocks, since rotating 0 still gives 0. After the load, op is 8'h01, then 02, 04, 08 and so on up to 80. At the next clock, bit 7 wraps around and op returns to 01, so the pattern repeats every eight clocks.
for Loop in Combinational Logic
Loops are also useful in combinational always @(*) blocks, where each iteration builds on the result of the previous one. This module counts how many bits of its input are 1, often called a population count:
module popcount #(parameter N = 8) (
input [N-1:0] in,
output reg [$clog2(N+1)-1:0] count // Wide enough to hold the value N
);
integer i;
always @(*) begin
count = 0; // Start from 0 on every evaluation
for (i = 0; i < N; i = i + 1)
count = count + in[i]; // Blocking (=): each iteration sees the
// value written by the previous one
end
endmodule
Synthesis result: Unrolled, the loop is a chain of N additions: ((0 + in[0]) + in[1]) + .... The synthesis tool usually rebalances such a chain into an adder tree, so the delay grows with log2N instead of N. The design uses blocking assignments, because each iteration must read the count value written by the iteration before it.
PPA Impact: Because every iteration becomes real hardware, the area of a loop grows with its iteration count. A loop over 1024 bits in a combinational block builds 1024 copies of its body. Check the loop bounds of wide parameterized designs, since a small edit to a bound can change the gate count by orders of magnitude.
Population Count in Hardware
Counting the 1 bits in a word appears in several places: error-correcting code (ECC) logic in memory controllers measures how many bits differ between two codes, network switches count active ports, and many processor instruction sets include a popcount instruction. Each is described with a loop like the one above and synthesized into an adder tree.
Loops in Testbenches
In verification, for loops run in time, because testbench code is simulated rather than synthesized and can include delays and clock waits. A memory test, for example, loops over every address, writes a pattern, waits for the write to finish, then loops again to read each location back and compare it.
Quick Check
Q: Can the loop bound in popcount be an input port, such as for (i = 0; i < num_bits; i = i + 1)?
Show Answer
Not for synthesis. The tool must know how many copies of the loop body to build, so the bound must be a constant or a parameter. To count only the lowest num_bits bits, loop over all N bits and add in[i] only when i < num_bits.
Common Beginner Mistakes
Mistake #1: Using ++ to step the loop
Wrong: for (i = 0; i < 8; i++)
Right: for (i = 0; i < 8; i = i + 1)
Why? Verilog has no ++ operator, so the compiler reports a syntax error. ++ is valid in SystemVerilog, which is why it appears in some code you may see elsewhere.
Mistake #2: A loop variable too narrow for the bound
Wrong: reg [2:0] i; with for (i = 0; i < 8; i = i + 1)
Right: integer i;
Why? A 3-bit i can only hold 0 to 7. After 7, i + 1 wraps to 0, so i < 8 is never false and the loop never ends. Simulation hangs, and synthesis fails or runs out of memory while unrolling. Declaring the loop variable as an integer (32 bits, signed) avoids this.
Mistake #3: Non-constant loop bounds in synthesizable code
Wrong: for (i = 0; i < len; i = i + 1), where len is a signal
Right: Loop up to a constant or parameter, and check the signal inside: if (i < len) ...
Why? The loop is unrolled at compile time, so its iteration count cannot depend on a value that only exists while the hardware runs. Synthesis tools report an error. Simulation accepts it, so the problem appears only when you synthesize.
Mistake #4: Non-blocking assignments in an accumulating loop
Wrong: always @(*) begin count = 0; for (...) count <= count + in[i]; end
Right: Use blocking assignments: count = count + in[i];
Why? A non-blocking assignment updates count only after the whole block finishes, so every iteration reads the same old value. Only the last iteration's result is kept, so count ends up equal to in[N-1]. When each iteration depends on the previous one, use blocking assignments. When each iteration writes a different signal, as in the shift register, non-blocking assignments are correct. See Verilog Blocking & Non-Blocking.
Practice Exercise
Challenge: Parameterized Bit Reverse
Write a combinational module bit_reverse with a parameter N, an N-bit input in and an N-bit output out, where out[i] = in[N-1-i] for every bit. Use a for loop. What hardware does it synthesize into?
Hint
Declare out as reg and write the loop in an always @(*) block. Each iteration assigns a different output bit, so no initial assignment is needed as long as the loop covers every bit.
Solution
module bit_reverse #(parameter N = 8) (
input [N-1:0] in,
output reg [N-1:0] out
);
integer i;
always @(*) begin
for (i = 0; i < N; i = i + 1)
out[i] = in[N-1-i]; // Bit 0 gets bit N-1, bit 1 gets bit N-2, ...
end
endmodule
The loop unrolls into N assignments that connect each output bit to a different input bit. Synthesis builds no gates at all, only wires. This shows that a for loop costs only as much hardware as its unrolled body: a loop of pure assignments is free, while a loop of additions builds an adder per iteration.
Summary
In this tutorial, you learned:
- A
forloop has an initial assignment, a condition and a step, and must be inside a procedural block - Synthesis unrolls the loop into one copy of the body per iteration, all working in parallel
- Loop bounds must be constants or parameters, and the loop takes no clock cycles in hardware
- Loops make designs shorter and let a parameter set the width, as in the rotating shift register
- The hardware cost of a loop is its unrolled body: wiring is free, arithmetic grows with the iteration count
- Use
integerloop variables andi = i + 1, since Verilog has no++ - Use blocking assignments when each iteration reads the previous result, and non-blocking for independent register bits