VHDL · Chapter 5.8 · Sequential Statements & Processes
The wait Statement
A process needs a way to suspend, and there are exactly two, a sensitivity list or wait statements. The wait form is the more flexible one, because it lets a process pause partway through its body, which is what testbenches need to sequence stimulus over time. It comes in four shapes, waiting until a condition, waiting for a time, waiting on a list of signals, and a plain unconditional wait. A process uses a sensitivity list or wait, never both. Most uses are in testbenches, where waiting for a time advances simulation, but one form that waits until a rising clock edge is a fully synthesizable way to describe a register without a sensitivity list. This lesson covers all four forms, which are synthesizable, and how wait drives stimulus.
Foundation14 min readVHDLwaitSuspensionTestbenchClockingSequential
1. Engineering intuition — pause the process on purpose
A sensitivity-list process always runs its whole body and then sleeps. Sometimes you want finer
control: run a few statements, pause until a condition or a time, then continue. That is what
wait gives — explicit suspension points inside the body. In a testbench this is exactly how you
write a script: drive these inputs, wait 10 ns, drive those, wait for a response. In synthesizable
RTL one specific wait form expresses a clocked register. Either way, wait is "suspend here until
the stated event."
2. Formal explanation — the four forms, and the one rule
wait until clk = '1' and clk'event; -- (or rising_edge(clk)) suspend until the condition becomes true
wait for 10 ns; -- suspend for a fixed simulation time (SIM ONLY)
wait on a, b; -- suspend until any listed signal changes (like a sensitivity list)
wait; -- suspend forever (e.g. end of a testbench stimulus process)A process that uses wait has no sensitivity list — the two mechanisms are mutually exclusive.
wait until resumes when its condition becomes true; wait for resumes after a fixed time (a
simulation construct — not synthesizable); wait on resumes on a signal change (equivalent to a
sensitivity list, but mid-body); plain wait never resumes. The synthesizable subset is essentially
wait until rising_edge(clk) at the top of a process — everything time-based is for simulation.
3. Production RTL & testbench — register and stimulus
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- (A) SYNTHESIZABLE register via wait — equivalent to a clocked sensitivity-list process.
reg : process
begin
wait until rising_edge(clk); -- suspend until the edge; resumes each rising clk
q <= d; -- captured on the edge → a flip-flop
end process;
-- (B) TESTBENCH clock generation (wait for → simulation time).
clk_gen : process
begin
clk <= '0'; wait for 5 ns;
clk <= '1'; wait for 5 ns; -- a 10 ns period clock; loops forever
end process;
-- (C) TESTBENCH stimulus sequence (drive, wait, check).
stim : process
begin
rst <= '1'; wait for 20 ns; -- assert reset for 20 ns
rst <= '0'; wait until rising_edge(clk);
din <= x"A5"; wait until rising_edge(clk);
-- ... checks ...
wait; -- stop this process for the rest of the run
end process;What hardware does this become? (A) a register — wait until rising_edge(clk) is synthesizable
and identical in effect to process(clk) ... if rising_edge(clk). (B) and (C) are not hardware —
they are testbench code: wait for advances simulated time to generate a clock and sequence stimulus,
and the final plain wait parks the stimulus process once it is done.
4. Hardware / simulation interpretation — suspension points
wait until rising_edge(clk): q captures d each edge (a register)
8 cycles5. Synthesizer interpretation — only edge-waits map to hardware
Synthesis accepts essentially one wait form: a wait until rising_edge(clk) (or the explicit
clk'event and clk = '1') used to describe a register, equivalent to the clocked sensitivity-list
process. Every time-based form — wait for, and any wait whose resumption depends on elapsed time —
is not synthesizable, because gates have no notion of "wait 10 ns." Those forms live in
testbenches and behavioural models. So in design code use wait until rising_edge(clk) (or the
sensitivity-list equivalent) and keep wait for strictly in verification code.
6. Debugging example — wait for in synthesizable RTL
Expected: a delay in the design. Observed: synthesis rejects the code (or ignores the delay),
and the design behaves differently from RTL simulation. Root cause: a wait for <time> (or a
time-based wait) was used in synthesizable RTL to model a delay; synthesis cannot realise wall-clock
time, so the construct is unsynthesizable or dropped. Fix: express real timing with a clocked
process and a counter (count clock cycles), not wait for; reserve wait for for testbenches.
Engineering takeaway: if a wait depends on time rather than a clock edge, it is simulation-only —
real delays come from clocked logic, never from wait for.
-- WRONG (RTL): wait for is not synthesizable; cannot model a hardware delay.
-- process begin pulse <= '1'; wait for 100 ns; pulse <= '0'; wait; end process;
-- RIGHT (RTL): count clock cycles for a timed pulse.
process (clk) begin
if rising_edge(clk) then
if cnt = TICKS-1 then pulse <= '0'; cnt <= (others => '0');
else cnt <= cnt + 1; end if;
end if;
end process;7. Common mistakes & what to watch for
wait forin synthesizable RTL. Not synthesizable; use a clocked counter for timed behaviour.- A sensitivity list and
waitin the same process. Illegal — choose one triggering mechanism. - A wait that never resumes. A
wait untilon a condition that never becomes true (or a plainwait) hangs that process; intended only to park a finished testbench process. - Forgetting the process suspends at the wait. Statements after a wait do not run until it resumes; ordering matters for stimulus.
- Using
waitfor design when a sensitivity list is clearer. For most RTL the sensitivity-list style is conventional; reservewaitfor the edge idiom and testbenches.
8. Engineering insight
wait is the process's explicit suspend, and its real power is sequencing over time — which is why
it dominates testbenches: drive, wait, check, repeat. In design code its role is narrow but important:
wait until rising_edge(clk) is a clean, synthesizable register description, fully equivalent to the
clocked sensitivity-list process. The discipline is simply to keep the time-based forms (wait for) on
the verification side of the line, because hardware has no clock but the one you give it. Knowing which
wait synthesizes and which does not keeps your RTL and testbench responsibilities clearly separated.
9. Summary
The wait statement suspends a process explicitly and comes in four forms: wait until <condition>,
wait for <time>, wait on <signals>, and plain wait. A process uses a sensitivity list or wait,
never both. wait until rising_edge(clk) is the one synthesizable idiom (a register); wait for and
other time-based waits are simulation/testbench only. wait is the backbone of testbench stimulus
sequencing.
10. Learning continuity
You have now seen both ways a process is triggered (sensitivity list and wait) and how its body is
structured (if, case, loops, variables). The module closes by returning to the assignment at the
heart of it all: Sequential Signal Assignment and Last-Assignment-Wins — how <= inside a process
schedules, why multiple assignments resolve to the last one, and how the default-then-override idiom
delivers latch-free logic — tying every construct back to the execution model from the anchor.