Both while and do while are looping constructs that run a set of statements for as long as a condition stays true. They differ only in when the condition is checked. A while loop checks before each pass, so its body may never run. A do while loop checks after each pass, so its body always runs at least once.
6 min read | Intermediate Level
What You'll Learn
- The syntax of
whileanddo while, and how their checks differ - How to poll for a condition in a testbench without hanging the simulation
- When to choose
do whileoverwhile - Why these loops are rarely used in synthesizable RTL
Syntax
while (<condition>) begin
// Multiple statements
end
do begin
// Multiple statements
end while (<condition>);
- condition: any expression. The loop continues while it is true, meaning non-zero. If it is X or Z, it counts as false.
begin ... endis only required when the body has more than one statement.- A
do whileloop must end with a semicolon after the closing parenthesis.
A while loop checks the condition first and runs the statements only if it is true. If the condition is false, the loop ends right there. A do while loop runs the statements once first and then checks the condition. It keeps going until the condition becomes false.
Example #1: while Loop
module tb;
initial begin
int cnt = 0;
while (cnt < 5) begin
$display("cnt = %0d", cnt);
cnt++;
end
end
endmodule
ncsim> run cnt = 0 cnt = 1 cnt = 2 cnt = 3 cnt = 4 ncsim: *W,RNQUIE: Simulation is complete.
The condition is checked six times. It is true for cnt = 0 to 4 and false when cnt reaches 5, so the body runs five times.
Example #2: while Loop That Never Runs
module tb;
initial begin
int cnt;
while (cnt != 0) begin
$display ("cnt = %0d", cnt);
cnt++;
end
end
endmodule
ncsim> run ncsim: *W,RNQUIE: Simulation is complete.
cnt is an int, which is 2-state and starts at 0. The condition cnt != 0 is false on the very first check, so the body never runs and nothing is printed.
Example #3: do while Loop
module tb;
initial begin
int cnt = 0;
do begin
$display("cnt = %0d", cnt);
cnt++;
end while (cnt < 5);
end
endmodule
ncsim> run cnt = 0 cnt = 1 cnt = 2 cnt = 3 cnt = 4 ncsim: *W,RNQUIE: Simulation is complete.
When the condition is true on entry, do while gives the same result as Example #1.
Example #4: do while Runs at Least Once
module tb;
initial begin
int cnt = 0;
do begin
$display("cnt = %0d", cnt);
cnt++;
end while (cnt == 0);
end
endmodule
ncsim> run cnt = 0 ncsim: *W,RNQUIE: Simulation is complete.
The body runs once, printing 0 and incrementing cnt to 1. Only then is cnt == 0 checked. It is false, so the loop ends after a single pass.
Quick Check
Q: If Example #2 used a do while loop with the same condition cnt != 0, what would happen?
Show Answer
The body would run once and print cnt = 0, then increment cnt to 1. Now cnt != 0 is true, and since nothing ever brings cnt back to 0, the loop would run until cnt wraps around after about 4 billion passes. The condition matters as much as the choice of loop.
Using while in Verification: Polling with a Timeout
The most common testbench use of while is waiting for the DUT to do something, such as raise a ready flag, while also guarding against waiting forever.
module tb;
bit clk, ready;
int cycles = 0;
always #5 clk = ~clk; // rising edges at 5, 15, 25 ... ns
initial #42 ready = 1; // stands in for the DUT raising ready
initial begin
// Check once per clock; give up after 100 cycles
while (!ready && cycles < 100) begin
@(posedge clk); // advances time, so ready can change
cycles++;
end
if (ready) $display ("[%0t] ready after %0d cycles", $time, cycles); // [45] ready after 5 cycles
else $display ("[%0t] TIMEOUT waiting for ready", $time);
$finish;
end
endmodule
Two details make this loop safe. The @(posedge clk) lets time advance, so the process that sets ready gets to run. The cycle limit guarantees the loop ends even if the DUT is broken.
Where are while and do while used in verification?
Drivers use while to hold a transfer until the DUT accepts it, as in AXI-style valid/ready handshakes. Tests use do while for operations that must be tried at least once, such as polling a status register over a bus until a busy bit clears. You always have to read the register once before you know whether to read it again.
while and do while in Digital Design
Synthesis tools can unroll a loop only when they can work out the number of passes at elaboration time. A while loop whose condition depends on signal values does not meet that rule and will not synthesize. Some tools accept a while loop with constant start, step and limit values, but a for loop expresses the same thing more clearly and is supported everywhere. In RTL, use for and keep while and do while for testbenches.
Common Beginner Mistakes
Mistake #1: A semicolon right after while (condition)
Wrong: while (cnt < 5); begin cnt++; end
Right: while (cnt < 5) begin cnt++; end
Why? The semicolon is an empty statement, so it becomes the whole loop body. cnt never changes, the loop never ends, and the begin ... end block is never reached.
Mistake #2: Forgetting the semicolon at the end of do while
Wrong: do begin ... end while (cnt < 5)
Right: do begin ... end while (cnt < 5);
Why? In a do while loop, the while (...) clause ends the statement and needs its own semicolon. Leaving it out is a syntax error, and compilers often report it on the line that follows.
Mistake #3: Polling a signal without letting time advance
Wrong: while (!ready) cycles++;
Right: while (!ready) @(posedge clk);
Why? ready is driven by another process, which can only run when this one waits. Without an event or delay in the body, the loop spins at the same timestamp forever.
Practice Exercise
Challenge: Pick a random address until it is aligned
Using $urandom_range(0, 63), pick a random address until you get one that is a multiple of 4. Print the address and how many tries it took. Which loop fits best, and why?
Hint
You cannot test an address before you have generated one, so the body must run at least once.
Solution
module tb;
int unsigned addr;
int tries;
initial begin
do begin
addr = $urandom_range(0, 63); // pick a candidate
tries++;
end while (addr % 4 != 0); // try again until 4-byte aligned
$display ("addr = %0d after %0d tries", addr, tries);
end
endmodule
do while fits because the address must be generated before it can be checked. With while, you would need an extra assignment before the loop. In a class-based testbench, a constraint such as addr % 4 == 0 does the same job more efficiently.
Summary
In this tutorial, you learned:
whilechecks its condition before each pass and may run its body zero timesdo whilechecks after each pass and always runs its body at least once- A
do whileloop must end with a semicolon afterwhile (condition) - When polling a signal, the body needs a timing control, and a cycle limit protects against hangs
- In RTL, a
forloop with constant bounds is preferred overwhile