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.

diagram
[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 + coverage
systemverilog
function 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
endfunction

Key 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

diagram
[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 TB
systemverilog
function 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);
endfunction

Contention 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).

systemverilog
// 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
bash
# 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.