Part 5 · Sequences · Intermediate
Driver get_next_item / item_done and drive_apb
Driver run_phase forever loop, get_next_item blocking, drive_apb pin conversion, and item_done pairing rules.
Driver role in the pull model
The uvm_driver is the only component that touches the virtual interface. Its run_phase loops forever on get_next_item, converts each item to pin activity, then calls item_done. The driver sets the pace — sequences wait on the driver's schedule, not the reverse.
get_next_item and item_done must be strictly paired: one item_done per get_next_item. Calling get_next_item twice without item_done corrupts sequencer internal state and produces unpredictable hangs.
[DRV] driver responsibilities
PULL get_next_item(req) ← request next [ITEM] when ready
DRIVE drive_apb(req) ← convert [ITEM] to pin wiggles
RELEASE item_done() ← unblock [SEQ] finish_item
REPEAT forever in run_phaseComplete apb_driver
class apb_driver extends uvm_driver #(apb_item);
`uvm_component_utils(apb_driver)
virtual apb_if vif;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "virtual apb_if not found")
endfunction
task run_phase(uvm_phase phase);
apb_item req;
// Initialize bus to idle
vif.psel <= 0;
vif.penable <= 0;
forever begin
// Pull next item — blocks when sequencer has nothing pending
seq_item_port.get_next_item(req);
`uvm_info("DRV", $sformatf("got item addr=0x%08x w=%0b",
req.addr, req.write), UVM_MEDIUM)
// Convert [ITEM] to pin activity
drive_apb(req);
// MUST call before next get_next_item — releases sequence
seq_item_port.item_done();
`uvm_info("DRV", "item_done", UVM_HIGH)
end
endtask
task drive_apb(apb_item req);
@(posedge vif.pclk);
vif.paddr <= req.addr;
vif.pwrite <= req.write;
vif.pwdata <= req.data;
vif.psel <= 1'b1;
vif.penable <= 1'b0;
@(posedge vif.pclk);
vif.penable <= 1'b1;
while (!vif.pready) @(posedge vif.pclk);
if (!req.write)
req.rdata = vif.prdata; // capture read data into [ITEM]
@(posedge vif.pclk);
vif.psel <= 0;
vif.penable <= 0;
endtask
endclassbuild_phase gets vif from config_db — fatal if missing.
run_phase forever loop — standard for all protocol drivers.
drive_apb reads req fields — never randomizes in driver.
Read path fills req.rdata before item_done — sequence sees it after finish_item.
get_next_item blocking
get_next_item blocks when no sequence has a pending start_item. This is normal at time zero before the test calls seq.start(), or between sequences. It is a hang only if the sequence never calls start_item — debug the test/sequence side first.
[DRV] get_next_item block scenarios
NORMAL BLOCK: driver ready, no sequence started yet
test calls seq.start() → sequence start_item → unblock
HANG: sequence at finish_item forever
driver never got item OR driver stuck in drive_apb
check item_done and vif.pready
NORMAL BLOCK: sequence between items, driver already called item_done
next get_next_item waits for next start_item[SEQ] [DRV] paired timeline
get_next_item(req) ◄── driver blocks until start_item+finish_item path ready
drive_apb(req) ◄── [DRV] active window — reads [ITEM]
item_done() ◄── MUST happen — unblocks finish_item
get_next_item(req) ◄── safe to pull nextdrive_apb walkthrough — pin by pin
APB setup/access/wait/idle phases mapped to item fields. All timing lives here — not in apb_item:
[DRV] APB write addr=0x4000 data=0xDEAD — pin timeline
Cycle 0 (SETUP): psel=1 penable=0 paddr=0x4000 pwrite=1 pwdata=0xDEAD
Cycle 1 (ACCESS): psel=1 penable=1 wait pready
Cycle N: pready=1 → beat complete
Cycle N+1 (IDLE): psel=0 penenable=0
[ITEM] req unchanged throughout — driver reads once at startSeparate drive tasks per direction
drive_write(apb_item req) and drive_read(apb_item req) — clarity over one mega-task.
Branch on req.write in drive_apb for simple APB — fine for learning.
Complex protocols: drive_aw, drive_w, drive_b as separate tasks.
item_done pairing rules
Violating item_done pairing corrupts the sequencer. These rules are absolute:
// CORRECT — paired
seq_item_port.get_next_item(req);
drive_apb(req);
seq_item_port.item_done();
// WRONG — double get without item_done
seq_item_port.get_next_item(req_a);
seq_item_port.get_next_item(req_b); // SEQUENCER CORRUPT
// WRONG — item_done before drive completes
seq_item_port.get_next_item(req);
seq_item_port.item_done(); // sequence may randomize next item
drive_apb(req); // race — req fields may change mid-drive[UVM] item_done timing
CORRECT: get → drive (complete) → item_done
WRONG: get → item_done → drive (sequence races ahead)
WRONG: get → get (sequencer corrupt)
WRONG: drive → forget item_done (sequence hangs forever)Connect phase — seq_item_port wiring
The agent connect_phase connects sequencer export to driver import. Without this connection, get_next_item blocks forever regardless of sequence activity:
class apb_agent extends uvm_agent;
apb_driver drv;
apb_sequencer sqr;
apb_monitor mon;
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if (is_active == UVM_ACTIVE)
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass[UVM] agent wiring
[SEQ] SEQUENCER.seq_item_export
│
│ connect_phase
▼
[DRV] DRIVER.seq_item_port
Broken connect → get_next_item hangs with active sequenceKey takeaways
Driver run_phase: forever get_next_item → drive → item_done.
One item_done per get_next_item — no exceptions.
item_done AFTER drive completes — not before.
Read data: fill req.rdata in driver, pass via item_done(req).
Common pitfalls
Missing item_done() — #1 sequence hang cause.
item_done before drive completes — sequence races ahead of pins.
Double get_next_item — sequencer state corruption.
Null vif — driver may hang in drive_apb before any item_done.