Part 5 · Sequences · Intermediate
Fork/Join Patterns: join, join_any, Layered vseqs
Parallel independent traffic, join_any with disable fork, IRQ wait patterns, and layered virtual sequence reuse.
Parallel independent traffic — fork/join
Many chip scenarios run independent traffic streams concurrently — AXI stress while APB background config continues. Inside a virtual sequence, fork/join starts multiple sub-sequences in parallel and waits for all to complete.
task body();
axi_wr_seq axi_stress = axi_wr_seq::type_id::create("axi_stress");
apb_bg_seq apb_bg = apb_bg_seq::type_id::create("apb_bg");
fork
begin
axi_stress.num_bursts = 100;
axi_stress.start(p_sequencer.axi_sqr);
end
begin
apb_bg.start(p_sequencer.apb_sqr);
end
join // [VSEQ] wait for BOTH to complete before continuing
`uvm_info("VSEQ", "parallel stress complete", UVM_MEDIUM)
endtask[VSEQ] fork/join — parallel sub-sequences
TIME ─────────────────────────────────────────────────────────────►
axi_stress ON axi_sqr: [burst][burst][burst]...[burst]──done
apb_bg ON apb_sqr: [wr][wr][wr]...[wr]──────────────done
vseq body: fork──────────────────────────join──►
▲
continues here after BOTH donefork/join_any — first completion wins
Use join_any when the scenario proceeds as soon as one branch completes — classic pattern: DMA transfer OR interrupt, whichever comes first. Follow with disable fork to kill the losing branch and prevent stray sequences.
task body();
irq_wait_seq irq = irq_wait_seq::type_id::create("irq");
dma_run_seq dma = dma_run_seq::type_id::create("dma");
fork
irq.start(p_sequencer.apb_sqr); // polls STATUS or waits for event
dma.start(p_sequencer.dma_sqr);
join_any
disable fork; // [VSEQ] kill the other branch — critical
`uvm_info("VSEQ", "DMA or IRQ completed first", UVM_MEDIUM)
endtask[STIM] join_any + disable fork — IRQ race
Branch A (irq_wait_seq): poll STATUS register every 100ns
Branch B (dma_run_seq): start DMA, wait for engine idle
If IRQ fires at 500ns, Branch A completes → join_any exits
disable fork kills Branch B — prevents duplicate status checks
WITHOUT disable fork: Branch B keeps running → objection leak or double actionLayered virtual sequences — reuse building blocks
Top-level chip virtual sequences compose smaller scenario vseqs. Each sub-vseq is independently reusable and starts on the same virtual sequencer — inheriting the same p_sequencer handles.
class chip_stress_vseq extends uvm_sequence;
`uvm_declare_p_sequencer(soc_virtual_sequencer)
`uvm_object_utils(chip_stress_vseq)
task body();
dma_xfer_vseq dma = dma_xfer_vseq::type_id::create("dma");
pcie_boot_vseq boot = pcie_boot_vseq::type_id::create("boot");
// [VSEQ] Sub-vseqs start on same virtual sequencer
dma.start(p_sequencer);
boot.start(p_sequencer);
endtask
endclass[VSEQ] layered virtual sequence hierarchy
chip_stress_vseq
│
├── dma_xfer_vseq.start(p_sequencer)
│ ├── prog_dma_seq.start(p_sequencer.apb_sqr)
│ └── axi_wr/rd.start(p_sequencer.axi_sqr)
│
└── pcie_boot_vseq.start(p_sequencer)
├── pcie_link_seq.start(p_sequencer.pcie_sqr)
└── pcie_cfg_seq.start(p_sequencer.pcie_sqr)
Each layer reusable independently in other top-level vseqsPattern selection guide
When to use which
Sequential sub-sequences — ordered steps, no fork (program then start).
fork/join — parallel independent streams, wait for all (stress + bg).
fork/join_any + disable fork — race, first event wins (IRQ vs timeout).
Layered vseqs — compose scenarios from reusable dma_xfer_vseq, boot_vseq blocks.
// Sequential then parallel — common DMA pattern
task body();
prog.start(p_sequencer.apb_sqr); // must complete first
fork
dma_engine.start(p_sequencer.dma_sqr);
axi_mon.start(p_sequencer.axi_sqr);
join
check.start(p_sequencer.axi_sqr); // after parallel phase
endtaskKey takeaways
fork/join — parallel sub-sequences, wait for all branches.
fork/join_any + disable fork — first completion wins; kill stragglers.
Layered vseqs compose reusable scenario blocks on same p_sequencer.
Common pitfalls
join_any without disable fork — losing branch keeps running.
Nested fork without clear objection ownership — premature test end.
Parallel sub-sequences on same sequencer — arbitration interleaves (see arbitration topic).