Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
    • What Is UVM?
    • Testbench Architecture and Environment Composition
      • The Four Layers
      • The Agent as the Reuse Unit
      • The Env Owns the Infrastructure
      • Coordinating Agents
      • Active-Passive Mixes and Composition Debug
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Testbench Architecture and Environment Composition
  1. Topics
  2. UVM Verification
  3. UVM Foundations
  4. Testbench Architecture and Environment Composition
  5. Active-Passive Mixes and Composition Debug

Part 1 · Foundations · Intermediate

Active-Passive Mixes and Composition Debug

The DMA env at block and subsystem level, the activity matrix, and a fixed five-step triage using UVM's own debug calls.

On this page

    The DMA moves into a subsystem. APB is now driven by a real CPU model, and the input stream comes from an upstream block, so the subsystem bench reuses dma_env with the APB agent and in_agt passive. The first run dies at time 0 with a null-object access in dma_base_test::reg_write. The second, after a quick fix, runs to the end with SB_EMPTY: nothing was compared. Two failures, two different boundaries. How do you find which one is broken without guessing?

    The idea: the same env, a different activity mix

    A reusable env is assembled differently in each bench. Which agents are active is a policy, and it changes the shape of the tree: passive agents have no sequencer or driver, so anything that expects them (tests, virtual sequences, handle copies) has to cope. When something goes wrong in a composed bench, the failure almost always sits on one of five boundaries: something was not created, not configured, not connected, has the wrong activity, or a checker is not ingesting traffic.

    Debugging those in a fixed order is like a mechanic's checklist for a car that will not start: fuel, spark, air, in that order, because each step is cheap and rules out a whole class of causes. Random poking ("let's look at waves") skips the cheap checks and usually starts at the most expensive one.

    The picture: the triage order

    Five triage steps in order: created, configured, connected, activity, ingestion, each with the UVM call that answers it.
    Each step is cheap, uses UVM's own reporting, and rules out one boundary before you open a waveform.

    Build it: the DMA env in two benches

    The activity matrix

    Agent

    Block bench

    Subsystem bench

    What changes for tests

    apb

    Active: the test programs the DMA

    Passive: the CPU model programs it

    No reg_write from the test; register intent comes from software

    in_agt

    Active: sequences make packets

    Passive: the upstream block sends them

    No packet sequences on this agent

    out_agt

    Passive

    Passive

    Nothing: the scoreboard still checks every word

    sb

    Checks from three monitors

    Checks from the same three monitors

    Nothing: this is the payoff

    The last two rows are the point of the whole architecture. The scoreboard never depended on stimulus, only on monitors, so it keeps checking the DMA at subsystem level without a line of change. Everything that does change is on the stimulus side.

    A debug packet every test prints

    Put the cheap checks into the base test once, and every failing run carries its own evidence. This report_phase prints the activity of each agent, the scoreboard's ingest counters and the analysis connections; the topology is already printed at end_of_elaboration.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // snippet: added to a base test
    function void report_phase(uvm_phase phase);
      `uvm_info("DBG", $sformatf("apb=%s in=%s out=%s",
        env.apb.get_is_active().name(), env.in_agt.get_is_active().name(),
        env.out_agt.get_is_active().name()), UVM_LOW)
      `uvm_info("DBG", $sformatf("sb: %0d matched, %0d mismatched, %0d expected left",
        env.sb.n_match, env.sb.n_mismatch, env.sb.expected.size()), UVM_LOW)
      env.apb.ap.debug_connected_to();      // prints what each port reaches
      env.in_agt.ap.debug_connected_to();
      env.out_agt.ap.debug_connected_to();
    endfunction

    Question

    UVM's answer

    Typical finding

    Was it created, and as which type?

    print_topology(); uvm_factory::get().print()

    An override that never applied; a passive agent with a driver

    Did configuration reach it?

    +UVM_CONFIG_DB_TRACE; check_config_usage(); print_config()

    A set whose path matches nothing; a get that never found its value

    Is every port connected?

    debug_connected_to(); get_connected_to()

    A monitor port with no subscribers

    Is it active as intended?

    get_is_active()

    is_active set too late, or super.build_phase skipped

    Is the checker hearing traffic?

    The scoreboard's own counters

    Zero compares: stimulus never ran, or the test ended early

    Watch it run: what the topology already told you

    Step 1 costs nothing, because the bench prints its topology at time 0. In the block bench, the stream agents print as in The Agent as the Reuse Unit: in_agt has drv, mon and sqr, while out_agt has only mon.

    In the subsystem bench, in_agt would look like out_agt: a monitor and nothing else. Read that against the test code and the hook's first failure explains itself before you open a single source file.

    What breaks: two failures, two boundaries

    The null at time 0 is an activity problem surfacing in a test. dma_base_test::reg_write starts apb_rw_seq on env.apb.sqr. With the APB agent passive, there is no sequencer: the handle is null, and starting a sequence on it is a null-object access. The test was written for a block bench, where the APB agent is always active. The fix is not to make the agent active again (it would fight the CPU model on the APB wires); it is a subsystem test that does not program registers itself, and a base test that checks get_is_active() before using a sequencer, failing with a clear message instead of a null access.

    The `SB_EMPTY` after the quick fix is an ingestion problem with a cause on the stimulus side. With the test no longer programming the DMA, nobody started a job: the CPU model's software had not been loaded in this configuration. The monitors were connected and the scoreboard was listening, but there was no traffic, and the scoreboard's "nothing was compared" check turned a silent false pass into a visible failure. That check is the cheapest piece of insurance in the bench; without it, this run would have been green.

    Common pitfall

    Do not "save simulation time" by switching off monitors on passive agents. At chip level the passive monitors are the only source of observed truth; a scoreboard with no input proves nothing.

    Interview

    "You integrate a block env into a subsystem and the scoreboard stops reporting matches. How do you debug it?"

    A weak answer: "I'd open the waveforms and look at the interfaces." Expensive, and it skips the likely causes.

    A strong answer gives an order and the tools: "First the topology print: is every component there, with the right type and the right active or passive shape? Then config: +UVM_CONFIG_DB_TRACE and check_config_usage for sets that matched nothing. Then connections: debug_connected_to on each monitor port. Then the scoreboard's own counters, to tell 'no traffic' from 'traffic not reaching me'. Only then waves, at a specific time I already suspect."

    Expect the follow-ups: Why does the same scoreboard work unchanged at subsystem level? (it depends only on monitors, never on stimulus) and How would you catch this class of bug automatically? (an end-of-elaboration check in the base test that the required sequencer handles are non-null, and a scoreboard that fails on zero compares).

    Check yourself A run shows monitor messages for every transfer at UVM_HIGH, but the scoreboard reports 0 matched and 0 mismatched with SB_EMPTY. Which triage step comes first?

    Connectivity (step 3). The monitors see traffic, so creation, configuration and activity on the monitor side are fine; the transactions are not reaching the scoreboard. Check debug_connected_to() on each agent's ap and the env's connect_phase before anything else.

    Check yourself Why must activity policy be fixed before build, rather than switched per test phase?

    Activity decides what the agent builds. uvm_agent::build_phase reads is_active once, and the driver and sequencer are created or skipped then. A later change rebuilds nothing, and the tree is frozen after build anyway, so creating a driver later would be an ILLCRT fatal.

    Check yourself In the subsystem bench, which DMA testplan rows can the reused env still check, and which need new stimulus?

    Every row whose check uses monitors (data XOR, early TLAST via STATUS reads observed on APB, W1C behaviour if software exercises it) can still be checked passively. Rows that need deliberate stimulus, such as KEY written mid-job or a long packet, need the CPU software or the upstream block to create them; the passive env can only check what real traffic happens to do.

    Next: the bench's structure is in place. Phases explains the schedule that builds, connects and runs it, and how a test decides it is finished.

    Related topics

    • Phases

    • The is_active knob

    • Subsystem and SoC composition

    Previous

    18 / 280

    Next: UVM Phases & Simulation Lifecycle

    Up next

    Up nextUVM Phases & Simulation Lifecycle

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Four Layers
    • The Agent as the Reuse Unit
    • The Env Owns the Infrastructure