Part 4 · TLM & Analysis · Intermediate
Sequencer-Driver TLM: seq_item_port / export Pull Pattern
How driver seq_item_port and sequencer seq_item_export implement a specialized TLM pull protocol with get_next_item/item_done semantics.
The canonical UVM pull handshake
The sequencer-driver path is a specialized TLM connection: driver pulls sequence items using seq_item_port connected to sequencer seq_item_export. This is the core handshake that converts abstract sequence intent into pin-level drive actions.
Although APIs differ from generic put/get names, behavior is conceptually similar to blocking pull plus completion acknowledgment.
Legend: [UVM] [TLM]
[UVM][TLM] seq-driver handshake
sequence.start(seqr)
|
v
sequencer arbitrates requests
|
driver.seq_item_port.get_next_item(req) // blocking pull
driver drives req on interface
driver.seq_item_port.item_done(rsp?) // completion notificationDriver is pull-based: it asks sequencer for next item.
get_next_item blocks until work exists.
item_done releases sequencer-side flow and enables next grant.
Connection and run-phase implementation
class bus_driver extends uvm_driver #(pkt);
`uvm_component_utils(bus_driver)
virtual bus_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
pkt req;
forever begin
seq_item_port.get_next_item(req); // blocking pull from sequencer
drive_one(req);
seq_item_port.item_done();
end
endtask
task drive_one(pkt req);
@(posedge vif.clk);
vif.valid <= 1'b1;
vif.data <= req.data;
do @(posedge vif.clk); while (!vif.ready);
vif.valid <= 1'b0;
endtask
endclass
class bus_agent extends uvm_agent;
`uvm_component_utils(bus_agent)
uvm_sequencer #(pkt) seqr;
bus_driver driver;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
seqr = uvm_sequencer#(pkt)::type_id::create("seqr", this);
driver = bus_driver::type_id::create("driver", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
driver.seq_item_port.connect(seqr.seq_item_export);
endfunction
endclass[UVM] relationship to generic TLM
driver.seq_item_port.get_next_item(req) ~ blocking_get(req)
driver.seq_item_port.item_done() ~ completion/ack to producer side
This specialized API adds sequence arbitration semantics on top of TLM pull.Response path and advanced handshake options
If the protocol produces per-item responses, drivers can return response objects either via item_done(rsp) or explicit response APIs, depending on sequence style.
task run_phase(uvm_phase phase);
pkt req;
pkt rsp;
forever begin
seq_item_port.get_next_item(req);
rsp = pkt::type_id::create("rsp");
rsp.set_id_info(req);
drive_and_collect(req, rsp);
seq_item_port.item_done(rsp); // return response associated with req
end
endtask
task drive_and_collect(pkt req, ref pkt rsp);
// drive request then capture result
rsp.data = req.data ^ 32'h00FF_00FF;
endtask[TLM] ordering guarantees
Driver contract:
get_next_item(req)
... exactly one terminal action ...
item_done() or item_done(rsp)
Missing item_done causes sequencer flow stall.Call exactly one completion action per granted item.
Use set_id_info(req) when building rsp to preserve correlation.
Guard against early returns/exceptions that skip item_done.
Debugging deadlocks and starvation in seq-driver links
[UVM] common seq-driver failure signatures
driver stuck on get_next_item:
- sequence never started
- arbitration blocked by locks/grabs
- wrong sequencer connection path
sequencer grants once then stalls:
- driver forgot item_done
- driver thread exited unexpectedly
- protocol wait loop never exitstask run_phase(uvm_phase phase);
pkt req;
forever begin
`uvm_info("DRV", "waiting get_next_item", UVM_HIGH)
seq_item_port.get_next_item(req);
`uvm_info("DRV", "got item, driving", UVM_HIGH)
drive_one(req);
seq_item_port.item_done();
`uvm_info("DRV", "item_done sent", UVM_HIGH)
end
endtaskKey takeaways
seq_item_port/export is UVM's standardized pull TLM channel for drivers.
get_next_item is blocking; item_done is mandatory completion signaling.
Most sequencer-driver deadlocks come from missing start/connect/done steps.
Common pitfalls
Skipping item_done on error paths.
Connecting driver port to wrong sequencer instance.
Assuming sequence runs automatically without explicit start.