One passing simulation does not finish a block. A defined set of gates does, with each gate catching a different kind of mistake.
Gate | Question it answers | Typical tool |
|---|---|---|
1. Lint | Is the code free of width, latch, multiple-driver and style problems? | Verilator |
2. Clean compile | Does it compile in every target simulator with no warnings? | Your simulators |
3. Directed tests | Do the known corner cases pass (reset, full, empty, back-to-back, limits)? | Self-checking testbench |
4. Randomized tests | Does it survive millions of unplanned input patterns? | Seeded constrained-random |
5. Assertions | Do protocol and invariant rules hold every cycle? | SVA in design or testbench |
6. Coverage | Did the tests actually exercise every state, branch and interesting combination? | Code and functional coverage |
7. Equivalence / formal | Does the synthesized netlist match RTL, and do key properties hold? | Formal tools |
8. CDC / reset review | Are clock-domain crossings and resets safe? | CDC and RDC tools |
9. Review | Can a second engineer explain every always block? | Code review |
All gates must be clean before sign-off. Cheap gates (lint, compile, directed tests) run on every change; the others run continuously and are closed together. The order below is roughly the order in which each gate first starts.
# 1) lint everything with warnings on
verilator --lint-only -Wall -Wno-DECLFILENAME top.sv
# 2) compile with a second tool to catch dialect differences
iverilog -g2012 -o sim.out top.sv tb_top.sv
# 3) run the self-checking test as a real gate: keep the simulator status, then require no FAIL and exactly one PASS
set -o pipefail
vvp sim.out | tee run.log || exit 1
! grep -q FAIL run.log && test "$(grep -c '^PASS:' run.log)" -eq 1
# vvp sim.out | grep -E "PASS|FAIL" # log filtering, not a gate: matches even a FAIL lineNo. The untested state could contain a bug that no test can reveal. Add a directed test (or constraint) that reaches the state, or justify and document why it is unreachable.