UVM
Sequencer Problems
Applying the debugging methodology to stimulus generation — why nothing drives the interface or a sequence hangs: a sequence never started on the sequencer, a sequence started on the wrong sequencer, the driver's get_next_item/item_done handshake broken (a missing item_done that blocks the sequence), or arbitration and locks starving a sequence; the blocking handshake where a break on either side stalls the other, with the symptom (idle interface versus hung sequence) telling you which side broke; how to observe with driver and sequencer verbosity and fix each so stimulus flows.
UVM Debugging · Module 27 · Page 27.5
The Engineering Problem
The interface is idle — nothing drives — while the test claims to run. Or the test hangs forever, having driven one transaction and then stalled. Or a sequence you started never seems to run while another monopolizes the bus. These are the sequencer's characteristic failures (from 27.1's map: nothing drives → sequencer/sequence), and they share a root mechanism: the sequence and driver are coupled by a blocking handshake. The driver blocks in get_next_item until a sequence produces an item; the sequence blocks in finish_item until the driver calls item_done. So a failure on either side stalls the other — and which side stalled is exactly what the symptom tells you. An idle interface (the driver waiting, no items ever arriving) means the producer side failed — no sequence was started, or it was started on the wrong sequencer. A hung sequence (an item produced, but finish_item never returns) means the consumer side failed — the driver didn't call item_done. The trap is debugging the side the symptom points at — a hung sequence makes you suspect the sequence, when the cause is the driver's missing item_done on the other side of the handshake. The problem this chapter solves is sequencer problems: applying the methodology to the sequence→sequencer→driver handshake — reading the symptom (idle interface vs hung sequence vs starved sequence), observing with driver and sequencer verbosity, and confirming and fixing each root cause so stimulus flows.
Sequencer problems are failures in the sequence→sequencer→driver chain — no stimulus, a hang, or a starved sequence — rooted in the blocking handshake between the sequence and the driver. The root causes: a sequence never started on the sequencer (created but never .start()-ed, or no default_sequence — the driver blocks in get_next_item forever, idle interface); a sequence started on the wrong sequencer (items flow to a different driver, the intended interface stays idle); a broken driver handshake (the driver forgets item_done after get_next_item, so the sequence's finish_item never returns and the sequence hangs); or arbitration and locks starving a sequence (multiple sequences on one sequencer where a high-priority, locking, or grabbing sequence monopolizes, so another is never granted). The crucial mechanism: the handshake is blocking on both sides, so a break on either side stalls the other, and the symptom names the broken side — idle interface (driver waiting → producer failed: no/wrong sequence) versus hung sequence (item stuck in finish_item → consumer failed: missing item_done). The methodology applied: the symptom is idle interface, hung sequence, or starved sequence; localize to the sequencer; observe with raised verbosity on the driver and sequencer (is get_next_item reached? did finish_item return? was the sequence started? is arbitration granting it?); confirm which side broke; and fix (start the sequence on the right sequencer, complete the handshake with item_done, fix arbitration/release locks). This chapter is sequencer problems: the handshake, the two-sided stall, the observation, and the fixes.
Why does nothing drive the interface, or a sequence hang, or a sequence never run — and how does the blocking sequence-driver handshake mean a break on either side stalls the other, with the symptom (idle interface versus hung sequence) telling you which side broke, so you fix the right one?
Motivation — why the handshake makes the symptom point at the wrong side
The sequence-driver blocking handshake is powerful (it paces stimulus to the driver's readiness) but means failures manifest as stalls on the opposite side. The reasons a method is needed:
- The handshake blocks both ways.
get_next_itemblocks the driver until an item arrives;finish_itemblocks the sequence untilitem_done. So either a missing producer (no sequence) or a missing consumer signal (noitem_done) stalls — and the stall appears on the waiting side. - The symptom points at the waiting side, not the broken side. A hung sequence (waiting in
finish_item) makes you look at the sequence — but the sequence is fine; the driver (the other side) didn't complete the handshake. The symptom's side is not the cause's side. - "Nothing drives" has multiple producer causes. An idle interface could be no sequence started, a sequence on the wrong sequencer, or no
default_sequence— all producer-side, all idle. You must observe to distinguish. - Arbitration failures look like hangs. A starved sequence (never granted because another locks or out-prioritizes it) appears hung — but the cause is arbitration/lock state, not the sequence or driver code.
- The flow is invisible without verbosity. Whether
get_next_itemwas reached, whether the sequence started, whetherfinish_itemreturned, whether arbitration granted — all invisible unless you raise verbosity to see the handshake events.
The motivation, in one line: the blocking sequence-driver handshake means a failure on either side stalls the other, and the stall appears on the waiting side — so a hung sequence points at the sequence but is caused by the driver, and an idle interface has several producer-side causes — so you must raise verbosity to observe the handshake events and determine which side actually broke before fixing.
Mental Model
Hold the sequence-driver flow as a cook and a waiter at a kitchen pass, exchanging tickets and a done-bell:
Picture a restaurant kitchen pass — the counter where the cook hands finished dishes to the waiter. The protocol between them is a strict back-and-forth. The cook stands at the pass and waits for an order ticket; with no ticket, there is nothing to cook, so the cook simply waits, idle, indefinitely. A waiter brings a ticket, places it, and then waits at the pass for the cook to finish and ring the done bell, signaling the dish is ready to take. Only when the bell rings does the waiter pick up the dish and go bring the next ticket. Now consider how this stalls. If no waiter ever brings a ticket — nobody is sending orders to this kitchen — the cook stands at the pass forever, doing nothing, because there is nothing to cook. From the dining room, no food ever comes out. The other failure is subtler: a waiter brings a ticket, the cook takes it and cooks the dish perfectly, but then forgets to ring the done bell. The dish is made, but the waiter, following the protocol, is still standing at the pass waiting for the bell that never rings. The waiter is frozen there, and no further tickets get placed, so the whole line backs up behind one un-rung bell. Notice the two failures look like they're on opposite people: no food coming out looks like a cook problem but is actually no-tickets; a waiter frozen at the pass looks like a waiter problem but is actually the cook's un-rung bell. The pass is a two-sided handshake, and a break on one side freezes the person on the other. Picture a restaurant kitchen pass — the counter where the cook hands dishes to the waiter. The protocol is a strict back-and-forth. The cook waits at the pass for an order ticket; with no ticket, there's nothing to cook, so the cook waits, idle, indefinitely. A waiter brings a ticket, places it, and waits at the pass for the cook to ring the done bell. Only when the bell rings does the waiter take the dish and bring the next ticket. Now consider the stalls. If no waiter ever brings a ticket, the cook stands at the pass forever — no food ever comes out. The subtler failure: a waiter brings a ticket, the cook cooks the dish perfectly, but forgets to ring the done bell — the dish is made, but the waiter is still standing at the pass waiting for the bell that never rings, frozen, and no further tickets get placed, so the whole line backs up. Notice the two failures look like they're on opposite people: no food coming out looks like a cook problem but is actually no-tickets; a waiter frozen at the pass looks like a waiter problem but is actually the cook's un-rung bell. The pass is a two-sided handshake, and a break on one side freezes the person on the other.
So the sequence-driver flow is a cook and a waiter at a kitchen pass: the driver is the cook (waits at the pass — get_next_item blocks for an item), the sequence is the waiter (brings a ticket — start_item/finish_item — and waits for the done bell — finish_item blocks until item_done), and item_done is the done bell. The two stalls map exactly: no sequence started = no waiter, cook idle forever (idle interface — looks like a driver/DUT problem but is no stimulus); driver forgets item_done = cook never rings the bell, waiter frozen (hung sequence — looks like a sequence problem but is the driver's fault). And arbitration/lock = many waiters, one cook, a pushy waiter monopolizes (starvation). The diagnostic lesson: the stall appears on the waiting side, not the broken side — so read the symptom to find the waiting side, then check the OTHER side for the break. Diagnose sequencer stalls by the handshake: an idle interface means no ticket was brought (no/wrong sequence — the producer), a hung sequence means the bell was never rung (missing item_done — the consumer/driver) — fix the side that broke, which is the opposite of the side that froze. Read which side froze; fix the side that broke.
Visual Explanation — the sequencer failure causes
The defining picture is the causes organized by which side of the handshake broke and the resulting symptom.
The figure shows the sequencer failure causes by side and symptom. Producer: sequence not started → idle interface (warning-colored): created but never .start()-ed, or no default_sequence — the driver blocks in get_next_item forever. Producer: wrong sequencer → idle interface (brand-colored): the sequence runs but its items go to a different driver; the intended interface stays idle. Consumer: missing item_done → hung sequence (warning-colored): the driver forgot item_done after get_next_item, so the sequence's finish_item never returns. Arbitration/locks → starved sequence (default-colored): a high-priority, locking, or grabbing sequence monopolizes; another is never granted and appears hung. The crucial reading is the organization by side: the producer-side failures (not started, wrong sequencer) all produce an idle interface (the driver waits, no items), while the consumer-side failure (missing item_done) produces a hung sequence (an item produced, finish_item blocked), and arbitration produces a starved sequence (one runs, another doesn't). So the symptom partitions the causes: idle from the start → producer side (no/wrong sequence); one item then stall → consumer side (missing item_done); one sequence running while another never does → arbitration. This partition is what the methodology exploits: the symptom's shape tells you which side to investigate, before you observe in detail. The crucial subtlety, flagged by the warning colors on the two most common causes (not started and missing item_done): these are the idle interface and the hung sequence — the two failures whose symptom points at the opposite side from the cause (idle looks like a driver/DUT issue but is no stimulus; hung looks like a sequence issue but is the driver). The diagram is the sequencer failure map: producer-side (idle) vs consumer-side (hung) vs arbitration (starved) — the symptom's shape names the side. The sequencer failures partition by symptom — idle interface (producer: no/wrong sequence), hung sequence (consumer: missing item_done), starved sequence (arbitration) — so the symptom's shape names the side to investigate.
RTL / Simulation Perspective — the handshake, the breaks, and the fixes
In code, the handshake is a strict protocol, and each break is a specific omission. The example shows the correct handshake and each failure.
// === THE HANDSHAKE: driver consumes, sequence produces — both block ===
class my_driver extends uvm_driver #(my_txn);
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // BLOCKS until a sequence produces an item
drive(req); // drive the signals
seq_item_port.item_done(); // ✓ unblocks the sequence's finish_item — the "done bell"
end
endtask
endclass
class my_seq extends uvm_sequence #(my_txn);
task body();
req = my_txn::type_id::create("req");
start_item(req); // request the sequencer
assert(req.randomize());
finish_item(req); // BLOCKS until the driver calls item_done
endtask
endclass
// ✓ START the sequence on the RIGHT sequencer (or set as default_sequence):
my_seq seq = my_seq::type_id::create("seq");
seq.start(env.agent.sequencer); // the sequencer whose driver drives the target interface
// ✗ CAUSE 1 — sequence NOT STARTED: nothing calls seq.start() and no default_sequence set
// → driver blocks in get_next_item forever → IDLE INTERFACE (nothing drives)
// ✗ CAUSE 2 — WRONG SEQUENCER: seq.start(other_agent.sequencer)
// → items go to the other driver → the INTENDED interface stays idle
// ✗ CAUSE 3 — MISSING item_done: the driver forgets it (or skips it via early continue/return)
class bad_driver extends uvm_driver #(my_txn);
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive(req);
// (forgot) seq_item_port.item_done(); ← finish_item NEVER returns → SEQUENCE HANGS
end
endtask
endclass
// ✗ CAUSE 4 — ARBITRATION/LOCK: a sequence grabs/locks the sequencer and never releases
// seqA.grab(sequencer); ... (no ungrab) → seqB never granted → seqB STARVED
// === OBSERVE: raise verbosity on driver + sequencer to see the handshake events ===
// simulate with: +UVM_VERBOSITY=UVM_HIGH
// → see whether get_next_item is reached, whether the sequence started, whether finish_item returnedThe code shows the handshake and its breaks. The correct handshake: the driver loops get_next_item (blocks) → drive → item_done (the done bell, unblocking the sequence), and the sequence does start_item → randomize → finish_item (blocks until item_done); the sequence is started on the right sequencer. Cause 1 (not started): nothing calls seq.start(), no default_sequence → the driver blocks forever → idle interface. Cause 2 (wrong sequencer): seq.start(other_agent.sequencer) → items go elsewhere → intended interface idle. Cause 3 (missing item_done): bad_driver forgets item_done (or skips it via an early continue/return) → finish_item never returns → sequence hangs. Cause 4 (arbitration/lock): a sequence grabs and never ungrabs → another is never granted → starved. Observe: raise +UVM_VERBOSITY=UVM_HIGH on the driver and sequencer to see whether get_next_item is reached, the sequence started, finish_item returned. The shape to carry: the handshake is get_next_item → drive → item_done on the driver paired with start_item → finish_item on the sequence, and each cause omits or misdirects one step — no start (driver waits), wrong sequencer (items elsewhere), no item_done (sequence waits), lock without release (starvation). Drive the handshake correctly — get_next_item then item_done per item on the driver, start the sequence on the right sequencer — and raise verbosity to see which handshake step is missing.
Verification Perspective — the two-sided stall
The defining mechanism is the two-sided blocking handshake: a break on one side stalls the other, so the symptom's side is the opposite of the cause's side. Seeing both directions clarifies why you fix the opposite side.
The figure shows the two-sided stall. The sequence and driver are coupled by a blocking handshake: the driver blocks in get_next_item waiting for an item, the sequence blocks in finish_item waiting for item_done. If the producer side fails (no sequence started — the warning-colored cause), the driver is left blocked in get_next_item (the waiting side): the symptom is an idle interface (the brand-colored symptom), and the cause is on the producer (sequence) side. If the consumer side fails (the driver forgets item_done — the warning-colored cause), the sequence is left blocked in finish_item: the symptom is a hung sequence, and the cause is on the consumer (driver) side. The verification insight is the crossing: in both rows, the symptom (right) is opposite the cause (left) — the stall surfaces on the side that was waiting, while the break is on the other side. So "idle interface" (driver waiting) → cause is the producer (sequence not started); "hung sequence" (sequence waiting) → cause is the consumer (driver's missing item_done). This is why the naive response fails: a hung sequence makes you open the sequence and scrutinize its body — but the sequence is correct; it's doing exactly what it should (waiting for the done bell), and the driver (the other side) never rang it. The crucial discipline is to read the symptom to identify the waiting side, then investigate the opposite side for the break: idle interface → check the producer (was a sequence started, on the right sequencer?); hung sequence → check the consumer (did the driver call item_done?). The warning causes → default waiting-side → brand symptoms rows show the fault crossing the handshake to surface as a stall on the far side. The crucial point is that the handshake's two-sidedness inverts the usual instinct — you don't debug where the stall is; you debug the side that should have unblocked it. The diagram is the two-sided stall: producer fails → driver waits → idle (fix the producer); consumer fails → sequence waits → hung (fix the consumer) — symptom opposite cause. In the blocking handshake the symptom surfaces on the waiting side while the break is on the other — an idle interface means fix the producer (sequence), a hung sequence means fix the consumer (driver's item_done).
Runtime / Execution Flow — diagnosing a sequencer stall
At run time, diagnosing a sequencer stall follows the methodology, using the symptom's shape to pick the side and verbosity to observe the handshake events. The flow shows it.
The flow shows the symptom-shape-driven diagnostic. Read the shape (step 1): idle from the start, one item then stall, or one sequence running while another never does. Idle → producer (step 2): raise verbosity: was a sequence started, on which sequencer, and is get_next_item reached? One-then-stall → consumer (step 3): is the driver calling item_done? — finish_item is blocked waiting for it. Starvation → arbitration; confirm → fix (step 4): check locks, grabs, priority; confirm in the verbose log; fix the broken side. The runtime insight is that the symptom's shape is a fast classifier: the three shapes map to the three sides (producer, consumer, arbitration) before any detailed observation — so you don't raise verbosity blindly; you know which side to look at. Idle from the start (valid flat the whole run) → producer (no item ever arrived). One item then stall (valid pulses once, then flat) → consumer (the first item drove — so a sequence ran and the driver got it — but then froze, meaning item_done wasn't called and finish_item blocked). One sequence running while another doesn't → arbitration. Then verbosity confirms: the driver/sequencer messages show whether get_next_item was reached, which sequencer the sequence started on, whether finish_item returned. This is the methodology (27.1) specialized: the symptom's shape localizes the side, the verbose log is the introspection, and you confirm before fixing. The brand (shape) → success (producer/consumer checks) → warning (arbitration/fix) flow shows the classification driving the investigation. The crucial point is that the "one item then stall" shape is the key tell for the missing-item_done case — it distinguishes it from "nothing ever drives" (no sequence): something drove, then stopped, which means the handshake completed once for the get but not the item_done, hanging the next finish_item. The flow is the sequencer diagnostic: symptom shape → side → verbosity confirms → fix the broken side. Classify the stall by its shape — idle from start (producer), one item then stall (consumer's missing item_done), or starvation (arbitration) — then raise verbosity to confirm and fix the side that broke.
Waveform Perspective — one item, then the stall
The missing-item_done failure has a distinctive shape: the first item drives, then the interface stalls — unlike the idle-from-the-start of a missing sequence. The waveform shows it.
One item drives, then the interface stalls — the driver's missing item_done hangs the sequence
12 cyclesThe waveform shows the missing-item_done shape. The first transaction drives normally: valid asserts and the item is presented. But item_done never fires (the driver forgot it), so the sequence's finish_item never returns, the sequence cannot produce a second item, and the interface goes idle for the rest of the run. The crucial reading is the shape: one item, then stall — unlike a missing sequence, where valid would be flat from the very start because no item ever arrives. The first item driving is significant: it proves a sequence ran (it produced an item) and the driver got it (it drove valid) — so neither "no sequence" nor "wrong sequencer" nor "driver not reaching get_next_item" can be the cause; something worked once. What didn't happen is item_done (flat the whole run), so the handshake completed the get half but never the done half, leaving the sequence's finish_item blocked and unable to issue the next item. So the interface stalls after exactly one transaction. Reading the symptom this way — drove once, then froze, with item_done never asserting — localizes the failure to the driver's broken handshake, not the sequence, even though the sequence is what is hung. The picture to carry is the contrast of shapes: flat from the start = producer (no item ever); one item then flat = consumer (the done bell never rung after the first). The single driven transaction followed by a permanent stall, with item_done never asserting, is the waveform signature of the missing-item_done hang. One item then a permanent stall, with item_done never asserting, is the signature of the driver's missing item_done — distinct from idle-from-the-start (no sequence), and it points at the driver even though the sequence is what hangs.
DebugLab — the hung sequence caused by the driver's missing item_done
A test that hung in the sequence, caused by the driver forgetting item_done on the other side of the handshake
A team's test hung — it ran forever, never reaching the end, the simulation making no further progress. Looking at the waveform, they saw the first transaction drive on the interface — valid asserted, the item presented — and then nothing: the interface went idle and stayed idle, forever. The test never completed. The engineer traced the hang to the sequence: it was stuck in finish_item(req) for the first item — finish_item had been called and had never returned. So they examined the sequence: the body() loop, the start_item/finish_item calls, the randomization — all correct, textbook. They re-read it several times, suspecting a malformed sequence, a bad loop, a randomization that blocked. They added prints in the sequence confirming it reached finish_item and stopped there. They spent hours in the sequence, because that's where the hang was — finish_item, not returning. The sequence, however, was doing exactly what finish_item is supposed to do: wait.
The driver forgot to call item_done() after get_next_item(), so the sequence's finish_item never received the completion signal and blocked forever — the hang surfaced in the sequence (the waiting side), but the cause was the driver (the other side of the handshake):
✗ THE DRIVER FORGOT item_done — the sequence's finish_item never returns:
class my_driver extends uvm_driver #(my_txn);
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req); // got the FIRST item (valid drove — visible on waveform)
drive(req);
// (forgot) seq_item_port.item_done(); ← the "done bell" is never rung
end // loops back to get_next_item, but finish_item never returned
endtask
endclass
// sequence: start_item(req); finish_item(req); ← finish_item BLOCKS forever (no item_done)
// → symptom: hang in finish_item (the SEQUENCE) ; cause: missing item_done (the DRIVER)
// → waveform: one item drives, then permanent stall
// OBSERVE: raise verbosity on driver + sequencer
// driver log shows get_next_item reached + drive done, but NO item_done
// sequencer log shows finish_item entered, never completed → the missing done bell
✓ FIX — call item_done() once per get_next_item():
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive(req);
seq_item_port.item_done(); // ✓ rings the done bell → finish_item returns → next item flows
end
endtaskThis is the missing-item_done bug — a cardinal sequencer failure and a perfect example of the symptom's side not being the cause's side (27.1). The test hung in the sequence's finish_item, so the engineer debugged the sequence — examining its loop, its calls, its randomization — all correct, because the sequence was doing exactly what finish_item does: block until the driver signals completion. The cause was on the other side of the handshake: the driver called get_next_item (got the first item — visible as the one driven transaction) and drive, but forgot item_done(). So the done bell was never rung, the sequence's finish_item never returned, and the driver looped back to get_next_item — blocking again (because the sequence couldn't produce a second item, being stuck in finish_item). The result was a permanent stall after one transaction, with the hang appearing in the sequence (the waiting side) and the cause in the driver (the broken side). The reason this misled is exactly the handshake's two-sidedness: the stall surfaces where something is waiting, and finish_item is where the sequence waits — so the symptom points at the sequence, while the driver's missing item_done is the real cause. The observation that resolves it is raising verbosity on both the driver and sequencer: the driver log shows get_next_item reached and drive done but no item_done; the sequencer log shows finish_item entered and never completed — together pointing at the missing done bell. The fix is one line: seq_item_port.item_done() after drive, which rings the bell, returns finish_item, and lets the next item flow. The general lesson, and the chapter's thesis: the sequence and driver are a blocking handshake, so a hung sequence (stuck in finish_item) is usually caused by the driver's missing item_done on the other side — the symptom surfaces on the waiting side (the sequence), the cause is on the broken side (the driver); don't debug the side where the stall appears — read the symptom to find the waiting side, then check the opposite side for the break, and raise verbosity on both the driver and sequencer to see which handshake step is missing, because a sequence hung in finish_item is doing exactly what it should — waiting — and the fault is the driver that never rang the done bell. A hung sequence points at the sequence but is caused by the driver's missing item_done — fix the opposite side from where the stall appears.
The tell is a test that hangs with the sequence stuck in finish_item, or an interface that drove once then stalled. Diagnose the missing item_done:
- Read the waveform shape. One transaction driven then a permanent stall points to a completed get but a missing item_done, not a missing sequence (which would be idle from the start).
- Check the side opposite the hang. The sequence is hung in finish_item, which is correct waiting behavior; investigate the driver, the other side of the handshake.
- Raise driver and sequencer verbosity. Look for get_next_item reached and drive done but no item_done, and finish_item entered but never completed.
- Verify one item_done per get_next_item. Confirm the driver calls item_done on every path, including after early continues or returns that might skip it.
Complete the handshake every time:
- Call item_done once per get_next_item. Make the get-drive-done sequence a strict, unconditional pattern in the driver's loop.
- Watch early exits in the driver loop. A continue or return that skips item_done hangs the sequence; ensure every path through the loop reaches item_done.
- Read the symptom to the opposite side. A hung sequence means investigate the driver; an idle interface means investigate the producer (sequence start).
- Use verbosity to see the handshake. Make raising driver and sequencer verbosity the first move on any stall, to see which handshake step is missing.
The one-sentence lesson: the sequence and driver are a blocking handshake, so a hung sequence stuck in finish_item is usually caused by the driver forgetting item_done on the other side — the symptom surfaces on the waiting side (the sequence) while the cause is on the broken side (the driver), so read the symptom to find the waiting side and fix the opposite one, calling item_done once per get_next_item on every path through the driver loop.
Common Mistakes
- Forgetting item_done in the driver. A missing item_done blocks the sequence's finish_item forever; call item_done once per get_next_item on every path.
- Debugging the hung sequence instead of the driver. The sequence is correctly waiting; the cause is the driver's missing item_done on the other side of the handshake.
- Not starting the sequence, or starting it on the wrong sequencer. An unstarted or misdirected sequence leaves the driver idle; start it on the sequencer whose driver drives the target interface.
- An early continue or return skipping item_done. A path through the driver loop that skips item_done hangs the sequence; ensure every path completes the handshake.
- Locks or grabs without release. A sequence that grabs or locks the sequencer and never releases starves the others; release locks and use arbitration deliberately.
- Diagnosing a stall without verbosity. The handshake events are invisible by default; raise driver and sequencer verbosity to see which step is missing.
Senior Design Review Notes
Interview Insights
Because the handshake is blocking on both sides, so each side waits on the other, and a failure on one side leaves the other waiting forever. The handshake works like this: the driver runs a loop calling get_next_item, which blocks until a sequence produces an item; then it drives the item; then it calls item_done, which signals completion. The sequence, meanwhile, calls start_item to request the sequencer, then finish_item, which blocks until the driver calls item_done. So there are two blocking points: the driver blocks in get_next_item waiting for an item, and the sequence blocks in finish_item waiting for item_done. Now consider the two breaks. If the producer side fails — no sequence is started, or it's started on the wrong sequencer — then no item ever arrives at the driver, so the driver stays blocked in get_next_item forever. The stall is on the driver side, and the symptom is an idle interface: nothing drives. If the consumer side fails — the driver gets the item and drives it but forgets item_done — then the sequence's finish_item never receives its completion signal and blocks forever. The stall is on the sequence side, and the symptom is a hung sequence. The crucial and counterintuitive consequence is that the symptom appears on the side that was waiting, which is the opposite side from where the break is. A hung sequence — finish_item not returning — surfaces in the sequence, but the sequence is doing exactly what it's supposed to do, wait, and the cause is the driver that didn't ring the done bell. An idle interface surfaces at the driver and the pins, but the cause is the producer that never sent an item. So the handshake's two-sidedness inverts the debugging instinct: you don't fix where the stall is, you fix the side that should have unblocked it. The diagnostic rule is to read the symptom to find the waiting side, then investigate the opposite side for the break — idle interface means fix the producer, the sequence start; hung sequence means fix the consumer, the driver's item_done. The kitchen analogy is exact: no food coming out is no-tickets, not a cook problem, and a waiter frozen at the pass is the cook's un-rung bell, not a waiter problem.
An idle interface means the driver is blocked in get_next_item with no item arriving, so the cause is on the producer side, and you diagnose it by checking whether a sequence was started, on which sequencer, and whether the driver even reaches get_next_item. First, recognize the shape: idle from the very start — valid and the other signals flat for the whole run, the test not ending — localizes to the sequencer or sequence, the producer side, because the driver is waiting for stimulus that never comes. It's not a DUT bug, because the DUT can't respond to stimulus that never arrives. Then there are a few producer-side causes to distinguish, which you do by raising verbosity on the driver and sequencer. One: no sequence was started at all — nothing called seq.start and no default_sequence was set on the sequencer. The verbose log shows no sequence starting, and the driver's get_next_item is reached but never returns. The fix is to start the sequence, on the right sequencer, or set it as the default sequence for the phase. Two: the sequence was started on the wrong sequencer — it runs, but its items go to a different driver, so the intended interface stays idle. The verbose log shows the sequence running and producing items, but on a sequencer other than the one feeding your interface; you'd see activity on the wrong agent. The fix is to start it on the sequencer whose driver drives the target interface. Three: the driver never reaches get_next_item — for instance its run_phase has a bug before the loop, or the driver wasn't built, or the virtual interface is null and it crashed earlier. Verbosity and the topology print clarify this. So the method is: confirm idle-from-start to localize to the producer, raise driver and sequencer verbosity, and check in order whether a sequence started, whether it's on the correct sequencer, and whether the driver reaches get_next_item. The verbose handshake events tell you which producer-side cause it is, and each has its fix — start the sequence, fix the sequencer, or fix the driver setup. The key is not to debug the DUT or the driver's drive logic, because nothing is being asked of them yet; the failure is upstream, in producing the stimulus.
item_done is the driver's signal to the sequencer that it has finished consuming the current item, which unblocks the sequence's finish_item and lets the sequence proceed to its next item — so if the driver forgets item_done, the sequence's finish_item never returns and the sequence hangs, stalling all further stimulus. The handshake protocol is strict: the driver calls get_next_item to obtain an item, drives it, then must call item_done to complete the transaction. On the sequence side, finish_item blocks precisely until that item_done arrives. So item_done is the completion half of the handshake — the done bell that releases the waiting sequence. If the driver forgets it, the sequence stays blocked in finish_item forever. And because the sequence is stuck, it can't produce its next item, so the driver, having looped back to get_next_item, also blocks again. The whole stimulus flow freezes after exactly one item. The waveform signature is distinctive: the first transaction drives — valid asserts, because the driver did get and drive that first item — and then the interface goes permanently idle, because no second item can be produced. This one-item-then-stall shape is what distinguishes a missing item_done from a missing sequence, where the interface would be idle from the very start because no item ever arrives. The dangerous part for debugging is that the hang surfaces in the sequence — finish_item not returning — so the instinct is to debug the sequence. But the sequence is correct; it's doing exactly what finish_item does, waiting. The cause is the driver on the other side. You confirm by raising verbosity: the driver log shows get_next_item reached and drive done but no item_done, and the sequencer shows finish_item entered but never completed. The fix is to call item_done once per get_next_item. A subtle variant is an item_done that's present but skipped on some path — an early continue or return in the driver loop that jumps past it — which hangs the sequence intermittently or under certain conditions. So the rule is to make get, drive, done an unconditional pattern, with item_done reached on every path through the loop. item_done is small but essential: it's the half of the handshake that keeps stimulus flowing, and forgetting it is one of the most common reasons a UVM test hangs.
When multiple sequences run on one sequencer, the sequencer arbitrates among their requests, and a sequence can be starved — never granted — if another sequence monopolizes the sequencer through a lock, a grab, or a dominating priority. The sequencer is a shared resource: several sequences may be running concurrently, each calling start_item to request access for their items, and the sequencer grants one at a time according to an arbitration scheme — FIFO, priority, weighted, and so on. Normally every requesting sequence eventually gets granted. But there are mechanisms that can prevent that. A lock: a sequence can call lock on the sequencer, which gives it exclusive access until it calls unlock — no other sequence is granted while the lock is held. If a sequence locks and never unlocks, every other sequence is starved indefinitely. A grab is similar but even higher priority, jumping ahead of locks; a sequence that grabs and never ungrabs starves the others. Priority: if arbitration is priority-based and one sequence runs at a high priority continuously, a lower-priority sequence may never be granted as long as the high one keeps requesting. The symptom of starvation is that one sequence runs — you see its items driving — while another sequence you started never seems to run, appearing hung even though it's actually just never being granted. This looks like a hang, but the cause is arbitration state, not the sequence or driver code. You diagnose it by raising sequencer verbosity to see the arbitration decisions and lock or grab state — which sequence holds a lock, what priorities are in play, which requests are pending and never granted. The fixes follow the cause: ensure locks and grabs are released, every lock paired with an unlock and every grab with an ungrab, often using a try-finally style so they're released even on an exception; reconsider priorities so a high-priority sequence doesn't continuously monopolize; or use a fairer arbitration scheme. The key insight is that a starved sequence is a third category of sequencer problem, distinct from the handshake stalls — it's not that the handshake broke, it's that the sequence never won access to start the handshake — so the investigation is the sequencer's arbitration and lock state rather than get_next_item or item_done.
The symptom points at the wrong place because the sequence-driver handshake is two-sided and blocking, so a break on one side surfaces as a stall on the other, waiting side — and you use that by deliberately inverting: read the symptom to find which side is waiting, then investigate the opposite side, where the break actually is. Concretely, there are two stalls. An idle interface is the driver blocked in get_next_item — the driver and the pins are where you see nothing happening — but the cause is the producer side, a sequence not started or on the wrong sequencer. If you debugged where the symptom is, you'd scrutinize the driver's drive logic or even the DUT, both of which are fine, because nothing has been asked of them yet. A hung sequence is the sequence blocked in finish_item — the sequence is where the hang is — but the cause is the consumer side, the driver's missing item_done. If you debugged where the symptom is, you'd scrutinize the sequence body, randomization, and loop, all of which are correct, because the sequence is just waiting as finish_item is meant to. In both cases the symptom's side is the waiting side and the cause's side is the other side. This is a specific instance of the general debugging principle that the symptom's location is not the cause's location, sharpened here into a precise rule because the handshake has exactly two sides. So you use it as a localization shortcut: idle interface means the driver is waiting, so the producer broke — check whether a sequence was started, on the right sequencer; hung sequence means the sequence is waiting, so the consumer broke — check whether the driver calls item_done. You confirm with verbosity on both the driver and the sequencer, which shows the handshake events: was get_next_item reached, did finish_item return, was item_done called. The discipline saves enormous time, because the natural instinct — debug where the stall appears — leads you to the correct-but-innocent side and away from the actual fault. The rule to internalize is: in a blocking handshake, fix the side opposite the stall.
Exercises
- Classify the stall. For each waveform — flat from the start; one transaction then flat; one sequence's items driving while another never appears — name the side that broke and the fix.
- Find the missing done bell. Given a driver loop with an early continue that skips item_done under a condition, explain when the sequence hangs and how to fix it.
- Right sequencer. Given a sequence started on the wrong agent's sequencer, explain why the intended interface is idle and how the verbose log would reveal it.
- Release the lock. Given a sequence that grabs the sequencer and never ungrabs, explain how another sequence is starved and how you'd confirm it.
Summary
- Sequencer problems are failures in the sequence→sequencer→driver chain — no stimulus, a hang, or a starved sequence — rooted in the blocking handshake between sequence and driver.
- The handshake blocks both ways: the driver blocks in
get_next_itemuntil an item arrives, the sequence blocks infinish_itemuntilitem_done— so a break on either side stalls the other, and the symptom names the waiting side, not the broken side. - The symptom partitions the causes: idle from the start → producer (no sequence started, or wrong sequencer); one item then stall → consumer (the driver's missing
item_done); one sequence running while another never does → arbitration (lock/grab/priority starvation). - The cardinal trap: a hung sequence (stuck in
finish_item) makes you suspect the sequence, but the cause is the driver's missingitem_doneon the other side — fix the side opposite the stall. - The durable rule of thumb: in the blocking sequence-driver handshake a break on either side stalls the other, and the symptom surfaces on the waiting side while the break is on the opposite side — an idle interface (driver waiting) means fix the producer (start the sequence on the right sequencer), a hung sequence (sequence waiting in finish_item) means fix the consumer (the driver's missing item_done, called once per get_next_item on every path); raise driver and sequencer verbosity to see which handshake step is missing, and never debug the side where the stall appears.
Next — Phase Issues: the methodology turns to phasing. Why the test ends before stimulus completes, or hangs at the end of a phase, or components do work in the wrong order — objections never raised or never dropped, work done in the wrong phase, run-phase versus the scheduled sub-phases confused, or phase.raise_objection/drop_objection mismatched — how to read the symptom (zero-time finish, or a phase that never ends), how to observe it (the objection trace), and how to fix each so phases sequence and complete correctly.