Part 5 · Sequences · Intermediate
Sequencer Connection & p_sequencer
connect_phase port wiring, null sequencer handles, declare macro typing, and virtual sequence start targets.
Checklist 2 — Sequencer–driver connection
The seq_item_port on the driver must connect to seq_item_export on the sequencer in connect_phase. A typo or missing connect is silent — get_next_item blocks forever with no error message.
connect_phase: drv.seq_item_port.connect(sqr.seq_item_export) — only in UVM_ACTIVE.
Typo in port or export name — compiles if wrong component, silent at runtime.
Multiple drivers on one sequencer export — illegal; only one driver per sequencer.
Reconnect without disconnect — some simulators warn, connection order matters.
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if (is_active == UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);
`uvm_info("AGT", "driver seq_item_port connected to sequencer", UVM_LOW)
end
mon.ap.connect(/* ... */);
endfunctionChecklist 3 — start() and sequencer handle
The seq.start(sqr) call requires a non-null sequencer handle to the agent's sequencer — not the env, not the agent component itself.
// Guard before start — catches null early with readable fatal
if (env.axi_agent.sqr == null)
`uvm_fatal("NULL_SQR", "axi sequencer null — passive agent?")
`uvm_info("TEST", $sformatf(
"starting %s on %s", seq.get_type_name(), env.axi_agent.sqr.get_full_name()),
UVM_LOW)
seq.start(env.axi_agent.sqr);Virtual sequence: start on v_sqr; sub-sequences start on p_sequencer.axi_sqr etc.
Starting same sequence twice on same sequencer without kill — can deadlock with lock.
seq.start(agent) is wrong — agent is uvm_component, not uvm_sequencer.
Checklist 4 — Null or wrong p_sequencer
Inside body(), p_sequencer is populated by start(). The declare macro binds the typed handle — if the macro says apb_sequencer but you started on
a virtual sequencer, fields like p_sequencer.axi_sqr do not exist and you get compile errors or null access at runtime.
class axi_burst_seq extends uvm_sequence #(axi_item);
`uvm_declare_p_sequencer(axi_sequencer) // MUST match start() target
task body();
if (p_sequencer == null)
`uvm_fatal("NULL_PSEQ", "p_sequencer null — wrong start() or missing macro")
`uvm_info(get_type_name(), $sformatf(
"running on %s", p_sequencer.get_full_name()), UVM_LOW)
// ...
endtask
endclass
class chip_vseq extends uvm_sequence;
`uvm_declare_p_sequencer(chip_virtual_sequencer) // virtual type
task body();
axi_burst_seq burst = axi_burst_seq::type_id::create("burst");
burst.start(p_sequencer.axi_sqr); // agent sequencer — NOT p_sequencer alone
endtask
endclassWalkthrough — wrong start target
Legend: [STIM] [SEQ] [UVM]
BUG: chip_vseq declares chip_virtual_sequencer
but axi_burst_seq.start(p_sequencer) — started on VIRTUAL sqr
RESULT:
axi_burst_seq.p_sequencer = virtual sequencer handle
start_item → virtual sequencer has no seq_item_export to driver
HANG at start_item forever
FIX:
axi_burst_seq.start(p_sequencer.axi_sqr) — real agent sequenceritem_done pairing — the finish_item hang
If the sequence blocks at finish_item but start_item already completed, the driver pulled the item but never called item_done().
Every get_next_item needs exactly one item_done — no exceptions.
Early return or exception path in drive() that skips item_done.
drive() fork without join — item_done in parent before child drive completes.
item_done in wrong thread without proper synchronization.
task drive(axi_item req);
fork
drive_write_channel(req);
drive_addr_channel(req);
join
seq_item_port.item_done(); // MUST run after drive completes — not before fork
endtaskKey takeaways
connect_phase wiring is silent-failure territory — log the connect.
p_sequencer type must match start() target; vseq uses child sequencer handles.
finish_item hang = missing item_done, not sequencer connection.
Common pitfalls
Starting vseq sub-seq on p_sequencer instead of p_sequencer.agent_sqr.
declare_p_sequencer(apb) but start on axi_sqr — field access compile fail.
connect only in test, not agent — breaks encapsulation and reuse.
item_done before drive finishes — sequence releases item too early.