A waveform shows what happened, not whether it was right. A testbench earns trust by checking results itself and reporting pass or fail.
6 lessons, about 27 min
An automatic check. Without a self-checking comparison the result depends on a person's attention, cannot run in a regression and will miss subtle mismatches.
The whole topic on one page, taken from each lesson's Remember box. Read it before an interview, or print it.
Minimal but Robust Testbench Structure
Six parts: clock, reset, DUT by name, stimulus, checks that count errors, and a watchdog.
Put checks in a task so every failure prints what, when, got and expected.
End with one PASS: or FAIL: line. Use $fatal(1, ...) on unexpected failure so automation sees a failing status.
Rule of thumb: a test that cannot fail, or cannot finish, is not a test.
Clock and Reset Best Practices
Initialise the clock to a known value, and keep the period in one place.
Drive inputs on the opposite edge or a short delay after the active edge, never on it.
Hold reset for several clocks, start from a known 1 so the falling edge is real, and release away from the clock edge.
Rule of thumb: never change an input at the instant the DUT samples it.
Self-Checking and Observability
The model comes from the spec, in a different style: a queue, an array or a plain function.
A fixed-latency block is checked cycle by cycle; a variable-latency block is checked by transaction order.
Run a deliberately broken DUT next to the real one: it must produce mismatches.
Rule of thumb: never trust a checker you have not seen fail.
Useful Runtime Instrumentation
Print the seed at the start of every run so any failure can be replayed.
Plusargs turn on verbose logging and dumping without editing the source.
Dump waveforms only for the test being debugged; they slow the run and the files are huge.
Rule of thumb: log the seed first.
Verification Quality Gates
Each gate catches a different kind of mistake: lint, compile, directed tests, assertions, coverage, and more.
Cheap gates run on every change. The others run continuously and close together.
Passing tests are not verification. An unreached state needs a directed test or a documented reason.
Rule of thumb: automate the gates in one script and run it before every commit.
First Project: a 4-bit ALU
Write a value for every output in every row of the spec before writing RTL; a blank cell becomes a latch or an argument.
Give every output a default at the top of always_comb, then let each case item override only what it defines.
C is the unsigned answer and V the signed one; for SUB they are independent, so define C (borrow or no borrow) explicitly.
Rule of thumb: a block is done when a bench that you have seen fail now passes on every input, and synthesis shows only the cells you expected.
Practice question: FSM verification: coverage and assertions for corner cases
Practice question: Simple ALU with add, sub, AND, OR, XOR
Code lab: 4-bit ALU slice
Interview: run a mock interview on this topic