Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
    • The Phase System
    • UVM Phases: Build, Connect, Elaborate and Run
    • Objections Deep Dive: Drain Time and Propagation
      • Objection Hierarchy Propagation: Up the Component Tree
      • Objection Counting Rules: Raises, Drops, and Edge Cases
      • set_drain_time Deep Dive: Grace Period After Objections Clear
      • phase_ready_to_end: Last-Chance Phase Extension
      • End-of-Test Strategy: Choosing a Mechanism
      • Objections at Scale
    • Phase Debugging and End-of-Test Signoff
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Objections Deep Dive: Drain Time and Propagation
  1. Topics
  2. UVM Verification
  3. UVM Phases & Simulation Lifecycle
  4. Objections Deep Dive: Drain Time and Propagation
  5. set_drain_time Deep Dive: Grace Period After Objections Clear

Part 2 · Phases & Lifecycle · Intermediate

set_drain_time Deep Dive: Grace Period After Objections Clear

Why a grace period follows the last drop, how drains stack per level, how a raise cancels one, and how to size it from measured latency.

On this page

    Look again at the end of dma_smoke_test: after the interrupt and the STATUS checks comes #100ns;, then the drop. Delete that one line and run, and sometimes the scoreboard reports SB_LEFTOVER: an expected word that never came out. The test's work was finished. The DUT's was not.

    That #100ns is a hand-made drain time. UVM has a built-in one. This lesson shows how it works, why it is per level, what cancels it, and why it is still a guess, which is the problem the next lesson solves.

    The idea: last orders, then a grace period

    A pub calls last orders, but nobody is thrown out the moment the bell rings: people get a few minutes to finish their drinks. The objection count reaching zero is the bell. The drain time is the grace period: after an object's total reaches zero, UVM waits that long before treating the object as finished and passing the drop to its parent. If anyone raises again during the wait, the wait is cancelled and the phase carries on.

    You set it on the phase's objection for a particular object:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet
    task run_phase(uvm_phase phase);
      phase.get_objection().set_drain_time(this, 100ns);  // set before the count reaches 0
      phase.raise_objection(this, "smoke test running");
      // ... the job ...
      phase.drop_objection(this, "smoke test done");      // the phase waits 100 ns here
    endtask

    The picture

    Sequence diagram: the test's total reaches zero, it waits its 100 ns drain, then the drop reaches uvm_top, which calls phase_ready_to_end and ends the phase.
    Drain time is a property of each object. The drop climbs one level only after that level's drain has passed.

    Three rules that decide the timing

    Drains belong to objects, and they stack

    The drain is stored per object. When an object's total reaches zero, UVM waits that object's drain, then passes the drop to its parent, where the parent's total may reach zero and the parent's drain starts. So a 50 ns drain on the env and a 100 ns drain on the test add up to 150 ns of settling. Most benches set exactly one drain, on the test (this in the test's run_phase), so the number in the code is the number you get.

    The window starts at zero, not at the drop call

    If two raisers drop at 300 ns and 400 ns, a drain on uvm_top's path starts at 400 ns, when the total actually reaches zero. Dropping early from one component does not start anyone's grace period.

    A raise cancels a drain in progress

    A raise that reaches an object during its drain stops the wait. When the total next reaches zero, the drain starts again from the beginning. This is what makes drain time safe with late stragglers, and also what makes a periodic raiser keep a phase alive forever.

    Watch it run

    The smoke test's real trace shows the drop and the end of the phase at the same instant, 655 ns, because the test's #100ns happens before the drop and no drain time is set:

    Diagram
    UVM_INFO @ 655000: run_objection [OBJTN_TRC] Object uvm_test_top dropped 1 run_objection objection(s) (smoke test done): count=0  total=0
    UVM_INFO @ 655000: run_objection [OBJTN_TRC] Object uvm_test_top all_dropped 1 run_objection objection(s) (smoke test done): count=0  total=0
    UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched

    Move the delay into set_drain_time(this, 100ns) and drop right after the STATUS checks: the dropped line moves 100 ns earlier, the all_dropped line stays at 655 ns, and the result is the same. The difference is that the delay is now visible to UVM. A raise during it, from a scoreboard for example, cancels it instead of being ignored by a blind #100ns.

    Sizing it: measure, never guess

    Drain time should cover the longest legal gap between the stimulus finishing and the last observable effect being checked. For the DMA that is the pipeline from the last accepted input word to the last output word, plus any output backpressure. Measure it on a known-good test, add a margin, and write down where the number came from.

    Symptom

    Diagnosis

    Fix

    SB_LEFTOVER now and then

    Drain shorter than the worst-case tail

    Measure the tail under backpressure and raise the drain.

    Every test idles at the end

    Drain far above the measured tail

    Lower it to the measured value plus margin.

    Drains set in env and test

    They stack, so settling is the sum

    Keep one drain, on the test.

    A missing drop 'fixed' with a long drain

    The drain is hiding an ownership bug

    Find the owner; drain is for trailing work only.

    What breaks: the drain that was right until it wasn't

    A DMA team sized their drain from the smoke test: 8 words, no output backpressure, last word out 60 ns after the last word in. They set 100 ns and moved on. Months later, a random test with 16-word jobs and a slow output agent started failing once every few hundred seeds with SB_LEFTOVER: 1 expected word(s) never came out. Nothing was wrong with the DUT. With backpressure, the tail had grown to 140 ns, and the drain cut it off.

    Raising the drain to 200 ns made the failure go away, and made every test in the regression 100 ns longer. The real problem is that a drain time is a guess about latency, frozen in a constant. The DMA knows exactly how many words it owes: LEN words per GO. A component that counts those can hold the phase for exactly as long as needed and no longer. That component is the subject of the next lesson.

    Interview

    "What is drain time, and how do you choose the value?"

    A weak answer: "It's a delay at the end of the test so things can finish. We use 1 µs." No mechanism, and a number with no basis.

    A strong answer: "It's set per object on the phase's objection with set_drain_time. When that object's total reaches zero, UVM waits the drain before propagating the drop to the parent; a raise during the wait cancels it. Drains on several levels stack. I set one, on the test, sized from the measured worst-case tail of the pipeline plus a margin. If the tail depends on the traffic, I stop using a drain and let a scoreboard hold the phase until the outstanding count is zero."

    Follow-ups: Why prefer set_drain_time over a #delay before the drop? (a raise can cancel it, and it is visible in the trace). What happens if a component raises every 50 ns during a 100 ns drain? (the drain restarts each time, so the phase never ends).

    Check yourself The env sets a 50 ns drain on itself and the test sets 100 ns on itself. The last objection is the env's. How long after its drop does the phase end?

    About 150 ns. The env's total reaches zero and waits 50 ns; then the drop reaches the test, whose total reaches zero, and it waits 100 ns; then the drop reaches uvm_top. Drains are per object and stack along the path.

    Check yourself During the test's 100 ns drain, a scoreboard raises for a late word and drops 30 ns later. When does the phase end?

    100 ns after the scoreboard's drop, assuming nothing else raises. The raise cancelled the test's drain; when the total reached zero again, the drain started from the beginning.

    Check yourself Why is a drain time a poor end-of-test mechanism for a design whose latency depends on traffic?

    Because it is a constant standing in for a variable. Too short and some seeds lose their last items; too long and every test pays idle time. A count of outstanding work, learned from what the DUT was asked to do, ends the test exactly when the work is done.

    Next: phase_ready_to_end lets a component look at the bench when the count reaches zero and say not yet, using a count instead of a guess.

    Related topics

    • phase_ready_to_end

    • End-of-test strategy

    • Scoreboard architecture

    Previous

    35 / 280

    Next: phase_ready_to_end: Last-Chance Phase Extension

    Up next

    Up nextphase_ready_to_end: Last-Chance Phase Extension

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Objection Hierarchy Propagation: Up the Component Tree
    • End-of-Test Strategy: Choosing a Mechanism
    • Objections at Scale