Part 5 answers: What are the usual suspects, and what does each one look like?
Most hardware bugs belong to a small number of classes, and each class leaves a recognisable signature. This part gives each class its signature, the first three checks, one real reproduction and the fix pattern, and links to the course that owns the topic.
Recognise the signature of each major bug class from a waveform or log.
Name the first three checks for each class.
Know which course to read for depth on each class.
16 lessons, about 166 min
The part on one page, taken from each lesson's Remember box.
Logic Bugs: Off-by-One, Width and Sign
Wrong by exactly a power of two means a lost top bit. Wrong only for negative values means an unsigned operand. Wrong only at the end means an off-by-one.
In Verilog the width of an expression comes from its context, and one unsigned operand makes the whole expression unsigned.
A cast silences the lint warning that would have caught the bug. Check the truncation is meant before you write one.
Rule of thumb: when a wrong value differs from the right one by a power of two, look for the bit that was dropped.
FSM Bugs
Read the state register first. Is its value legal, how long has it held it, and which event would move it on?
A one-hot state with two bits set is two machines at once: each drives its own outputs.
A new input added to an FSM must be handled in every state. Review a change by its rows (states), not by its new line.
Rule of thumb: for every state and every input, be able to say where the machine goes next, and make the simulator check it.
X and Reset Bugs
Missing reset: X in four-state, a pass in two-state, a seed-dependent failure with random start values. Wrong reset value: the same failure everywhere.
A synchronous reset needs a clock edge while reset is held. If the clock starts after reset ends, the flop was never reset.
Read every register one cycle after reset and compare it with the register map. It is the cheapest test in the plan.
Rule of thumb: for every register, know its value one cycle after reset and who reads it before its first write.
Races and Simulation-Synthesis Mismatches
A blocking assignment in a clocked block that another block reads is a race: each simulator may order it differently, and the chip has no order at all.
Synthesis ignores sensitivity lists and delays, and an ASIC cannot honour initial values. The netlist is what the chip does; simulate it when RTL runs disagree.
Rule out X first: a two-state simulator and a four-state one also disagree on unknown values.
Rule of thumb: if two simulators or the RTL and the netlist disagree, suspect the RTL's ordering before the tools.
Latches and Combinational Loops
A combinational output that depends on what happened before is a latch. Look for a path through an always block that does not assign it.
A simulation that stops advancing time without an error is the signature of a zero-delay loop. Verilator calls it DIDNOTCONVERGE.
Loops often span two modules. Check them on the elaborated hierarchy: Yosys check after flatten, or Verilator lint on the top.
Rule of thumb: treat every "Latch inferred" and every logic-loop warning as a bug until someone has written down why it is meant.
CDC Bugs
Zero-delay RTL simulation never shows metastability: every flop captures cleanly. Only a model, a CDC tool or silicon shows these three bugs.
One signal, one synchroniser, then fan out. Two flops that sample the same asynchronous signal can disagree for a cycle.
Never synchronise a word bit by bit. Hold it stable and synchronise one qualifier, use Gray code for a counter, or use an async FIFO.
Rule of thumb: if a failure needs a value nobody sent or a state nobody can reach, ask which crossing it came through.
Reset-Domain Crossing Bugs
An asynchronous reset changes its flops at any time in the cycle. Any flop outside that reset that samples them is a crossing, even on the same clock.
Isolate before you reset: hold the receiving side, then assert, release, wait, and only then let it sample again.
Benches that release every reset together cannot see release-order bugs. Randomise which domain leaves reset first.
Rule of thumb: for every reset in the design, ask who is still running when it asserts, and who is ready when it releases.
Timing-Only Bugs
A failure that goes away at a slower clock is setup-type. One that stays at every frequency is hold-type. RTL simulation shows neither.
A multicycle or false path is a promise the RTL must keep. Static timing believes it; only the design can make it true.
High bits wrong and low bits right, on an arithmetic path, is a long carry chain arriving late.
Rule of thumb: for every timing exception, find the line of RTL that makes it true.
Protocol Violations
Most protocol bugs only fail under back-pressure. A test whose sink is always ready proves almost nothing about the handshake.
Check the three rules first: valid held until ready, payload stable while waiting, one response per accepted request, to the master that made it.
A protocol checker reports the rule at the cycle it breaks; a scoreboard reports a wrong word later and points at the data path.
Rule of thumb: put a rules checker on every valid/ready port before you trust any scoreboard result.
Hangs and Deadlocks
A hang is silence: counters freeze, and the only message is the watchdog's. Snapshot every agent and write down what each one waits for.
Follow the waits. A chain that comes back to its start is a deadlock; a chain that ends at an idle agent means a credit or token was lost.
Prevent with a check on the cause: a conservation invariant for credits, a no-hold-while-waiting rule for locks. Both fire long before a watchdog.
Rule of thumb: in a hang, follow the waits until they loop or end, and fix the arrow that should not exist.
Starvation and Livelock
Starvation: progress for some, never for one. Livelock: requests and retries keep coming, nothing completes. Neither is silent.
An activity watchdog cannot see either. Count completions per agent and the age of the oldest waiting request.
Fix the policy, not the symptom: a fair arbiter for starvation, broken symmetry (a different or random back-off, or a winner) for livelock.
Rule of thumb: measure progress per agent, not activity on the bus.
Performance Bugs
A performance bug has right values and wrong timing. Only counters and cycle budgets fail on it; scoreboards never will.
Read stall counters stage by stage. A stage that stalls its input in a cycle when its own output is being taken is the bubble's source.
Every fix and every speed-up changes cycles. Compare cycles per test against a baseline in every regression, not only when someone complains.
Rule of thumb: a test that does not check cycles passes performance bugs; give every directed test a cycle budget.
Power-Intent Bugs
Missing isolation: X or noise on the always-on side, starting the cycle a domain powers off. In a two-state simulator it can show up as a hang or a phantom event.
Missing retention: after a wake the register reads its reset value, not garbage. Only tests that sleep with non-default settings see it.
Order bugs live in a window of a few cycles around a power transition. Check the sequence against the spec before you look at the data.
Rule of thumb: for every signal that leaves a switchable domain, ask which supply powers its reader; if it is a different one, the signal needs a clamp.
DFT-Mode Bugs
A register holding its old value shifted by N bits, after N cycles of unrelated pin activity, is a scan shift: look for an ungated scan enable.
Flops that ignore a functional reset, or run at the wrong clock, point at a test mux that picked the test input.
Scan muxes are added at insertion, so the RTL may have no path for the bug: it shows at gate level, in DFT rule checks or on the tester.
Rule of thumb: in functional mode every test control is constant and inactive: check that before you debug the data.
Configuration and Register-Map Bugs
A wrong reset value only shows before software writes the register. Tests that always program it first never see it.
When writing one register changes another, the register you wrote reads back fine. Read every register after writing every register.
Test each access type with both values: write 0 and 1 to a W1C bit, write a read-only one, read a read-to-clear one twice.
Rule of thumb: test the register block against the register map, typed from the spec, before any test that uses the block.
Testbench Bugs That Look Like Design Bugs
Mismatches that appear only under back-pressure or gaps, off by one beat, while design-side assertions stay quiet: check when the monitor samples.
A result that changes with the simulator or an unrelated edit, by exactly one cycle, is a race. Drive away from the edge the design samples on.
A checker that made zero comparisons has not passed anything. Every checker reports its count, and zero is a failure.
Rule of thumb: if the failure moves when you change only the bench's timing, the bug is in the bench.
Practice question: Counter with wrap and saturate modes
Practice question: FIFO flag off-by-one at wrap
Code lab: Signed comparator
Code lab: Saturation logic
Practice question: FSM illegal state debugging with assertions
Interview: run a mock interview on this topic