Part 8 · Checking & Coverage · Intermediate

Env Wiring: connect_phase Scoreboard Integration

Full env build_phase and connect_phase — DUT agent, golden agent, ref model, and scoreboard TLM connections.

Why connect_phase matters for checking

build_phase creates the component tree — agents, scoreboard, ref model — but components cannot talk to each other yet. connect_phase is where TLM bindings happen: analysis ports on monitors connect to analysis imps on the scoreboard. If you wire this wrong, the scoreboard never receives transactions, or receives them on the wrong imp, and the test passes with zero compares.

The scoreboard is passive — it never initiates communication. It only reacts when write_exp or write_act is called. That means every checking bug that looks like 'scoreboard not working' is ultimately a connect_phase bug: wrong port, wrong imp, or missing connection.

diagram
[UVM] env component tree (build_phase creates)

  block_env
    ├── dut_agt (bus_agent)
    │     ├── sqr
    │     ├── drv
    │     └── mon ── ap ──────────────┐
    ├── ref_agt (optional golden)     │
    │     └── mon ── ap ───┐          │
    ├── ref_model          │          │
    │     ├── req_imp ◄────┼──────────┤ dut_mon.ap (fan-out)
    │     └── ap ──────────┼──┐       │
    └── scb (scoreboard)   │  │       │
          ├── exp_imp ◄────┘  │       │
          └── act_imp ◄───────┴───────┘

Canonical env — golden monitor path

The simplest wiring uses a golden (reference) agent whose monitor output becomes the expected stream. The DUT agent monitor becomes the actual stream. No reference model code required.

systemverilog
class block_env extends uvm_env;
  `uvm_component_utils(block_env)

  bus_agent      dut_agt;
  bus_agent      ref_agt;
  bus_scoreboard scb;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    dut_agt = bus_agent::type_id::create("dut_agt", this);
    ref_agt = bus_agent::type_id::create("ref_agt", this);
    scb     = bus_scoreboard::type_id::create("scb", this);
  endfunction

  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    // [CHECK] wiring rules — monitor only, never driver
    dut_agt.mon.ap.connect(scb.act_imp);   // actual = DUT reality
    ref_agt.mon.ap.connect(scb.exp_imp);   // expected = golden reality
  endfunction
endclass
  • act_imp ◄── DUT agent MONITOR analysis port only.

  • exp_imp ◄── golden agent MONITOR analysis port only.

  • NEVER connect driver seq_item_port or sequencer to scoreboard.

  • Both agents need identical stimulus — virtual sequence or broadcast driver.


Reference model path — predict in the loop

When you have a reference model instead of a golden DUT, the DUT monitor fans out to both the ref model (for predict) and the scoreboard (for actual). The ref model's analysis port feeds expected.

diagram
[UVM] ref model wiring topology

  dut_agt.mon.ap ──┬──► ref_model.req_imp
                   │         │
                   │         ▼ predict(req)
                   │    ref_model.ap ──► scb.exp_imp
                   │
                   └──► scb.act_imp

  [CHECK] req observed at DUT  model predicts  exp queued
          rsp observed at DUT  act compared
systemverilog
function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);

  // Fan-out: one monitor port, multiple subscribers
  dut_agt.mon.ap.connect(ref_model.req_imp);
  dut_agt.mon.ap.connect(scb.act_imp);

  // Model output → expected stream
  ref_model.ap.connect(scb.exp_imp);
endfunction
  • ref_model.req_imp receives observed requests — what DUT slave saw.

  • ref_model.ap publishes predicted responses — spec-expected output.

  • Fan-out is legal — uvm_analysis_port supports multiple connections.

  • Ref model must clone input in write() before async predict completes.

When to choose golden vs ref model

  • Golden path: trusted reference RTL/C model exists, stimulus sync is manageable.

  • Ref model path: no golden, or golden too slow for regression, or block is algorithmic.

  • Hybrid: ref model for data path, golden monitor for control-path sideband signals.


connect_phase rules — the wiring checklist

These rules are non-negotiable. Every scoreboard debug session that starts with 'zero compares' traces back to a violated rule.

diagram
[CHECK] connect_phase wiring rules

  RULE 1: act_imp ◄── DUT monitor ONLY
          Never driver, never sequencer, never predictor output

  RULE 2: exp_imp ◄── golden monitor OR ref_model.ap
          Never DUT monitor (that would compare DUT to itself)

  RULE 3: ref_model.req_imp ◄── DUT monitor (observed requests)
          Never driver (intent ≠ observed input)

  RULE 4: Use mon.ap not agt.ap unless agent re-exports correctly
          Verify with get_full_name() in connect_phase

  RULE 5: Filter request/response in write_act OR split monitor ports
          Comparing REQUEST to RESPONSE always fails
systemverilog
// Debug: log every connection at UVM_MEDIUM during bring-up
function void connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  dut_agt.mon.ap.connect(scb.act_imp);
  ref_model.ap.connect(scb.exp_imp);

  `uvm_info("CONNECT", $sformatf("act: %s → %s",
    dut_agt.mon.ap.get_full_name(), scb.act_imp.get_full_name()), UVM_MEDIUM)
  `uvm_info("CONNECT", $sformatf("exp: %s → %s",
    ref_model.ap.get_full_name(), scb.exp_imp.get_full_name()), UVM_MEDIUM)
endfunction
  • get_full_name() confirms hierarchical path — catches wrong agent instance.

  • Zero compares in report_phase = broken connect or no stimulus reaching monitor.

  • UVM_MEDIUM on connect logs are cheap insurance during env bring-up.


Request/response filter — when monitor emits both

Many monitors emit a transaction for every protocol phase — address phase, data phase, response. If the monitor puts both requests and responses on one analysis port, the scoreboard must filter. Otherwise write_act compares an actual response against an expected request (or vice versa) and produces false mismatches.

systemverilog
// Option A: filter in write_act (simple, one monitor port)
function void write_act(bus_txn t);
  if (t.kind != RESPONSE) return;
  bus_txn snap = bus_txn::type_id::create("snap");
  snap.copy(t);
  compare(snap);
endfunction

function void write_exp(bus_txn t);
  if (t.kind != RESPONSE) return;
  bus_txn snap = bus_txn::type_id::create("snap");
  snap.copy(t);
  exp_q.push_back(snap);
endfunction

Option B — split monitor analysis ports

systemverilog
// Monitor exposes separate ports
class bus_monitor extends uvm_monitor;
  uvm_analysis_port #(bus_txn) req_ap;
  uvm_analysis_port #(bus_txn) rsp_ap;
  // sample request → req_ap.write(req)
  // sample response → rsp_ap.write(rsp)
endclass

// connect_phase — no filter needed in scoreboard
dut_agt.mon.rsp_ap.connect(scb.act_imp);
ref_model.ap.connect(scb.exp_imp);
dut_agt.mon.req_ap.connect(ref_model.req_imp);
  • Split ports are cleaner for complex protocols (AXI separate R/B channels).

  • Filter approach works when monitor refactor is not worth the effort.

  • Apply same filter in write_exp — queue only responses.

Key takeaways

  • connect_phase wires monitor analysis ports to scoreboard imps — scoreboard is passive.

  • DUT monitor → act_imp; golden monitor or ref_model.ap → exp_imp.

  • Ref model path: dut_mon fans out to ref_model.req_imp and scb.act_imp.

  • Filter request/response or split monitor ports — never compare mixed kinds.

Common pitfalls

  • Driver connected to scoreboard — checks intent not DUT pins.

  • DUT monitor to both act_imp and exp_imp — compares DUT to itself.

  • Subscriber on agent.ap when only mon.ap is connected — verify re-export.

  • No filter on mixed req/rsp monitor — false mismatches on every compare.