VHDL · Chapter 14.5 · Testbench Development
assert and report Statements
Self-checking rests on two language constructs. The assert statement evaluates a condition and, when it is false, prints a message at a given severity, which is the basic check. The report statement prints unconditionally, for logging and progress. Both classify their output with a severity level, ordered note, warning, error, then failure, and the simulator is configured to halt at a chosen threshold, commonly error or failure. Good messages are built with concatenation and image attributes so they show the offending values. Assertions are not only for testbenches, since an assert with failure severity makes a great illegal-state guard in RTL, for example a case that should never reach its others branch. This lesson covers the mechanics of both statements, severities and the stop threshold, message building, and the testbench and RTL uses.
Foundation13 min readVHDLassertreportSeverityTestbenchVerification
1. Engineering intuition — say something when reality disagrees
An assertion is a statement of expectation: "this had better be true." When it is, the simulation is silent; when
it is not, the assertion speaks up — printing a message you wrote, tagged with how serious it is. That single
idea powers checking: every comparison in a self-checking testbench is an assert. The companion report is just
the unconditional version — say this regardless, for logging a phase or a value. The severity is the volume
knob: a note is informational, a warning is suspicious, an error is wrong, a failure is fatal — and the
simulator can be told to stop once things reach a chosen level, so a real failure halts the run instead of
scrolling past.
2. Formal explanation — assert, report, and severity
-- ASSERT: fires its report ONLY when the condition is FALSE.
assert sum = expected
report "MISMATCH: got " & to_hstring(sum) & " exp " & to_hstring(expected) -- message via &
severity error; -- severity level
-- REPORT: prints UNCONDITIONALLY (logging / progress).
report "starting phase 2 at " & time'image(now) severity note;
-- SEVERITY ladder (increasing): note < warning < error < failure.
-- note : information
-- warning : suspicious but continue
-- error : a real problem (sims often set the STOP threshold here)
-- failure : fatal — stop now
-- The simulator halts when severity reaches its configured threshold.
-- Building messages: concatenation '&' plus 'image / to_hstring / integer'image.
report "count=" & integer'image(n) & " state=" & state_t'image(st) severity note;assert evaluates a condition and emits its report clause when false; report emits
unconditionally. Each carries a severity (note/warning/error/failure), and the simulator stops at a
configurable threshold. Messages are ordinary strings built with & and value-to-string conversions
('image, to_hstring).
3. Production usage — checks, logging, and RTL guards
-- (1) TESTBENCH CHECK: the heart of self-checking.
assert dout = golden report "output mismatch at " & time'image(now) severity error;
-- (2) LOGGING / progress (unconditional):
report "applied " & integer'image(n_vectors) & " vectors" severity note;
-- (3) RTL ILLEGAL-STATE GUARD: assert something that must never happen.
case state is
when IDLE => ...
when RUN => ...
when others =>
assert false report "FSM reached illegal state" severity failure; -- should be unreachable
end case;
-- Synthesis IGNORES assertions (no hardware), but in simulation this catches the bug loudly.What hardware does this become? Nothing — assertions and reports are ignored by synthesis; they
generate no logic. That is exactly why they are safe to sprinkle through both testbenches and RTL: in
simulation they catch problems and print context, and in synthesis they simply vanish. A severity failure on an
"impossible" branch turns a silent latent bug into an immediate, labeled stop during simulation, at zero hardware
cost. Use assert for checks and guards, report for the running commentary that makes a log readable.
4. Structural interpretation — the severity ladder and assert flow
5. Why this is structural, not timing
assert/report are a reporting mechanism — a condition, a message, and a severity classified on a fixed
ladder — so the structure above (the ladder and assert flow) captures them, not a waveform. They produce text and
a possible stop, not signal behavior; their only run-time effect is when, during simulation, a condition happens
to be false and a message prints. Because synthesis discards them entirely, they have no hardware timing at all.
What matters is the mechanism: false condition → message at severity → maybe halt.
6. Debugging example — the wrong-polarity assert (and the useless message)
Expected: an assertion fires on a real failure with a helpful message. Observed: the assertion fires
constantly on correct behavior (or never fires on a wrong one), and/or its message gives no useful
information. Root cause: the assert condition has inverted polarity — remember assert reports when the
condition is false, so you assert the expected-true property, not the failure condition; writing assert sum /= expected fires on every match. The unhelpful message omitted the values. Fix: assert the property that
should hold (assert sum = expected), and build the message with the actual/expected values
(to_hstring, 'image) and now so a failure is diagnosable. Engineering takeaway: assert fires when its
condition is false, so assert what must be true; and always include the offending values and time in the
report.
-- BUG: inverted condition fires on every CORRECT result; message has no values.
-- assert sum /= expected report "error" severity error;
-- FIX: assert the property that must hold; include the values.
assert sum = expected
report "mismatch got=" & to_hstring(sum) & " exp=" & to_hstring(expected) severity error;7. Common mistakes & what to watch for
- Inverted assert polarity.
assertreports when the condition is false; assert what must be true, not the failure condition. - Uninformative messages. Include actual/expected values (
to_hstring/'image) andnow; a bare "error" wastes the assertion. - Wrong severity / stop threshold. Use
error/failurefor real problems so the run halts; do not bury failures asnote. - Assuming assertions synthesize. They are ignored by synthesis — great for RTL guards, but they enforce nothing in hardware.
- Overusing
failure. Reservefailurefor truly fatal/impossible conditions; routine mismatches are usuallyerrorso the test can tally them.
8. Engineering insight & continuity
assert and report are the language's checking and messaging primitives: assert fires its message when the
condition is false, report logs unconditionally, both classified on the note < warning < error < failure
ladder with a simulator stop threshold — and both are ignored by synthesis, so they double as RTL illegal-state
guards. Clear conditions, value-rich messages, and the right severity make failures obvious. These messages often
need to go beyond the console — into files of expected vectors or results logs — which is the next lesson,
File I/O with textio: reading stimulus from and writing results to files.