Part 6 · Agents & Protocol IP · Intermediate

is_active Knob: Ownership, Defaults, and Override Precedence

How to model, set, and validate uvm_active_passive_enum configuration so agents reliably instantiate in the intended mode.

Why the knob matters

The is_active setting is the top-level policy switch that controls whether an agent drives pins. A wrong value changes testbench behavior drastically: traffic may disappear entirely or accidental dual-driving may occur.

Treat mode selection as part of interface ownership architecture. The environment should define sane defaults while tests can override intentionally for scenario-specific needs.

diagram
[UVM][AGT] configuration ownership chain

project policy default
   -> env-level cfg
      -> optional test override
         -> agent build decision

recommended precedence:
  test override > env override > agent default
diagram
[AGT] mode contract

UVM_ACTIVE:
  create sequencer + driver + monitor
  allow sequences to generate traffic

UVM_PASSIVE:
  create monitor only
  prohibit driver ownership of vif
  • Set explicit default mode in cfg object constructors.

  • Apply overrides before agent build_phase executes.

  • Log final resolved mode per agent instance at UVM_LOW/UVM_MEDIUM.


Canonical cfg object pattern

Keep mode inside the agent configuration object so all behavioral knobs travel together. This avoids split-brain configuration between raw config_db keys and object fields.

systemverilog
class protocol_agent_cfg extends uvm_object;
  `uvm_object_utils(protocol_agent_cfg)

  // Core mode switch
  uvm_active_passive_enum is_active = UVM_ACTIVE;

  // Typical adjacent knobs
  bit has_coverage = 1;
  bit checks_enable = 1;
  int unsigned monitor_verbosity = UVM_LOW;

  // Interface handle (often set by env)
  virtual protocol_if vif;

  function new(string name = "protocol_agent_cfg");
    super.new(name);
  endfunction
endclass
systemverilog
class protocol_agent extends uvm_agent;
  `uvm_component_utils(protocol_agent)

  protocol_agent_cfg cfg;
  protocol_driver    drv;
  protocol_sequencer sqr;
  protocol_monitor   mon;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    if (!uvm_config_db#(protocol_agent_cfg)::get(this, "", "cfg", cfg))
      `uvm_fatal("CFG", "protocol_agent_cfg not found")

    mon = protocol_monitor::type_id::create("mon", this);

    if (cfg.is_active == UVM_ACTIVE) begin
      sqr = protocol_sequencer::type_id::create("sqr", this);
      drv = protocol_driver::type_id::create("drv", this);
    end

    `uvm_info("AGT_MODE",
      $sformatf("%s mode=%s", get_full_name(), cfg.is_active.name()),
      UVM_LOW)
  endfunction
endclass
diagram
[VIP][AGT] cfg placement pattern

env build:
  create cfg per agent instance
  set vif handles and defaults
  set cfg in config_db under agent path

agent build:
  get cfg
  instantiate components by cfg.is_active
  publish final mode log
  • Single cfg object keeps mode and adjacent knobs consistent.

  • Agent should fatal when cfg missing; silent defaults hide integration mistakes.

  • Mode decisions should happen once in build_phase, not dynamically later.


Override strategies and guardrails

Large environments need controlled override points. Use pattern-based config_db only where necessary and prefer object field updates in test build hooks for readability and traceability.

systemverilog
class soc_base_test extends uvm_test;
  `uvm_component_utils(soc_base_test)
  soc_env env;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = soc_env::type_id::create("env", this);

    // Example: keep CPU control bus active
    env.cpu_apb_cfg.is_active = UVM_ACTIVE;

    // Example: force fabric probes passive
    env.fabric_axi_cfg.is_active = UVM_PASSIVE;
  endfunction
endclass
systemverilog
// Optional wildcard override (use sparingly)
initial begin
  uvm_config_db#(uvm_active_passive_enum)::set(
    null,
    "*.dma_axi_agent*",
    "is_active",
    UVM_PASSIVE
  );
end
diagram
[UVM] guardrails for safe overrides

1) prefer cfg-object edits over wildcard strings
2) if wildcard used, constrain path narrowly
3) print resolved mode table at end_of_elaboration
4) fail build if forbidden combinations detected
5) document per-agent mode rationale in env notes
  • Use wildcard config_db only as an integration escape hatch.

  • Prefer deterministic per-instance cfg handles for maintainability.

  • Add environment-level validation pass to detect illegal mode combos.


Validation and debugging checklist

Mode bugs are easy to detect if you instrument early. Check creation counts, presence/absence of seq_item links, and any monitor-only assumptions before stimulus starts.

systemverilog
function void end_of_elaboration_phase(uvm_phase phase);
  super.end_of_elaboration_phase(phase);
  uvm_top.print_topology();
endfunction
diagram
[AGT] expected topology by mode

active agent:
  protocol_agent
    mon
    sqr
    drv

passive agent:
  protocol_agent
    mon
  (no sqr, no drv)
diagram
[VIP][AGT] mode sanity matrix

symptom                                  probable cause
----------------------------------------------------------------
no stimulus in active test               cfg resolved to PASSIVE
driver null dereference                  code assumes drv exists in PASSIVE
double-driven interface                  two ACTIVE agents on same vif
monitor silent in passive deployment     vif not assigned to monitor cfg
unexpected sqr in passive topology       unconditional sqr create

Key takeaways

  • Treat is_active as a first-class architectural control with explicit ownership.

  • Centralize mode in cfg objects and resolve before agent component creation.

  • Deterministic precedence and logging eliminate most mode ambiguity bugs.

  • Topology inspection is the fastest way to validate final mode resolution.

Common pitfalls

  • Hidden defaulting when cfg get() fails and agent continues anyway.

  • Overusing wildcard overrides that unintentionally affect extra instances.

  • Conditionally creating monitor by mode; monitor should exist in both modes.

  • Ignoring mode validation until run_phase when failures are noisier.