UVM
The Passive Agent
The agent's passive mode — monitor only, observing an interface without driving it. Why passive is right for DUT-driven and internal interfaces, and how it preserves all checking and coverage while dropping only the stimulus when an interface becomes internal at integration.
Agents · Module 14 · Page 14.3
The Engineering Problem
The active agent drives and observes an interface the testbench controls (Module 14.2). This chapter is its counterpart — the passive agent, which builds only a monitor and observes without driving. Passive is not "a lesser active agent"; it's the correct, required configuration for an entire class of interfaces: ones the testbench must not drive. A DUT output the design masters; an internal interface between two DUT sub-blocks; and — most importantly — an interface that becomes internal at integration: a block's external interface (which the testbench drove at block level) turns into a subsystem's internal interface, now driven by the integrated neighbor block. The problem this chapter solves is how to keep checking an interface you can no longer drive. The naive move — remove the agent because "we don't drive it anymore" — throws away the monitor's protocol checking and coverage, leaving the now-internal interface unverified. The passive agent is the answer: keep observing, stop driving.
A passive agent is the agent configured to observe an interface without driving it — it builds only the monitor (no sequencer, no driver, no stimulus path) and exports the monitor's analysis port so the scoreboard and coverage still subscribe. It is the right (and only safe) choice wherever the testbench does not master the interface: DUT-driven outputs, internal inter-block interfaces, and interfaces that go internal at integration. Its defining value is that it preserves all monitoring — reconstruction, protocol checking, coverage (Modules 13.2–13.4) — while dropping only the stimulus: the same agent that drove an interface at block level can be configured passive at subsystem/SoC level to keep checking the now-internal interface without conflicting with the real block that now drives it. This chapter is the passive agent: its monitor-only structure, why DUT-driven and internal interfaces require it, the active→passive transition that makes agents reusable across integration levels, and the mistake of removing an agent instead of passivating it.
What is a passive agent — the monitor-only configuration that observes an interface without driving it — and why is it the right (and only safe) choice for interfaces the testbench doesn't master, preserving all checking and coverage while dropping only the stimulus?
Motivation — why an entire class of interfaces needs passive
Passive is not an afterthought to active — it's mandatory for interfaces the testbench can't drive, and valuable for keeping them checked. The reasons:
- Some interfaces are driven by the DUT, not the testbench. A DUT output — a result bus the design produces — is mastered by the DUT. The testbench must not drive it (that's contention, Module 14.2); it can only observe. That observation requires a passive agent.
- Internal inter-block interfaces must never be perturbed. An interface between two DUT sub-blocks carries real traffic the testbench watches but never touches. Any driving would corrupt the inter-block communication. A passive agent is the only safe observer there.
- Interfaces become internal at integration. A block's external interface — driven by the testbench at block level — becomes a subsystem's internal interface, now driven by the integrated neighbor block. The testbench's driving is now redundant and conflicting; the checking is still wanted. Passive mode is the transition that handles this.
- Checking and coverage are still needed on interfaces you don't drive. An interface you don't drive still carries bugs — protocol violations, wrong data, missed scenarios. The monitor's job (reconstruct, check, cover) is just as valuable there. Passive keeps that.
- Passive ≠ idle. A passive agent actively observes — it reconstructs transactions, checks the protocol, and feeds the scoreboard and coverage. It does everything an active agent's monitor does; it just doesn't drive.
The motivation, in one line: an entire class of interfaces — DUT-driven, internal, and internal-at-integration — must not be driven by the testbench but still needs checking and coverage, so UVM provides the passive agent: a monitor-only observer that keeps all the monitoring while dropping the stimulus, making it the required configuration where the testbench watches but does not master.
Mental Model
Hold the passive agent as the driving instructor who moves to the passenger seat:
A passive agent is the driving instructor who, once the student takes the wheel, moves to the passenger seat — stops driving, but keeps watching every move and grading it. The car still has an evaluator; it just isn't the one driving anymore. Picture a dual-control training car. During training (block-level verification), there's no student yet — so the instructor drives (the agent is active, providing the stimulus the interface needs). Then the student arrives — at integration, the real neighbor block is connected, and it now takes the wheel (drives the interface). The instructor does not leave the car and does not keep grabbing the wheel — grabbing the wheel now would fight the student and crash (two drivers, contention). Instead, the instructor moves to the passenger seat (the agent goes passive): they stop driving, but they keep doing the part that still matters — watching every move, checking that the student follows the rules of the road (protocol checking), and recording what maneuvers were practiced (coverage). The car is still being evaluated; the evaluator just isn't the driver anymore. The worst mistake would be to conclude "the instructor isn't driving, so remove them from the car" — then no one is grading, and the student's mistakes go unnoticed. The instructor in the passenger seat is exactly as vigilant as before — same eyes, same rulebook, same scorecard — only the driving has been handed over.
So the passive agent is the instructor in the passenger seat: it stops driving (the real block — the student — now drives the interface) but keeps watching, checking, and grading (the monitor still reconstructs, checks the protocol, and feeds coverage). The active→passive transition is moving from the driver's seat to the passenger seat — not leaving the car. And the cardinal error is removing the agent (taking the instructor out) instead of passivating it (moving them over): removing it loses the grading; passivating it keeps the grading and only yields the wheel.
Visual Explanation — monitor only, no stimulus path
The defining picture is what a passive agent lacks: the stimulus path (sequencer, driver) is gone; only the monitor and its analysis port remain.
The figure shows the passive agent as the active agent minus the stimulus path. There is no sequencer and no driver — nothing inside the agent drives the interface. The interface is not mastered by the testbench (it's warning-colored to mark that someone else — a DUT output or an integrated neighbor block — drives it). The agent's sole component is the monitor, which observes the interface (never driving it — Module 13.1's passivity) and exports its analysis port upward, so the scoreboard and coverage still subscribe. The crucial reading is what's present versus absent. Absent: the entire sequencer-driver stimulus path of the active agent (Module 14.2) — removed because the testbench does not drive this interface. Present: everything on the observe side — the monitor's reconstruction (Module 13.2), protocol checking (Module 13.3), and analysis-port broadcast (Module 13.4) to the scoreboard and coverage. The success-colored path from the analysis port to the scoreboard/coverage is the point: a passive agent is not idle — it fully feeds checking and coverage, exactly as an active agent's monitor does. The only difference from active is the missing brand-colored stimulus path. The diagram is the passive agent's essence: strip the driving, keep the watching — a complete observer for an interface the testbench can't or shouldn't drive, still delivering all the monitoring value up to the checkers.
RTL / Simulation Perspective — the active→passive transition across integration levels
The passive agent's most important use is the integration transition: the same agent that's active at block level is configured passive at subsystem/SoC level, where the interface has gone internal. The hierarchy shows the transition.
The figure shows why passive mode makes agents reusable across integration. At block level, the interface is external — there's no neighbor block yet, so the testbench drives it; the agent is active (full trio, providing stimulus). As the design is integrated up, the neighbor block — the one that will drive this interface in the real chip — is connected. Now, at subsystem level and SoC level, that neighbor drives the interface (it has become internal), so the same agent is configured passive: it stops driving (the neighbor block is the driver now — driving would be contention) but keeps observing — reconstructing, checking the protocol, and feeding coverage on the now-internal interface. The RTL/structural insight is the one agent class, configured per level: the agent is unchanged code — only its is_active setting changes, tracking whether the testbench or the design drives the interface at that level. This is the deep payoff of the active/passive knob (Module 14.1): the protocol-checking and coverage you built into the agent's monitor at block level keep running at every higher level for free, automatically following the interface as it goes internal — you don't rewrite anything, you flip a configuration bit. Without passive mode, you'd face a bad choice at integration: keep the agent active (and fight the neighbor → contention) or delete it (and lose the checking). Passive is the third, correct option: keep the agent, flip it passive — yielding the wheel while keeping the eyes. The figure is the argument that passive isn't a niche mode — it's the mechanism by which an interface's verification (its monitor) survives integration intact.
Verification Perspective — passive preserves checking, drops only stimulus
The passive agent's value is precisely what it keeps: all the monitor's checking and coverage. Removing the agent (instead of passivating it) loses that — seeing what survives is seeing why passive matters.
The figure shows the surgical precision of going passive: it removes one thing and keeps everything else. The active agent has two capabilities — a stimulus path (sequencer + driver) and the monitor's observe capabilities. Going passive removes only the stimulus path (warning-colored "dropped" — no driving, the sole loss). Everything the monitor provides survives: reconstruction (observed transactions → the scoreboard), protocol checking (violations still flagged, Module 13.3), and coverage (scenarios still sampled). The verification insight is that a passive agent retains the entire checking-and-coverage capability of the agent — it loses only the ability to generate stimulus, which on a DUT-driven or internal interface is exactly the part you don't want anyway. This is why passive is not a downgrade: on an interface the testbench shouldn't drive, the stimulus path is useless (or harmful), and the monitor is all you wanted — passive gives you precisely that. The contrast that makes the point: removing the agent entirely (a tempting "cleanup" at integration) would lose all of it — no reconstruction, no protocol checking, no coverage on that interface — leaving the now-internal interface completely unverified. So the choice at integration is not "active or delete"; it's passive — keep the monitor's full value, drop only the now-redundant stimulus. The figure is the argument against the delete instinct: passive is checking-and-coverage preserved, driving removed — and on an interface you don't master, that's all upside. The monitor's verification work is too valuable to throw away just because the driving became redundant.
Runtime / Execution Flow — the passive agent at run time
At run time, a passive agent runs only the monitor's observe loop — it never drives, never needs a sequence — yet fully feeds the scoreboard and coverage. The flow shows its operation.
The flow shows that a passive agent's run time is just the monitor's loop — complete on the observe side, empty on the drive side. Someone else drives (step 1): the DUT or an integrated neighbor block masters the interface — the agent never drives, so there is no stimulus step at all. Monitor observes (step 2): the agent's only component does the full monitor job — reconstruct transactions (Module 13.2), check the protocol (Module 13.3) — exactly as it would in an active agent. Broadcast (step 3): the monitor's analysis port feeds subscribers (Module 13.4), identically to active. Consume (step 4): the scoreboard and coverage, subscribed, check and sample — full checking and coverage on the interface. The runtime insight is that steps 2–4 are identical to an active agent's observe side — a passive agent's monitor is not a reduced monitor; it's the same monitor doing the same work. The only difference is the absence of the stimulus steps (the active agent's "test runs a sequence → driver drives," Module 14.2): a passive agent has nothing to run a sequence on (no sequencer) and nothing to drive with (no driver), and it needs neither, because the interface is driven externally. This is why a passive agent is fully functional verification despite generating no stimulus: its value was always on the observe side, and that side is wholly intact. The flow is the passive agent delivering complete checking and coverage on an interface it watches but doesn't touch — the monitor's work, unchanged, with the driving simply not there.
Waveform Perspective — observing without driving
A passive agent's defining behavior is visible on a timeline: the interface is driven by someone else, and the agent observes it without contributing any drive. The waveform shows the passive agent watching a neighbor-driven interface.
A passive agent observes a neighbor-driven interface, driving nothing itself
12 cyclesThe waveform shows the passive agent observing without driving. The interface is driven by an integrated neighbor block — nbr_valid and the data come from the neighbor, not the agent. The crucial visual is what the agent does not contribute: there is no agent-driven signal on the interface — the passive agent drives nothing. Its monitor observes: mon_capture pulses when it samples the neighbor's activity, and it emits the observed transaction on its analysis port (ap_emit) — checking and coverage proceed (Modules 13.3–13.4). The contrast with the active agent (Module 14.2) is exactly the absence of the drive path: the active agent's waveform had drv_valid — its own driver asserting the interface — whereas here there is no such signal; the only activity attributable to the agent is observation (mon_capture, ap_emit). This is passivity made visible: the agent watches the neighbor-driven interface and checks it, but never touches the wheel. Reading a waveform for passive-agent behavior — is the agent driving anything (it must not)? is its monitor observing and emitting (it should)? — confirms the agent is a pure observer. The picture to carry: a passive agent appears on the interface only as observation — no drive whatsoever — while fully performing the monitor's reconstruction, checking, and coverage. The driving belongs to someone else (the DUT or the neighbor block); the watching and grading belong to the passive agent — eyes on, hands off.
DebugLab — the checking that vanished at integration
An internal interface went unverified because the agent was removed instead of made passive
At block level, an interface was thoroughly verified — its agent drove stimulus and its monitor flagged protocol violations and recorded coverage. After integration into the subsystem, a real bug on that now-internal interface (a protocol violation between the two integrated blocks) escaped: no violation was flagged, no coverage was recorded, the scoreboard saw nothing. The interface that had been the most thoroughly checked at block level was, at integration, completely unverified — its checking had silently disappeared.
When the interface became internal at integration, the engineer removed the agent — reasoning "the testbench no longer drives this interface, so we don't need the agent" — which also removed the monitor, deleting the protocol checking and coverage. They conflated "stop driving" with "stop watching":
✗ REMOVE the agent at integration (conflates 'don't drive' with 'don't watch'):
// env: "we don't drive this interface anymore, delete its agent"
// → driver/sequencer gone (correct: the neighbor drives now)
// → MONITOR also gone (WRONG: protocol checking + coverage DELETED)
// → the now-internal interface is UNVERIFIED → real protocol bug ESCAPES
✓ make the agent PASSIVE at integration (keep the monitor, drop only the stimulus):
uvm_config_db#(uvm_active_passive_enum)::set(this, "if_agent", "is_active", UVM_PASSIVE);
// → sequencer/driver gone (correct: the neighbor drives now — no contention)
// → MONITOR KEPT → protocol checking + coverage still run on the internal interface
// → the same checks that passed at block level keep guarding it at integrationThis is the remove-vs-passivate bug — throwing away the monitor's verification value by deleting the agent when the correct move was to passivate it. The engineer was half right: at integration, the neighbor block drives the interface, so the testbench should stop driving it (keeping the driver would be contention, Module 14.2). But they over-corrected: instead of dropping just the driver (via passive mode), they deleted the whole agent — including the monitor. The monitor was doing valuable, still-needed work: reconstructing the inter-block transactions, checking the protocol between the two integrated blocks, and recording coverage of the now-internal traffic. Deleting it removed all of that, leaving the now-internal interface — which still carries bugs — completely unchecked. So a real protocol violation between the integrated blocks escaped: nothing was watching. The fix is to make the agent passive (is_active = UVM_PASSIVE) instead of removing it: the sequencer and driver go (correctly — the neighbor drives now, no contention), but the monitor stays, continuing to check the protocol and feed coverage on the internal interface. The same checks that passed at block level keep guarding the interface at integration. The general lesson, and the chapter's thesis: passive ≠ removed — when an interface goes internal at integration, don't delete its agent; make it passive. Removing the agent conflates "we stop driving" (true) with "we stop watching" (false, and dangerous) — it throws away the monitor's protocol checking and coverage, leaving the now-internal interface unverified. Passive keeps the watching, drops only the driving — exactly the surgical change integration calls for. Move the instructor to the passenger seat; don't push them out of the car.
The tell is verification that disappears when an interface goes internal. Diagnose remove-vs-passivate mistakes:
- Check what happened to the agent at integration. If an interface's agent was deleted when it went internal, its monitor's checking and coverage went with it.
- Look for coverage that dropped at integration. Coverage on an interface that vanishes at the subsystem level signals a removed (not passivated) agent.
- Confirm whether internal interfaces are still monitored. An integrated, internal interface with no monitor has no protocol checking — a real bug there will escape.
- Distinguish 'stop driving' from 'stop watching'. If the reasoning was "we don't drive it, so remove the agent," the watching was lost unnecessarily — passive was the right move.
Passivate, never delete, when an interface goes internal:
- Make the agent passive at integration. Set
is_active = UVM_PASSIVEso the driver and sequencer drop (no contention) but the monitor keeps checking and covering. - Keep monitoring internal interfaces. Internal inter-block interfaces carry bugs; their monitors' protocol checking and coverage are still needed after integration.
- Never conflate driving with watching. Stopping the stimulus is correct at integration; stopping the observation is not — they are separate, and passive separates them.
- Verify coverage persists across levels. Confirm an interface's coverage and protocol checks survive integration — if they vanish, the agent was removed instead of passivated.
The one-sentence lesson: passive ≠ removed — when an interface goes internal at integration, make its agent passive (is_active = UVM_PASSIVE), never delete it; removing the agent conflates "stop driving" (correct) with "stop watching" (wrong), throwing away the monitor's protocol checking and coverage and leaving the now-internal interface unverified, while passive keeps the watching and drops only the driving.
Common Mistakes
- Removing an agent when an interface goes internal. Make it passive instead; deleting it loses the monitor's protocol checking and coverage on the now-internal interface.
- Driving an interface the testbench doesn't master. DUT outputs and internal interfaces must be observed with a passive agent — driving them causes contention or corrupts inter-block traffic.
- Thinking passive means idle. A passive agent fully reconstructs, checks the protocol, and feeds coverage; it does everything an active agent's monitor does, minus the driving.
- Trying to run a sequence on a passive agent. A passive agent has no sequencer; stimulus belongs to active agents, and the interface here is driven externally.
- Keeping an agent active after integration. Once the neighbor block drives the interface, an active agent fights it (contention); switch it to passive.
- Forgetting to export the passive agent's analysis port. Even passive, the monitor's port must be exported so the scoreboard and coverage subscribe — passive feeds checking too.
Senior Design Review Notes
Interview Insights
A passive agent is the agent configured to observe an interface without driving it — it builds only the monitor, with no sequencer and no driver, and exports the monitor's analysis port so the scoreboard and coverage still subscribe. You use it wherever the testbench does not master the interface. There are three main cases. First, a DUT output — a bus the design drives to report results; the testbench must not drive it, only observe it, so a passive agent watches it. Second, an internal interface between two DUT sub-blocks; that interface carries real inter-block traffic the testbench watches but must never perturb, so a passive agent is the only safe observer. Third, and most importantly, an interface that becomes internal at integration: a block's external interface that the testbench drove at block level becomes a subsystem's internal interface, now driven by the integrated neighbor block. There, the testbench's driving is redundant and would conflict, but the checking is still wanted — so the same agent is configured passive. The defining property of a passive agent is that it preserves all the monitoring — reconstruction, protocol checking, coverage — while dropping only the stimulus. It does everything an active agent's monitor does; it just doesn't drive. So passive is not a downgrade or an idle mode — it's a complete observer for an interface the testbench can't or shouldn't drive. The mental model is a driving instructor who moves to the passenger seat once the student takes the wheel: they stop driving but keep watching and grading. Eyes on, hands off.
Because it lets the same agent follow an interface as it goes internal, keeping the interface's verification intact without rewriting anything. At block level, an interface is external — there's no neighbor block yet, so the testbench drives it, and the agent is active, providing stimulus. As you integrate up to subsystem and SoC level, the real neighbor block that will drive that interface in the chip gets connected, and now it drives the interface — the interface has become internal. At that point, keeping the agent active would be wrong: its driver would fight the neighbor block, causing contention. But you still want the checking — protocol violations and coverage on the now-internal interface. Passive mode is the transition that resolves this. You configure the same agent passive: the sequencer and driver drop, so there's no contention, but the monitor stays, continuing to reconstruct transactions, check the protocol, and feed coverage. The crucial part is that this is one agent class, configured per level — the agent's code is unchanged; only its is_active setting changes, tracking whether the testbench or the design drives the interface. So all the protocol-checking and coverage you built into the agent's monitor at block level keep running at every higher level automatically, just by flipping a configuration bit. Without passive mode, integration would force a bad choice: keep the agent active and fight the neighbor, or delete it and lose the checking. Passive is the correct third option — keep the agent, flip it passive, yield the driving but keep the eyes. That's why passive isn't a niche mode; it's the mechanism by which an interface's monitor survives integration, which is exactly what makes agents reusable across the block-to-SoC hierarchy.
Going passive drops only the stimulus path and keeps everything the monitor provides. The active agent has two capabilities: a stimulus path, which is the sequencer and driver that generate and drive traffic, and the monitor's observe capabilities, which are reconstruction of transactions, protocol checking, and coverage sampling, all broadcast through the analysis port. Going passive removes only the stimulus path — no sequencer, no driver, no driving. Everything the monitor does survives: it still reconstructs observed transactions and sends them to the scoreboard, it still flags protocol violations, and it still samples coverage. So a passive agent retains the entire checking-and-coverage capability of the agent and loses only the ability to generate stimulus. That's why passive is not a downgrade — on an interface the testbench shouldn't drive, the stimulus path is useless or even harmful, so dropping it loses nothing you wanted, and the monitor, which is all you wanted, is fully intact. The important contrast is with removing the agent entirely. Removing it would lose all of it — no reconstruction, no protocol checking, no coverage on that interface — leaving it unverified. So at integration the choice is not active versus delete; it's passive, which keeps the monitor's full value and drops only the now-redundant stimulus. Concretely, the passive agent still exports its analysis port so the scoreboard and coverage subscribe exactly as before. The monitor in a passive agent is not a reduced monitor; it's the same monitor doing the same work. The driving is simply absent because the interface is driven externally, by the DUT or an integrated neighbor block.
Removing the agent is a mistake because it conflates stopping the driving with stopping the watching, and it throws away the monitor's still-needed checking and coverage. At integration, when an interface goes internal and the neighbor block drives it, it's correct that the testbench should stop driving — keeping the driver would cause contention. But the reasoning "we don't drive this interface anymore, so we don't need the agent" over-corrects: deleting the whole agent also deletes the monitor, which was doing valuable work — reconstructing the inter-block transactions, checking the protocol between the two integrated blocks, and recording coverage of the now-internal traffic. Removing it leaves the internal interface completely unchecked, so a real protocol violation between the integrated blocks escapes, with nothing watching. The interface that was the most thoroughly verified at block level becomes unverified at integration. What you should do instead is make the agent passive — set is_active to UVM_PASSIVE. That drops the sequencer and driver, which is correct because the neighbor drives now and you avoid contention, but it keeps the monitor, so the protocol checking and coverage keep running on the internal interface. The same checks that passed at block level keep guarding it at integration. The principle is that "stop driving" and "stop watching" are separate decisions, and passive mode separates them: it lets you stop driving while continuing to watch. Internal interfaces carry bugs too, so their monitors are still needed after integration. The image is moving the instructor to the passenger seat rather than pushing them out of the car — you yield the wheel but keep the evaluation. So passive, not delete, is the surgical change integration calls for.
Exercises
- Name what's absent. State which components a passive agent does not build, and what it still builds and exports.
- Trace the transition. Describe how the same agent is configured at block level versus after the interface goes internal at integration, and why.
- Defend passivation. An interface goes internal and a teammate proposes deleting its agent. Explain what would be lost and what to do instead.
- Classify the interfaces. For each, pick active or passive: a result bus the DUT outputs; an internal interface between two integrated blocks; a stimulus bus into the block under test.
Summary
- A passive agent is the agent configured to observe an interface without driving it — it builds only the monitor (no sequencer, no driver, no stimulus path) and exports the monitor's analysis port so the scoreboard and coverage still subscribe.
- It is the right (and only safe) choice wherever the testbench doesn't master the interface: DUT-driven outputs, internal inter-block interfaces, and interfaces that go internal at integration — driving any of these causes contention or corrupts inter-block traffic.
- Passive preserves all monitoring — reconstruction, protocol checking, coverage — and drops only the stimulus: it is not idle, it does everything an active agent's monitor does, minus the driving.
- The active→passive transition makes agents reusable across integration: the same agent is active at block level and passive at subsystem/SoC level, with only
is_activechanging — so an interface's verification follows it as it goes internal. - The durable rule of thumb: use a passive agent wherever the testbench doesn't drive the interface, and when an interface goes internal at integration, make its agent passive — never delete it; removing the agent conflates "stop driving" (correct) with "stop watching" (wrong), throwing away the monitor's protocol checking and coverage, while passive keeps the watching and drops only the driving. Passive ≠ idle, and passive ≠ removed.
Next — Agent Reuse: the active and passive modes are the foundation of the agent's greatest strength — reuse. The next chapter generalizes beyond mode-switching to the full picture: how a protocol agent, built once, is reused across blocks, subsystems, and projects — the conventions, packaging, and configuration that make an agent a true plug-and-play verification component.