UVM Sequencer and Driver Communication:
In short: The driver and sequencer talk through a pair of matching ports: the driver has a seq_item_port, the sequencer has a seq_item_export, and connect_phase joins them. The driver pulls one item with get_next_item, drives it onto the pins, then calls item_done to release the sequence. That get, drive, done handshake is the heartbeat of every UVM driver, and getting the pairing right is what stops the two most common hangs in a testbench.
The link between the sequencer and the driver is where transaction-level stimulus becomes real pin activity. It is a small interface, just a port, an export, and a handful of methods, but the exact order you call those methods decides whether your testbench runs cleanly or hangs waiting forever. This guide covers the full handshake with runnable code, the difference between the two ways to pull items, how the return path for responses works, a method comparison table, and the mistakes that cause the classic sequence hang. We reuse one memory-bus example throughout.
A simple way to picture it
Picture a kitchen order rail, the metal bar where order slips get clipped. The sequence writes an order and clips it to the rail. The line cook (the driver) takes the next slip off the rail, cooks that dish (drives the pins), and only when the plate is finished does the cook mark the slip done so the next order can come forward. The cook handles one slip at a time and never grabs the next until the current dish is served. If the cook forgets to mark a slip done, the rail jams and no new orders move. The UVM handshake works exactly like that rail: get_next_item takes the slip, driving cooks it, and item_done clears it.
The two ends: port and export
The connection has two matching halves. The driver owns a seq_item_port, which is the requesting end. The built-in sequencer owns a seq_item_export, which is the providing end. In connect_phase you join them, and after that the driver can pull items that any sequence starts on that sequencer.
| Side | Object | Owns | Role |
|---|---|---|---|
| Requester | uvm_driver | seq_item_port | Asks for the next item and signals completion |
| Provider | uvm_sequencer | seq_item_export | Delivers items that sequences produce |
class bus_agent extends uvm_agent;
`uvm_component_utils(bus_agent)
bus_sequencer sqr;
bus_driver drv;
function new(string n, uvm_component p); super.new(n,p); endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
sqr = bus_sequencer::type_id::create("sqr", this);
drv = bus_driver ::type_id::create("drv", this);
endfunction
function void connect_phase(uvm_phase phase);
// join the driver port to the sequencer export
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclassThis one connect line is what makes everything else work. Forget it and the driver blocks on the very first get_next_item, because it is connected to nothing.
The handshake, step by step
The driver spends its whole life in a forever loop running three calls. Here is the canonical loop for our memory-bus driver, with a comment on each step.

task run_phase(uvm_phase phase);
bus_item req;
forever begin
seq_item_port.get_next_item(req); // 1) block until an item is ready
drive(req); // 2) turn the item into pin activity
seq_item_port.item_done(); // 3) release the sequence to send more
end
endtask
task drive(bus_item req);
@(posedge vif.clk);
vif.addr <= req.addr;
vif.wdata <= req.data;
vif.we <= req.is_write;
vif.valid <= 1'b1;
@(posedge vif.clk);
vif.valid <= 1'b0;
endtaskOn the sequence side, start_item and finish_item are the matching pair. start_item asks the sequencer for permission to send (it blocks until the driver is ready for a new item), and finish_item hands the completed item across.
task body();
bus_item req = bus_item::type_id::create("req");
start_item(req); // wait for the sequencer/driver to be ready
assert(req.randomize() with { addr==16'h100; is_write==1; });
finish_item(req); // send it; returns when driver calls item_done
endtaskThe two sides interlock: the sequence blocks in finish_item until the driver calls item_done, and the driver blocks in get_next_item until the sequence calls finish_item. That mutual waiting is what keeps them in lockstep with no lost or duplicated items.
get_next_item versus try_next_item
There are two ways to pull an item, and the choice affects how the driver behaves when no stimulus is waiting.
| Method | Behavior when no item is ready | Use it when |
|---|---|---|
| get_next_item | Blocks until an item arrives | The driver has nothing to do but wait for stimulus |
| try_next_item | Returns immediately with a null handle | The driver must keep the interface active (drive idle) even with no stimulus |
Use try_next_item when the protocol needs the driver to keep toggling something, for example driving idle cycles or a heartbeat, rather than freezing the pins while it waits. Here is the idle-aware pattern:
task run_phase(uvm_phase phase);
bus_item req;
forever begin
seq_item_port.try_next_item(req);
if (req == null) begin
drive_idle(); // keep the bus alive
end
else begin
drive(req);
seq_item_port.item_done(); // only when we actually took an item
end
end
endtaskNote the important rule: you only call item_done when try_next_item actually returned an item. Calling it after a null return corrupts the handshake.
Returning a response to the sequence
Sometimes the sequence needs to know what happened, for example the read data that came back. There are two ways to send a response upstream. The lightweight way passes the same request object to item_done, since the driver can fill in fields on it. The formal way builds a separate response and uses put_response.

// lightweight: fill the request object and pass it back
task run_phase(uvm_phase phase);
bus_item req;
forever begin
seq_item_port.get_next_item(req);
drive(req);
if (!req.is_write) req.data = vif.rdata; // capture read data
seq_item_port.item_done(req); // return the updated item
end
endtask// on the sequence side, read the response back
task body();
bus_item req = bus_item::type_id::create("req");
start_item(req);
assert(req.randomize() with { addr==16'h100; is_write==0; });
finish_item(req);
get_response(rsp); // rsp.data now holds the read value
endtaskThe full method map
These are all the calls involved, grouped by which side uses them, so you can see how a driver call pairs with a sequence call.
| Driver side | Sequence side | What happens |
|---|---|---|
| get_next_item(req) | start_item(req) then finish_item(req) | Driver receives the item the sequence sent |
| item_done() | finish_item returns | Sequence is released to build the next item |
| item_done(rsp) | get_response(rsp) | Driver returns a response the sequence reads |
| try_next_item(req) | same start/finish | Non-blocking pull; null if nothing is ready |
What actually flows on the timeline
Follow one write from sequence to pins and back:
- The sequence calls start_item, which blocks until the driver is ready.
- The sequence randomizes the item and calls finish_item, handing it across.
- The driver, waiting in get_next_item, receives the item and returns from that call.
- The driver drives addr, wdata, we, and valid onto the pins over one or more clocks.
- The driver calls item_done, which unblocks the sequence finish_item call.
- The sequence loops and builds the next item, and the driver loops back to get_next_item.
Common mistakes to avoid
- Forgetting item_done. The sequence blocks in finish_item forever and the whole test hangs. This is the number-one sequencer-driver bug.
- Forgetting the connect line in connect_phase, so the driver port is joined to nothing and get_next_item blocks on the first call.
- Calling item_done twice for one item, which desynchronizes the handshake and can drop the next item.
- Calling item_done after try_next_item returned null. Only signal done when you actually took an item.
- Doing a get_next_item then never driving, so time never advances and the item is never finished.
- Trying to use get_response without the driver returning a response through item_done(rsp) or put_response.
Keep learning
This handshake sits at the bottom of the whole stack. See how the driver and sequencer fit the bigger picture in the UVM testbench architecture guide, learn how items are scheduled when several sequences compete in UVM sequence arbitration, and see the difference between the two sequencer handles in m_sequencer vs p_sequencer. Build the whole flow from scratch in how to build a UVM environment part 2, and find more on the UVM category page.
Frequently asked questions
Once the single handshake is clear, see how a virtual sequence coordinates several sequencers at once, and how the m_sequencer and p_sequencer handles let a sequence reach its sequencer.
What connects the driver to the sequencer?
The driver has a seq_item_port and the sequencer has a seq_item_export. You join them in connect_phase with drv.seq_item_port.connect(sqr.seq_item_export). After that line the driver can pull items from any sequence started on that sequencer.
Why does my sequence hang at finish_item?
Almost always because the driver never called item_done for that item. finish_item does not return until the driver signals the item is complete, so a missing or skipped item_done leaves the sequence waiting forever. Check that every path through your driver loop ends with item_done.
What is the difference between get_next_item and try_next_item?
get_next_item blocks until a sequence provides an item, so the driver simply waits when there is no stimulus. try_next_item returns immediately with a null handle when nothing is ready, which lets the driver keep the interface active by driving idle cycles instead of freezing.
How does the sequence get read data back from the driver?
The driver fills the response into the item and returns it with item_done(rsp), or builds a separate response and uses put_response. The sequence then reads it with get_response. For simple cases, reusing the same request object and calling item_done(req) is the lightest approach.
Do I call item_done after try_next_item returns null?
No. You only call item_done when you actually received an item. If try_next_item returned null, there is nothing to complete, and calling item_done anyway corrupts the handshake and can drop the next real item.
Can more than one sequence talk to one driver?
Yes. Several sequences can run on the same sequencer at once, and the sequencer arbitrates which item reaches the driver next. The driver does not know or care which sequence produced an item; it just pulls whatever the sequencer delivers.



