Part 4 · TLM & Analysis · Intermediate
Monitor Analysis Wiring: monitor.ap -> Scoreboard/Coverage
Canonical connect_phase wiring from monitor analysis_port to scoreboard and coverage subscribers, with lifecycle checks and debug strategy.
Canonical observation topology
The standard topology is monitor as producer, with scoreboards and coverage as passive consumers. The monitor should not know checker internals; it only publishes observed transactions.
[MON][TLM][CHECK] canonical topology
[MON] bus monitor
└─ ap.write(tr)
├─► [CHECK] scoreboard expected/actual logic
├─► [CHECK] coverage bins
└─► [CHECK] optional predictor/model
monitor stays passive and protocol-focused
checkers stay consumer-focusedclass my_agent extends uvm_agent;
`uvm_component_utils(my_agent)
my_monitor mon;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
mon = my_monitor::type_id::create("mon", this);
endfunction
endclass[CHECK] separation of concerns
monitor:
decode bus activity into transactions
scoreboard:
compare expected vs observed behavior
coverage:
collect scenario statistics and crossesKeep monitor logic protocol-centric and checker-agnostic.
Use analysis fan-out to avoid bespoke branching in monitor code.
Treat scoreboard and coverage as independent consumers.
connect_phase wiring patterns
Perform analysis connections in connect_phase after all components are built. This keeps topology setup explicit and debuggable.
class my_env extends uvm_env;
`uvm_component_utils(my_env)
my_agent agt;
my_scoreboard sb;
my_cov_sub cov;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agt = my_agent::type_id::create("agt", this);
sb = my_scoreboard::type_id::create("sb", this);
cov = my_cov_sub::type_id::create("cov", this);
endfunction
function void connect_phase(uvm_phase phase);
agt.mon.ap.connect(sb.actual_in);
agt.mon.ap.connect(cov.analysis_export);
endfunction
endclass[TLM] phase placement checklist
build_phase:
create monitor/checkers/subscribers
connect_phase:
connect monitor.ap -> consumer endpoints
run_phase:
monitor writes observations
consumers ingest/check/sample[MON][CHECK] topology validation
before run:
assert handles not null
print connection map at UVM_LOW
during run:
confirm each consumer sees first N transactionsConnections belong in connect_phase for predictable lifecycle behavior.
Avoid hidden runtime dynamic connects unless thoroughly justified.
Add early sanity logs so missing links fail fast.
Transaction safety across multiple consumers
When one monitor stream feeds many consumers, decide and document whether transactions are immutable or clone-on-consume. This prevents subtle cross-consumer interference.
[MON][CHECK] transaction safety policy
Policy A: immutable monitor txn objects
+ low overhead
- requires strict team discipline
Policy B: clone in each subscriber
+ local mutation safe
- more memory/time overhead
Policy C: clone once in monitor, pass snapshots
+ central control
- monitor becomes heavierfunction void write(my_txn t);
my_txn local;
local = t.clone();
// Subscriber-specific normalization
local.addr = normalize_addr(local.addr);
q.push_back(local);
endfunction[CHECK] debugging symptom map
symptom: coverage bins shifted unexpectedly
possible root: scoreboard mutating shared transaction
symptom: random compare mismatches
possible root: one consumer edits transaction before others use itPick one transaction ownership policy and enforce it consistently.
Clone before local mutation in mixed-consumer topologies.
Cross-consumer side effects are common if policy is undefined.
Walkthrough: practical monitor wiring debug
If scoreboard is silent, debug in layers: monitor production, connection correctness, and consumer ingestion. This is usually faster than deep waveform-first debugging.
task smoke_analysis_path(my_env env);
// monitor emits at least one transaction
wait (env.agt.mon.sample_count > 0);
// consumer confirms ingestion
wait (env.sb.actual_seen > 0);
wait (env.cov.sample_count > 0);
`uvm_info("AN_PATH_OK", "analysis wiring appears active", UVM_LOW)
endtask[MON][TLM][CHECK] triage order
1) monitor sample_count increasing?
2) connect_phase has expected connect calls?
3) consumer write() counters increasing?
4) compare logic behaving as intended?
stop at first failing boundaryKey takeaways
Monitor-to-scoreboard/coverage wiring is the central analysis topology in UVM.
Perform connects in connect_phase and verify path activity early in run.
Define transaction ownership/mutation policy before scaling subscribers.
Layered triage quickly isolates missing-wire vs bad-checker issues.
Common pitfalls
Connecting in ad-hoc places that depend on fragile build ordering.
Assuming monitor activity implies consumer ingestion without counters/logs.
Skipping transaction safety policy and debugging random downstream behavior.
Overcoupling monitor with checker internals through direct method calls.