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

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
    • Scoreboard Architecture: Expected vs Actual
      • Expected vs Actual: The Two-Stream Model
      • Three Streams, One Scoreboard: imp_decl and Virtual write_*
      • Env Wiring: From Block Scoreboard to End-to-End Checker
      • In-Order Matching: One Queue, Four Outcomes
      • Out-of-Order Matching: A Queue per ID
      • Debugging a Scoreboard: Silence, Shapes and Cascades
      • Scoreboards for Messy Traffic: Drops, Reorders, Splits and Merges
      • End-of-Test Checks: What Must Be Empty Before a Test Passes
    • Reference Models: Predictors, DPI-C and Built-in Comparators
    • Coverage & Closure
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Scoreboard Architecture: Expected vs Actual
  1. Topics
  2. UVM Verification
  3. Checking & Coverage in UVM
  4. Scoreboard Architecture: Expected vs Actual
  5. Debugging a Scoreboard: Silence, Shapes and Cascades

Part 8 · Checking & Coverage · Intermediate

Debugging a Scoreboard: Silence, Shapes and Cascades

Reading a failing scoreboard's numbers: what silence, a constant difference and a cascade of mismatches each point at, and how to make a scoreboard explain itself.

On this page

    Monday morning, three red tests from the nightly run. The first says SB_EMPTY and nothing else. The second has one SB_MISMATCH. The third has nine mismatches and one SB_LEFTOVER. A new engineer opens the third, because it looks the worst, picks the last mismatch, and spends the morning in the waveform at the end of the test. The bug was at the first mismatch, and the other eight were echoes of it. A scoreboard's numbers are evidence. Which layer of the bench does each pattern point at, and where do you look first?

    The idea: read the numbers like a blood test

    A doctor does not start with surgery. They read the blood test, and each abnormal value points at an organ. A scoreboard's summary works the same way. Silence (nothing compared) points at stimulus, end of test or connections, never at the DUT's data path. A constant difference between expected and actual points at model state, such as a key or a configuration bit. A single flipped bit points at a wire in the DUT. A cascade, many mismatches that start at one point and never recover, points at one lost or extra item. Only after you have read the shape do you open the waveform, at the first bad item.

    The picture: a triage path

    Triage flowchart: if nothing was compared, check the objection, stimulus and connections; if there are leftovers but no mismatches, check for loss or an early end; if there are mismatches, XOR expected and actual: a constant points at model state, one bit at the DUT data path, and varying values from one point on at a lost or extra item.
    Start from the summary, not the waveform. Each branch ends at the layer to open first.

    Build it: make the scoreboard explain itself

    Count every outcome, separately

    dma_scoreboard keeps n_match and n_mismatch and counts an unexpected word as a mismatch. That is enough for a pass/fail verdict but not for triage. Keep one counter per outcome (match, mismatch, unexpected, leftover) and print them in one summary line, so the shape of a failure is visible without reading every error.

    Say what was left, not only how many

    SB_LEFTOVER in the bench reports a count. When it fires, the next question is always which items, so print the first few at the same time:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // snippet: a check_phase that names what was left
    function void check_phase(uvm_phase phase);
      if (expected.size() != 0) begin
        `uvm_error("SB_LEFTOVER", $sformatf("%0d expected word(s) never came out", expected.size()))
        foreach (expected[i]) begin
          if (i == 4) break;                      // the first few are enough
          `uvm_info("SB_LEFTOVER", $sformatf("  [%0d] expected %08h", i, expected[i]), UVM_NONE)
        end
      end
      if (n_match == 0)
        `uvm_error("SB_EMPTY", "no words were compared: the test checked nothing")
    endfunction

    Leave a trace you can turn on

    The bench's stream monitor already prints every beat at UVM_HIGH (ID AXIS_MON), so +UVM_VERBOSITY=UVM_HIGH shows every word each monitor saw. Add the same in the scoreboard: one UVM_HIGH line per prediction and per compare, with the queue depth. Off by default, it costs nothing; on, it lets you line up input, prediction and output for one word in a text log instead of a waveform.

    Silence: SB_EMPTY is never a DUT bug

    A scoreboard that compared nothing was never given anything to compare. The bench's SB_EMPTY check exists to turn that into a failure, and its causes are always upstream of the compare:

    Evidence

    Cause

    Where it is taught

    The run ends at time 0

    No objection raised before the first wait

    Objection counting rules

    Monitors print at UVM_HIGH; the scoreboard never sees them

    A port with no subscribers, for example an agent ap nobody feeds

    Connecting across the hierarchy

    No traffic on the waves at all

    Stimulus never started: a sequence on the wrong sequencer, firmware not loaded

    Testbench architecture

    Diagram
    UVM_ERROR dma_tb.sv(407) @ 0: uvm_test_top.env.sb [SB_EMPTY] no words were compared: the test checked nothing

    That line, at time 0, is the signature of a missing objection. The same error at 2 ms is a connection or stimulus problem. The time stamp alone splits the two.

    Shape: XOR expected with actual

    When words mismatch, XOR the two values before opening anything else. The first-testbench lesson broke the DUT on purpose so that word 6 used ~key instead of key, and got exactly one error:

    Diagram
    UVM_ERROR dma_tb.sv(396) @ ...: uvm_test_top.env.sb [SB_MISMATCH] expected 52c7d685, got ad38297a
    UVM_INFO dma_tb.sv(411) @ ...: uvm_test_top.env.sb [SB] 7 words matched, 1 mismatched

    0x52c7d685 XOR 0xad38297a = 0xffffffff: every bit inverted, on one word only. That reads as "the DUT inverted something for this word", which is exactly the planted bug. Compare the two other shapes in this topic. In the KEY written while busy run, every mismatch XORed to 0x33333333, the difference between two keys: model state. In the miswired end-to-end checker, every mismatch XORed to 0x11112222, one DMA's key: a wrong connection. A transform other than XOR needs its own inverse, but the idea carries: compute what would turn expected into actual, and see whether it is constant, local or random.

    Cascades: debug the first error, ignore the echoes

    In-order matching has one weakness: a single lost word shifts every later pair. Here is what a 6-word job looks like when the DUT drops word 2:

    Output word

    Paired with

    Result

    w0

    e0

    match

    w1

    e1

    match

    w3

    e2

    SB_MISMATCH

    w4

    e3

    SB_MISMATCH

    w5

    e4

    SB_MISMATCH

    (end)

    e5 still queued

    SB_LEFTOVER: 1

    The signature is mismatches that start at one point and never recover, plus leftovers equal to the number of lost items. Only the first mismatch is informative. A scoreboard can say so itself: on a mismatch, look ahead in the queue for the actual value before blaming the data.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // snippet: in write_out, after popping exp; classify a loss instead of cascading
    if (t.data !== exp) begin
      int idx[$] = expected.find_first_index(e) with (e == t.data);
      if (idx.size() != 0) begin
        `uvm_error("SB_LOST", $sformatf("%0d word(s) before %08h never came out", idx[0] + 1, t.data))
        expected = expected[idx[0] + 1 : $];      // resynchronise after the gap
        n_lost += idx[0] + 1;
      end else begin
        `uvm_error("SB_MISMATCH", $sformatf("expected %08h, got %08h", exp, t.data))
        n_mismatch++;
      end
    end

    The look-ahead is a debug aid with a cost: a word whose corrupted value happens to equal a later prediction is misclassified as a loss. Keep it reporting an error either way, never a silent resync, and prefer real sequence numbers when the protocol carries them.

    What breaks: debugging the ninth error

    The Monday-morning story is the common one. The third test's log had nine SB_MISMATCH lines and one leftover. The engineer chose the last mismatch because it was nearest the end, where the test had been "doing the interesting part". The expected and actual values there were unrelated, the waveform showed a perfectly legal transfer, and the morning went on that. The first mismatch, 400 ns into the test, sat one clock after a backpressure stall; the DUT had dropped a word when TREADY fell in the same cycle a new input word was accepted. Every later error was the queue being one word out of step.

    Two habits would have found it in minutes. Read the summary first: nine mismatches and one leftover, with values that XOR to nothing in particular, is the cascade shape. Then go to the first error and look one or two items before it. In a scoreboard log, the earliest failure is almost always the real one.

    Interview

    "Your scoreboard reports 1 match, 9 mismatches and 1 leftover. Walk me through what you do."

    Weak: "Open the waveform at the first mismatch and see what the DUT output was."

    Strong: "First the shape. One leftover with mismatches that never recover is a lost word in an in-order compare, so eight of the nine are echoes. I XOR expected and actual on the first mismatch: if the actual equals the next prediction, the word before it was lost. Then I go to that time in the trace with the monitors at UVM_HIGH, find the input word that was accepted and never came out, and look at what the DUT was doing that cycle: backpressure, reset, an early TLAST. Only then the waveform, at that point."

    Follow-ups: What if there are no leftovers but the same pattern? (an extra word was inserted, the mirror image) and What if every word mismatches by the same XOR? (model state, such as the key, is wrong; check the register stream into the scoreboard).

    Check yourself A test reports SB_EMPTY at 1.2 ms, and the input monitor's UVM_HIGH trace shows every beat. Where is the problem?

    Between the input side and the compare. The run lasted long enough and stimulus reached the pins, so neither the objection nor the stimulus is at fault. Either the output side saw nothing (the output monitor or its connection to out_export), or the scoreboard was never connected. Check out_agt.ap's connections and the output monitor's trace next.

    Check yourself Every mismatch in a run has expected XOR actual = 0x00010000. What do you suspect, and what would change your mind?

    A single DUT data-path bit, bit 16, stuck or inverted: the same one bit on every failing word. If only some words fail, check whether they are the words where bit 16 of the input or key is 0 (a stuck-at-1) or 1 (a stuck-at-0). If the XOR varied from word to word, it would not be a single wire.

    Check yourself Why should the look-ahead resync still report an error rather than quietly realign the queue?

    Because a lost word is a failure. Realigning quietly turns a DUT bug into a pass with fewer matches. The look-ahead's job is to make the error message accurate ("2 words lost" instead of nine mismatches), not to excuse it.

    Next: not every missing word is a bug. Messy Traffic handles DUTs that are allowed to drop, reorder, split and merge, and the DMA's own two cases of legitimate loss.

    Related topics

    • Scoreboards for messy traffic

    • End-of-test checks

    • The end-of-test chain

    Previous

    131 / 280

    Next: Scoreboards for Messy Traffic: Drops, Reorders, Splits and Merges

    Up next

    Up nextScoreboards for Messy Traffic: Drops, Reorders, Splits and Merges

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Expected vs Actual: The Two-Stream Model
    • Three Streams, One Scoreboard: imp_decl and Virtual write_*
    • Env Wiring: From Block Scoreboard to End-to-End Checker