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

  • RTL Foundations57
    • Digital Logic for RTL Engineers
    • Number Systems for RTL Engineers
    • Verilog Fundamentals
    • Verilog Operators and Types
    • SystemVerilog for RTL
    • Simulation and Testbenches
      • Minimal but Robust Testbench Structure
      • Clock and Reset Best Practices
      • Self-Checking and Observability
      • Reference Model Plus Checks
      • Useful Runtime Instrumentation
      • Verification Quality Gates
      • First Project: a 4-bit ALU
      • Interview-Ready Simulation Patterns
      • Simulation TB Summary
      • Worked Examples and Waveforms
  • Combinational Design58
  • Sequential Design61
  • FSM & Control66
  • Timing & Clocking64
  • Reset & CDC58
  • Memory & Buses94
  • Advanced RTL60
  • Senior & Interview175
  • Glossary and Notation
Loading lesson

‹Simulation and Testbenches
  1. Topics
  2. RTL Design
  3. RTL Foundations
  4. Simulation and Testbenches
  5. Self-Checking and Observability

Part 1 · Foundations · Beginner

Self-Checking and Observability

A reference model, ordered comparison and readable failure logs that prove bugs get caught.

On this page

    Why this matters

    A reference model that copies the RTL shares its bugs, so every comparison matches. A checker that has never been seen to fail may be checking nothing, which is why the lesson runs a deliberately broken copy.

    After this lesson you can: write an independent reference model, compare it to the DUT on every transaction, and prove that the checker can fail.

    Reference model and comparison

    A reference model describes the expected result independently of the design. The same stimulus drives both, and a comparator reports each mismatch.

    For a fixed-latency pipeline, a queue is the clearest model. Whatever enters must leave a fixed number of cycles later.

    The stimulus d drives three blocks: the good pipe2, the broken pipe2 with BUG set to 1, and a reference queue. The queue supplies the expected value to a comparator that also receives both pipe2 outputs. The comparator leads to the verdict: the good design passes and the bad design is caught.
    The self-checking loop in tb_pipe2: one stimulus drives the design, a deliberately broken copy and an independent queue model, and one comparator judges both designs against the queue

    Read the chart top to bottom: everything below the stimulus runs in parallel on the same inputs.

    • The queue is the reference: it is written from the spec (whatever enters leaves two cycles later), not copied from the RTL.

    • The broken copy proves the checker works: the test passes only if the good design matches and the bad one does not.

    • One comparator with !==, so an x output counts as a mismatch.

    Tip

    This is mutation testing in miniature. Instantiating a deliberately broken copy next to the real design proves that your checker can fail. A checker that has never been seen to fail may be checking nothing.

    What makes a good reference model

    • Independent: written from the spec, in a different style. Copying the RTL into the testbench just duplicates its bugs.

    • Cycle-accurate where needed: for a fixed-latency pipeline, track the latency exactly; for a variable-latency block, compare transactions in order rather than cycle by cycle.

    • Simple: queues, associative arrays, plain functions. If the model is as complex as the design, it will have the same bugs.

    Observability

    Practice

    Why

    Print time, input and both values on a mismatch

    You can reproduce and understand the failure from the log alone.

    Count errors and stop only at the end (or after N errors)

    One run reveals the pattern, not just the first symptom.

    Use !== for comparisons

    An x output is reported as a failure instead of ignored.

    Log the random seed

    Any failing random test can be replayed exactly.

    Add assertions inside the design for impossible states

    Failures point at the cause, not just the symptom.

    Check yourself The reference model is a copy-paste of the RTL's always_ff block. Why does the test pass but prove little?

    Both share the same logic, so any bug in the RTL is also in the model and the comparison always matches. The model must come from the specification, expressed independently (for example a queue instead of two registers).

    Testbench

    A two-stage pipeline checked against a queue, with a broken copy proving the checker fails.

    pipe2.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    module pipe2 #(parameter int BUG = 0) (
      input  logic       clk, rst_n,
      input  logic [7:0] d,
      output logic [7:0] y
    );
      logic [7:0] s1, s2;
      always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) begin s1 <= '0; s2 <= '0; end
        else        begin s1 <= d;  s2 <= s1;  end
      assign y = (BUG == 1) ? s1 : s2;           // BUG=1 drops one pipeline stage
    endmodule
    

    The verdict comes after 41 comparisons: the initial reset value and all 40 generated inputs, including the final input drained from the queue. The bad DUT must produce at least one mismatch; its exact error count depends on the random sequence because adjacent samples can coincide. An empty queue and the comparison count check prevent a final transaction from escaping verification.

    Remember

    • 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.

    How this was checked

    Run with Icarus Verilog 13.0 (2026-10-16): the verdict comes after 41 comparisons, the initial reset value plus 40 generated inputs. The exact error count of the bad DUT depends on the random sequence.

    Keep going

    • Interview: run a mock interview on this topic

    Related topics

    • Runtime instrumentation

    Previous

    50 / 694

    Next: Reference Model Plus Checks

    Up next

    Up nextReference Model Plus Checks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Minimal but Robust Testbench Structure
    • Useful Runtime Instrumentation
    • Verification Quality Gates