Ask ten verification engineers what bugs they found last year and the answers sound different but sort into a few piles: a FIFO overflowed when it was exactly full, two events in one cycle were handled as if one came first, a block assumed its neighbour would never stall, a register reset to the wrong value, the design was correct but too slow. Each pile has its own hiding places, and its own technique that finds it.
What kinds of bugs are there, and why does the kind matter?
The obvious view treats bugs as one undifferentiated thing: write enough tests and they will turn up. But a technique that finds one class is nearly blind to another. Random traffic finds races but rarely misconfigured registers; register tests find reset values but never starvation; functional checks never see a performance bug at all. Without a taxonomy, a team cannot tell which classes its plan is missing until silicon tells it.
Class | What goes wrong | Hides in | Usually found by |
|---|---|---|---|
Specification | The document is wrong, ambiguous or incomplete | Assumptions nobody wrote down | Spec review, independent readings, validation |
Corner case and boundary | Off-by-one at full, empty, maximum, wrap-around | Limits of counters, FIFOs, lengths | Biased random, directed boundary tests, formal |
Concurrency and race | Two events in one cycle handled as if ordered | Simultaneous push and pop, grant and release | Random timing, back-pressure, formal |
Integration and interface | Blocks agree with themselves but not each other | Boundaries between blocks and teams | Subsystem and SoC tests, protocol checkers |
Configuration and mode | Wrong behaviour in some setting combinations | Parameters, registers, modes | Configuration coverage, randomised setup |
Performance and fairness | Correct results, too slow or unfair | Arbiters, credits, queues | Performance checks, latency and fairness monitors |
Reset, clock and power | Wrong reset values, crossings, power states | Initialisation, domain boundaries, sleep and wake | Reset tests, CDC tools, power-aware simulation |
Error handling | Faults handled wrongly or not reported | Rarely exercised error paths | Error injection, negative tests |
It tells you where to look: a FIFO's bugs are at its limits; an arbiter's at simultaneous requests and in fairness.
It tells you which technique to use: a class that random stimulus rarely reaches needs biasing, directed tests or formal (Part 4, Part 7).
It shows gaps in a plan: map the plan's techniques against the classes; an empty row is a class nobody is looking for.
It turns escapes into lessons: classifying every escaped bug shows which technique the plan was missing.
Practice | Purpose |
|---|---|
Bug class as a field in the bug tracker | Shows which classes are being found, and when |
Plan reviews against the class list | Finds classes no technique covers |
Escape post-mortems by class | Feeds missing techniques back into the next plan (Part 16) |
The DCRouter Bug Zoo | In this course: 24 planted bugs spread across these classes, used to measure each technique |
A team (illustrative) reviewed its bug tracker at tape-out and saw 140 bugs, nearly all functional and corner-case bugs found by random simulation. Not one performance or power bug had been filed, and nobody had asked why. In silicon, the first two escapes were a starved low-priority queue and a wake-up that failed from one sleep state: classes the plan had no technique for, so they could not have been found.
Performance and fairness bugs: results are correct, so functional checks pass however slow or unfair the design is.
A corner case at a boundary; biased stimulus that fills the FIFO, a directed boundary test, or a formal check of the FIFO.
Bugs fall into classes: specification, corner case, concurrency, integration, configuration, performance, reset/clock/power, error handling.
Each class hides in a different place and is found by a different technique.
Review plans and escapes against the classes to find what nobody is looking for.