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
  • 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
    • Verification Architecture and Planning
      • Writing a Testplan from the Spec
      • Estimating Verification Effort
      • A Block Bench That Upstream Can Reuse
      • From Block to Subsystem to SoC
      • Configuration Strategy Across Levels
      • Passive Agents and Who Owns the Check
      • Buy vs Build VIP, and Qualifying VIP
      • Choosing the Verification Strategy per Block
    • UVM with SVA and Formal
    • Leadership and Senior Judgment
    • Interview Bank
Loading lesson

‹Verification Architecture and Planning
  1. Topics
  2. UVM Verification
  3. Senior Prep: Architecture, Leadership and Interviews
  4. Verification Architecture and Planning
  5. From Block to Subsystem to SoC

Part 13 · Senior Prep · Intermediate

From Block to Subsystem to SoC

Composing the DMA block env into the two-DMA subsystem and a chip, and the integration plan that only higher levels can check.

On this page

    The two-DMA subsystem bench passes: both block scoreboards and the end-to-end checker agree on every word. Then the chip integration lead asks a question that bench cannot answer: if somebody swapped the two DMAs' interrupt lines in the subsystem netlist, which of your tests would fail? You look at dma_subsys_test. It waits until both interrupts are high. Swapped, they would still both go high. None of your tests would fail.

    This lesson builds upwards from the block bench to the subsystem and to a chip around it, and asks the question that organises every level: what can this level see that the level below cannot?

    The idea: each level checks what only it can see

    A higher level is not a bigger block bench. Block benches have already checked each block's behaviour with full control and visibility. A subsystem or chip bench repeats none of that; it adds integration checks: connections between blocks, flows that cross them, shared resources, and the software view. Everything else it inherits by reusing the block envs, with their scoreboards still running where they can see their inputs.

    An orchestra rehearses the same way. Each section practises its part alone, where a wrong note is easy to hear. The full rehearsal does not re-check whether the violins can play scales; it checks entries, balance and whether everyone follows the same conductor. A full rehearsal that spends its time on scales finds integration problems last.

    The picture: the DMA in a chip bench

    Diagram: inside soc_env, a CPU running C tests drives an interconnect monitor, which connects to dma0 and dma1 block envs with passive APB agents; both feed an end-to-end scoreboard; a chip register model supports virtual sequences.
    At chip level the CPU programs the DMAs through the interconnect, so the block APB agents go passive. The block envs keep their scoreboards; the chip adds the register model, end-to-end checking and software-driven stimulus.

    Build it: block, then subsystem, then chip

    Step 1: the subsystem env is composition, not new code

    dma_subsys_env creates two copies of dma_env, makes dma1's input agent passive because dut0 drives that bus, and adds one checker that sees the whole chain. Its config object carries the two decisions that differ between subsystem configurations.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    // snippet: from the course DMA bench
    // Two DMAs in a chain (dma0's output feeds dma1's input), verified by reusing the
    // block env twice, unchanged, plus an end-to-end checker.
    class dma_subsys_cfg extends uvm_object;
      bit has_e2e_checker = 1;                   // the end-to-end scoreboard
      bit chain_internal  = 1;                   // dma1's input is driven by dma0, so its agent is passive
      `uvm_object_utils(dma_subsys_cfg)
      function new(string name = "dma_subsys_cfg");
        super.new(name);
      endfunction
    endclass
    
    class dma_subsys_env extends uvm_env;
      `uvm_component_utils(dma_subsys_env)
      dma_subsys_cfg     cfg;
      dma_env            dma0, dma1;             // the block env, reused as is
      dma_e2e_scoreboard e2e;
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(dma_subsys_cfg)::get(this, "", "cfg", cfg))
          cfg = dma_subsys_cfg::type_id::create("cfg");   // defaults if the test set none
        if (cfg.chain_internal)
          uvm_config_db#(uvm_active_passive_enum)::set(this, "dma1.in_agt", "is_active", UVM_PASSIVE);
        dma0 = dma_env::type_id::create("dma0", this);
        dma1 = dma_env::type_id::create("dma1", this);
        if (cfg.has_e2e_checker) e2e = dma_e2e_scoreboard::type_id::create("e2e", this);
      endfunction
    
      function void connect_phase(uvm_phase phase);
        if (cfg.has_e2e_checker) begin
          dma0.apb.ap.connect(e2e.key0_export);
          dma1.apb.ap.connect(e2e.key1_export);
          dma0.in_agt.ap.connect(e2e.first_export);
          dma1.out_agt.ap.connect(e2e.last_export);
        end
      endfunction
    endclass

    Notice where the end-to-end checker gets its inputs: the APB monitors for both keys, dma0's input monitor and dma1's output monitor. All four already exist inside the block envs. The integration checker adds a model of the chain (in ^ key0 ^ key1), not new observation points. Vertical reuse walks the top and the test in detail.

    Step 2: an integration testplan, not a bigger block plan

    The subsystem plan lists only what no block bench can check. For the two-DMA chain:

    Integration item

    Why the block benches miss it

    Check

    Data through the chain

    Each block sees half the path

    dma_e2e_scoreboard

    Backpressure from dma1 stalls dma0 with no loss

    The block bench's sink is a testbench agent, not another DMA

    Both block scoreboards plus E2E, with dma1 stalled

    Each interrupt reaches its own controller input

    One DMA per bench; no wiring to get wrong

    Run one DMA alone and check only its interrupt; or formal connectivity

    Each instance answers at its own base address

    The block sits at offset 0

    Chip register model with submaps; block sequences run per instance

    Both DMAs active at once on shared resources

    No sharing at block level

    Concurrent jobs; bandwidth or arbitration checks where a fabric exists

    Reset of one domain while the other runs

    One reset per block bench

    Reset-aware scoreboards per instance

    The software flow: driver programs, waits, clears

    Block tests are UVM sequences, not driver code

    C tests on the CPU at chip level

    Step 3: what changes again at chip level

    • The APB agents go passive. The CPU, through the interconnect, now owns the register bus. The block's APB monitor keeps publishing, so dma0.sb still learns each KEY write and keeps checking.

    • Stimulus moves to software and virtual sequences. Directed C tests on the CPU exercise the real driver flow; UVM virtual sequences coordinate the CPU, external traffic and checking. Block sequences can still run on an interconnect agent through layering in a CPU-less configuration.

    • The register model composes. The chip register model places each block's map at its base address, and block register sequences run on soc.dma1 without edits.

    • Some questions leave simulation. Pin and interrupt connectivity under every configuration is a structural question, cheaper as formal connectivity checking; an OS boot needs emulation; power-domain behaviour needs power-aware simulation.

    Watch it run

    Diagram
    +UVM_TESTNAME=dma_subsys_test   (run.sh dma_subsys_tb.sv)
    UVM_INFO dma_tb.sv(411) @ 875000: uvm_test_top.env.dma0.sb [SB] 10 words matched, 0 mismatched
    UVM_INFO dma_tb.sv(411) @ 875000: uvm_test_top.env.dma1.sb [SB] 10 words matched, 0 mismatched
    UVM_INFO dma_tb.sv(1182) @ 875000: uvm_test_top.env.e2e [E2E] 10 words matched end to end, 0 mismatched
    UVM_ERROR :    0
    • Three checkers, three claims. dma0.sb says block 0 is right, dma1.sb says block 1 is right, e2e says the chain is right. If only e2e failed, the bug would be in the connection between them.

    • What it does not claim. Nothing in this log says which interrupt belongs to which DMA, which address decodes to which instance, or what happens when one DMA is reset alone. Those rows of the integration plan need their own tests.

    What breaks: the swap nobody's test could see

    Read dma_subsys_test again: it programs both DMAs, sends ten words and then waits for irq0_vif.irq === 1'b1 && irq1_vif.irq === 1'b1. If the two interrupt wires were swapped in the subsystem, both would still rise, both waits would still finish, and every checker would still pass. The test was written to make the chain work, not to tell the two DMAs apart, and that is the most common shape of an integration hole: a test whose stimulus is symmetric cannot detect a symmetric wiring error.

    Teams find this class of bug in the lab more often than they would like, typically as a driver that services the wrong channel's interrupt. The fixes are cheap once you see the pattern: an asymmetric test (start only dma1, assert irq1 rises and irq0 stays low), a check in the interrupt monitor that each interrupt follows its own DMA's STATUS, or a formal connectivity check that proves each interrupt output reaches the right controller input. Put the row in the integration plan first; the test follows.

    Interview

    "How do you go from block-level to SoC-level verification?"

    A weak answer: "We reuse the block testbench at SoC level and run all the block tests again."

    A strong answer separates reuse from new work: "I reuse the block envs as sub-envs: agents on buses something else now drives go passive, the block scoreboards keep checking from monitors, and the block register model becomes a submap of the chip model, so block register sequences run per instance. Then I write an integration plan with only what blocks can't see: data across blocks, backpressure between them, interrupt and address connectivity, sharing, reset of one domain, and the software flow, which runs as C on the CPU. I make the tests asymmetric, because a test that starts both DMAs and waits for both interrupts can't detect swapped wires. And I move structural questions like connectivity to formal, and OS-level runs to emulation."

    Follow-ups: Do you keep block scoreboards at chip level? (yes, wherever their inputs are observable; they localise failures). What do you not rerun at chip level? (block-level random regressions and directed block corner cases; the block bench already owns them).

    Check yourself The end-to-end checker fails, and both block scoreboards pass. Where is the bug most likely to be, and what does that tell you about keeping block scoreboards?

    Between the blocks or in the E2E model itself: a wiring or width problem on the middle bus, or a chain model that gets the key order wrong. Each block was correct on what its own monitors saw. That localisation is the reason to keep the block scoreboards running at higher levels: without them, every E2E failure starts with "which block?"

    Check yourself At chip level the APB agents are passive and the CPU programs the DMAs. How does dma0.sb still know the KEY for each job?

    Its APB monitor still watches dma0's APB interface and publishes every transfer, whoever drives it. The scoreboard's write_apb sees the CPU's KEY write exactly as it saw the block driver's. That is why the scoreboard was built to learn KEY from the monitor rather than from the stimulus.

    Check yourself Name one row of the integration plan that is cheaper outside UVM simulation, and the method you would use.

    Interrupt and address-decode connectivity: a formal connectivity check proves every interrupt output reaches the intended controller input, and every base address decodes to the right instance, for every configuration, in minutes. A simulation test can only show one configuration at a time.

    Next: three levels of env means three levels of settings. Configuration Strategy Across Levels keeps them in one tree.

    Related topics

    • Vertical reuse: from block to subsystem

    • From block scoreboard to end-to-end checker

    • The register model at SoC scale

    • Where the virtual sequencer lives

    Previous

    252 / 280

    Next: Configuration Strategy Across Levels

    Up next

    Up nextConfiguration Strategy Across Levels

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Writing a Testplan from the Spec
    • Estimating Verification Effort
    • Passive Agents and Who Owns the Check