Part 6 · Agents & Protocol IP · Intermediate

Passive Monitor-Only Agents: Observation Without Driving

Designing passive deployments that decode live DUT traffic, publish stable analysis streams, and remain reusable across subsystem and SoC environments.

Passive mode intent

Passive mode exists for interfaces the testbench does not own. Firmware, embedded controllers, or external BFMs may already drive signals; the UVM agent should only observe and publish transactions.

A good passive agent is not a reduced-quality fallback. It is a first-class observability component with robust protocol decoding, timestamping, and coverage hooks.

diagram
[AGT][MON] passive data path

DUT/bus activity
   |
   v
monitor samples interface signals
   |
   +--> reconstruct protocol_item
   |
   +--> analysis_port.write(item)
            |
            +--> scoreboard
            +--> protocol checker
            +--> coverage subscribers
diagram
[VIP] why passive is powerful

no driving side effects
safe to attach to live firmware runs
enables protocol compliance checks
enables coverage collection from real traffic
usable at block, subsystem, and full-chip levels
  • Passive mode still requires a high-quality monitor implementation.

  • Protocol reconstruction quality determines checker/coverage value.

  • Keep publication latency and object cloning discipline explicit.


Monitor implementation details

Passive monitors need deterministic sampling points and robust transaction boundary detection. Use clocking blocks or explicit event sampling to avoid race conditions.

systemverilog
class protocol_monitor extends uvm_monitor;
  `uvm_component_utils(protocol_monitor)
  uvm_analysis_port #(protocol_item) ap;
  protocol_agent_cfg cfg;
  virtual protocol_if vif;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    ap = new("ap", this);
  endfunction

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    vif = cfg.vif;
    if (vif == null)
      `uvm_fatal("VIF", "monitor vif is null")
  endfunction
endclass
systemverilog
task run_phase(uvm_phase phase);
  protocol_item tr;
  forever begin
    @(posedge vif.clk);
    if (!vif.rst_n) continue;

    if (vif.valid && vif.ready) begin
      tr = protocol_item::type_id::create("tr");
      tr.addr = vif.addr;
      tr.data = vif.data;
      tr.kind = vif.write ? PROTO_WRITE : PROTO_READ;
      tr.timestamp = $time;
      ap.write(tr);
    end
  end
endtask
diagram
[MON] quality checklist

sampling:
  consistent edge and reset filtering

reconstruction:
  decode all required fields
  include direction and metadata

publication:
  clone if downstream mutation possible
  emit one write per protocol transfer
  • Passive monitor must never drive interface signals.

  • Always include reset and protocol-invalid filtering.

  • Encode enough metadata for downstream scoreboards and coverage.


Passive deployment at scale

Full-chip environments may host dozens of passive instances. Standardize naming, instance indexing, and analysis routing so integration remains manageable.

diagram
[SOC][VIP] many-passive-agent topology

soc_env
  passive_axi_agent[0] -> noc_sb.in_axi_ch0
  passive_axi_agent[1] -> noc_sb.in_axi_ch1
  passive_axi_agent[2] -> noc_sb.in_axi_ch2
  passive_axi_agent[3] -> noc_sb.in_axi_ch3
  passive_apb_agent    -> csr_sb.in_apb
  passive_stream_agent -> perf_cov.in_stream

pattern:
  one monitor output channel per physical interface
systemverilog
foreach (env.passive_axi_agent[i]) begin
  env.passive_axi_cfg[i].is_active = UVM_PASSIVE;
  env.passive_axi_cfg[i].vif = axi_vif[i];
end

foreach (env.passive_axi_agent[i]) begin
  env.passive_axi_agent[i].mon.ap.connect(env.noc_sb.axi_in[i]);
end
diagram
[AGT] passive scaling practices

1) keep cfg arrays aligned with vif arrays
2) emit instance id in each transaction
3) map each monitor stream to explicit scoreboard input
4) isolate per-instance protocol checker state
5) add per-interface coverage bins for balanced observability
  • Use index-stable naming for easier triage in logs and waveforms.

  • Attach channel or port IDs to published transactions.

  • Avoid multiplexing unrelated monitor streams through one anonymous pipe.


Passive-specific debug

When passive pipelines fail, symptoms usually appear as missing transactions, malformed decodes, or poor alignment with expected firmware traffic. Debug by validating visibility first, decoding second.

diagram
[MON] passive triage order

1) confirm vif activity in waveform
2) confirm monitor sampling edge and reset gate
3) confirm transaction boundary detection logic
4) confirm ap.write invocation count
5) confirm subscriber connect_phase wiring
6) confirm downstream consumer accepts item
diagram
[VIP][AGT] passive failure signatures

symptom                                 likely issue
---------------------------------------------------------------
zero monitor transactions               vif null or wrong interface
half-rate publication                   wrong sampling edge/clock domain
garbled fields                          decode packing mismatch
scoreboard receives nothing             ap not connected
coverage flat despite traffic           missing bins or wrong filter
systemverilog
`uvm_info("MON_TRACE",
  $sformatf("%s tr kind=%0d addr=0x%0h data=0x%0h t=%0t",
            get_full_name(), tr.kind, tr.addr, tr.data, tr.timestamp),
  UVM_HIGH)

Key takeaways

  • Passive agents are production-grade observability tools, not secondary components.

  • Monitor reconstruction quality determines checker and coverage effectiveness.

  • Scalable passive deployment needs disciplined routing and instance metadata.

  • Debug passive pipelines by validating visibility before decode semantics.

Common pitfalls

  • Assuming passive mode means relaxed monitor implementation quality.

  • Dropping transaction metadata needed for per-interface attribution.

  • Combining multiple monitor streams without source tagging.

  • Ignoring sampling edge correctness across clocking block conventions.