Agent questions test whether you have built reusable VIP or only used it. The interviewer is waiting for the reuse story: the same agent driving at block level and only watching at chip level, a monitor that never trusts the driver, a reset in the middle of a burst. Answers that only list the parts (driver, monitor, sequencer) score nothing.
A senior answer describes the contract each part keeps and the bug you get when it breaks. The DMA bench gives you all of them: a passive output agent, an instance-overridden reactive sink, reset-aware drivers, an interrupt agent and a subsystem env that reuses the block env twice unchanged.
The questions What does is_active change, and how is it set? Weak answer: A passive agent just does not start any sequences.
Strong answer: A passive agent does not build its driver and sequencer at all, only the monitor, so nothing can drive. The env sets is_active through config_db before creating the agent, and uvm_agent::build_phase reads it, which is why the agent's own build must call super.build_phase(); skip it and the agent stays UVM_ACTIVE. The DMA bench's env makes out_agt passive, and the subsystem env makes dma1's input agent passive because dma0's output already drives it.
Follow-up: “How do components ask?” get_is_active(), rather than reading the variable, so a derived agent can override the decision.
Home lesson: The is_active knob
Should the monitor take information from the driver or the sequence? Weak answer: Yes, it saves decoding the bus twice.
Strong answer: Never. The monitor reconstructs transactions from the pins alone. If it trusts the driver, it reports what the driver meant to do, so driver bugs become invisible, and the agent cannot work passive at chip level where no driver exists. The same rule holds for the scoreboard: it predicts from observed inputs and observed register state, never from the sequence.
Follow-up: “Does the monitor raise objections while a transfer is in progress?” No. Monitors never raise objections; end of test belongs to the test, plus a scoreboard through phase_ready_to_end if it owes items.
Home lesson: Passive, monitor-only reuse
Your monitor occasionally misses a beat. Where do you look? Weak answer: Add a #1 delay before sampling.
Strong answer: At what each thread waits on. On the DMA bench, the APB driver and the stream driver waited on two different clocking blocks of the same clock; their events fire in an undefined order within a time step, so the stream driver caught the current edge instead of the next and believed TREADY for a beat the DUT never took. The fix: every driver waits on @(posedge clk) and drives with nonblocking assignments, and monitors keep sampling through a clocking block with #1step input skew. A #1 hides the race in one simulator and moves it in the next.
Follow-up: “Why do clocking blocks help?” Inputs are sampled in the preponed region with #1step, so the monitor sees values from before the edge, regardless of process order.
Home lesson: Monitor sampling races
Why must a monitor broadcast a new object for each transaction? Weak answer: It does not matter; subscribers copy what they need.
Strong answer: Analysis ports pass handles. A monitor that reuses one item and refills it changes every item already stored by subscribers: the scoreboard's queue becomes many references to the last transfer. Create a new item per transaction or clone before write. Subscribers that keep an item must not expect it to stay unchanged if the monitor reuses its handle.
Follow-up: “Is cloning in the monitor expensive?” One allocation per transfer is cheap next to a false mismatch hunt; it is the publisher's job, so subscribers do not each clone.
Home lesson: Passive sampling
How does the virtual interface reach the driver and monitor? Weak answer: Every component gets the interface from config_db itself.
Strong answer: The top module sets it once in config_db, scoped to one agent's subtree: the DMA bench uses uvm_test_top.env.out_agt.*, so that agent's driver and monitor read it and nobody else does. Many teams put it in the agent's config object instead, so the agent reads one handle and passes it down. Every reader fatals if it is missing. For reuse across parameterizations, an abstract class or a bound interface hides the parameters from the classes.
Follow-up: “Why fatal rather than error on a missing vif?” Everything after it would dereference null; one clear message at build beats a crash at time 0 deep in a driver.
Home lesson: Interface abstraction
A reset asserts in the middle of a transfer. What does each part of the agent do? Weak answer: Reset the DUT and restart the test.
Strong answer: The driver races each transfer against reset, drives reset values (the bench parks APB and drops TVALID), and still calls item_done for the item it took. The monitor discards the partial transaction. The scoreboard flushes, and the timing matters: the DMA bench's dma_scoreboard_ra flushes when reset ends , because a beat can complete on the very edge where reset asserts. The test kills the old sequences and starts new ones that reprogram the DUT.
Follow-up: “What does the bench log show?” In dma_reset_test: both drivers report reset seen, the scoreboard reports reset: 0 prediction(s) dropped , and the second job then matches 8 words.
Home lesson: Reset on the fly
How does a reactive slave agent decide its responses? Weak answer: The slave driver randomizes its own responses.
Strong answer: The decisions stay in a sequence so tests can change them. On the DMA bench, axis_sink_agent drives TREADY; axis_ready_seq runs forever supplying per-beat stall counts, started with fork ... join_none and no objection, so it never holds the test open and is killed at phase end. dma_backpressure_test swaps it in with an instance override at env.out_agt and logs the slave stalled the DMA for 28 clocks .
Follow-up: “When does the slave need the request first?” When the response depends on it (a memory slave). The monitor or driver publishes the request, and the sequence waits for it and responds.
Home lesson: Reactive slave agents
Error injection: callbacks or a factory override of the driver? Weak answer: Add an inject flag to the driver.
Strong answer: Both work if the driver has a hook. dma_truncate_test overrides axis_driver with axis_truncating_driver, which uses a virtual pre_drive hook and clones the item before modifying it; the DMA reports ERR, as expected. A factory override is simple when one variant replaces the driver; callbacks win when several independent injectors must stack without a subclass per combination. Either way the checker must expect the fault.
Follow-up: “Why clone before modifying?” The item is shared with the sequence (and anything else holding the handle); modifying it in place changes what the sequence thinks it sent.
Home lesson: Callbacks versus factory overrides
How should a sequence wait for an interrupt? Weak answer: Wait on the interrupt signal through the virtual interface.
Strong answer: Through an interrupt agent. On the DMA bench irq_agent's monitor triggers a global uvm_event named "dma_irq", and sequences wait on the event. The sequence stays pin-free and reusable, and the interrupt is observed exactly once, by a monitor that can also publish it to the scoreboard and coverage.
Follow-up: “Event or analysis port?” Analysis port for checkers and coverage; an event (or a FIFO the sequence reads) for stimulus that must block until it happens.
Home lesson: Interrupt agents
What must not change when your block agent goes into a subsystem bench? Weak answer: Nothing changes; you reuse the same env.
Strong answer: The agent classes themselves must not change. dma_subsys_env reuses dma_env twice unchanged: dma1's input agent becomes passive because dma0's output drives it, each block scoreboard keeps checking from its monitors, and an end-to-end checker subscribes on top. That only works if the agent has no hard-coded paths, takes everything from its config object, and its monitor works without a driver.
Follow-up: “How do you tell two instances' coverage apart?” Give each covergroup instance a distinct name ( set_inst_name or option.name), or the reports merge into one.
Home lesson: VIP parameterization
Rapid fire Check yourself A passive agent at chip level still drives the bus. Name two likely causes. The agent's build_phase skips super.build_phase(), so is_active stays UVM_ACTIVE; or a lower-precedence setter (the env) set it while the test set ACTIVE, and the test wins during build. A wildcard like env.agt.* that misses the agent itself is a third.
Check yourself Every coverage report shows two identical rows named cg for the two stream agents. What is missing? Distinct instance names. Each covergroup instance per agent needs its own name, set with set_inst_name() or option.name, or the reports cannot be told apart.
Next: Concept Round: Register Layer and Checking : the mirror disagrees with the DUT after a legal write; whose bug is it?