Part 5 · Sequences · Intermediate
Virtual Sequencer Wiring: soc_virtual_sequencer & connect_phase
Virtual sequencer class structure, env integration, connect_phase handle assignment, and null-handle prevention.
Virtual sequencer — handle bundle, no driver
A virtual sequencer extends uvm_sequencer but is never connected to a driver. It exists solely to hold typed handles to real sequencers built inside agents. Virtual sequences run on this component and access sub-sequencers via p_sequencer.
class soc_virtual_sequencer extends uvm_sequencer;
`uvm_component_utils(soc_virtual_sequencer)
// [UVM] Handles to REAL sequencers — populated in connect_phase
apb_sequencer apb_sqr;
axi_sequencer axi_sqr;
dma_sequencer dma_sqr;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass[UVM] virtual sequencer vs agent sequencer
apb_agent.sequencer:
seq_item_port connected to apb_driver.seq_item_export [STIM]
Produces items → drives DUT pins
soc_virtual_sequencer (env.v_sqr):
NO driver connection
apb_sqr / axi_sqr / dma_sqr = pointers to agent sequencers
Virtual sequences start HERE; delegate via handlesEnv structure — v_sqr as sibling to agents
The virtual sequencer lives in the env, sibling to agents — not inside any agent. The env builds agents first, then the virtual sequencer, then assigns handles once agents exist.
class soc_env extends uvm_env;
`uvm_component_utils(soc_env)
apb_agent apb_agent;
axi_agent axi_agent;
dma_agent dma_agent;
soc_virtual_sequencer v_sqr;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
apb_agent = apb_agent::type_id::create("apb_agent", this);
axi_agent = axi_agent::type_id::create("axi_agent", this);
dma_agent = dma_agent::type_id::create("dma_agent", this);
v_sqr = soc_virtual_sequencer::type_id::create("v_sqr", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// [UVM] Wire handles AFTER agents built — sequencers exist now
v_sqr.apb_sqr = apb_agent.sqr;
v_sqr.axi_sqr = axi_agent.sqr;
v_sqr.dma_sqr = dma_agent.sqr;
endfunction
endclassconnect_phase wiring diagram
[UVM] connect_phase — handle assignment
soc_env
├── apb_agent
│ └── sqr ◄──────────────────── v_sqr.apb_sqr
├── axi_agent
│ └── sqr ◄──────────────────── v_sqr.axi_sqr
├── dma_agent
│ └── sqr ◄──────────────────── v_sqr.dma_sqr
└── v_sqr (soc_virtual_sequencer)
apb_sqr ──pointer──► apb_agent.sqr
axi_sqr ──pointer──► axi_agent.sqr
dma_sqr ──pointer──► dma_agent.sqr
Assignment in connect_phase — NOT build_phase
(agent.sqr must exist; build order within env is guaranteed)Use connect_phase, not build_phase, for handle assignment. While build_phase creates children in order, connect_phase is the conventional place for TLM connections and cross-component references — consistent with monitor-to-scoreboard wiring.
Null handle prevention
The most common virtual sequence failure is null object access on p_sequencer.apb_sqr — connect_phase never assigned the handle, or the vseq was started on the wrong sequencer (agent.sqr instead of env.v_sqr).
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
v_sqr.apb_sqr = apb_agent.sqr;
v_sqr.axi_sqr = axi_agent.sqr;
v_sqr.dma_sqr = dma_agent.sqr;
// Defensive check — catch wiring bugs early
if (v_sqr.apb_sqr == null)
`uvm_fatal("CONNECT", "v_sqr.apb_sqr not assigned")
if (v_sqr.axi_sqr == null)
`uvm_fatal("CONNECT", "v_sqr.axi_sqr not assigned")
endfunction[VSEQ] null p_sequencer debug
Error: NULL pointer dereference at prog.start(p_sequencer.apb_sqr)
Checklist:
1. vseq.start(env.v_sqr) — NOT env.apb_agent.sqr
2. uvm_declare_p_sequencer(soc_virtual_sequencer) in vseq class
3. connect_phase assigns v_sqr.apb_sqr = apb_agent.sqr
4. apb_agent builds sqr in its own build_phaseKey takeaways
Virtual sequencer holds handles to real sequencers — no driver.
Build v_sqr in env build_phase; assign handles in connect_phase.
v_sqr is env sibling to agents — never inside an agent.
Add null checks in connect_phase to catch wiring bugs early.
Common pitfalls
Assigning handles in build_phase before agent sub-components exist.
Starting vseq on agent.sqr — p_sequencer type mismatch or null handles.
Typo: v_sqr.apb_sqr = apb_agent.sequencer (wrong member name).