Skip to content

UVM

finish_item()

The second half of the item handshake — finish_item closes the randomization window, delivers the finalized item to the driver, and blocks until the driver signals item_done, pacing the sequence to the driver.

Sequences · Module 9 · Page 9.4

The Engineering Problem

The previous chapter covered start_item — winning the grant and opening the randomization window (Module 9.3). This chapter is the call that closes that window and completes the handshake: finish_item. Where start_item blocks on the grant, finish_item blocks on the driver — it delivers the finalized item and then waits until the driver has driven it. Misunderstand that and you either mutate an item the driver has already taken (so your change never reaches the pins), or you stare at a sequence frozen at finish_item because the driver never signalled completion.

finish_item(item) is the second half of the two-phase handshake. It does three things in order: it closes the randomization window (the item is now final), it delivers the item to the driver (satisfying the driver's get_next_item), and it blocks until the driver calls item_done. So what finish_item waits on is the driver's completion — the time-consuming driving from Module 8.3 — which makes finish_item the point where the sequence is paced by the driver (back-pressure). It's also the point of no return: once finish_item has handed the item over, the item belongs to the driver, and changing it afterward can't affect what's driven. This chapter is finish_item: what it sends, what it blocks on, why it's the point of no return, and why a finish_item that never returns means the driver never said item_done.

What does finish_item do — how does it close the window, deliver the item to the driver, and block until item_done — why is it the point of no return and the back-pressure point, and why does a hung finish_item mean the driver never completed the item?

Motivation — why "finish" is a separate, blocking call

finish_item is a distinct, blocking call (rather than a fire-and-forget send) because delivering the item and waiting for it to be driven are both essential, and in that order:

  • The item must be delivered after it's finalized, so "finish" closes the window. start_item opened the window to randomize (Module 9.3); something has to close it and mark the item final. finish_item is that boundary — after it, the item is the driver's, so it must come after the randomization.
  • The sequence must wait for the driver, so "finish" blocks. A sequence that fired items without waiting would race ahead of a driver that hasn't consumed them. Blocking until item_done means the sequence advances in lockstep with the driver — one item fully driven before the next is prepared (Module 8.3).
  • Blocking on item_done is the back-pressure mechanism. Because the driver calls item_done only when it (and the DUT behind it) has finished, finish_item's wait is governed by the design's pace. This is how stimulus is automatically paced to the DUT, with no manual flow control (Module 8.3).
  • It's the point of no return, which keeps the model coherent. Once the item is delivered, the driver reads its values; allowing later changes to matter would make "what was driven" ambiguous. Making finish_item a hard handoff means the item driven is exactly the item as it was at finish_item.
  • It's the response rendezvous point. If the driver returns a response with item_done(rsp), it becomes available after finish_item (retrieved via get_response, Module 8.2) — so finish_item completing is also when a request's answer has been produced.

The motivation, in one line: finish_item must deliver the finalized item and then wait for the driver to drive it, so it's a separate blocking call — closing the window, handing the item over (a point of no return), and suspending the sequence until item_done, which is exactly what paces the sequence to the driver.

Mental Model

Hold finish_item as submitting the form and waiting at the counter until it's processed:

finish_item is handing your completed form to the clerk and waiting until they've fully processed it before you do anything else — and once it's in their hands, you can't change it. In the previous step you filled in the form on your turn (start_item + randomize). finish_item is submitting it: you slide it across the counter (deliver the item to the driver), and now it's theirs — you can't reach over and edit it, and any scribble you make on your copy afterward changes nothing about what they process (the point of no return). Then you wait at the counter (finish_item blocks) until the clerk has finished processing it and stamps it done (item_done) — and only then do you step away to handle your next item. The waiting matters: you can't fire off ten forms and walk away; the clerk processes one at a time at their pace, so a slow clerk slows you down (back-pressure). And if the clerk takes your form but never stamps it done — gets distracted, drops the step — you wait at the counter forever (finish_item hangs because item_done never comes).

So finish_item is submit-and-wait-until-done: hand over the finalized item, accept that it's now the driver's, and stay blocked until the driver stamps it complete. The wait is the pacing; the handoff is the point of no return.

Visual Explanation — closing the window, sending, waiting

The defining picture is finish_item's three ordered actions: close the window, deliver to the driver, block until item_done — the mirror of start_item's "open."

finish_item closes the randomization window, delivers the item to the driver, blocks until item_done, then returnsclose window → deliver item → block until item_done → returnclose window → deliver item → block until item_done → return1Close the window — item is finalthe randomization window (Module 9.3) ends here; the item's valuesare now fixed.2Deliver to the driver (point of no return)the item satisfies the driver's get_next_item; it now belongs tothe driver.3Block until item_donesuspended while the driver drives the item — the back-pressurepoint (Module 8.3).4item_done → finish_item returnsthe driver signals completion; finish_item unblocks; the sequenceadvances (response available).
Figure 1 — finish_item closes the window, delivers, and waits. After the randomization window (Module 9.3), finish_item closes it — the item is final. It delivers the item to the driver, satisfying the driver's get_next_item. Then it blocks until the driver, having driven the item, calls item_done. item_done unblocks finish_item, which returns to the sequence. finish_item's wait is the driver's driving time — the back-pressure point — and the delivery is the point of no return.

The figure shows finish_item as the closing mirror of start_item's opening. Step 1, it closes the randomization window: whatever the item was set to in the window (Module 9.3) is now finalfinish_item is the boundary past which the item can't be meaningfully changed. Step 2, it delivers the item to the driver, satisfying the driver's pending get_next_item — and this is the point of no return, because the item now belongs to the driver, which will read its values to drive the pins. Step 3, finish_item blocks: the sequence is suspended while the driver drives the item over clock cycles (the time-consuming "how", Module 8.1), and the length of this block is the driver's driving time — which makes it the back-pressure point where the driver (and the DUT behind it) paces the sequence. Step 4, when the driver calls item_done, finish_item unblocks and returns to the sequence, which can now prepare its next item (and retrieve any response, Module 8.2). The shape to carry is the symmetry with start_item: start_item opens (request grant → window), finish_item closes (deliver → wait for done) — together they bracket the item, with the randomization safely in between and the driver's work in the finish_item block.

RTL / Simulation Perspective — finish_item in code

finish_item is one call that pairs with start_item, and the surrounding callbacks (mid_do, post_do) mark the send and completion points.

finish_item: deliver the finalized item and wait for item_done
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
task my_seq::body();
  bus_item req = bus_item::type_id::create("req");
  start_item(req);                    // ① grant + open window (Module 9.3)
  assert(req.randomize());            // ② finalize the item in the window
 
  finish_item(req);                   // ③ CLOSE window, DELIVER to driver, BLOCK until item_done
                                      //    (mid_do called before send; post_do after item_done)
  // ── finish_item has returned → the driver has driven the item ──
  get_response(rsp);                  // optional: retrieve the response (Module 8.2)
endtask
 
// ── DRIVER side: get_next_item is satisfied by finish_item; item_done unblocks it ──
task my_driver::run_phase(uvm_phase phase);
  forever begin
    seq_item_port.get_next_item(req); // receives the item finish_item delivered
    drive_on_pins(req);               // CONSUMES TIME — this is finish_item's block (Module 8.3)
    seq_item_port.item_done();        // ← signals completion → unblocks the sequence's finish_item
  end
endtask

The idiom is the closing half of the pair. After start_item and the randomization (Module 9.3), finish_item(req) does the three actions in one call: it closes the window (the item is now final), delivers the item to the driver (satisfying the driver's seq_item_port.get_next_item), and blocks until the driver calls item_done. The line after finish_item does not execute until the driver is done — so finish_item's return is the signal that the item has been driven. On the driver side, get_next_item(req) receives exactly the item finish_item delivered, drive_on_pins(req) is the time-consuming work that constitutes finish_item's block (Module 8.3), and item_done() is what unblocks the sequence's finish_item. The two sides interlock precisely: finish_itemget_next_item for the handoff, item_donefinish_item-return for the completion. Around finish_item, UVM calls the mid_do callback (after finalize, before the send) and post_do (after item_done) — hooks for per-item logic at the send and completion points. And if the driver returns a response (item_done(rsp)), it's available after finish_item via get_response (Module 8.2). The shape: finish_item is the blocking "deliver and wait" call, and the driver's item_done is what releases it.

Verification Perspective — the finish_item ↔ item_done interlock

finish_item's blocking is one side of a precise interlock with the driver: the sequence's finish_item and the driver's get_next_item/item_done wait on each other, which is what synchronizes them.

finish_item delivers to get_next_item and blocks; the driver drives then item_done unblocks finish_itemdelivers itemdriver drives (time)driving completeunblocksfinish_item (seq)deliver + blockget_next_item (drv)receives the itemdrive on pinsconsumes time (Module 8.3)item_done (drv)signals completionfinish_item returns (seq)sequence advances12
Figure 2 — the finish_item ↔ driver interlock. The sequence's finish_item delivers the item, satisfying the driver's get_next_item, then blocks. The driver drives the item (consuming time), then calls item_done, which unblocks the sequence's finish_item. So finish_item and the driver interlock at two points: delivery (finish_item → get_next_item) and completion (item_done → finish_item returns). Neither side runs ahead — the sequence waits for the driver to finish, which is the back-pressure that paces stimulus to the driver.

The interlock is the heart of finish_item's behavior. There are two synchronization points between the sequence and the driver. The first is delivery: the sequence's finish_item hands the item over, satisfying the driver's get_next_item — so the driver, which was blocked waiting for an item (Module 8.3), now has one. The second is completion: after the driver drives the item (the time-consuming work), it calls item_done, which unblocks the sequence's finish_item. Between these two points, the sequence is suspended inside finish_item while the driver works — neither side runs ahead of the other. This mutual waiting is exactly what makes the sequence and driver advance in lockstep: the driver can't get a second item until the sequence delivers one (it's blocked in get_next_item), and the sequence can't deliver a second item until the driver finishes the first (it's blocked in finish_item). The consequence is the back-pressure of Module 8.3: because finish_item waits for item_done, and item_done comes only as fast as the driver (and DUT) allow, the sequence's rate is set by the driver's rate. So finish_item isn't just "send" — it's the sequence's half of a two-point interlock that paces it to the driver, and understanding both points (delivery and completion) is what lets you reason about where a stalled handshake is stuck.

Runtime / Execution Flow — the point of no return and pacing

At run time, finish_item marks the transition from "the sequence's item" to "the driver's item," and its block is the entire driving span — so it's both the point of no return and the pacing point.

finish_item transfers ownership to the driver (point of no return) and blocks for the driving span (pacing)window (editable) → finish_item delivers (no return) → block (driver's pace) → nextwindow (editable) → finish_item delivers (no return) → block (driver's pace) → next1Before: item is the sequence'sin the window — still finalizable; the sequence owns it.2finish_item delivers (point of no return)ownership transfers to the driver; it reads the item's values todrive. No later change matters.3Blocked for the driving span (pacing)the sequence is suspended for the whole drive — its rate is thedriver's rate (back-pressure).4item_done → advance to next itemfinish_item returns; the sequence prepares the next item (one perDUT-paced cycle).
Figure 3 — finish_item is the point of no return and the pacing point. Before finish_item, the item belongs to the sequence (still editable, in the window). At finish_item, it's delivered — now the driver's, and reading its values; changes after this don't reach the pins. finish_item then blocks for the whole driving span, so the sequence advances only when the driver completes (item_done). The block's length is the driver's pace — back-pressure. After it returns, the sequence prepares the next item.

The runtime view ties finish_item's two famous properties to its mechanics. Before finish_item (step 1), the item is the sequence's: it's still in the window, finalizable, owned by the sequence. At finish_item (step 2), ownership transfers to the driver — the driver reads the item's values to drive the pins — and this is the point of no return: a change to the item after finish_item is delivered can't affect what's driven, because the driver already has (and is reading) the item as it was at delivery. This is why you must finalize before finish_item (Module 9.3's window) and why mutating afterward is a bug. During finish_item's block (step 3), the sequence is suspended for the entire driving span, so its progress is gated by the driver — the pacing/back-pressure point: one item advances per DUT-paced driving cycle, and a slow DUT naturally slows the sequence (Module 8.3). After item_done (step 4), finish_item returns and the sequence prepares the next item. So the two properties are one mechanism viewed two ways: the handoff makes it the point of no return, and the wait for item_done makes it the pacing point. Both follow from finish_item being a blocking delivery — which is exactly why it's a separate call from start_item, and why the line after it runs only once the driver is done.

Waveform Perspective — finish_item's block is the driving time

finish_item's defining behavior — blocking for the whole driving span — is clearest on a timeline: the call returns only when the driver, after driving across many cycles, signals item_done.

finish_item blocks for the entire driving span, until item_done

12 cycles
finish_item blocks for the entire driving span, until item_donefinish_item delivers the item: fi_wait high — blockedfinish_item delivers t…driver drives over several cycles (D0..D4) — this is finish_item's blockdriver drives over sev…item_done: driver signals completionitem_done: driver sign…fi_wait falls: finish_item returns, sequence advancesfi_wait falls: finish_…clkfi_waitdrv_activedataD0D1D2D3D400000000000000item_donet0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — finish_item on a timeline. At the send, fi_wait goes high: the sequence is blocked inside finish_item, having delivered the item. drv_active shows the driver driving the item over several cycles — this whole span is finish_item's block. When the driver finishes and pulses item_done, fi_wait falls and finish_item returns. So finish_item's duration equals the driver's driving time — the back-pressure. A driver that never pulses item_done leaves fi_wait high forever.

The waveform shows finish_item's block is the driving time. From the moment of delivery, fi_wait is high: the sequence is blocked inside finish_item, having handed the item to the driver. The drv_active trace and the data beats (D0D4) show the driver driving the item over several cycles — and this entire span is finish_item's block, because the sequence can't proceed until the driver is done. When the driver completes and pulses item_done, fi_wait falls: finish_item returns, and the sequence advances to its next item. So the length of finish_item equals the length of the driving — which is precisely the back-pressure relationship: the sequence is paced by how long the driver (and DUT) take. Compare this with start_item (Module 9.3), whose block was the grant wait; together, start_item waits for the grant, then finish_item waits for the drive, so the full handshake's time is grant-wait plus drive-time, with the small randomization window in between. The diagnostic reading mirrors start_item's: if fi_wait never falls, finish_item is stuck waiting for an item_done that isn't coming — the driver took the item but never signalled completion (the DebugLab). The timeline makes finish_item's nature concrete: a blocking delivery whose duration is the driver's driving time.

DebugLab — the finish_item that never returned

A sequence frozen at finish_item because the driver skipped item_done

Symptom

A sequence drove fine for a while, then stalled: it produced one item and then a message right after its finish_item(req) never printed. The simulation made no further progress on that agent — no more transactions — and eventually hit a timeout. There was no error; the sequence was simply suspended inside finish_item, waiting. The driver appeared to have received the item (it drove something), but the sequence never got control back.

Root cause

The driver called get_next_item but, on a particular path, returned to the top of its loop without calling item_done — so the item was never completed, and the sequence's finish_item waited for an item_done that never came:

why finish_item blocked forever
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
driver run loop:
  get_next_item(req);          // receives the item finish_item delivered
  if (req.is_read && special) begin
     handle_special(req);
     continue;                 // ✗ early continue — item_done() NEVER called for this item
  end
  drive_on_pins(req);
  item_done();                 // only reached on the normal path
 
sequence:
  finish_item(req);            // delivered the item, now BLOCKS waiting for item_done...
                               // → item_done never comes → finish_item never returns
 
result:  the sequence is suspended at finish_item indefinitely (no error — a completion hang)
         and the driver's next get_next_item may also error ("get_next_item called twice
         without item_done") because the prior item was never completed
fix — pair every get_next_item with item_done on EVERY path:
  get_next_item(req);
  if (req.is_read && special) begin handle_special(req); item_done(); continue; end  // ✓ complete it
  drive_on_pins(req);
  item_done();                 // ✓ normal path

This is the completion-side mirror of the previous chapter's grant hang. finish_item delivers the item and then blocks until the driver calls item_done (Figure 2) — that's its defining wait. The driver, however, hit an early continue on a special path and skipped item_done, so the item it took was never marked complete. The sequence's finish_item is therefore waiting for a completion signal that will never arrive, and it hangs with no error (a completion hang, not a crash). There's often a second symptom: when the driver loops back and calls get_next_item again without having completed the previous item, UVM flags "get_next_item called twice without item_done" — a strong hint pointing straight at the missing completion. The fix is the driver-side pairing discipline: every get_next_item must be matched by exactly one item_done, on every path — including special-case, error, and early-continue paths. The general lesson is diagnostic and symmetric with start_item: a start_item that never returns means the grant isn't coming (Module 9.3); a finish_item that never returns means item_done isn't coming — the driver took the item but never completed it. The wait is in finish_item; the cause is the driver's missing item_done.

Diagnosis

The tell is a finish_item that never returns, often with a "get_next_item twice" warning. Diagnose completion hangs:

  1. Confirm the hang is at finish_item. A message right after finish_item that never prints localizes it to the completion wait — distinct from a start_item (grant) hang.
  2. Check the driver pairs get_next_item with item_done. Trace every path between the driver's get_next_item and the loop's next iteration; any path missing item_done is the bug.
  3. Look for the "get_next_item called twice" warning. It means a prior item was never completed — a direct pointer to the missing item_done.
  4. Distinguish from a start_item hang. If the hang is at start_item, it's the grant (Module 9.3); at finish_item, it's item_done. Confirm which call is stuck.
Prevention

Complete every item, exactly once, on every path:

  1. Pair get_next_item with item_done on all paths. Including special-case, error, and early-exit paths — the moment you call get_next_item, ensure exactly one item_done follows however the code leaves.
  2. Don't mutate the item after finish_item. Past delivery the item is the driver's (point of no return); set everything the driver needs before finish_item, not after.
  3. Match get_response to a responding driver. If the sequence calls get_response, the driver must return a response (item_done(rsp), with set_id_info, Module 8.2); otherwise skip get_response.
  4. Treat a hung finish_item as a driver-completion problem. The wait is in the sequence, but the fix is in the driver's item_done pairing — look there.

The one-sentence lesson: finish_item delivers the item and blocks until the driver calls item_done, so a finish_item that never returns means the driver took the item but skipped item_done (often on a special-case or early-exit path) — pair every get_next_item with exactly one item_done on every path, because the sequence's completion wait depends entirely on the driver signalling done.

Common Mistakes

  • Mutating the item after finish_item. Past delivery the item belongs to the driver; later changes don't reach the pins. Finalize everything before finish_item (in the window, Module 9.3).
  • A driver path that skips item_done. The sequence's finish_item hangs waiting for a completion that never comes; pair every get_next_item with exactly one item_done on all paths.
  • Expecting finish_item not to block. It blocks for the entire driving time — the sequence is suspended until item_done. Treat it as a synchronization point, not a fire-and-forget send.
  • finish_item without a preceding start_item. The handshake must be opened first; finish_item alone has no granted item to send.
  • Calling get_response when the driver returns none. It blocks waiting for a response that never comes; only retrieve responses for items the driver answers (Module 8.2).
  • Confusing a finish_item hang with a start_item hang. A start_item hang is the grant (Module 9.3); a finish_item hang is item_done. Identify which call is stuck.

Senior Design Review Notes

Interview Insights

finish_item is the second half of the two-phase item handshake. It does three things in order: it closes the randomization window that start_item opened, so the item is now final; it delivers the item to the driver, satisfying the driver's get_next_item; and it blocks until the driver calls item_done. So what finish_item blocks on is the driver's completion — the time-consuming driving of the item over clock cycles. That makes finish_item's duration equal to the driving time, which is the back-pressure point: because the driver calls item_done only when it and the DUT have finished, the sequence's rate is paced by the driver. It's also the point of no return: once finish_item delivers the item, the item belongs to the driver, which reads its values to drive the pins, so changing the item after finish_item can't affect what's driven. The line after finish_item doesn't execute until item_done arrives, so finish_item returning is the signal that the item has been driven, and any response is then available via get_response. The relationship with start_item is symmetric: start_item blocks on the grant, finish_item blocks on item_done, with the randomization window in between, so the full handshake time is grant-wait plus drive-time. When a finish_item never returns, the driver took the item but never called item_done — the wait is in the sequence, but the cause is the driver's missing completion.

Exercises

  1. Order the calls. List the sequence-side and driver-side calls for one item in execution order (start_item, randomize, finish_item, get_next_item, drive, item_done), and mark which two sequence-side calls block and on what.
  2. Fix the hang. A driver continues on a special case before item_done. Describe the symptom and rewrite the path so finish_item returns.
  3. Explain the boundary. A sequence changes a field after finish_item and the change doesn't appear on the pins. Explain why, naming the property of finish_item.
  4. Reason about pacing. Explain why a sequence slows down when the DUT inserts wait states, referencing what finish_item blocks on.

Summary

  • finish_item(item) is the second half of the handshake: it closes the randomization window (the item is now final), delivers the item to the driver (satisfying get_next_item), and blocks until the driver calls item_done.
  • It blocks on the driver's completion — the time-consuming driving — so finish_item's duration is the driving time, making it the back-pressure point where the sequence is paced by the driver (and the DUT behind it).
  • It's the point of no return: once delivered, the item belongs to the driver, which reads its values to drive — so a change after finish_item can't reach the pins (finalize before, in the window of Module 9.3).
  • finish_item and the driver interlock at two points — delivery (finish_itemget_next_item) and completion (item_donefinish_item returns) — so neither runs ahead, advancing the sequence and driver in lockstep.
  • The durable rule of thumb: finalize the item before finish_item, then finish_item to deliver and wait for item_done — and when finish_item never returns, the driver took the item but skipped item_done (pair every get_next_item with exactly one item_done, on every path), because finish_item's wait is entirely the driver's completion signal.

Next — Sequence Control: with the item handshake (start_item/finish_item) understood, the next chapter steps up to controlling sequences themselves — running them in order or in parallel, nesting sub-sequences, and the macros and methods that orchestrate stimulus above the single-item level.