Skip to content

UVM

In-Order Checking

The simplest pairing — when the DUT's outputs arrive in input order, the scoreboard pairs each observed with the next expected from a FIFO queue; what this catches (data errors, drops, extras, reordering), and why the checker's ordering must match the DUT's.

Scoreboards · Module 18 · Page 18.5

The Engineering Problem

Data checking compares a paired observed and expected transaction (Module 18.4) — but which expected pairs with which observed? That is the pairing problem, and this chapter is its simplest case: in-order checking, when the DUT's outputs arrive in the same order as its inputs. If the DUT is in-order — the Nth output corresponds to the Nth input — then the expected stream (computed from the inputs, in order) and the observed stream (outputs, in order) are in the same sequence, and you can pair them positionally: the next observed with the next expected. The implementation is a FIFO queue. But this simplicity carries a sharp requirement: it is correct only if the DUT is actually in-order. Apply in-order checking to an out-of-order DUT — one that legally completes transactions in a different order than they arrived — and it false-mismatches on legal reordering, because it pairs positionally and matches each output against the wrong expected. The problem this chapter solves is in-order pairing: the FIFO pattern, what it catches, and the requirement that the checker's ordering match the DUT's.

In-order checking pairs each observed output with the next expected from a FIFO queue. The predictor pushes expected transactions onto the queue in input order; the scoreboard, on each observed output, pops the front (oldest) expected and compares it (data checking, Module 18.4). First-in-first-out preserves the order, so the front is always the expected for the next output. This catches data errors (the Nth output's data is wrong → mismatch), drops (a missing output → leftover expected in the queue at end of test), extras (an unexpected output → the queue is empty when it arrives), and — for an in-order DUT — reordering (swapped outputs → each is paired with the wrong expected → data mismatches). The crucial requirement: in-order checking is correct for an in-order DUT and wrong for an out-of-order DUT — the checker's ordering model must match the DUT's ordering semantics, or legal reordering becomes a false mismatch. This chapter is in-order checking: the FIFO pattern, what it catches (and at end of test), and the ordering-match requirement.

How does in-order checking pair each observed output with the next expected from a FIFO queue — what does the positional pairing catch (data errors, drops, extras, reordering) — and why must the checker's in-order model match an actually-in-order DUT, or legal reordering becomes a false mismatch?

Motivation — why the simplest pairing, and its requirement

In-order checking is the default pairing — simple and powerful — but it requires the DUT to actually be in-order. The reasons:

  • Many DUTs are in-order. A pipeline, a single-channel processor, a FIFO-based block — output the Nth result for the Nth input, in order. For these, positional pairing is exactly right and trivial to implement.
  • The FIFO pattern is simple and complete. Push expected in order, pop-and-compare per output. The front of the queue is always the next expected — no matching logic needed (unlike out-of-order, Module 18.6). It also gives drop and extra detection for free (queue non-empty at end = drops; queue empty on arrival = extras).
  • Positional pairing catches reordering — as a data mismatch. Because it pairs by position, any reordering makes each output land against the wrong expected → a mismatch. For an in-order DUT, reordering is a bug, and in-order checking catches it (correctly).
  • But it requires the DUT to be in-order. The positional pairing assumes the Nth output is the Nth input's result. If the DUT legally reorders (out-of-order completion — common in multi-channel, pipelined-with-variable-latency, cached designs), that assumption is false, and in-order checking false-mismatches on legal reordering.
  • The checker must match the DUT's ordering semantics. In-order checker for in-order DUT; out-of-order checker for out-of-order DUT. A mismatch between the checker's model and the DUT's reality produces false mismatches (or missed reorder bugs).

The motivation, in one line: in-order checking is the simplest, complete pairing — a FIFO (push expected in order, pop-and-compare per output) that catches data errors, drops, extras, and reordering — but it is correct only for an actually-in-order DUT, because its positional pairing assumes output order equals input order; apply it to an out-of-order DUT and legal reordering becomes a false mismatch, so the checker's ordering model must match the DUT's.

Mental Model

Hold in-order checking as a coat check — tickets and coats both in order, match the next coat to the next ticket:

In-order checking is a coat check: tickets are handed out in order as coats come in, and coats are returned in order, so you match the next returned coat against the next ticket on the stack. A missing coat leaves tickets piling up; an extra coat has no ticket; a shuffled return doesn't match its ticket. This works because the coats are supposed to come back in order — for a valet who returns them out of order, it would be all wrong. Picture a coat check. As each coat arrives (an input), you write a ticket for what should come back (an expected — the predictor computing the result) and stack the tickets in order (the FIFO queue). When a coat is returned (an observed output), you take the next ticket off the stack (the front — the oldest) and check the coat against it (the comparison, Module 18.4). Because coats are returned in the same order they came in, the next ticket is always the right one for the next coat. Three problems are caught naturally. A missing coat (a drop): the coats run out but the tickets pile upleftover tickets at closing time mean coats never returned. An extra coat (a duplicate/extra): a coat comes back with no ticket left on the stack — an output with no expected. A shuffled return (reordering): the valet hands back coat B when the next ticket is for coat A — they don't match. The crucial assumption is that coats come back in order: this coat-check scheme works for a valet who preserves order, but for a valet who deliberately returns coats out of order (a legitimate out-of-order service — fastest-ready-first), the next-ticket-next-coat matching would flag every coat as wrong — even though every coat is fine, just returned in a different order. So the coat-check (positional) scheme is right only when the valet returns coats in order — match the checker to the valet's policy.

So in-order checking is a coat check with order-preserved tickets: stack the tickets (expected) in order, and match each returned coat (output) against the next ticket (front of the queue). It catches missing coats (drops → piled tickets), extra coats (extras → no ticket), and shuffled returns (reordering → wrong coat). But it assumes coats come back in order — so it's right for an in-order valet and wrong for an out-of-order one (where it would flag legitimate reordering as errors). Match the next coat to the next ticket — but only if the coats come back in order.

Visual Explanation — the FIFO queue pattern

The defining picture is the FIFO: the predictor pushes expected in order, and the scoreboard pops the front to compare against each observed output.

The predictor pushes expected onto a FIFO; the scoreboard pops the front per output and comparespush expected (in order)pushexpected (i…observed outputpop front (oldest expected)pop front(oldest…compare (data checking)compare(data…Predictorcomputes expectedFIFO queueE1, E2, E3 ... (input order)Output monitorobserved outputsScoreboardpop front, pair with outputCompare + verdictmatch / mismatch12
Figure 1 — the FIFO queue pattern of in-order checking. The predictor pushes expected transactions onto a FIFO queue in input order (E1, E2, E3, ...). On each observed output, the scoreboard pops the front (oldest) expected — E1 for the first output, E2 for the second — and compares it (data checking). First-in-first-out preserves the order, so the front of the queue is always the expected for the next output. The queue's state also signals drops (leftover expected at end of test) and extras (empty queue when an output arrives).

The figure shows the FIFO queue pattern — the mechanism of in-order checking. The predictor computes the expected and pushes it onto a FIFO queue in input order (E1, E2, E3, ...). The output monitor observes the outputs. On each observed output, the scoreboard pops the front (the oldest expected — E1 for the first output, E2 for the second, ...) and pairs it with the output, then compares (data checking, Module 18.4) → match / mismatch. The crucial reading is why FIFO is exactly right for an in-order DUT: first-in-first-out preserves the order, so the front of the queue is always the expected for the next output — no matching logic needed (the position in the queue is the pairing). The warning-colored FIFO is the heart of the pattern: expected go in (back) in input order and come out (front) in the same order, aligned with the outputs (which, for an in-order DUT, also arrive in input order). The queue's state also gives completeness checking for free: at end of test, leftover expected in the queue = drops (outputs the DUT should have produced but didn't); an empty queue when an output arrives = extras (the DUT produced more than expected). The success-colored scoreboard consumes the FIFO and the output stream in lockstep. The diagram is the anatomy of in-order checking: a FIFO that holds the expected in order, popped front-first to pair with each output positionallysimple, complete (catches data errors, drops, extras), and correct because the FIFO order matches the in-order DUT's output order. The positional pairing is implicit in the FIFO: the Nth pop matches the Nth output, which (for an in-order DUT) is the Nth input's resultexactly the right pairing.

RTL / Simulation Perspective — push expected, pop and compare

In code, the predictor pushes expected onto a queue, and the scoreboard pops the front per output and compares — with empty-queue (extra) and end-of-test (drop) checks. The code shows it.

in-order checking: push expected onto a FIFO, pop the front per output, compare
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class inorder_scoreboard extends uvm_scoreboard;
  bus_txn expected_q[$];            // the FIFO queue of expected transactions (input order)
 
  // EXPECTED from the predictor: push onto the BACK (in input order)
  function void write_expected(bus_txn e);  expected_q.push_back(e);  endfunction
 
  // OBSERVED output from the monitor: pop the FRONT (oldest expected) and compare
  function void write_actual(bus_txn observed);
    if (expected_q.size() == 0) begin
      `uvm_error("SB", "EXTRA output: got an output with NO expected (queue empty)")  // extra/duplicate
      return;
    end
    bus_txn expected = expected_q.pop_front();        // the FRONT — positional pairing (FIFO)
    if (!observed.compare(expected))                  // data checking (Module 18.4)
      `uvm_error("SB", $sformatf("MISMATCH: got data=%0h, expected data=%0h", observed.data, expected.data))
  endfunction
 
  // END OF TEST: the queue should be EMPTY — leftover expected = DROPPED outputs
  function void check_phase(uvm_phase phase);
    if (expected_q.size() != 0)
      `uvm_error("SB", $sformatf("DROPS: %0d expected outputs never arrived", expected_q.size()))
  endfunction
endclass
// ✗ using this for an OUT-OF-ORDER DUT → legal reordering pairs each output with the WRONG expected (DebugLab)

The code shows the FIFO mechanism and its completeness checks. The scoreboard holds a FIFO queue (expected_q). write_expected pushes the predictor's expected onto the back (in input order). write_actual, on each observed output, first checks for an extra (size() == 0 → an output with no expecteduvm_error), then pops the front (pop_front() — the oldest expected, the positional pairing) and compares (observed.compare(expected), data checking) — reporting a precise mismatch on failure. check_phase (at end of test) checks for drops: a non-empty queue means leftover expected — outputs the DUT should have produced but didn't. The closing comment marks the requirement: using this for an out-of-order DUT false-mismatches on legal reordering (the DebugLab). The shape to carry: in-order checking is push-back / pop-front — the predictor pushes expected in order, the scoreboard pops the front per output and compares, with an empty-queue check (extra) and an end-of-test check (drops). The FIFO gives the positional pairing (front = next expected) and the completeness checks (empty-on-arrival = extra; non-empty-at-end = drop) for free. The one assumption baked in is output order = input order — the pop_front assumes the next output is the next expected — which is correct for an in-order DUT and false for an out-of-order one.

Verification Perspective — what in-order checking catches

In-order checking's positional pairing catches four classes of error — data, drops, extras, reordering — each from a recognizable signature. Seeing all four shows the pattern's power (and its one assumption).

In-order checking catches data errors, drops, extras, and reordering via positional FIFO pairingwrong data →mismatchexpected neverpoppedoutput, emptyqueuepaired with wrong expectedpaired withwrong…Data errorNth output wrongField mismatchcaught (18.4)Dropmissing outputLeftover at endqueue non-emptyExtraunexpected outputEmpty on arrivalno expectedReorderingswapped outputsData mismatcheswrong-expected pairing12
Figure 2 — what in-order checking catches. Data error: the Nth output's data is wrong, so it mismatches its expected. Drop: an output is missing, so an expected is never popped — leftover in the queue at end of test. Extra: an unexpected output arrives, so the queue is empty when it does. Reordering: outputs arrive swapped, so each is paired positionally with the wrong expected — data mismatches. All four are caught because the FIFO pairs positionally — which is correct as long as the DUT is in-order (so the positional pairing is the right pairing).

The figure shows the four classes of error in-order checking catches, each from its signature. Data error: the Nth output's data is wrong, so it mismatches its (correctly-paired) expected → a field mismatch (caught by data checking, Module 18.4). Drop: an output is missing, so an expected is never poppedleftover in the queue at end of test (non-empty queue). Extra: an unexpected output arrives, so the queue is empty when it does (output with no expected). Reordering: outputs arrive swapped, so each is paired positionally with the wrong expected → data mismatches (the warning-colored signature). The verification insight is that all four are caught because the FIFO pairs positionally — the position in the stream is the pairing, so any deviation (wrong data, missing, extra, swapped) shows up. Three of the four (data, drop, extra) are unambiguously caught. The fourthreordering — is caught as data mismatches (the scoreboard can't tell it's reordering; it just sees each output land against the wrong expected, producing wrong-data reports). This is fine for an in-order DUT, where reordering is a bug — in-order checking catches it (even if it reports it as data mismatches rather than "reordering"). But it's exactly the limitation: the positional pairing is correct only if the DUT is in-order (so the Nth output is the Nth input's result). For an out-of-order DUT, reordering is legal, and in-order checking would flag the legal reordering as data mismatches — a false failure (the DebugLab). The figure is in-order checking's coverage: data errors, drops, extras, and reorderingall caught by positional pairing — provided the positional pairing is valid, which requires the DUT to be in-order. Catch everything positionally — as long as position is the right pairing.

Runtime / Execution Flow — pairing per output, plus the end check

At run time, in-order checking runs a per-output loop (pop front, compare) plus an end-of-test completeness check. The flow shows both.

In-order checking: per output pop and compare or flag extra; at end, flag leftover dropsper output: queue empty? extra : pop front + compare (pass / mismatch) — end of test: queue non-empty? dropsper output: queue empty? extra : pop front + compare (pass / mismatch) — end of test: queue non-empty? drops1Output arrives: queue empty?if the queue is empty, the output is an extra — no expected for it;report it.2Pop the front (the next expected)otherwise, take the oldest expected — the positional pair for thisoutput.3Compare (pass or precise mismatch)data-check the pair: match → pass; differ → report which field, gotvs expected.4End of test: queue non-empty? dropsleftover expected are outputs that never arrived — report the countof drops.
Figure 3 — in-order checking per output, plus the end-of-test check. On each observed output: if the queue is empty, it's an extra (output with no expected); otherwise, pop the front (the next expected) and compare it (data checking) — pass or report a precise mismatch. At end of test: if the queue is non-empty, the leftover expected are drops (outputs that never arrived). The per-output loop catches data errors, extras, and reordering (as mismatches); the end check catches drops. Together they give complete in-order checking.

The flow shows in-order checking's two parts: the per-output loop and the end-of-test check. Output arrives, queue empty? (step 1): if the queue is empty, the output is an extrano expected for it; report it. Pop the front (step 2): otherwise, take the oldest expected — the positional pair for this output. Compare (step 3): data-check the pair (Module 18.4) — match → pass; differ → report which field, got vs expected. End of test (step 4): if the queue is non-empty, the leftover expected are outputs that never arrivedreport the count of drops. The runtime insight is that the FIFO gives complete in-order checking from two simple operations: pop-and-compare per output (catching data errors, extras, and reordering-as-mismatch) and an end-of-test empty-queue check (catching drops). The per-output loop handles the bulkevery output is paired (front of queue) and checked, with the empty-queue case flagging extras. The end check handles completenessevery expected must be consumed; leftovers are drops. Together, they cover all four error classes (Figure 2). This is why in-order checking is simple yet complete: the FIFO's natural behavior (push-back, pop-front, size) encodes the pairing (front = next expected), the extra detection (empty on pop), and the drop detection (non-empty at end) — no extra bookkeeping. The flow is the complete in-order check: for each output, pop-and-compare (or flag extra); at the end, flag leftover drops — a minimal, complete check that works because the FIFO order matches the in-order DUT's output order.

Waveform Perspective — the FIFO pairing in action

In-order checking is visible on a timeline: expected fill the queue in order, outputs arrive in order, and each output pops-and-matches the front. The waveform shows the FIFO pairing, including a drop.

In-order FIFO pairing: outputs pop and match the front of the expected queue; a drop leaves a leftover

12 cycles
In-order FIFO pairing: outputs pop and match the front of the expected queue; a drop leaves a leftoverpush E1, E2, E3 → q_depth rises to 3 (expected, in input order)push E1, E2, E3 → q_de…output 1 pops front (E1) → match; output 2 pops E2 → match (q_depth → 1)output 1 pops front (E…output 3 DROPPED: E3 never popped → q_depth stuck at 1 → drop flagged at endoutput 3 DROPPED: E3 n…clkpush_expq_depth112232211111out_validsb_matcht0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — the FIFO pairing. Expected are pushed in order: the queue depth (q_depth) rises as E1, E2, E3 are pushed. Outputs arrive in order: out_valid pulses, and each output pops the front and matches it (sb_match). Output 1 matches E1, output 2 matches E2. But output 3 is DROPPED (it never arrives): so E3 is never popped, and q_depth stays at 1 at end of test — the leftover expected is the drop, flagged by the end-of-test check. The FIFO order matches the in-order DUT's output order, so each pop is the right expected; the leftover at end reveals the dropped output.

The waveform shows the FIFO pairing, including a drop. Expected are pushed in order: the queue depth (q_depth) rises as E1, E2, E3 are pushed (push_exp pulses, depth → 3). Outputs arrive in order: out_valid pulses, and each output pops the front and matches it (sb_match) — output 1 matches E1, output 2 matches E2 (depth → 1). But output 3 is dropped (it never arrives): so E3 is never popped, and q_depth stays at 1 at end of test — the leftover expected is the drop, flagged by the end-of-test check. The crucial reading is the two behaviors visible: (1) the FIFO pairing — outputs pop the front in order (E1, then E2), and because the DUT is in-order, each pop is the right expected (output 1 ↔ E1, output 2 ↔ E2); and (2) the drop detection — the missing output 3 leaves E3 unpopped, so q_depth doesn't return to 0 — the leftover at end reveals the dropped output. The picture to carry is the queue depth as the checking state: it rises with pushes (expected), falls with pops (matched outputs), and a non-zero depth at end = drops (expected that never got an output). Reading this waveform — do outputs pop and match the front in order? does the queue return to empty (no drops) or stay non-empty (drops)? — confirms the in-order check. The FIFO depth rising with expected, falling with matched outputs, and a leftover signaling a drop is the signature of in-order checking: positional pairing (front-first) plus completeness (empty-at-end) — simple, complete, and correct because the FIFO order matches the in-order DUT's output order.

DebugLab — in-order checking on an out-of-order DUT floods false mismatches

A flood of mismatches because in-order checking was used on a DUT that legally reorders

Symptom

A scoreboard flagged mismatches on most transactions — but the data was all there. The expected values (A, B, C, D) all appeared in the observed output stream — just in a different order (B, A, D, C). The precise mismatch reports said things like "got data=B, expected data=A" — and the very next one said "got data=A, expected data=B" — the values were swapped. The DUT was functionally correct (it produced every expected result, with correct data) — but it completed them out of order, and the in-order scoreboard paired each output positionally with the wrong expected.

Root cause

The DUT was out-of-order (it legally completed transactions in a different order than they arrived — fastest-ready-first), but the scoreboard used in-order (FIFO) checking, which pairs positionally — so each output was matched against the wrong expected:

why in-order checking false-mismatches on legal reordering
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
✗ IN-ORDER (FIFO) checking on an OUT-OF-ORDER DUT → positional pairing is wrong:
  // inputs:  1→A, 2→B, 3→C, 4→D   (expected queue, in order: A, B, C, D)
  // DUT legally completes out of order: outputs arrive B, A, D, C
  // FIFO pairs positionally: output B vs front A → MISMATCH; output A vs front B → MISMATCH; ...
  // every output paired with the WRONG expected → flood of false mismatches (DUT is CORRECT)
  // the SET of outputs matches the SET of expected — only the ORDER differs (legal for this DUT)
 
✓ OUT-OF-ORDER checking: pair by an ID/key, not by position (Module 18.6):
  // each transaction carries an ID; match the output to the expected with the SAME ID
  // output B (id=2) matched to expected B (id=2) → match; output A (id=1) to expected A → match
  // pairing by key, not position → legal reordering does NOT cause mismatches

This is the checker-ordering-mismatch bug — applying in-order checking to an out-of-order DUT, so legal reordering becomes a flood of false mismatches. The DUT is out-of-order: it legally completes transactions in a different order than they arrived (e.g., fastest-ready-first, common in multi-channel, cached, or variable-latency designs). The inputs 1→A, 2→B, 3→C, 4→D give an expected queue A, B, C, D (in input order). The DUT correctly produces all four results — but out of order: B, A, D, C. The in-order scoreboard pops the front positionally: it pairs output B with front expected Amismatch; output A with front Bmismatch; and so on — every output paired with the wrong expected → a flood of false mismatches, even though the DUT is correct. The tell is distinctive: the set of observed values matches the set of expected valuesevery expected (A, B, C, D) appears in the output stream — only the order differs; and the mismatch reports show swapped values ("got B, expected A" then "got A, expected B"). This is very different from a real data bug (where a value never appears, or is corrupted). The root issue is that the checker's ordering model (in-order) doesn't match the DUT's ordering semantics (out-of-order) — the DUT reorders legally, but the in-order checker assumes order. The fix is to use out-of-order checking (Module 18.6): pair each output to the expected with the same ID/key, not by position — so legal reordering does not cause mismatches (output B with id=2 matches expected B with id=2, regardless of order). The general lesson, and the chapter's thesis: in-order checking requires the DUT to be in-order — its positional (FIFO) pairing assumes the Nth output is the Nth input's result; applying it to an out-of-order DUT (where reordering is legal) makes the legal reordering a flood of false mismatches, because each output is paired with the wrong expected. The checker's ordering model must match the DUT's ordering semanticsin-order checker for an in-order DUT, out-of-order checker for an out-of-order DUT. When the observed set matches the expected set but the order differs, the DUT is reordering — and you need a checker that pairs by key, not position.

Diagnosis

The tell is swapped-value mismatches where the observed set matches the expected set. Diagnose ordering mismatches:

  1. Check whether the observed values are all present, just reordered. If every expected value appears in the output stream but in a different order, the DUT is reordering, not producing wrong data.
  2. Look for swapped-value mismatch reports. "Got B, expected A" followed by "got A, expected B" is the signature of positional pairing against a reordered stream.
  3. Confirm the DUT's ordering semantics. If the spec allows out-of-order completion (multi-channel, cached, variable-latency), in-order checking is the wrong model.
  4. Distinguish from a real data bug. A real data bug produces a value that never appears or is corrupted, not a mere reordering of the expected set.
Prevention

Match the checker's ordering model to the DUT's:

  1. Use in-order checking only for in-order DUTs. Confirm the DUT produces outputs in input order before relying on positional FIFO pairing.
  2. Use out-of-order checking for reordering DUTs. Pair by an ID or key (Module 18.6) when the DUT may legally complete out of order.
  3. Derive the ordering model from the spec. Whether the DUT may reorder is a spec property; choose the checker to match.
  4. Recognize legal reordering vs a bug. A reordered-but-complete output set is legal for an out-of-order DUT; only treat it as a bug for an in-order DUT.

The one-sentence lesson: in-order checking requires the DUT to be in-order, because its positional FIFO pairing assumes the Nth output is the Nth input's result — applying it to an out-of-order DUT (where reordering is legal) floods false mismatches as each output is paired with the wrong expected, so match the checker's ordering model to the DUT's: in-order for in-order, out-of-order (pair by key) for reordering DUTs.

Common Mistakes

  • Using in-order checking on an out-of-order DUT. Positional FIFO pairing false-mismatches on legal reordering; use out-of-order checking (pair by key) for reordering DUTs.
  • Forgetting the end-of-test drop check. A non-empty queue at end means dropped outputs; check it in check_phase, or drops go unnoticed.
  • Forgetting the empty-queue extra check. An output arriving with an empty queue is an extra; flag it, or extras go unnoticed.
  • Mistaking reordering for a data bug. Swapped-value mismatches where the observed set matches the expected set are reordering, not corrupted data.
  • Not deriving the ordering model from the spec. Whether the DUT may reorder is a spec property; choosing the wrong checker model causes false mismatches or missed reorder bugs.
  • Pushing expected out of order. The predictor must push expected in input order, or the FIFO pairing is wrong even for an in-order DUT.

Senior Design Review Notes

Interview Insights

In-order checking is the pairing strategy for a DUT whose outputs arrive in the same order as its inputs — the Nth output corresponds to the Nth input. Because the expected stream, computed from the inputs in order, and the observed output stream are in the same sequence, you pair them positionally: the next observed with the next expected. The FIFO pattern implements this. The predictor pushes expected transactions onto a queue in input order, pushing to the back. The scoreboard, on each observed output, pops the front of the queue, which is the oldest expected, and compares it against the output using data checking. First-in-first-out preserves the order, so the front of the queue is always the expected for the next output — the position in the queue is the pairing, with no matching logic needed. The FIFO also gives completeness checks for free. If an output arrives when the queue is empty, that's an extra — an output with no expected. At end of test, if the queue is non-empty, the leftover expected are drops — outputs the DUT should have produced but didn't. So in-order checking catches four classes of error: data errors, where the output's data is wrong and mismatches its expected; drops, where an expected is never popped and remains at end; extras, where the queue is empty when an output arrives; and reordering, where swapped outputs are paired positionally with the wrong expected, producing data mismatches. The mental model is a coat check: tickets are stacked in order as coats come in, and each returned coat is matched against the next ticket. A missing coat piles up tickets, an extra coat has no ticket, and a shuffled return doesn't match. So in-order checking is push-back, pop-front, compare, with the queue depth signaling drops and extras — simple and complete, as long as the DUT is in-order.

Because its positional pairing assumes the Nth output is the Nth input's result, and if the DUT reorders, that assumption is false and you pair each output with the wrong expected. The FIFO pattern pops the front of the expected queue for each output, pairing by position: the first output with the first expected, the second with the second, and so on. This is correct only if the DUT produces outputs in input order, so that the position of an output in the stream corresponds to the position of its input. For an in-order DUT — a pipeline, a single-channel block — this holds, and the positional pairing is exactly right. But many DUTs are out-of-order: they legally complete transactions in a different order than they arrived, like fastest-ready-first in a multi-channel, cached, or variable-latency design. For such a DUT, reordering is legal and correct. If you apply in-order checking, the FIFO pairs each output positionally with the wrong expected. Say inputs give expected A, B, C, D in order, but the DUT correctly produces B, A, D, C. The FIFO pairs output B with front A — mismatch; output A with B — mismatch; and so on. Every output is paired with the wrong expected, producing a flood of false mismatches even though the DUT is correct. The tell is that the set of observed values matches the set of expected values — every expected appears — only the order differs, and mismatch reports show swapped values. So the checker's ordering model must match the DUT's ordering semantics. In-order checking is correct for an in-order DUT and wrong for an out-of-order one. For a reordering DUT, you need out-of-order checking, which pairs each output to the expected with the same ID or key rather than by position, so legal reordering doesn't cause mismatches. Whether the DUT may reorder is a spec property, and you choose the checker to match. So in-order checking requires an in-order DUT because positional pairing is only valid when output order equals input order.

Through the state of the FIFO queue: a non-empty queue at end of test means drops, and an empty queue when an output arrives means an extra. Normally, every expected pushed onto the queue is eventually popped and matched by a corresponding output, so the queue returns to empty by the end. A drop is a missing output — the DUT should have produced an output but didn't. When that happens, the expected for that output is pushed onto the queue but never popped, because no output arrives to pop it. So at end of test, that expected, and any after it that were also dropped, remain in the queue. The end-of-test check, typically in check_phase, examines the queue: if it's non-empty, the leftover expected are drops, and you report the count — that many expected outputs never arrived. An extra is an unexpected output — the DUT produced an output it shouldn't have, or a duplicate. When an output arrives, the scoreboard tries to pop the front of the queue to pair with it. If the queue is empty, there's no expected for this output, which means it's an extra, and you report it immediately. So the two checks are complementary: the per-output empty-queue check catches extras as they arrive, and the end-of-test non-empty-queue check catches drops after all outputs have been processed. Both come naturally from the FIFO, requiring no extra bookkeeping beyond checking the queue size. This is part of what makes in-order checking simple yet complete: the same queue that provides the positional pairing also provides drop and extra detection through its depth. A common mistake is to forget one of these checks — without the end-of-test check, drops go unnoticed because the test just ends with expected still queued; without the empty-queue check, an extra output might cause a pop on an empty queue or be silently ignored. So you implement both: check for empty on each output for extras, and check for non-empty at end for drops.

You check whether the observed values are all present but in a different order, which indicates reordering, versus a value that never appears or is corrupted, which indicates a real data bug. With reordering, the DUT produces every expected result with correct data, just in a different order than expected. So if you look at the full set of observed output values and compare it to the set of expected values, they match — every expected value appears somewhere in the output stream. The mismatches you see are an artifact of positional pairing against a reordered stream: the scoreboard pairs output B with expected A and reports got B expected A, then pairs output A with expected B and reports got A expected B. So the signature is swapped-value mismatch reports — pairs of mismatches where the values are exchanged — and a complete observed set that just isn't in expected order. A real data bug is different. There, the DUT produces a wrong value: a value that should be A comes out as some corrupted value that isn't in the expected set at all, or a field is wrong. So the observed set does not match the expected set — there's a value present that shouldn't be, or one missing that should be, beyond mere reordering. The mismatches aren't neat swaps; they're genuine wrong values. So to distinguish them, you look at the aggregate: is every expected value accounted for in the output, just reordered, or is there a value that's truly wrong or missing? This is why precise mismatch reporting helps — seeing the swapped-value pattern points to reordering. And once you suspect reordering, you confirm the DUT's ordering semantics from the spec: if out-of-order completion is legal, the reordering is correct and you need out-of-order checking; if the DUT is supposed to be in-order, the reordering is a real bug that in-order checking correctly caught. So the distinction is reordered-but-complete set means reordering, while a wrong-or-missing value means a data bug.

You use in-order checking when the DUT produces outputs in the same order as its inputs, and out-of-order checking when the DUT may legally complete transactions in a different order than they arrived. The deciding factor is the DUT's ordering semantics, which is a spec property. An in-order DUT — a simple pipeline, a single-channel processor, a FIFO-based block — guarantees that the Nth output corresponds to the Nth input. For these, in-order checking with its FIFO positional pairing is correct, simple, and complete: push expected in order, pop the front per output, compare. An out-of-order DUT — a multi-channel block, a design with caching or variable latency, anything that completes fastest-ready-first or by some non-arrival order — may legally produce outputs in a different order than inputs. For these, positional pairing is wrong, because the Nth output isn't necessarily the Nth input's result. Applying in-order checking would flood false mismatches on legal reordering. So you use out-of-order checking, which pairs each output to its expected by a matching key, like a transaction ID, rather than by position. That way, an output is matched to the expected with the same ID regardless of order, and legal reordering doesn't cause mismatches. The choice should come from the spec: does the DUT guarantee in-order outputs, or may it reorder? If in-order, use in-order checking for its simplicity. If it may reorder, use out-of-order checking. Getting this wrong in either direction is a problem: in-order checking on a reordering DUT gives false mismatches, and out-of-order checking on an in-order DUT, while it would work, gives up the in-order check's ability to catch illegal reordering as an error. So match the checker to the DUT's ordering semantics, derived from the spec. In-order is the default and simplest when applicable, and out-of-order is necessary when the DUT legally reorders.

Exercises

  1. Trace the FIFO. For inputs producing expected A, B, C and in-order outputs A, B, C, trace the queue depth and the pairing.
  2. Detect the drop. Show what the queue looks like at end of test if the second output is dropped, and how it's reported.
  3. Spot the ordering mismatch. Given swapped-value mismatches with a complete observed set, name the cause and the fix.
  4. Choose the checker. For a single-channel pipeline and a multi-channel cached block, choose in-order or out-of-order checking and justify.

Summary

  • In-order checking pairs each observed output with the next expected from a FIFO queue: the predictor pushes expected in input order, and the scoreboard pops the front (oldest) per output and compares it (Module 18.4) — first-in-first-out makes the front always the next expected.
  • The positional pairing catches data errors (wrong data → mismatch), drops (leftover expected in the queue at end of test), extras (empty queue when an output arrives), and reordering (swapped outputs → paired with the wrong expected → data mismatches).
  • The FIFO gives completeness for free: a non-empty queue at end of test = drops; an empty queue on arrival = extras — no extra bookkeeping.
  • It is correct only for an actually-in-order DUT: the positional pairing assumes the Nth output is the Nth input's result — applying it to an out-of-order DUT (legal reordering) floods false mismatches, so the checker's ordering model must match the DUT's ordering semantics.
  • The durable rule of thumb: use in-order (FIFO) checking for an in-order DUT — push expected in input order, pop-and-compare per output, and check the queue (empty-on-arrival = extra, non-empty-at-end = drops) — but only when the DUT actually produces outputs in input order; on an out-of-order DUT, positional pairing flips legal reordering into a flood of false mismatches (observed set matches expected set, only the order differs), so use out-of-order checking that pairs by key.

Next — Out-of-Order Checking: the harder pairing — out-of-order checking: when the DUT may legally complete transactions in a different order than they arrived, the scoreboard pairs each observed output with its expected by a matching key (an ID), the associative-array pattern that implements it, and the cases (missing keys, duplicate keys) it must handle.