Functional Verification — Fundamentals
Overview built a self-checking testbench for one module (seq_detector_101) using plain Verilog, checking a handful of hand-picked bit patterns. Real verification needs two things plain directed testing doesn't give you: a way to know how much of the design's actual behavior you've tested (functional coverage), and a way to catch bugs continuously during simulation rather than only at the end (assertions). This page introduces both, using the counter_4bit module from RTL Design & Computer Architecture as the running example — the exact same wraparound behavior (0 through 15, then back to 0) already verified there.
Analogy — Directed testing (Overview's approach) is studying only the exact past exam questions a teacher handed out — you'll pass if the real exam matches those exactly, but you have no idea whether you understand the material broadly. Functional coverage is a checklist of every topic the exam could draw from, tracked as you study, so you can see exactly which topics you've covered and which you haven't — even if you never see the exact same practice question twice. Assertions are a tutor watching you work through problems in real time, immediately flagging a wrong step the moment it happens, rather than only grading the final answer at the end.
Functional Coverage: Measuring How Much You've Actually Tested
Annotated Example — Computing Coverage for a Partial Test
Suppose a test suite for counter_4bit only ran long enough to observe count values 0 through 9 (10 distinct values), never reaching 10 through 15.
Verified computationally: 10 observed values out of 16 possible values gives exactly 62.5% coverage. This is a real, common outcome — not a hypothetical — because a test that only runs a counter for a short, fixed number of clock cycles (rather than deliberately running it long enough to wrap around, the way RTL Design's 18-edge trace did) will systematically miss the high end of the range. A coverage report showing 62.5% here is a direct, actionable signal: extend the test to run at least 16 clock edges to reach full coverage, rather than guessing whether "enough" testing has been done.
Assertions: Catching Bugs the Moment They Happen
Tracing the Assertion Against RTL Design's Verified Counter Trace
RTL Design & Computer Architecture traced counter_4bit over 18 clock edges: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1. Checking the assertion's rule (count == previous count + 1, except after 15) against every consecutive pair in that exact trace:
Every one of the 17 consecutive pairs in RTL Design's exact verified trace either satisfies count == previous + 1 or falls under the explicit wraparound exemption — confirming the assertion correctly describes the real, already-verified behavior of this exact module, rather than describing a different or idealized counter.
Try It (2 Minutes)
A modified counter design has a bug: it skips the value 8 every time (goes ...6, 7, 9, 10... instead of ...6, 7, 8, 9, 10...).
count == $past(count) + 1 fire an error?You should land on: the assertion fires exactly at the 7→9 transition, since $past(count) + 1 would be 7 + 1 = 8, but the actual count is 9 — an immediate, precisely-located error at that exact cycle. The coverage report would also catch it, differently: the bin for value 8 would show 0 hits (0% coverage for that one specific bin) even though overall coverage might look reasonably high otherwise — which is exactly why coverage and assertions are used together rather than as substitutes for each other: the assertion catches the bug the instant it happens, while coverage independently confirms which specific values were never reached, which is useful even for scenarios that aren't bugs, just under-tested.

