Verification

Active vs Passive Agent UVM

By CourseTron Team ·  7 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    ? Print / Save as PDF

    In short: An active UVM agent builds and runs a sequencer and driver to stimulate the DUT, while a passive agent builds only the monitor to observe a bus, and the is_active field (UVM_ACTIVE or UVM_PASSIVE) decides which set gets created during build_phase.

    Why one agent class serves two jobs

    Every interface in a testbench needs a reusable verification component. The same AXI, APB or I2C bus is sometimes driven by the testbench and sometimes merely watched. Think of a block-level bench where your env plays the CPU and drives the peripheral, versus an SoC-level bench where a real CPU RTL model already drives that bus and you only want protocol checking and coverage.

    Rather than write two components, UVM lets you write one agent and switch its behaviour with a single configuration value. That is the entire idea behind the active and passive split. For the broader architecture of agents, see our guide on active vs passive agents in UVM, and for the foundations, what UVM is.

    What is inside the agent

    A standard agent contains three children: a sequencer, a driver and a monitor. The monitor is always present because observation is needed in every mode. The sequencer and driver exist only when the agent must generate pin-level activity.

    Active agent vs passive agent ACTIVE (UVM_ACTIVE) Sequencer Driver Monitor seq_item drives pins and samples pins PASSIVE (UVM_PASSIVE) no sequencer no driver Monitor only samples pins, never drives CourseTron · coursetron.com
    The monitor is common to both modes. Only the active agent instantiates a sequencer and driver.

    How is_active controls construction

    The agent base class uvm_agent carries a field named is_active of type uvm_active_passive_enum. Its default is UVM_ACTIVE. Inside build_phase, you create the monitor unconditionally and gate the other two on get_is_active(). Because building happens top-down in the UVM phases, the value must be set before the agent's build_phase runs, usually from the env or the test through uvm_config_db.

    class apb_agent extends uvm_agent;
      `uvm_component_utils(apb_agent)
      apb_monitor   mon;
      apb_sequencer sqr;
      apb_driver    drv;
    
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        mon = apb_monitor::type_id::create("mon", this);
        if (get_is_active() == UVM_ACTIVE) begin
          sqr = apb_sequencer::type_id::create("sqr", this);
          drv = apb_driver::type_id::create("drv", this);
        end
      endfunction
    
      function void connect_phase(uvm_phase phase);
        if (get_is_active() == UVM_ACTIVE)
          drv.seq_item_port.connect(sqr.seq_item_export);
      endfunction
    endclass
    
    // in the env or test
    uvm_config_db#(uvm_active_passive_enum)::set(this, "apb_agt", "is_active", UVM_PASSIVE);

    Key fact: the connect_phase must use the same guard as build_phase. Connecting drv when it is null is the most common cause of a null-object fatal in the first second of simulation.

    The guard must appear twice, not once

    Candidates often remember the build guard and forget two other places. First, the connect_phase shown above. Second, any virtual sequence or test code that refers to env.agt.sqr. In a passive configuration that handle is null, so a virtual sequencer should only collect handles for agents that are active. A cleaner pattern is to keep sequencer handles inside the virtual sequencer and assign them under the same condition.

    Where passive agents earn their place

    • Block to SoC reuse. A UART agent is active on the block bench, then flipped to passive at chip level where the real UART traffic is generated by firmware running on the CPU.
    • Output interfaces. The DUT's response port (for example the read-data side of a DMA) is nearly always passive: nobody drives it, but the scoreboard needs its transactions.
    • Back-to-back or loopback. When two DUT ports are wired together, only one side gets an active agent; the other is monitored.
    • Protocol checkers. Assertion-heavy monitors for AMBA or PCIe can run passively in emulation on Synopsys ZeBu or Cadence Palladium, where the driver would be a testbench-to-hardware bottleneck.

    Seeing the data flow

    The animation below shows an active agent: a transaction leaves the sequencer, crosses the driver handshake, becomes pin activity on the interface, and is re-observed by the monitor, which publishes it through an analysis port. The pull model between sequencer and driver is detailed in the driver-sequencer handshake article.

    Transaction flow in an active agent Sequencer Driver DUT pins Monitor item signals sample The packet travels left to right once every 3 seconds CourseTron · coursetron.com
    A packet moves from sequencer to driver, onto the pins, and is observed by the monitor. A passive agent has only the last hop.

    Comparison at a glance

    AspectActivePassive
    Components builtSequencer, driver, monitorMonitor only
    Drives interfaceYes (modport or clocking block)No
    Typical useBlock level, stimulusSoC level, output ports, checking
    Sequences can run on itYesNo sequencer, so no
    Scoreboard feedVia monitor analysis portVia monitor analysis port

    Key fact: switching to passive never removes the monitor, so coverage and scoreboarding behave identically in both modes. This is why many teams run the same functional coverage model from block to chip.

    Pitfalls seen in reviews and interviews

    Treat the following as a checklist. The driver and monitor roles are covered in UVM driver and monitor, and the sequencing side in sequences and sequencer.

    • Driving an interface twice. Leaving a block-level active agent on a bus that the RTL CPU also drives creates multiple drivers, so signals go X in a 4-state simulator such as VCS, Xcelium or Questa.
    • Setting is_active too late. If the config_db set happens in the test's end_of_elaboration, the agent has already built. Set it in the test's build_phase before creating the env.
    • Monitor depending on the driver. A monitor must sample only the interface. If it reads data from the driver's handle, passive mode breaks.
    • Per-instance versus per-class configuration. A multi-agent env, for example four AXI masters, needs each instance set separately with its own path string.
    • Coverage in the driver. Putting covergroups in the driver means passive mode loses coverage. Put them in the monitor or a subscriber.

    Reusing the same agent at three levels

    One agent, three bench levels Block bench input agent: ACTIVE output agent: PASSIVE Subsystem bench external bus: ACTIVE internal bus: PASSIVE Chip bench most agents: PASSIVE pad agents: ACTIVE CourseTron · coursetron.com
    Only the is_active setting changes. The agent code and the monitor-driven scoreboard stay the same.

    Engineers practising this at scale, including the design-services teams at Avecas, typically wrap each agent's configuration in a small config object holding the virtual interface and the is_active flag, so a single test-level override reconfigures the whole hierarchy. Communication between monitor and scoreboard uses analysis ports, explained in TLM in UVM.

    Want to build this yourself on a real bench? Explore the hands-on UVM modules in our CourseTron courses, and for instructor-led placement training see ChipXpert.

    FAQ

    What is the default value of is_active?

    UVM_ACTIVE. If you never set it, the agent builds the sequencer and driver.

    Can a passive agent run sequences?

    No. It has no sequencer, so there is nothing to start a sequence on. Only the monitor and its analysis port operate.

    Can is_active change during simulation?

    No. It is consumed in build_phase, and components cannot be created or removed afterwards, so the mode is fixed for the run.

    Does a passive agent still need a virtual interface?

    Yes. The monitor samples signals through the virtual interface, so it must still be supplied through the config_db.

    Advertisement