Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • Meet DCRouter
  • Glossary
  • Part 0: What Verification Is8
  • Part 1: What Can Go Wrong11
  • Part 2: Specifications as Checkable Rules8
  • Part 3: Observability and Controllability7
  • Part 4: Stimulus11
  • Part 5: Checking11
    • Self-Checking Testbenches
    • Where to Check
    • Assertions as Local Checkers
    • Scoreboards
    • How Precise a Reference Model?
    • Ordering and Timing in Checks
    • End-of-Test Checks
    • False Passes
    • Checking Performance
    • Hunting the Zoo with Checkers
    • Drills: Checking
  • Part 6: Coverage10
  • Part 7: Formal and Simulation10
  • Part 8: Verification Levels and Reuse8
  • Part 9: Planning and Risk9
  • Part 10: Emulation and Prototyping8
  • Part 11: Post-Silicon Validation8
  • Part 12: Bug Economics8
  • Part 13: Debug7
  • Part 14: Sign-Off7
  • Part 15: Verification in Context8
  • Part 16: Case Studies5
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 5: Checking
  1. Topics
  2. First Principles of Verification Strategy
  3. Part 5: Checking
  4. Self-Checking Testbenches

Part 5 · Checking · Foundation (F)

Self-Checking Testbenches

A testbench that drives stimulus and prints values is a demonstration; a self-checking testbench decides, for every output, whether it is right, and ends with one verdict. The parts every self-checking bench needs (a reference, a comparison, end-of-test checks, a watchdog, a single verdict), built and run on a correct and a buggy FIFO.

On this page

    Note

    Level: Foundation (F) · Answers: How do we know the output is right?

    Before this: Part 5 overview, Intent and the Oracle

    A new engineer's first testbench drives a FIFO with pushes and pops and prints every value that comes out. The waveform looks plausible, the printout looks plausible, and the test "passes" because nothing crashed. Run the same testbench on a FIFO with a broken full flag and it prints different numbers that look just as plausible. Nobody can tell from the output which run was correct, least of all a regression script running ten thousand tests overnight.

    What turns a testbench that runs into a testbench that checks?

    The obvious answer, and why it breaks

    The obvious answer is that someone looks at the waveform or the log. That works once, for a small test, with an engineer who knows the right answers. It does not scale to thousands of tests, millions of cycles and every overnight run, and people miss things they are not looking for. A test whose verdict depends on a person reading its output is a demonstration, not a test.

    The idea: every output judged, one verdict

    The same stimulus drives the design and a reference; every output is compared, and the run ends in one machine-readable verdict.

    Part

    Job

    Without it

    Reference (oracle)

    Knows the right answer for every output

    Nothing to compare against (lesson 2.1)

    Comparison on every output

    Turns each mismatch into a counted error

    Wrong values pass silently

    End-of-test checks

    Expected outputs that never arrived; counters; leftovers

    Lost data passes (lesson 5.7)

    Watchdog

    Ends a hung run with a failure

    A hang looks like a slow pass, or blocks the regression

    One verdict line

    PASS or FAIL that a script can read

    Results need a person to interpret

    Evidence the checker can fail

    Shows the checks are real

    A checker that checks nothing passes everything (lesson 5.8)

    Build it: one checker, two FIFOs

    Two copies of a 4-entry FIFO receive identical random traffic: one correct, one whose full flag rises one entry late. Each copy gets the same checker: a reference queue predicts every popped word, mismatches are counted, leftovers are checked at the end, and a watchdog stops a hung run. The test passes only if the checker is silent on the correct FIFO and reports the buggy one.

    fifo4.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    // Lesson 5.1: a self-checking testbench. Two copies of a 4-entry FIFO run the same random
    // pushes and pops: one correct, one whose full flag rises one entry late (it accepts a fifth
    // word and overwrites the oldest). Each copy has the same checker: a reference queue predicts
    // every popped word, mismatches are counted, leftovers are checked at the end, and a watchdog
    // ends a hung run. The test passes only if the checker is silent on the correct FIFO and
    // reports the buggy one, so the run also shows the checker can fail.
    module fifo4 #(parameter bit BUGGY = 0) (
      input  logic clk, rst, push, pop,
      input  logic [7:0] din,
      output logic [7:0] dout,
      output logic full, empty
    );
      logic [7:0] mem [0:3];
      logic [1:0] wp, rp;
      logic [2:0] count;
      assign full  = BUGGY ? (count > 3'd4) : (count == 3'd4);
      assign empty = (count == 3'd0);
      assign dout  = mem[rp];
      always_ff @(posedge clk)
        if (rst) begin wp <= 0; rp <= 0; count <= 0; end
        else begin
          if (push && !full) begin mem[wp] <= din; wp <= wp + 2'd1; end
          if (pop && !empty) rp <= rp + 2'd1;
          count <= count + 3'(push && !full) - 3'(pop && !empty);
        end
    endmodule
    
    module fifo_checker (input logic clk, rst, push, pop, full, empty, input logic [7:0] din, dout);
      logic [7:0] model [$];
      int errors = 0, checked = 0;
      always @(posedge clk) if (!rst) begin
        if (pop && !empty) begin                        // every popped word must be the oldest pushed
          if (model.size() == 0) errors++;
          else begin
            if (dout !== model[0]) errors++;
            void'(model.pop_front());
          end
          checked++;
        end
        if (push && !full) model.push_back(din);
      end
      function automatic int leftovers();               // end of test: words the model still holds
        return model.size();
      endfunction
    endmodule
    
    module tb;
      logic clk = 0, rst = 1, push = 0, pop = 0;
      always #5 clk = ~clk;
      logic [7:0] din = 0, dout_g, dout_b;
      logic full_g, empty_g, full_b, empty_b;
      fifo4 #(0) good (.clk, .rst, .push, .pop, .din, .dout(dout_g), .full(full_g), .empty(empty_g));
      fifo4 #(1) bad  (.clk, .rst, .push, .pop, .din, .dout(dout_b), .full(full_b), .empty(empty_b));
      fifo_checker chk_g (.clk, .rst, .push, .pop, .full(full_g), .empty(empty_g), .din, .dout(dout_g));
      fifo_checker chk_b (.clk, .rst, .push, .pop, .full(full_b), .empty(empty_b), .din, .dout(dout_b));
    
      initial begin
        repeat (2) @(posedge clk); rst <= 0;
        for (int k = 0; k < 400; k++) begin             // random traffic, biased to fill the FIFO
          @(posedge clk);
          push <= ($urandom_range(0, 99) < 60);
          pop  <= ($urandom_range(0, 99) < 40);
          din  <= 8'($urandom);
        end
        @(posedge clk); push <= 0; pop <= 1;            // drain
        repeat (8) @(posedge clk); pop <= 0;
        @(posedge clk);
        $display("correct FIFO: %0d pops checked, %0d errors, %0d leftovers", chk_g.checked, chk_g.errors, chk_g.leftovers());
        $display("buggy FIFO:   %0d pops checked, %0d errors, %0d leftovers", chk_b.checked, chk_b.errors, chk_b.leftovers());
        if (chk_g.errors == 0 && chk_g.leftovers() == 0 && chk_g.checked > 100 && chk_b.errors > 0)
          $display("PASS: the checker passes the correct FIFO and reports the buggy one");
        else $display("FAIL: checker verdicts are wrong");
        $finish;
      end
      initial begin #100000; $display("FAIL: watchdog timeout"); $finish; end
    endmodule

    It prints:

    Diagram
    correct FIFO: 160 pops checked, 0 errors, 0 leftovers
    buggy FIFO:   163 pops checked, 45 errors, 0 leftovers
    PASS: the checker passes the correct FIFO and reports the buggy one

    How to read it: the checker compared 160 pops on the correct FIFO and found nothing wrong, with no words left over; on the buggy FIFO it found 45 wrong words, every one caused by a fifth push overwriting the oldest entry. A print-only testbench would have produced two equally plausible logs. The final line is a single verdict a regression script can read, and it holds only because the checker demonstrably fails on a real bug.

    Habits of a good self-checking testbench

    • Count errors, report the first few in detail: enough to debug, not a flood.

    • Fail loudly on anything unexpected: an output with no prediction, a prediction never matched, a timeout.

    • Print exactly one verdict line: scripts match it; anything else is a failure.

    • Keep the reference independent of the design: same specification, different implementation (lesson 3.4).

    • Prove the checks can fail: plant a bug, as here and in the Bug Zoo, or use mutation testing (lesson 6.8).

    How real teams build them

    Practice

    Purpose

    Scoreboards and monitors in every environment

    Every output is compared automatically (lesson 5.4)

    Error counters and a summary report at the end

    One verdict per test, readable by regression tools

    Global timeouts in every test

    Hangs become failures, not stuck jobs

    Bug seeding or mutation campaigns

    Evidence that the checkers detect real bugs

    What breaks

    A team (illustrative) inherited a regression where each test printed "TEST DONE" at the end and the script treated any run that printed it as a pass. Several tests' checkers had been commented out during debug months earlier. The tests kept printing "TEST DONE". The checker-free tests were found only when a bug they should have caught reached a later stage.

    Why this matters

    Senior lens. Judge a testbench by what happens when the design is wrong. If the answer is "someone would notice in the waves", it is a demo. Insist on a reference, a comparison on every output, end-of-test checks, a watchdog, one verdict line, and evidence from a planted bug that the checks fire.

    Interview

    Asked: "What makes a testbench self-checking?"

    Weak answer: It prints the outputs so you can check them.

    Strong answer: It decides correctness itself for every output and ends with one verdict a script can read. That needs a reference that predicts the right answer, independent of the design; a comparison on every output with errors counted; end-of-test checks for things that never arrived and for counters; a watchdog so a hang is a failure; and evidence that the checker can fail, from a planted bug or mutation. Without those it's a demonstration, and a demo passes on a broken design.

    Follow-ups: (1) Why does a self-checking bench need a watchdog? (2) How do you show the checker can fail?

    Check yourself Why does the example run the checker on a buggy FIFO as well as the correct one?

    To show the checker detects a real bug; a checker that passes the correct FIFO might also pass everything.

    Check yourself A test prints "TEST DONE" and the regression treats that as a pass. What can go wrong?

    A test whose checks never ran or were disabled still prints it and passes; the verdict must come from the checks, with errors counted.

    Key takeaways

    • A test whose verdict needs a person reading the output is a demonstration.

    • Self-checking means a reference, a comparison on every output, end-of-test checks, a watchdog and one verdict.

    • Show the checks can fail, with a planted bug or mutation.

    Keep going

    Next: Lesson 5.2, Where to Check. Local checks at the cause, or end-to-end checks at the effect.

    Related topics

    • Part 5 overview: Checking

    Previous

    51 / 160

    Next: Where to Check

    Up next

    Up nextWhere to Check

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Assertions as Local Checkers
    • Scoreboards
    • How Precise a Reference Model?