Skip to content

UVM RAL · Chapter 7 · RAL Sequences

Sequence Debugging

Register sequences fail in a small number of recurring ways, and by far the most disorienting is the hang, where the simulation runs, prints nothing, raises no error, and simply stops making progress until the harness timeout kills it hours later. A hang gives you no message to grep, so the cure is a disciplined bisection that locates where in the sequence's life it stalls. If it hangs before the first bus transaction, the cause is usually the wrong sequencer or an unset model handle. If it hangs mid-sequence, the culprit is often an unbounded wait on a status bit the DUT never asserts. If the test ends too early or never ends, the cause is usually mismatched objections. This page gives a triage decision tree and breaks the most common hang, a poll on a done bit that never asserts, with the fix being a bounded poll that fails cleanly and names the real problem.

Foundation12 min readUVM RALDebuggingsequence hangunbounded waitobjections

Chapter 7 · Section 7.6 · RAL Sequences

1. Why Should I Learn This?

A hanging register sequence is one of the most time-wasting failures in verification — no error, no message, just a simulation that runs until the harness timeout, and an engineer with nowhere obvious to look. Knowing the small set of ways register sequences hang, and a disciplined way to localize the stall, is what turns hours of helpless staring into minutes of directed diagnosis.

Learning to triage a register-sequence hang — before the first item (sequencer/handle), mid-sequence (unbounded wait), or at phase boundaries (objections) — is the debug craft that keeps the sequences of 7.1–7.5 dependable. It ties together the model handle (7.1) and the poll-with-timeout discipline (7.5) into a diagnostic method.

2. Industry Story — the regression that hung every night

A nightly regression starts timing out: several jobs run to the harness limit and are killed with no error, no failure message — just 'timeout.' The team burns days because a timeout with no message feels like an infrastructure problem, so they look at the grid, the licenses, the disk — everywhere but the sequence. Eventually someone runs one hanging test interactively, interrupts the simulator, and looks at where it is stopped: a register sequence, parked forever on wait(status.done == 1).

The done bit was never asserting because an upstream configuration register had been left at a wrong value (a real bug), so the block never finished and never set done. The sequence's unbounded wait then blocked forever, and because nothing bounded it, the entire simulation hung with no diagnostic — the harness timeout was the only signal, and it pointed at 'time,' not at the real cause. Once the wait was made bounded — poll done with a timeout, and uvm_error if it never sets (7.5) — the same underlying bug produced a clear, immediate message ('done never asserted within timeout') pointing straight at the stalled block, and the config bug was found in minutes. The post-mortem lesson: a register sequence that waits unbounded on a status bit turns any upstream bug that stops the bit from asserting into a silent, message-less hang of the whole simulation; bounding every wait converts that hang into a clear, localized error — and localizing a hang starts with finding where the sequence is stalled.

3. Concept — localize the stall, then infer the family

A hang has no message, so you cannot grep your way to it; you localize it by asking where in the sequence's life it stalls, which partitions the causes. Three families:

  • Stuck before the first bus transaction. No register operation ever reaches a driver. Usual causes: the sequence was started on the wrong sequencer or no sequencer (items go nowhere), or the model handle is null/unset (7.1) so the first register access crashes or stalls. Tell: you never see a single bus item from this sequence.
  • Stuck mid-sequence, after some transactions. Some operations completed, then progress stopped. Usual cause: an unbounded wait on a status bit — wait(done == 1), an unbounded ready poll — that never becomes true, often because of a real upstream bug (7.5). Tell: bus activity from the sequence stops at a specific point and never resumes.
  • Ends too early, or the phase never ends. The sequence and the phase boundary disagree. Usual cause: objections — raised and never dropped (phase hangs at its end waiting for a drop) or dropped too early (the test ends before the sequence finishes). Tell: the stall is at run_phase end, or the test finishes with the sequence incomplete.

Here is the triage tree — read where it stalls, and the family follows:

Register-sequence hang triage: no bus item -> sequencer/handle; stops mid-way -> unbounded wait; phase-end -> objectionsnoitemitems seenstopped mid-wayran toend-ishat phase endSequence hangs —no error, noprogress. WHEREdoes it stall?Did you ever see ONE bus item from this sequence?Did you eversee ONE busitem fromthis…NO -> not running on a driver: wrong/null sequencer, or null model handle (7.1)NO -> notrunning on adriver:wrong/null…Some items ran, then activity STOPPED at a point?Some itemsran, thenactivitySTOPPED at a…YES -> unbounded wait on a status bit that never asserts (7.5) — the common hangYES -> unboundedwait on a statusbit that neverasserts (7.5) —…Stall at phase END, or test ends with sequence unfinished?Stall atphase END, ortest endswith sequenc…YES -> objections: raised-and-never-dropped (hang) or dropped-too-early (ends)YES ->objections:raised-and-never-dropped(hang) or…Family known -> confirm the specific cause and fix (bound the wait, fix the sequencer/handle, balance objections)Family known ->confirm thespecific causeand fix (bound…
Figure 1 — triaging a register-sequence hang by WHERE it stalls. Never see a bus item from the sequence? -> it is not running on a driver: wrong/null sequencer or an unset model handle (7.1). Some items ran, then activity stops at a point? -> an unbounded wait on a status bit that never asserts (7.5) — the most common hang. Stall at phase end, or test ends with the sequence unfinished? -> objections raised-and-never-dropped or dropped-too-early. Localizing the stall turns a message-less hang into a named cause.

4. Mental Model — a hang is a location before it is a cause

5. Working Example — instrumenting a sequence to localize a hang

Add progress markers and bound the waits, so a hang tells you where — and so the next one cannot be silent:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Progress markers turn a silent hang into a visible 'last thing that happened'.
virtual task body();
  uvm_status_e s;  uvm_reg_data_t rd;
  `uvm_info("SEQ", "start: about to write mode", UVM_LOW)     // did we even start? (handle/sequencer)
  regs.mode.write(s, cfg.mode);
  `uvm_info("SEQ", "mode written; enabling", UVM_LOW)         // did the first bus item complete?
  regs.ctrl.enable.set(1'b1);  regs.ctrl.update(s);
  `uvm_info("SEQ", "enabled; polling done", UVM_LOW)          // did we reach the poll?
  // ... bounded poll below ...
endtask
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// BOUNDED wait: the fix that turns a 'done never asserts' hang into an immediate, localized error.
bit got_done = 0;
for (int i = 0; i < cfg.done_timeout; i++) begin
  regs.status.read(s, rd);
  if (rd[DONE_BIT]) begin got_done = 1; break; end
  #(cfg.poll_period);
end
if (!got_done)
  `uvm_error("SEQ", "done never asserted within timeout — check upstream config that gates 'done'")
// Same underlying bug that WOULD have hung forever now prints a message pointing at the real cause.
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Sequencer/handle sanity at the top catches the 'no bus item ever' family before it hangs:
if (regs   == null) `uvm_fatal("SEQ", "model handle 'regs' is null — set it before start (7.1)")
if (m_sequencer == null) `uvm_fatal("SEQ", "sequence started with no sequencer")

The markers localize where a hang stops; the bounded wait ensures the most common hang can never be silent again. Both convert 'it hangs' into 'it stopped here, because of this.'

6. Debugging Session — the unbounded wait that hung the whole sim

1

An unbounded wait on a done bit that never asserts hangs the entire simulation with no message; a bounded poll turns it into an immediate, localized error

BOUND THE WAIT
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The sequence polls 'done' with an UNBOUNDED wait:
virtual task body();
  uvm_status_e s;
  regs.mode.write(s, cfg.mode);
  regs.ctrl.enable.set(1'b1);  regs.ctrl.update(s);
  wait(regs.status.done.get() == 1);   // BUG: unbounded. If 'done' never asserts, this blocks forever
  // ... never reached if done stays 0 ...
endtask
// An upstream config register is wrong, so the block never finishes -> done stays 0 -> whole sim hangs.
Symptom

The test runs, produces some early bus activity, and then stops — no error, no message, no further progress — until the harness timeout kills it much later with only a 'timeout' signal. In a regression, several jobs time out identically, and because a message-less timeout looks like an infrastructure problem, time is lost investigating the grid, licenses, and disk before anyone suspects the sequence. Interrupting one run interactively shows the simulation parked on the wait(status.done == 1) line, which has been true-waiting since the block failed to finish.

Root Cause

Two layers, and the debug method separates them. The proximate cause is the sequence's unbounded wait: wait(done == 1) has no timeout, so if done never becomes 1 it blocks forever, and because it is on the main sequence thread, the whole simulation makes no further progress — a silent hang with no diagnostic beyond the eventual harness timeout (7.5). The underlying cause is why done never asserts: an upstream configuration register was left at a wrong value, so the block never completed its operation and never set done — a real functional bug. The unbounded wait did not cause that bug, but it hid it, converting a diagnosable 'block never finished' into an undiagnosable 'simulation hung.' The reason the hang was so hard to localize is exactly the absence of a message: an unbounded wait fails silently, giving the debugger nothing but a location to find by interrupting the sim.

Fix

Bound the wait: poll done in a loop with a timeout and raise a uvm_error if it never asserts, naming the likely cause (7.5). The same upstream config bug now produces an immediate, localized message — 'done never asserted within timeout' — that points straight at the stalled block instead of a silent hang, and the config bug is found in minutes. More broadly, the debug method the bug teaches: localize a hang by finding where it stalls — a mid-sequence stall on a wait is an unbounded poll on a bit that never asserts — and then bound every wait so the hang becomes a message. A bounded wait does not just fix this hang; it pre-empts the entire class, because every future 'bit never asserts' bug will now announce itself instead of hanging silently.

7. Common Mistakes

  • Unbounded waits on status bits. wait(done == 1) with no timeout turns any 'bit never asserts' bug into a silent, whole-sim hang — always bound the wait (7.5).
  • Guessing the cause before finding the location. A hang has a location; interrupt the sim or add progress prints and let where it stopped name the family.
  • Treating a message-less timeout as infrastructure. A no-error timeout is usually a hang in the sequence, not the grid — check the sequence first.
  • Starting a sequence on the wrong or no sequencer. Items never reach a driver and you never see a bus transaction — a 'stuck before the first item' hang.
  • Objection imbalance. Raised-and-never-dropped hangs the phase end; dropped-too-early ends the test before the sequence finishes.

8. Industry Best Practices

  • Bound every wait. Poll status bits with a timeout and a clear uvm_error — never an unbounded wait that can hang the whole simulation (7.5).
  • Instrument sequences with progress markers. uvm_info at each stage turns a silent hang into a visible 'last thing that happened,' pre-localizing the stall.
  • Triage a hang by location. No bus item -> sequencer/handle; stops mid-way -> unbounded wait; phase-end -> objections. Location before cause.
  • Sanity-check handle and sequencer at the top. A null model handle (7.1) or missing sequencer should fatal with a clear message, not hang.
  • Treat a bounded wait as a diagnostic investment. It converts a whole class of future 'bit never asserts' bugs from silent hangs into immediate, localized messages.

9. Interview / Review Questions

10. Key Takeaways

  • The worst register-sequence failure is the silent hang — no error, no message, no progress — and you diagnose it by localizing where it stalls, not by guessing the cause.
  • Where it stalls names the family: no bus item ever -> wrong/null sequencer or unset model handle (7.1); stops mid-sequence -> unbounded wait on a bit that never asserts (7.5); stall at phase end -> objections raised-and-never-dropped or dropped-too-early.
  • The most common hang is an unbounded wait on a status bit the DUT never asserts (from a real upstream bug), which hangs the whole simulation with only a harness timeout to show for it.
  • Bound every wait — poll with a timeout and a clear uvm_error — to convert that silent hang into an immediate, localized message, and to pre-empt the entire class of future 'bit never asserts' bugs.
  • Instrument sequences with progress markers and sanity-check the handle/sequencer at the top: turn a hang into a visible 'last thing that happened,' and the location does most of the diagnosis.