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

Part 5 · Checking · Working (W)

Scoreboards

A scoreboard records what should come out of a design and checks what does: every output matched against a prediction, every prediction matched by an output by the end. How DCRouter's scoreboard predicts at the inputs and compares at the outputs, the failure messages it produces for lost, misrouted and corrupted packets, and the design decisions behind it.

On this page

    Note

    Level: Working (W) · Answers: How do we know the output is right?

    Before this: Where to Check, Directed Tests

    Every Part 4 testbench caught most of its bugs with messages such as "unexpected flit", "flit 1239, expected 8d57 from input 3" and "flits still pending". None of those tests contained a check written for that bug. All of them came from the scoreboard inside the shared environment, quietly comparing every flit DCRouter sent with the flit it should have sent.

    How does a scoreboard decide whether every output was right?

    The obvious answer, and why it breaks

    The obvious scoreboard keeps a list of expected outputs and compares each actual output with the next one on the list. For a design that reorders legally, such as a router interleaving packets from four inputs, "the next one on the list" is the wrong expectation and the scoreboard reports false failures. Its structure must follow the specification's ordering promise (lesson 2.7), and it must also notice outputs that never come.

    The idea: predict, compare, account for everything

    The scoreboard predicts from what entered, matches each output against the oldest prediction of its flow, and at the end requires every prediction to have been matched.

    Part

    DCRouter

    Input monitor

    Every flit accepted (in_vld and in_rdy) is a fact about what must come out

    Prediction

    The reference model: here simple, the same flit at output dst, with out_err expected on the tail of a faulty packet

    Matching structure

    One queue per flow (input, output), because order is promised only within a flow

    Output monitor

    Every flit sent (out_vld) is compared with the oldest prediction of its flow

    End-of-test check

    Every queue must be empty; anything left was lost (lesson 5.7)

    DCRouter's scoreboard

    Prediction, at the inputs: each accepted flit goes into the queue of its flow, with the error flag expected on a faulty tail.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    logic [16:0] exp_q [4][4][$];   // expected {err on tail, data} per (input, output)
    for (genvar i = 0; i < 4; i++) begin : g_src
      always @(posedge clk) if (!rst) begin
        if (in_vld[i] && in_rdy[i]) exp_q[i][drv[i].dst].push_back({drv[i].last && drv[i].err, drv[i].data});
        if (!(in_vld[i] && !in_rdy[i])) begin          // never change valid data while stalled
          in_vld[i] <= 0;
          if (fq[i].size() > 0 && $urandom_range(0, 99) >= gap_pct[i]) begin
            drv[i] = fq[i].pop_front();
            in_vld[i] <= 1;
            in_data[16*i +: 16] <= drv[i].data;
            in_last[i] <= drv[i].last;
          end
        end
      end
    end

    Comparison, at the outputs: the header of each packet names its input (the testbench put the source in the header's free bits), so every later flit of the packet is matched against that flow's queue.

    design.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
    // ---------------- sinks and scoreboard ----------------
    int drain_pct [4] = '{100, 100, 100, 100};
    int occ [4] = '{0, 0, 0, 0}, got_pkts [4] = '{0, 0, 0, 0}, got_flits [4] = '{0, 0, 0, 0};
    int cur_src [4];
    bit in_pkt [4] = '{0, 0, 0, 0};
    for (genvar o = 0; o < 4; o++) begin : g_snk
      always @(posedge clk) begin
        int n;
        out_crd[o] <= 0;
        n = occ[o];
        if (!rst && out_vld[o]) begin
          logic [15:0] d;
          logic [16:0] e;
          d = out_data[16*o +: 16];
          n++; got_flits[o]++;
          if (!in_pkt[o]) begin cur_src[o] = int'(d[8:7]); in_pkt[o] = 1; end
          if (exp_q[cur_src[o]][o].size() == 0) fail($sformatf("out%0d: unexpected flit %h", o, d));
          else begin
            e = exp_q[cur_src[o]][o].pop_front();
            if (e[15:0] !== d) fail($sformatf("out%0d: flit %h, expected %h from input %0d", o, d, e[15:0], cur_src[o]));
            if (out_err[o] !== (out_last[o] && e[16])) fail($sformatf("out%0d: out_err %0b on flit %h, expected %0b", o, out_err[o], d, out_last[o] && e[16]));
          end
          if (out_last[o]) begin in_pkt[o] = 0; got_pkts[o]++; end
        end
        if (n > 4) fail($sformatf("out%0d: %0d flits in a 4-slot sink (credit overrun)", o, n));
        if (n > 0 && $urandom_range(0, 99) < drain_pct[o]) begin n--; out_crd[o] <= 1; end
        occ[o] = n;
      end
    end

    What its failures look like

    Failure message (from the Part 4 runs)

    What happened

    BUG_24 caught ERROR @785: out0: unexpected flit ff82

    A flit arrived at an output where no flow expected one: misrouted (BUG_24)

    BUG_2 caught ERROR @135: out1: flit 0000, expected 3b10 from input 3

    The right flow, the wrong data: a corrupted or skipped flit (BUG_2)

    BUG_13 caught ERROR @36725: out2: out_err 0 on flit 0084, expected 1

    The right data, the wrong error flag (BUG_13)

    BUG_21 caught ERROR @439965: 92 flits still pending after 40000 cycles (lost or stuck)

    Predictions never matched: flits lost or stuck (BUG_21)

    Each kind of mismatch has its own message, and each names the output, the flit and, where known, the input: enough to start debugging without opening a waveform.

    Design decisions in every scoreboard

    • Where predictions come from: from accepted inputs, never from the design's internals (lesson 3.4).

    • How deep the model is: transaction results, not cycles (lesson 5.5).

    • How outputs are matched: by the specification's ordering domain, here the flow (lesson 5.6).

    • How identity is carried: a source and sequence number in a free field makes matching unambiguous.

    • What happens at the end: leftovers are failures (lesson 5.7).

    How real teams build scoreboards

    Practice

    Purpose

    Monitors separate from drivers

    Predictions come from what the design actually accepted

    Matching keyed by ID, address or flow

    Legal reordering never fails a test

    Reference models shared with architecture teams

    One model of intent, used by everyone

    Detailed mismatch messages

    Debug starts from the log, not the waveform

    What breaks

    A scoreboard (illustrative) predicted outputs from the driver's intended transactions instead of from what the design accepted. When the design back-pressured and the driver dropped a stalled transaction instead of holding it, a testbench bug, the scoreboard still expected it, and every run with heavy back-pressure failed with a lost packet the design never received. Predicting from accepted inputs makes the scoreboard judge the design, not the driver's intentions.

    Why this matters

    Senior lens. Review a scoreboard's structure against the specification before its code: where predictions come from, what the ordering domain is, how identity is carried and what happens at the end. Most scoreboard bugs are structural, false failures from wrong ordering or false passes from missing end checks, not coding errors.

    Interview

    Asked: "How does a scoreboard work?"

    Weak answer: It compares the DUT output with the expected output.

    Strong answer: A monitor on the inputs records what the design accepted; a reference model turns that into predictions; a monitor on the outputs matches each output against a prediction using the specification's ordering domain, by ID or flow when outputs can reorder; and at the end every prediction must have been matched, so lost outputs fail. Predictions come from accepted inputs, never from the design's internals, and each mismatch type gets a clear message. On DCRouter that one structure produced most of the Part 4 catches: misrouted, corrupted, wrongly flagged and lost packets.

    Follow-ups: (1) Why predict from accepted inputs, not driven ones? (2) What goes wrong with a single expected queue?

    Check yourself Why does DCRouter's scoreboard keep one queue per (input, output) flow instead of one per output?

    Order is promised only within a flow; packets from different inputs may interleave at an output in any order, so a single queue per output would report false failures.

    Check yourself How does the scoreboard know which input a packet at an output came from?

    The testbench writes the source input into the header's free bits, and the comparison reads it from each header.

    Key takeaways

    • A scoreboard predicts from accepted inputs, matches every output, and fails on leftovers.

    • Its matching structure must follow the specification's ordering promise.

    • Distinct failure messages for misrouted, corrupted, mis-flagged and lost outputs make debug fast.

    Keep going

    Next: Lesson 5.5, How Precise a Reference Model?. How precise the reference model needs to be.

    Related topics

    • Part 5 overview: Checking

    • Protocols course: Ordering-Aware Scoreboards

    Previous

    54 / 160

    Next: How Precise a Reference Model?

    Up next

    Up nextHow Precise a Reference Model?

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Self-Checking Testbenches
    • Where to Check
    • Ordering and Timing in Checks