Part 5 · Sequences · Intermediate
Pull Model Overview: Sequence, Sequencer, Driver Timeline
Why UVM uses pull not push, the three-component handshake timeline, and back-pressure semantics.
Why pull, not push
In a naive push model, the sequence would fire transactions at the driver whenever body() runs — regardless of whether the DUT can accept them. Real buses have back-pressure: APB waits for PREADY, AXI stalls on AWREADY, PCIe credits limit outstanding TLPs. The pull model lets the driver request the next item only when it is ready to drive, naturally matching DUT readiness.
The sequencer sits between sequence and driver as an arbiter and queue. When multiple sequences compete for one driver, the sequencer serializes start_item requests. The driver never sees competing sequences directly — only the next granted item via get_next_item.
finish_item blocks the sequence thread until the driver completes the current item. This is intentional back-pressure propagated up to the test — a slow DUT slows stimulus generation automatically.
PUSH (not UVM) vs PULL (UVM)
PUSH: seq → driver "here's a txn" → drive immediately (ignores DUT ready)
PULL: driver "I'm ready" → seq → item → drive → item_done → seq continues
[DRV] controls throughput — sequence cannot outrun pin-level realityThree-component timeline
The canonical handshake involves three active participants. The timeline below shows one APB write beat from all three perspectives:
Legend: [SEQ] [DRV] [ITEM]
TIME →
─────────────────────────────────────────────────────────────────
[SEQ] SEQUENCE
│ start_item(req)
│──────────────────────────────► sequencer (item pending)
│ randomize(req) [ITEM] addr,data,write filled
│ finish_item(req) ─── BLOCKS ─────────────────────────────┐
│ │
[SEQ] SEQUENCER │
│ get_next_item(req) ◄── [DRV] request │
│──────────────────► deliver req to driver │
│ item_done() ◄── [DRV] done │
│ finish_item UNBLOCKS ◄─────────────────────────────────────┘
│ (sequence continues)
[DRV] DRIVER
│ get_next_item(req) ← blocks until pending
│ drive_apb(req) ← reads [ITEM]
│ item_done() ← releases [SEQ][STIM] [SEQ] [DRV] [ITEM] [UVM] — stack view during one beat
[STIM] test waiting on seq.start() to return
[SEQ] apb_wr_seq blocked at finish_item
[SEQ] sequencer holding req in pending slot
[DRV] apb_driver in drive_apb — wiggling psel, waiting pready
[ITEM] apb_item { addr=0x4000, data=0xDEAD, write=1 } — read-only at driver
[UVM] agent connects sqr ↔ driver via seq_item_portBlocking points — where threads stall
Understanding where each thread blocks is essential for hang debug. There are exactly four blocking API calls in the basic handshake:
Blocking call Who blocks Until when
─────────────────────────────────────────────────────────────
start_item(req) sequence sequencer grants slot
finish_item(req) sequence driver calls item_done
get_next_item(req) driver sequence calls finish_item path
seq.start(sqr) test/vseq body() returnsstart_item rarely blocks long — unless arbitration queues behind another sequence.
finish_item is the long block — entire drive_apb duration.
get_next_item blocks when no sequence has called start_item yet.
seq.start blocks for entire body() — all items in the sequence.
Multi-sequence arbitration preview
When two sequences run on the same sequencer, items interleave at start_item boundaries — not mid-transaction. The pull model preserves atomic per-item drive:
[SEQ] two sequences, one [DRV] — FIFO arbitration
bg_seq: start_item → finish_item (item A)
test_seq: start_item → finish_item (item B)
bg_seq: start_item → finish_item (item C)
Driver sees: A → B → C (serialized at item boundaries)
[DRV] never gets two items simultaneously without explicit pipelinefork
bg_seq.start(sqr); // long-running background
test_seq.start(sqr); // short directed burst
join
// Items interleave — see Sequencer Arbitration topic for lock/grabPull model and passive agents
Passive agents have a monitor but no driver. Sequences started on a passive agent's sequencer block forever at finish_item — nothing calls get_next_item. This is a common integration mistake when mixing active and passive agents.
[UVM] active vs passive — handshake requirement
ACTIVE agent: sqr ←→ driver ←→ DUT pins handshake complete
PASSIVE agent: sqr (no driver) finish_item HANGS
Passive agent: do NOT start sequences on its sequencer
Use active agent sqr for stimulusKey takeaways
Pull model: driver requests when ready — matches DUT back-pressure.
finish_item blocks until item_done — the primary sequence stall point.
Three components: sequence produces, sequencer mediates, driver executes.
Common pitfalls
Expecting push semantics — sequence cannot 'fire and forget' items.
Starting sequences on passive agent sequencers — permanent hang.
Assuming fork order equals item order — arbitration interleaves.