while, repeat and forever
Verilog has four loops. for with constant bounds is the one for RTL. The other three, repeat, while and forever, live almost entirely in testbenches, and the reason is the one from the last article: a synthesiser has to know how many copies of the body to build before anything runs, and a loop whose end depends on data cannot tell it.
| Loop | Synthesises | Ends | For |
|---|---|---|---|
for |
yes, with constant bounds | at a constant | replicated hardware |
repeat |
tool-dependent | after a fixed count | testbench cycle counts |
while |
rarely | on a condition | testbench waits |
forever |
never | never | clocks and monitors |
repeat
repeat (n) runs its body a fixed number of times, with no index:
repeat (10) begin
@(posedge clk);
end
Some synthesisers accept a constant repeat as an unrolled loop and some reject it, so in a design file write the for. In a testbench it is the clearest way to say “wait this many cycles”:
initial begin
rst_n = 1'b0;
repeat (5) @(posedge clk); // five cycles of reset
rst_n = 1'b1;
repeat (100) @(posedge clk);
$display("done after 105 cycles");
$finish;
end
Having no index means you cannot compute anything from the iteration number; if you need stimulus[i], use a for.
while
while (condition) is the most general loop and the one to use least in RTL. A tool could in principle unroll a while it can prove terminates after a constant number of iterations, and a few accept the obvious cases, but the same loop written as a for is unambiguous and universally supported, so write that. Where while belongs is a testbench waiting for the design:
initial begin
// ... stimulus ...
while (!done) begin
@(posedge clk);
end
if (result !== expected)
$display("FAIL: result=%0d expected=%0d", result, expected);
end
If you catch yourself writing while or forever in a design file, stop. What you are trying to express is a process that takes several cycles, and that is a counter or a state machine, written with registers.
forever
forever never ends and has no hardware meaning at all. Its job is the clock:
`timescale 1ns / 1ps
`default_nettype none
module tb_my_design;
reg clk;
reg rst_n;
wire [7:0] result;
wire valid;
my_design u_dut (.clk(clk), .rst_n(rst_n), .result(result), .valid(valid));
initial begin
$dumpfile("tb_my_design.vcd");
$dumpvars(0, tb_my_design);
end
initial begin
clk = 1'b0;
forever #5 clk = ~clk; // 10 ns period
end
initial begin
rst_n = 1'b0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
// ... stimulus ...
#500 $finish;
end
endmodule
always #5 clk = ~clk; with a separate initial clk = 0; does the same and is the more common spelling; forever inside an initial is handy when the clock needs a starting delay. The other use is a watchdog, so that a hung simulation stops instead of running until the disk fills:
initial begin
#100_000;
$display("WATCHDOG: time limit exceeded, possible hang");
$finish;
end
Practice Exercise
For each construct: does it synthesise, and if so to what; if not, where does it belong?
// 1
integer i;
always @(*) begin
out = 8'h00;
for (i = 0; i < 8; i = i + 1)
out[i] = in_a[i] & in_b[i];
end
// 2
initial begin
forever @(posedge clk)
$display("clk rose at %0t", $time);
end
// 3
integer k;
always @(*) begin
found = 1'b0;
for (k = 7; k >= 0; k = k - 1) begin
if (data[k] && !found) begin
msb_pos = k[2:0];
found = 1'b1;
end
end
end
// 4
while (!rx_done) @(posedge clk);
// 5
integer m;
always @(*) begin
result = 16'h0000;
for (m = 0; m < WIDTH; m = m + 1)
result = result + {{15{1'b0}}, data[m]};
end
Solution
1 synthesises: eight AND gates in parallel, the same as out = in_a & in_b. 2 does not: a monitor printing on every edge, testbench only. 3 synthesises: a priority encoder for the highest set bit, unrolled to eight conditional steps, the found flag giving the priority; also leaves msb_pos unassigned when nothing is set, so it wants a default. 4 does not: a testbench wait on a runtime signal. 5 synthesises if WIDTH is a parameter, to a popcount adder tree; if WIDTH is a port, the bound is not constant and synthesis fails.
Design files get for with constant bounds. Testbenches get the rest. The next article uses for to build four bit-manipulation circuits that scale with a parameter.