Part 5 · Sequences · Intermediate

The Coordination Problem: DMA + APB + AXI Scenario

Why chip-level scenarios need orchestration, the three-agent DMA walkthrough, and test sprawl without virtual sequences.

One scenario, three agents

Consider a DMA engine transfer test: program source address, destination address, and length over the APB config port; write source data to memory over AXI; assert the DMA START bit; wait for completion interrupt; read back destination memory over AXI and compare. This single scenario touches three agents — APB, AXI master, and optionally an interrupt/DMA status agent.

Each agent has its own sequencer and driver. The scenario has a strict ordering constraint (program before start, start before memory check) and parallel phases (AXI reads and writes while DMA runs). Something must coordinate across agents — that coordinator is a virtual sequence.

diagram
[STIM] DMA transfer scenario — agent involvement

  Step 1  [APB]  Program DMA_SRC, DMA_DST, DMA_LEN registers
  Step 2  [AXI]  Write source buffer data to memory
  Step 3  [APB]  Write DMA_CTRL.START = 1
  Step 4  [APB]  Poll DMA_STATUS or wait for IRQ
  Step 5  [AXI]  Read destination buffer, compare to source

  Agents:  apb_agent   axi_agent   (irq_monitor for Step 4)
  Sequencers: apb_sqr   axi_sqr

The sprawl problem — test reaches into agents

Without virtual sequences, the test's run_phase directly starts sequences on each agent's sequencer. Scenario logic sprawls across the test file, cannot be reused in other tests, and breaks agent encapsulation — the test knows internal agent hierarchy details.

systemverilog
// ANTI-PATTERN: scenario logic in the test
task run_phase(uvm_phase phase);
  prog_dma_seq  prog = prog_dma_seq::type_id::create("prog");
  axi_wr_seq    wr   = axi_wr_seq::type_id::create("wr");
  axi_rd_seq    rd   = axi_rd_seq::type_id::create("rd");

  phase.raise_objection(this);

  prog.src = 32'h1000; prog.dst = 32'h2000; prog.len = 256;
  prog.start(env.apb_agent.sqr);           // test knows apb_agent

  wr.addr = 32'h1000; wr.len = 256;
  wr.start(env.axi_agent.sqr);           // test knows axi_agent

  // ... 50 more lines of coordination ...

  phase.drop_objection(this);
endtask
diagram
[VSEQ] sprawl vs encapsulation

  WITHOUT virtual sequence:
    TEST ──direct──► apb_agent.sqr
    TEST ──direct──► axi_agent.sqr
    TEST ──direct──► dma_agent.sqr
    Scenario logic in test  not reusable, env coupling

  WITH virtual sequence:
    TEST ──► dma_xfer_vseq.start(env.v_sqr)
    VSEQ ──► sub-sequences on p_sequencer.apb_sqr / .axi_sqr
    Scenario in one reusable class  test stays thin

Scenario timeline — ordering and parallelism

The DMA scenario mixes sequential steps (program before start) with parallel traffic (AXI read and write streams during transfer). A virtual sequence expresses both with procedural SystemVerilog in body().

diagram
[VSEQ] dma_xfer_vseq — scenario timeline

  TIME ─────────────────────────────────────────────────────────────►

  APB:  [prog SRC][prog DST][prog LEN]──────[START]──────[poll STATUS]
  AXI:              [wr stream src buf]───────────────────────────────
  AXI:                                    [rd stream dst buf]────────
  DMA:                                    ▲ engine active ──────────▲

  Sequential:  prog completes before START
  Parallel:    AXI wr during DMA; AXI rd after DMA done
  Coordinator: virtual sequence body() — fork/join + ordered calls

Why agents cannot own this scenario

  • APB agent knows APB protocol — not AXI memory patterns or DMA flow.

  • Scenario spans agents — belongs at env/chip level, not inside one VIP.

  • Reuse: same dma_xfer_vseq in smoke test, stress test, and directed corner test.

  • [VSEQ] Virtual sequence is the scenario container; agents stay protocol-pure.


Virtual sequence as the answer

A dma_xfer_vseq encapsulates the entire scenario. The test sets knobs (src, dst, len), starts the vseq on the virtual sequencer, and drops objection when body() completes. All cross-agent ordering lives in one class.

systemverilog
// CLEAN pattern: thin test
task run_phase(uvm_phase phase);
  dma_xfer_vseq vseq = dma_xfer_vseq::type_id::create("vseq");
  vseq.randomize() with { len == 256; };
  phase.raise_objection(this);
  vseq.start(env.v_sqr);
  phase.drop_objection(this);
endtask

Key takeaways

  • Chip scenarios span agents — need a coordinator above agent level.

  • Tests that reach into agent.sqr sprawl and break reuse.

  • Virtual sequence encapsulates cross-agent ordering and parallelism.

Common pitfalls

  • Putting DMA scenario in test run_phase — copy-paste across tests.

  • One mega-sequence inside AXI agent that calls APB — wrong ownership.

  • Assuming monitors coordinate stimulus — monitors observe; vseqs stimulate.