Part 8 · Checking & Coverage · Intermediate

Expected vs Actual: The Two-Stream Model

Why scoreboards compare two streams, where expected values come from, and the analysis TLM path from monitor to check.

The checking question

Every verification test ultimately asks one question: did the DUT do the right thing? Before scoreboards, engineers answered that by staring at waveforms — workable for three transactions, hopeless for ten million. A scoreboard automates the answer by comparing two representations of the same logical event: what should have happened (expected) versus what did happen (actual).

The two-stream model exists because stimulus and observation are fundamentally different activities. The driver expresses intent — what the test wants to send. The monitor samples reality — what actually appeared on the interface after protocol adapters, arbiters, and the DUT transformed the transaction. Comparing driver to monitor checks whether your testbench drove correctly; comparing expected to actual checks whether the DUT behaved correctly. Only the latter is self-checking.

Manual checking (waveform eyeballing) fails at scale for three reasons: humans miss subtle field errors, regression runs have no human in the loop, and out-of-order protocols make temporal alignment impossible without structured pairing. Self-checking via scoreboard fixes all three.

diagram
[CHECK] manual vs self-checking

  MANUAL (waveform review)
    engineer watches paddr, pwdata, prdata
    compares against spreadsheet / spec PDF
    fails at: volume, regression, OOO reorder

  SELF-CHECKING (scoreboard)
    monitor  actual stream (automated)
    ref model / golden  expected stream (automated)
    compare()  UVM_ERROR on mismatch (automated)
    scales to millions of transactions per regression

Driver vs monitor — why actual comes from monitor only

The most common scoreboard wiring mistake connects the driver analysis port to the scoreboard. That seems convenient — the driver already has the transaction object — but it validates the wrong thing.

Consider an APB write where the driver sets addr=0x1000 and data=0xDEAD. The DUT has a bug that maps address bit 12 incorrectly, so the slave at 0x0000 receives the write instead. The driver item still shows addr=0x1000. A driver-fed scoreboard would compare expected 0x1000 against driver 0x1000 and pass. The monitor, sampling pins, sees paddr=0x0000 on the bus and reports the truth.

diagram
[UVM] driver vs monitor — different truths

  SEQUENCE ──► DRIVER ──► DUT pins
                  │            │
                  │            ▼
                  │       MONITOR samples pins
                  │            │
                  ▼            ▼
            intent txn     actual txn
            (addr=0x1000)  (addr=0x0000)  ← DUT bug visible here only

  WRONG:  driver.ap ──► scoreboard.act_imp
  RIGHT:  monitor.ap ──► scoreboard.act_imp
  • Driver knows what it tried to send — not what the bus carried after mux/arbiter/DUT.

  • Monitor samples virtual interface signals at protocol-legal sample points.

  • Actual stream = monitor only. No exceptions for 'simple' protocols.


Three sources of expected

The expected stream answers: given what the DUT received, what response does the specification require? There are three common ways to generate that answer, each with different trade-offs.

diagram
[CHECK] three expected sources

  1. REFERENCE MODEL (most common)
     DUT monitor req ──► ref_model.predict(req) ──► exp response
     Pros: spec-accurate, independent of DUT internals
     Cons: model development cost

  2. GOLDEN-PATH MONITOR (known-good DUT or VIP)
     Same stimulus ──► golden DUT ──► golden monitor ──► exp response
     Pros: no model to write; golden is the spec
     Cons: two DUTs to maintain; stimulus sync

  3. SECOND BUS MONITOR (reference interconnect)
     Traffic on ref bus ──► ref monitor ──► exp stream
     Pros: good for bus bridges / protocol converters
     Cons: need matched stimulus on both sides

Reference model path — most flexible

The reference model receives observed requests from the DUT monitor (what the slave actually saw), computes the spec-required response, and publishes it to the scoreboard's exp_imp. The scoreboard never talks to the model directly — TLM connections in connect_phase wire ref_model.ap to scb.exp_imp.

systemverilog
// Typical ref-model path (connect_phase)
dut_agt.mon.ap.connect(ref_model.req_imp);  // observed requests
ref_model.ap.connect(scb.exp_imp);          // predicted responses
dut_agt.mon.ap.connect(scb.act_imp);        // actual responses
  • Model input = monitor-sampled request, not driver item.

  • Model output = expected response transaction with same ID/addr as request.

  • Model is unit-testable outside UVM — pure predict() function.

Golden-path path — when you have a trusted reference

Some blocks ship with a cycle-accurate C model or prior-generation RTL that is known correct. You instantiate it as a second DUT, drive identical stimulus to both, and compare the golden monitor's output against the DUT monitor's output. The golden monitor stream becomes expected.

diagram
[UVM] golden-path topology

  stimulus ──┬──► DUT (device under test) ──► dut_mon ──► act_imp
             │
             └──► GOLDEN (trusted ref)  ──► gold_mon ──► exp_imp
                                                    │
                                                    ▼
                                              [CHECK] compare
  • Both DUTs must see identical stimulus — use broadcast or dual-driver virtual seq.

  • Golden monitor → exp_imp; DUT monitor → act_imp.

  • No reference model code to write — but two implementations to maintain.


APB write walkthrough — end-to-end timeline

Walk through a single APB write to see how expected and actual streams converge in the scoreboard. This is the simplest case — strict ordering, one outstanding transaction — so FIFO matching works (covered in the FIFO lesson).

diagram
[CHECK] APB write — event timeline

  T0  seq starts APB write: addr=0x4000, data=0xBEEF, write=1
  T1  driver drives pins: paddr=0x4000, pwdata=0xBEEF, pwrite=1
  T2  DUT slave accepts write, updates register
  T3  dut_mon samples response: addr=0x4000, data=0xBEEF, kind=RESPONSE, err=0
  T4  ref_model.predict(req) queued expected: addr=0x4000, data=0xBEEF, err=0
      write_exp  exp_q.push_back(exp)
  T5  dut_mon.ap.write(act)  write_act(act)
  T6  scoreboard compare: pop_front(exp_q), act.compare(exp)  MATCH
systemverilog
// Simplified APB transaction for the walkthrough
class apb_txn extends uvm_sequence_item;
  rand bit [31:0] addr;
  rand bit [31:0] data;
  rand bit        write;
  bit             err;          // 0 = OK, 1 = slave error
  apb_kind_e      kind;         // REQUEST or RESPONSE

  `uvm_object_utils_begin(apb_txn)
    `uvm_field_int(addr,  UVM_ALL_ON)
    `uvm_field_int(data,  UVM_ALL_ON)
    `uvm_field_int(write, UVM_ALL_ON)
    `uvm_field_int(err,   UVM_ALL_ON)
    `uvm_field_enum(apb_kind_e, kind, UVM_ALL_ON)
  `uvm_object_utils_end
endclass
  • T3–T4 order may swap — ref model may predict before monitor samples actual.

  • FIFO queue absorbs ordering difference as long as responses stay in-order.

  • kind=RESPONSE filter in write_act prevents comparing requests to responses.


Analysis TLM path — monitor to scoreboard

Transactions flow from monitor to scoreboard through UVM's analysis port / analysis export TLM pattern. The monitor calls ap.write(txn), which invokes the scoreboard's write_act(txn) (or write_exp(txn)) via the connected analysis imp. This is a broadcast fan-out — multiple subscribers can connect to one monitor port.

diagram
[UVM] analysis TLM fan-out from one monitor

  dut_mon.ap (uvm_analysis_port)
       │
       ├──► ref_model.req_imp     (feeds predict)
       ├──► scb.act_imp           (actual stream)
       └──► cov_collector.imp     ([COVER] parallel sampling)

  ref_model.ap
       │
       └──► scb.exp_imp           (expected stream)
  • Expected can arrive before or after actual — matching strategy must handle both.

  • Actual with no expected = unexpected response (DUT bug or wrong monitor filter).

  • Expected with no actual = DUT dropped a response — caught in check_phase drain.

  • Never tap driver.ap for checking — only monitor.ap.

Key takeaways

  • Expected = spec intent (ref model, golden monitor, or ref bus monitor).

  • Actual = DUT-facing monitor sample — always ground truth for checking.

  • Driver = intent, monitor = reality — scoreboard compares the latter pair.

  • Analysis TLM connects monitors to scoreboard imps in connect_phase.

Common pitfalls

  • Checking driver stream — validates stimulus, not DUT behavior.

  • Single-stream scoreboard counting transactions — not self-checking.

  • Feeding ref model from driver items — model sees intent, not DUT input.

  • Comparing requests to responses — filter by txn.kind in write_act/write_exp.