Part 6 · Agents & Protocol IP · Intermediate
Virtual Sequencer Bridge: Coordinating Multi-Agent Scenarios
How virtual sequencers and virtual sequences coordinate traffic across multiple protocol agents with clear ownership.
Coordination problem and solution
Complex SoC scenarios span multiple interfaces (control bus, data bus, interrupts, sideband links). A virtual sequencer provides handles to child agent sequencers, and a virtual sequence orchestrates their interaction.
[UVM][SEQ][AGT] virtual bridge model
virtual sequence
│
▼
virtual sequencer
├─ ctrl_sqr handle
├─ dma_sqr handle
└─ irq_sqr handle
virtual sequence coordinates start/order/constraints
child sequencers still arbitrate within each agent domainclass soc_vseqr extends uvm_sequencer #(uvm_sequence_item);
`uvm_component_utils(soc_vseqr)
ctrl_sequencer ctrl_sqr;
dma_sequencer dma_sqr;
irq_sequencer irq_sqr;
endclassVirtual sequencer is a coordination handle container, not a protocol sequencer replacement.
Each child agent sequencer still owns local arbitration decisions.
Use virtual sequences for cross-agent ordering and dependency logic.
Practical virtual sequence pattern
A typical virtual sequence starts setup on control bus, waits for model/monitor confirmation, then starts high-volume data traffic and coordinated sideband events.
class boot_dma_vseq extends uvm_sequence #(uvm_sequence_item);
`uvm_object_utils(boot_dma_vseq)
`uvm_declare_p_sequencer(soc_vseqr)
task body();
ctrl_cfg_seq cfg;
dma_burst_seq dma;
irq_expect_seq irq;
cfg = ctrl_cfg_seq::type_id::create("cfg");
dma = dma_burst_seq::type_id::create("dma");
irq = irq_expect_seq::type_id::create("irq");
cfg.start(p_sequencer.ctrl_sqr);
irq.start(p_sequencer.irq_sqr);
dma.start(p_sequencer.dma_sqr);
endtask
endclass[SEQ] coordination best practices
1) explicit ordering points for inter-agent dependencies
2) avoid hidden sleeps; prefer protocol/model events
3) keep child sequence responsibilities local
4) isolate cross-agent policy in virtual sequence layer[MON][UVM][AGT] monitor-assisted coordination
virtual sequence may wait on monitor-derived events
(via scoreboards/events), but should not read monitor internals directly.
preferred:
wait(event_pool.get("cfg_done"))
avoid:
wait(env.ctrl_agt.mon.private_queue.size() > 0)Use protocol/model events for synchronization instead of arbitrary delays.
Keep monitor interaction indirect through stable env events/checkers.
Preserve clear split between coordination and local sequence generation.
Bridge debug and failure isolation
Virtual coordination failures can stem from wrong handle wiring, child sequence startup errors, or missing cross-agent synchronization events. Instrument each layer separately.
[SEQ] virtual bridge debug sequence
layer 1: verify virtual sequencer got all child handles
layer 2: verify each child sequence started on intended sequencer
layer 3: verify synchronization events triggered
layer 4: verify downstream monitor/scoreboard behavior matches orchestrationfunction void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
if (ctrl_sqr == null || dma_sqr == null || irq_sqr == null)
`uvm_fatal("VSEQR", "virtual sequencer missing child handles")
endfunction[SEQ][UVM] common bridge mistakes
- forgetting to assign one child sequencer handle
- starting child seq on wrong sequencer type
- phase mismatch: child sequence starts before required setup
- implicit dependencies with no event/handshake enforcementKey takeaways
Virtual sequencer bridges cross-agent orchestration, not protocol data driving.
Child sequencers keep local arbitration ownership under virtual control.
Event-based synchronization is safer than delay-based coordination.
Layered debug quickly isolates handle, startup, or synchronization failures.
Common pitfalls
Using virtual sequencer as a monolithic replacement for child sequencers.
Directly coupling virtual sequences to monitor private implementation details.
Implicit cross-agent dependencies with no explicit event barriers.
Ignoring child handle null checks until run-time failures.