UVM
Agent Reuse
What makes a protocol agent a plug-and-play verification component reused across instances, integration levels, projects, and teams — self-containment, configuration over modification, a clean standard boundary, and packaging the agent with its sequences and config as a distributable VIP.
Agents · Module 14 · Page 14.4
The Engineering Problem
The agent is UVM's unit of reuse (Module 14.1), and active/passive mode lets the same agent follow an interface across integration levels (Module 14.3). This chapter generalizes to the full reuse picture — and to a hard truth: reuse is not automatic. An agent is reusable only if it is built to be. A protocol agent should drop into any environment that has its interface — a second instance on the same DUT, a higher integration level, a different project, even a different team's testbench. But the moment the agent makes one assumption about the specific DUT or environment it was first written for — a hardcoded hierarchical path, a reference to a named scoreboard, a baked-in protocol width — it stops being reusable: it works there and breaks everywhere else. The problem this chapter solves is how to build an agent that actually reuses — the disciplines that turn "an agent" into "a plug-and-play verification component."
Agent reuse is the discipline that makes a protocol agent, built once, reusable across instances, integration levels, projects, and teams. It rests on a few principles. Self-containment: the agent depends only on its interface protocol — never on the specific DUT, hierarchy, or environment it's used in. Configuration over modification: all per-use variation flows through a config object (the virtual interface, is_active, protocol parameters) — you change the config, never the code. A clean, standard boundary: the agent exposes only a sequencer (run sequences) and an analysis port (subscribe) — the same interaction everywhere. And packaging: the agent travels with its sequence library, config, and protocol checks as a distributable VIP (verification IP). The agent is a sealed, standard-fit module — you plug it in and configure it, you never open it up and rewire it. This chapter is the reuse discipline: the reuse spectrum, self-containment, configuration over modification, VIP packaging, and the env-specific dependency that silently breaks reuse.
What makes a protocol agent actually reusable across instances, integration levels, projects, and teams — and why do self-containment, configuration over modification, a clean standard boundary, and VIP packaging turn an agent into a plug-and-play verification component?
Motivation — why reuse must be designed in
The agent being the "unit of reuse" is a goal, not a guarantee. Reuse is worth designing for, and easy to break. The reasons:
- The same protocol appears everywhere. An AXI interface recurs across dozens of DUTs, every integration level, and every project that uses AXI. Building its agent once and reusing it saves enormous re-implementation — if the agent is reusable.
- Reuse spans multiple scales. Within a project: multiple instances (several AXI ports) and multiple integration levels (block → SoC, Module 14.3). Across projects: the same agent on a new chip. Across teams/vendors: a third-party VIP you buy and drop in. The agent must reuse at all these scales.
- One env-specific dependency breaks all of it. A hardcoded interface path, a reference to a named environment component, an assumption about the hierarchy — any of these couples the agent to one environment, so it works there and breaks everywhere else. Reusability is fragile: it takes one bad dependency to lose it.
- Variation must be configured, not coded. Different uses need different settings — active vs passive, data width, timing mode, number of outstanding transactions. If these are hardcoded, each use needs a code change (a fork). If they're config knobs, each use is a configuration — one codebase, many uses.
- The unit of distribution is the package, not the class. An agent is useless without its sequences, config, and protocol checks. Reuse means shipping them together as a VIP — so a consumer gets a complete, working component, not a bare class.
The motivation, in one line: the same protocol recurs across instances, levels, projects, and teams, so an agent's reuse is immensely valuable — but it is fragile, broken by any env-specific dependency, so reuse must be designed in through self-containment, configuration over modification, a clean boundary, and packaging the agent as a complete VIP.
Mental Model
Hold the reusable agent as a sealed, standard-fit module you plug in and configure, never open and rewire:
A reusable agent is a sealed, standard-fit module — like a standardized appliance with a standard plug: it works in any compatible socket without rewiring, because it depends only on the socket standard, not on the building. You plug it in and turn the dial; you never open the case and re-solder it for each room. Picture a standardized appliance. It has a standard plug (the agent's clean boundary — sequencer and analysis port) and runs on a standard supply (the interface protocol). Because it depends only on the standard — the plug shape, the voltage — it works in any building with a compatible socket: this room, that house, a different city, someone else's property (instances, levels, projects, teams). To suit each use, you turn its dial (set its configuration — active/passive, width, timing); you never open the case and re-solder its internals (you never modify the code). And it ships as a complete unit — the appliance plus its power cord, manual, and settings card (the VIP: agent plus sequences, config, protocol checks) — so a new owner gets something that works out of the box. Now the fatal flaw: imagine an appliance with a wire hardwired into one specific room's junction box — bypassing the standard plug to depend on that room's wiring. It works perfectly in that room — and is unusable anywhere else; moving it means rewiring. That one hardwired dependency destroys the reuse. A truly reusable appliance depends only on the standard, exposes only the standard plug, and is configured by its dial — never hardwired to one environment.
So the reusable agent is a sealed, standard-fit module: it depends only on its protocol (the supply standard), exposes only a clean boundary (the standard plug — sequencer and analysis port), is configured not modified (the dial, not the soldering iron), and ships complete (the VIP — appliance plus cord, manual, settings). And the one thing that destroys reuse is a hardwired dependency on one specific environment — any such wire and the module works there and nowhere else. Plug and configure; never open and rewire.
Visual Explanation — the reuse spectrum
The defining picture is the spectrum: one protocol agent, built once, reused across instances, integration levels, projects, and teams.
The figure shows the reuse spectrum — the range of contexts one agent serves. A single protocol agent, built once, fans out to four scales of reuse. Many instances: the same agent instantiated multiple times within one DUT — several AXI ports, each its own instance of the same code. Integration levels: reused up the hierarchy — block, subsystem, SoC — active where the testbench drives, passive where the design does (Module 14.3). Other projects: dropped into a different chip that uses the same protocol — no re-implementation. Teams / vendors: shared across teams, or bought as a third-party VIP and dropped in. The crucial reading is that all of these are the same agent code — they differ only in configuration (which instance, active/passive, which protocol parameters). The brand-colored agent is built once; the success-colored contexts are all served without rewriting it. This is the economic argument for the reuse discipline: an agent built reusably is built once and amortized across every AXI interface anywhere — dozens of instances, every level, every project, every team — while an agent built non-reusably (with env-specific dependencies) is rebuilt every time, a staggering duplication of effort and a divergence risk. The diagram is the payoff the rest of the chapter protects: the whole spectrum of reuse is available — but only if the agent is built self-contained, configured not modified, with a clean boundary, and packaged complete. Break any of those, and the spectrum collapses to "works in the one place it was written."
RTL / Simulation Perspective — self-containment and configuration
In code, the reuse discipline is concrete: the agent receives everything env-specific through a config object and hardcodes nothing about its environment. The code shows the self-contained, configured agent.
// === CONFIG OBJECT: all per-use variation lives here (set by the environment) ===
class axi_agent_config extends uvm_object;
virtual axi_if vif; // the interface handle — given by the env
uvm_active_passive_enum is_active = UVM_ACTIVE;
int data_width = 32; // protocol parameter — a knob, not hardcoded
int max_outstanding = 16;
endclass
// === REUSABLE AGENT: gets its config, depends only on the protocol ===
class axi_agent extends uvm_agent;
axi_agent_config cfg;
function void build_phase(uvm_phase phase);
if (!uvm_config_db#(axi_agent_config)::get(this, "", "cfg", cfg)) // RECEIVE config
`uvm_fatal("NOCFG", "no agent config")
mon = axi_monitor::type_id::create("mon", this);
if (cfg.is_active == UVM_ACTIVE) begin // configured, not hardcoded
seqr = axi_sequencer::type_id::create("seqr", this);
drv = axi_driver ::type_id::create("drv", this);
end
// ✗ NEVER: vif = top.dut.axi0; (hardcoded path → breaks in every other environment)
// ✗ NEVER: reference env.scoreboard or any named environment component
endfunction
endclass
// === The ENVIRONMENT supplies the config per instance — the agent makes no assumptions ===
function void env::build_phase(uvm_phase phase);
axi_agent_config c = axi_agent_config::type_id::create("c");
c.vif = m_vif; c.is_active = UVM_ACTIVE; c.data_width = 64; // THIS env's choices
uvm_config_db#(axi_agent_config)::set(this, "axi_agt", "cfg", c);
axi_agt = axi_agent::type_id::create("axi_agt", this);
endfunctionThe code shows self-containment and configuration over modification in practice. The config object (axi_agent_config) holds all per-use variation — the virtual interface (vif), is_active, and protocol parameters (data_width, max_outstanding). The agent receives this config (uvm_config_db#(axi_agent_config)::get(...)) and depends only on the protocol: it builds from the config (active/passive configured, not hardcoded), and it hardcodes nothing about its environment — the two ✗ NEVER comments mark the reuse-killers: never hardcode an interface path (vif = top.dut.axi0 — that path exists in one environment), and never reference a named environment component (env.scoreboard — that couples the agent to one env). The environment supplies the config per instance — it decides vif, is_active, data_width for its use — and the agent makes no assumptions about which environment it's in. The shape to carry: a reusable agent is a config-driven, self-contained component — all variation enters through a config object the environment sets, and the agent reaches out to nothing env-specific. Configuration over modification is literal here: to use the agent on a 64-bit bus, the environment sets data_width = 64 — the agent code is unchanged. The one discipline that makes this work is refusing to hardcode — every env-specific value is a config field, handed in, never reached for.
Verification Perspective — the VIP package
A reusable agent doesn't travel alone — it ships with its sequence library, config, and protocol checks as a VIP (verification IP). Reuse means reusing the whole package, the unit of distribution.
The figure shows that the unit of reuse is the VIP package, not the bare agent class. The agent (sequencer, driver, monitor) is the core — but it's useless alone: an agent with no sequences generates no stimulus, with no config can't be tuned, with no checks verifies nothing. The VIP bundles the agent with the sequence library (the traffic generators tests run on its sequencer — Module 10's reusable sequences), the config object (the knobs: width, mode, is_active), and the protocol checks (the assertions and coverage that verify the protocol — Module 13.3). A consumer — another team, another project — gets the whole package and has a working component out of the box: drop it in, configure it, run its sequences, subscribe to its analysis port. The verification insight is that reuse operates on the package, because reuse of the agent alone would force every consumer to re-write the sequences, re-derive the config, and re-author the protocol checks — defeating the reuse. The brand-colored VIP is what's actually distributed; the layers beneath are what travels inside it. This is why mature verification organizations (and VIP vendors) ship packages, not classes: a third-party AXI VIP arrives as an agent + a rich sequence library + a config API + a complete assertion/coverage suite — a consumer plugs it in and is immediately productive. The practical discipline: when you build a reusable agent, build its sequences, config, and checks alongside it, and package them together — so the next user inherits a complete capability, not a construction project. The figure is the argument that an agent's reusability includes everything needed to use it — the VIP is the real deliverable.
Runtime / Execution Flow — dropping a VIP into a new environment
Reusing an agent is a fixed, lightweight flow: instantiate the VIP, supply its config, connect its boundary, run its sequences — no modification. The flow shows the drop-in.
The flow is the lightweight drop-in that reusability enables. Instantiate (step 1): the environment creates the agent from the VIP package — same code, untouched. Supply config (step 2): it sets this environment's choices in the config object — the interface handle, active or passive, protocol parameters — handing them to the agent. Connect the boundary (step 3): it connects the agent's clean boundary — subscribing the scoreboard and coverage to the analysis port, and (if active) targeting the sequencer. Run (step 4): tests run the VIP's packaged sequences on the agent's sequencer — no agent modification. The runtime insight is that all four steps are configuration and connection — none is modification. Adapting the agent to a new environment is entirely a matter of supplying config and wiring the boundary; the agent's code is never opened. This is why reuse is cheap when the discipline holds: dropping a VIP into a new project is minutes (instantiate, configure, connect, run), not weeks (re-implement). And it's why a single env-specific dependency is so costly: it turns step 2 (configure) into "configure, then also edit the agent to match this environment" — re-introducing modification, forking the code, and destroying the drop-in property. The flow is the reuse promise made operational: a well-built agent is adopted by configuration and connection alone — plug it in, set the dials, wire the standard plug, run — and the entire value of the reuse discipline is keeping it that way.
Waveform Perspective — protocol behavior is invariant across contexts
The deepest sign of reusability is invariance: the agent's protocol-level behavior is identical in every context, because it depends only on the protocol, not the environment. The waveform shows the agent's context-independent protocol behavior.
The agent's protocol behavior is the same in every reuse context — it depends only on the protocol
12 cyclesThe waveform shows reusability as invariance. The agent drives (or observes) its protocol's handshake — valid asserts with data, the partner returns ready, the transfer completes (xfer) — and this behavior is identical regardless of which DUT, which integration level, or which project the agent is in. The crucial reading is why it's invariant: the agent depends only on the interface protocol, not on the environment — so its protocol-level behavior is determined by the protocol, which is the same in every context. Configuration changes the particulars — which data values, the width (8-bit here, 64-bit elsewhere), active (driving) vs passive (observing) — but the protocol-correct handshake (valid/ready, the transfer semantics) is the same everywhere. This invariance is exactly what lets one agent serve every context: a consumer knows that dropping in the agent yields protocol-correct behavior without re-verifying the protocol itself — the agent carries its correct protocol behavior with it. The picture to carry: a reusable agent's interface behavior is protocol-determined and context-independent — plug it into any compatible interface and it speaks the protocol correctly, every time. Reading a waveform for reusability — is the protocol behavior the same as it was in the original context? does the agent honor the handshake regardless of environment? — confirms the agent depends only on the protocol. The invariance of this waveform across DUTs, levels, and projects is the visible signature of an agent that reuses — its correctness travels with it, because it's anchored to the protocol, not the environment.
DebugLab — the agent that worked in one project and broke in the next
A 'reusable' agent that hardcoded its environment and failed when dropped into a new project
An AXI agent was declared reusable and worked perfectly in its original project. When a second project tried to reuse it — drop it in, configure it, run — it failed immediately: a UVM_FATAL at build, "null virtual interface," or it found the wrong interface entirely. The exact same agent code that was rock-solid in project A would not even build in project B. The agent claimed to be reusable but was welded to its first environment.
The agent had a hardcoded environment dependency — it reached for its virtual interface by an absolute hierarchical path (or a fixed config-db scope/name) that existed only in project A's hierarchy. When dropped into project B's different hierarchy, that path didn't exist, so the agent got null:
✗ HARDCODED env dependency baked into the agent (works in ONE hierarchy only):
// inside the agent:
vif = uvm_test_top.dut_env.axi_subsys.axi0_if; // absolute path — exists ONLY in project A
// project B's hierarchy differs → this path is invalid → null vif → UVM_FATAL
// the agent is welded to project A's environment structure
✓ SELF-CONTAINED: receive the interface via config set by WHATEVER env instantiates it:
// inside the agent — no assumption about the hierarchy:
if (!uvm_config_db#(axi_agent_config)::get(this, "", "cfg", cfg)) `uvm_fatal("NOCFG","")
vif = cfg.vif; // the ENV handed us the interface
// each environment sets cfg.vif for ITS hierarchy → the agent works in ALL of themThis is the env-specific-dependency bug — the single most common way a "reusable" agent isn't — and it follows from violating self-containment: the agent reached out to its environment's specific structure instead of receiving what it needed. The agent obtained its virtual interface by an absolute hierarchical path — uvm_test_top.dut_env.axi_subsys.axi0_if — which existed in project A's hierarchy and worked there. But that path is a dependency on project A's environment structure: project B's hierarchy is different (different env names, different nesting), so the path is invalid, the agent gets a null interface, and it fatals at build — before it can do anything. The agent was welded to one environment by one hardcoded reference. The fix is self-containment: the agent must receive its interface (and all env-specific values) through a config object that whatever environment instantiates it sets for its own hierarchy. The agent gets cfg.vif — it doesn't know or care where the interface lives in the hierarchy; each environment sets cfg.vif for its structure, so the same agent works in all of them. The general lesson, and the chapter's thesis: a reusable agent must be self-contained — it depends only on its protocol and a config object handed to it, and makes no assumption about the environment's hierarchy, names, or components; one hardcoded env-specific dependency (an absolute path, a named scoreboard, a fixed scope) welds the agent to one environment and breaks reuse everywhere else. Receive everything env-specific through config; reach out to nothing. The "reusable" label is earned by self-containment — and forfeited by a single hardcoded wire.
The tell is an agent that works in one environment and fails when moved. Diagnose reuse-breaking dependencies:
- Grep the agent for absolute paths and named components. Any
uvm_test_top.*,top.dut.*, or reference to a specific env component is a hardcoded dependency. - Check how the agent gets its interface and parameters. If it reaches for them by a fixed path or scope rather than receiving them via config, it's welded to one hierarchy.
- Try instantiating it in a different hierarchy. A null interface or wrong-handle fatal at build in a new environment confirms an env-specific dependency.
- Look for protocol parameters as constants. A hardcoded width or mode that should be a config knob breaks reuse on a different variant.
Make the agent depend only on its protocol and its config:
- Receive everything env-specific via a config object. The interface handle,
is_active, and protocol parameters are set by the environment per instance — never hardcoded in the agent. - Reach out to nothing. The agent must not reference absolute hierarchical paths or named environment components; it exposes a clean boundary and the environment connects to it.
- Make every variation a config knob. Width, mode, outstanding count — anything that differs per use is a configuration field, not a constant.
- Test reuse explicitly. Instantiate the agent in a second, differently-structured environment to confirm it builds and runs on configuration alone.
The one-sentence lesson: a reusable agent must be self-contained — depending only on its protocol and a config object handed to it, making no assumption about the environment's hierarchy, names, or components; receive the interface and all parameters through config and reach out to nothing, because one hardcoded env-specific dependency (an absolute path, a named component, a fixed parameter) welds the agent to one environment and breaks reuse everywhere else.
Common Mistakes
- Hardcoding the interface path. The agent must receive its virtual interface via config set by the environment, never reach for an absolute hierarchical path that exists in one hierarchy only.
- Referencing named environment components. An agent that names a specific scoreboard or env component is welded to one environment; expose a clean boundary and let the environment connect.
- Baking protocol parameters in as constants. Width, mode, and outstanding count must be config knobs, or the agent works for only one protocol variant.
- Modifying the agent per use. Reuse is configuration, not modification; editing the agent for a project forks it and destroys the single-source reuse.
- Shipping the agent without its package. An agent without its sequences, config, and checks isn't reusable in practice; distribute the complete VIP.
- Assuming a fixed number of instances or a fixed environment shape. A reusable agent makes no assumptions about how many instances exist or how the environment is structured.
Senior Design Review Notes
Interview Insights
An agent is reusable when it's built to depend only on its interface protocol and to be adapted entirely by configuration, and reuse isn't automatic because a single environment-specific dependency breaks it. The agent being UVM's unit of reuse is a goal, not a guarantee. Four principles make it real. First, self-containment: the agent depends only on its interface protocol, never on the specific DUT, hierarchy, or environment it was first written for. Second, configuration over modification: all per-use variation — the virtual interface, active or passive, protocol parameters like width and outstanding count — flows through a config object the environment sets, so you change configuration, never code. Third, a clean standard boundary: the agent exposes only a sequencer to run sequences on and an analysis port to subscribe to, so the interaction is the same in every environment. Fourth, packaging: the agent ships with its sequence library, config, and protocol checks as a complete VIP, because a bare agent class is useless without them. Reuse isn't automatic because it's fragile — it takes only one bad dependency to lose it. The moment the agent hardcodes an interface path, references a named environment component, or bakes in a protocol width, it's welded to the one environment it was written for: it works there and breaks everywhere else. So reuse has to be designed in deliberately, by refusing every environment-specific dependency and routing all variation through configuration. The mental model is a standardized appliance with a standard plug: it works in any compatible socket because it depends only on the standard, you configure it with a dial, and you never open the case and rewire it for each room. One hardwired connection to a specific room destroys that.
An agent gets reused across four scales: multiple instances, integration levels, projects, and teams or vendors. Within a single project, first there's instance reuse — the same agent instantiated multiple times on one DUT, for example several AXI ports, each its own instance of the same code. Then there's integration-level reuse — the same agent reused up the hierarchy from block to subsystem to SoC, active where the testbench drives the interface and passive where the design drives it, following the interface as it goes internal, with only is_active changing. Across projects, there's project reuse — the same protocol agent dropped into a different chip that uses the same protocol, with no re-implementation, just configuration for the new environment. And across organizations, there's team and vendor reuse — an agent shared between teams, or a third-party VIP that you buy and drop into your testbench. All of these are the same agent code; they differ only in configuration — which instance, active or passive, which protocol parameters. That's the economic argument for the reuse discipline: an agent built reusably is built once and amortized across every interface of that protocol anywhere — dozens of instances, every level, every project, every team — whereas an agent built with environment-specific dependencies has to be rebuilt or forked every time, which is a huge duplication of effort and a divergence risk. The same protocol recurs everywhere — AXI appears in dozens of DUTs and every project that uses it — so the payback on building its agent once, reusably, is enormous. The whole spectrum of reuse is available, but only if the agent is self-contained, configured not modified, with a clean boundary, and packaged complete. Break any of those and the spectrum collapses to the one place it was written.
A VIP, verification IP, is the complete package that bundles the agent with everything needed to use it: the sequence library, the config object, and the protocol checks. The agent — sequencer, driver, monitor — is the core, but it's useless alone. An agent with no sequences generates no stimulus; with no config it can't be tuned; with no protocol checks it verifies nothing. So the VIP bundles the agent with the sequence library, which is the traffic generators tests run on the agent's sequencer; the config object, which holds the knobs like width, mode, and is_active; and the protocol checks, which are the assertions and coverage that verify the protocol. A consumer — another team or project — reuses the whole package and has a working component out of the box: drop it in, configure it, run its sequences, subscribe to its analysis port. The reason the package, not the bare class, is the unit of reuse is that reusing just the agent would force every consumer to re-write the sequences, re-derive the config, and re-author the protocol checks, which defeats the reuse. The point of reuse is to inherit a complete capability, not a construction project. This is why mature verification organizations and VIP vendors ship packages, not classes: a third-party AXI VIP arrives as an agent plus a rich sequence library plus a config API plus a full assertion and coverage suite, and the consumer is immediately productive. So when you build a reusable agent, you build its sequences, config, and checks alongside it and package them together, so the next user inherits everything needed to use it. The VIP is the real deliverable; the agent class is just its core.
Configuration over modification means all per-use variation enters the agent through a config object that the environment sets, so adapting the agent to a new use is changing configuration, never editing code. The agent declares a config object holding everything that can differ per use: the virtual interface handle, the active-passive setting, and protocol parameters like data width, timing mode, and maximum outstanding transactions. The agent receives this config — typically through the config database — and builds and behaves according to it: it builds a driver and sequencer only if configured active, it uses the configured width, and so on. The environment creates the config object, sets this environment's choices, and hands it to the agent. So to use the agent on a 64-bit bus, the environment sets data_width to 64; the agent code is unchanged. To make it observe instead of drive, the environment sets is_active to passive; the agent code is unchanged. This matters for reuse because the alternative — hardcoding these values — would force a code change for each use, which means forking the agent. If the width is a constant in the agent, a 64-bit project has to edit the agent, creating a second copy that then drifts from the original. If the width is a config knob, the 64-bit project just configures it, and there's one codebase serving all widths. The same logic applies to active versus passive, timing modes, and instance count. So configuration over modification is what keeps reuse to one source of truth: every use is a configuration of the same code, never a modification that forks it. The discipline is to make every value that could vary per use a config field handed in by the environment, and to refuse to hardcode any of them.
The most common way is a hardcoded environment-specific dependency — typically the agent reaching for its virtual interface by an absolute hierarchical path or a fixed config scope that exists only in the original environment. The agent works perfectly in the project it was written for, because that path is valid there. But when a second project tries to reuse it, the new project's hierarchy is different — different environment names, different nesting — so the hardcoded path is invalid, the agent gets a null interface, and it fatals at build before it can do anything. The agent claimed to be reusable but was welded to its first environment by that one reference. Other forms of the same bug are referencing a named environment component, like the agent's monitor directly accessing a specific scoreboard, which couples the agent to an environment that has that exact component; or baking a protocol parameter in as a constant, like assuming 32-bit data, which breaks on a 64-bit variant. All of these violate self-containment: the agent reached out to its environment's specific structure instead of receiving what it needed. The fix is to receive everything environment-specific through a config object that whatever environment instantiates the agent sets for its own hierarchy. The agent gets the interface handle from cfg.vif and doesn't know or care where the interface lives in the hierarchy; each environment sets cfg.vif for its structure, so the same agent works in all of them. The rule is: receive everything environment-specific through config, and reach out to nothing — no absolute paths, no named components, no hardcoded parameters. The reusable label is earned by self-containment and forfeited by a single hardcoded wire, so you test reuse by actually instantiating the agent in a second, differently-structured environment.
Exercises
- List the principles. Name the four disciplines that make an agent reusable, and what each prevents.
- Spot the dependency. Given an agent that sets
vif = top.dut.axi0, explain why it isn't reusable and how to fix it. - Package a VIP. List what ships in a VIP beyond the agent class, and why each is needed.
- Reuse without modification. Describe every step to reuse an agent in a new project, and confirm none of them edits the agent.
Summary
- Agent reuse makes a protocol agent, built once, a plug-and-play verification component reused across instances, integration levels, projects, and teams — but it must be designed in, never assumed; one env-specific dependency breaks it.
- Self-containment: the agent depends only on its interface protocol — never on the specific DUT, hierarchy, or environment — and reaches out to nothing (no absolute paths, no named components).
- Configuration over modification: all per-use variation (the virtual interface,
is_active, protocol parameters) flows through a config object the environment sets — you change config, never code — so each use is a configuration, not a fork. - Clean boundary + packaging: the agent exposes only a sequencer and an analysis port (the same interaction everywhere), and ships with its sequence library, config, and protocol checks as a distributable VIP — the real unit of reuse.
- The durable rule of thumb: build the agent to depend only on its protocol and a config object handed in — receive the interface and every parameter through config, reach out to nothing, expose only a sequencer and an analysis port, and ship it as a complete VIP — so reuse across instances, levels, projects, and teams is configuration and connection alone; one hardcoded path, named component, or baked-in parameter welds the agent to one environment and forfeits the reuse.
Next — Protocol Agents: the reuse discipline in its most concrete form — building agents for real protocols. The next chapter looks at protocol-specific agents (AXI, APB, and the like): how a standard protocol shapes the agent's transaction, driver, monitor, and sequences, and what makes a protocol agent a high-value, widely-reused VIP.