Part 11 · Senior Prep · Intermediate
Passive Agent SoC Pattern: Active at Block, Passive at Chip
The canonical reuse pattern — same agent VIP flips from UVM_ACTIVE at block to UVM_PASSIVE at SoC when RTL drives the interface, preserving monitors and coverage.
Active → passive flip
At block level the agent drives pins . At chip level RTL drives the same pins — the agent becomes UVM_PASSIVE to monitor without contending for the bus.
[ARCH][SENIOR][UVM] active/passive flip
BLOCK:
bus_agent [ACTIVE]
drv + sqr + mon
drives bus_if signals
SoC:
bus_agent [PASSIVE]
mon only
RTL/master drives bus_if
agent observes for SB + coveragefunction void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(bus_cfg)::get(this, "", "cfg", cfg))
`uvm_fatal("AGT", "bus_cfg not found")
mon = bus_monitor::type_id::create("mon", this);
if (cfg.is_active == UVM_ACTIVE) begin
sqr = bus_sequencer::type_id::create("sqr", this);
drv = bus_driver::type_id::create("drv", this);
`uvm_info("AGT", "built ACTIVE agent", UVM_LOW)
end else begin
`uvm_info("AGT", "built PASSIVE agent (monitor only)", UVM_LOW)
end
endfunctionKey takeaways
Same agent source serves block and chip — only cfg.is_active changes.
Monitor, coverage, and analysis port work identically in both modes.
Never drive pins at chip level when RTL already owns the interface.
Common pitfalls
Copy-paste agent with monitor-only version — two codebases to maintain.
Forgetting to flip is_active at chip — double-driving the bus.
Passive agent with driver still created due to wrong cfg default.
Passive integration checklist
Validate passive mode as rigorously as active mode — it is the primary chip-level usage.
Passive mode validation
[ARCH][SENIOR][UVM] passive mode checklist
build:
[ ] drv and sqr NOT created when PASSIVE
[ ] mon created and connected to analysis export
[ ] log confirms "built PASSIVE agent"
connect:
[ ] mon.ap → chip_scoreboard (not block SB if disabled)
[ ] no seq_item_port connections (no driver)
runtime:
[ ] monitor sees RTL-driven traffic
[ ] coverage samples on observed txns
[ ] no X/Z on driven signals from TBfunction void connect_phase(uvm_phase phase);
super.connect_phase(phase);
mon.cfg = cfg;
mon.vif = vif;
if (cfg.is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
drv.vif = vif;
end
// mon always connected
mon.ap.connect(parent_sb_export);
endfunctionContention detection
Assert no TB driver assigns when is_active == PASSIVE.
Log warning if monitor sees no traffic within N cycles at chip boot.
Compare passive monitor output against active block run for same stimulus.
Document which interfaces remain active at chip (pin-level agents only).
// safety assert in passive driver stub
class bus_driver extends uvm_driver #(bus_item);
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (cfg.is_active == UVM_PASSIVE)
`uvm_info("DRV", "passive mode — driver idle", UVM_LOW)
endfunction
endclass# compare block active vs chip passive monitor output
simv +UVM_TESTNAME=block_directed_test -l block.log
simv +UVM_TESTNAME=chip_passive_observe_test -l chip.log
diff <(grep "MON::" block.log) <(grep "MON::" chip.log)Common pitfalls
Passive agent still connecting driver to vif through hierarchy bug.
Chip test expecting sequence response from passive agent sequencer.
Monitor clocking block wrong after passive integration — sampled garbage.