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. In-Order Matching: One Queue, Four Outcomes

Part 8 · Checking & Coverage · Intermediate

In-Order Matching: One Queue, Four Outcomes

Queue-based pairing for streams that keep their order: the four outcomes of every compare, how deep the queue gets, and deciding which fields the compare covers.

On this page

    Every DMA test in the course passes. The scoreboard compares every output word, and every word matches. Then a designer refactors the output register and, in doing so, stops asserting TLAST on the final word. You rerun the whole suite. It stays green. Not one test fails, because not one check in the bench looks at TLAST on the output. The scoreboard was matching data perfectly while ignoring half the meaning of the stream. In-order matching is the simplest pairing rule there is. What does it actually promise, and what does it leave to you?

    The idea: a queue at a bakery counter

    In-order matching keeps predictions in a first-in, first-out queue and pairs each actual item with the oldest prediction. It is correct exactly when the DUT preserves order: one input stream, one output stream, no overtaking. Think of a bakery counter with a single queue. The baker may be slow or fast, and several customers may be waiting at once, but the first customer to order is the first to be served. You never need ticket numbers.

    The DMA is that kind of design. It takes one word at a time and emits it one register later, so its output order is its input order. The queue's depth, the number of predictions waiting at any moment, is simply the number of words inside the DUT plus any skew between when the input monitor and the output monitor report. Here it never exceeds two.

    The picture: one word through the scoreboard

    Sequence diagram: the input monitor writes two words, each pushed onto the scoreboard's queue; the output monitor writes two words, each compared against the oldest queued prediction.
    Predictions are pushed as input words are observed and popped as output words arrive. Order alone pairs them.

    Build it: four outcomes, one line each

    dma_scoreboard's compare is short, so read it as a list of outcomes rather than as code. Each actual word, and the end of the test, can end in exactly one of these:

    Outcome

    When

    Bench ID

    Where it is raised

    Match

    The oldest prediction equals the actual word (four-state)

    counted in n_match

    write_out

    Mismatch

    The oldest prediction differs

    SB_MISMATCH

    write_out, at once

    Unexpected

    An actual word arrives with nothing predicted

    SB_UNEXP

    write_out, at once

    Leftover

    Predictions still queued when the test ends

    SB_LEFTOVER

    check_phase

    Add the zero-compare check, SB_EMPTY, and every way a stream can go wrong is covered: wrong data, extra data, missing data, and no data at all. Report mismatches and unexpected items at once, inside write_out, so the error carries the simulation time of the bad word and you can jump straight to it in the waves. Only things that can be known only at the end belong in check_phase.

    Use the four-state compare

    The bench compares with !==. With !=, an X on the output bus makes the comparison X, and an if on X takes the else branch, which here is the match branch. A DUT driving X would pass. !== treats X and Z as values, so an X is a mismatch, as it should be.

    Decide which fields the compare covers

    This is the opening story. dma_scoreboard predicts and compares data only. The output also carries TLAST, which the DUT sets on the LEN-th word or when the input packet ends early. Nobody decided that TLAST was unimportant; the compare simply did not include it. The fix is to predict it from the same observed inputs, using the same rule the spec gives:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // snippet: predict TLAST as well as data (a derived scoreboard)
    bit          exp_last[$];
    int unsigned len_reg, taken;
    
    virtual function void write_apb(apb_item t);
      super.write_apb(t);
      if (t.write && !t.slverr && t.addr == LEN)  len_reg = t.data[15:0];
      if (t.write && !t.slverr && t.addr == CTRL && t.data[0]) taken = 0;  // GO: a new job
    endfunction
    
    virtual function void write_in(axis_item t);
      super.write_in(t);
      taken++;
      exp_last.push_back(taken == len_reg || t.last);   // the spec's TLAST rule
    endfunction
    
    virtual function void write_out(axis_item t);
      if (exp_last.size() != 0 && t.last !== exp_last.pop_front())
        `uvm_error("SB_LAST", $sformatf("TLAST wrong on word %08h: got %0b", t.data, t.last))
      super.write_out(t);
    endfunction

    The general rule: list every field of the output transaction and, for each one, either predict it or write down why it is excluded. Excluded fields are a decision; forgotten fields are a hole.

    One queue per independent stream

    In-order matching is per stream. A DUT with two output ports that each keep their own order needs two queues, one per port, and a prediction must go into the queue of the port it will leave on. One shared queue would pair a word from port A with a prediction for port B as soon as the two ports' timing differs. The subsystem bench does the equivalent by giving each DMA its own scoreboard. When the DUT may reorder within one stream, a single queue is wrong altogether: that is the next lesson.

    Tip

    The library's uvm_in_order_comparator does this same pairing for you, fed by a predictor. It compares whole items, and with a class that lacks do_compare it can call every pair a match; see Built-in comparators before you trust one.

    Watch it run

    Diagram
    +UVM_TESTNAME=dma_rand_test
    UVM_INFO dma_tb.sv(731) @ 0: uvm_test_top [JOB] key=90fdd8fd len=10 irq_en=1
    UVM_INFO dma_tb.sv(731) @ 645000: uvm_test_top [JOB] key=0e564b57 len=1 irq_en=1
    UVM_INFO dma_tb.sv(731) @ 885000: uvm_test_top [JOB] key=5a1793f4 len=3 irq_en=1
    UVM_INFO dma_tb.sv(411) @ 1305000: uvm_test_top.env.sb [SB] 14 words matched, 0 mismatched
    UVM_ERROR :    0

    Three jobs, three different keys, 10 + 1 + 3 = 14 words, every one matched in order. The random values differ by simulator and seed; the match count always equals the sum of the job lengths, which is the cross-check to make when you read a log like this.

    What breaks: the stream that was green with TLAST missing

    The opening story is not exotic. A scoreboard written for the first smoke test compares the field that test cared about, and nobody returns to it as the design grows. TLAST, byte strobes, a user sideband, a response code: each is easy to forget because the data still matches. The symptom is the worst kind: no symptom. It is found late, by a downstream block or by software, as a packet boundary in the wrong place.

    Two habits prevent it. First, the field list above: every output field predicted or explicitly excluded, reviewed with the designer. Second, a mutation check: break the DUT on purpose (stop driving TLAST, invert one data bit, swap two words) and confirm that some test fails. A mutation that no test notices marks a hole in checking, the same way an uncovered bin marks a hole in stimulus.

    Interview

    "When is an in-order scoreboard enough, and when is it not?"

    Weak: "In-order is for simple protocols like APB; for complex ones you need out-of-order."

    Strong: "It is correct whenever the DUT preserves order on that stream, regardless of how many items are in flight: the queue depth just equals what is inside the DUT. It is wrong when the DUT may legally reorder, as with AXI responses on different IDs; then I key queues by ID. Either way I keep one queue per independent output stream, report mismatches and unexpected items immediately, check leftovers and zero compares at the end, use four-state compares, and make sure every output field is predicted or deliberately excluded."

    Follow-ups: What does a shifted mismatch pattern tell you? (an item was lost or added, see the debugging lesson) and How deep can the queue get? (as deep as the DUT's buffering plus monitor skew; a depth far beyond that is itself a bug signal).

    Check yourself A DMA variant adds a 16-entry output FIFO. Does the in-order scoreboard need to change?

    No. Buffering changes latency and queue depth (up to about 16 predictions waiting), not order. In-order matching only requires that words leave in the order they entered, which a FIFO preserves. It would be worth adding a sanity bound on the queue depth, so that a scoreboard holding hundreds of predictions reports it.

    Check yourself The output monitor sometimes reports a word with X in the data. The scoreboard uses !=. What does the log show, and why?

    A match. With !=, comparing against X gives X, the if condition is not true, and the code falls into the match branch. The X-driving DUT passes. With !==, X is compared as a value and the word is reported as SB_MISMATCH.

    Check yourself Why are SB_MISMATCH and SB_UNEXP raised inside write_out rather than collected and reported in check_phase?

    So each error carries the simulation time and context of the bad word. An error at 345 ns sends you straight to that point in the waveform; a summary at the end only tells you something went wrong somewhere. check_phase is for facts that are known only once traffic has stopped, such as leftovers and zero compares.

    Next: some DUTs are allowed to answer out of order. Out-of-Order Matching builds the AXI-style scoreboard, and shows why the key is a queue per ID rather than a slot per ID.

    Related topics

    • Out-of-order matching

    • Debugging a scoreboard

    • Built-in comparators

    Previous

    129 / 280

    Next: Out-of-Order Matching: A Queue per ID

    Up next

    Up nextOut-of-Order Matching: A Queue per ID

    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_*
    • Debugging a Scoreboard: Silence, Shapes and Cascades