Part 10 · Advanced Topics · Intermediate

set_drain_time and Trailing Activity

Why drain windows exist, how phase_done.set_drain_time behaves, timing diagrams, and tuning drain values for regression scale.

Why zero-count is not always safe to end immediately

When objection count reaches zero, active stimulus may be done, but passive infrastructure can still be processing trailing activity: monitors sampling final beats, scoreboards matching delayed responses, predictors updating mirrors, and coverage collecting last samples.

A fixed drain window via phase.phase_done.set_drain_time(source, delay) adds a post-zero wait before phase end. It is not a replacement for functional checks; it is a buffer that allows trailing passive work to complete naturally.

diagram
[UVM] motivation for drain_time

  last active sequence drops objection at T100ns
  bus still has delayed response at T108ns
  monitor publishes response at T109ns
  scoreboard compares at T110ns

  no drain_time:
    run_phase may end near T100ns -> final compare missed

  drain_time = 20ns:
    phase waits until at least T120ns -> trailing compare included

API usage and placement

Set drain time from a stable owner (often test) early in run_phase or during setup. Keep ownership clear so later overrides are intentional.

systemverilog
task run_phase(uvm_phase phase);
  // Fixed post-zero window for run phase
  phase.phase_done.set_drain_time(this, 100ns);

  phase.raise_objection(this, "execute scenario");
  main_vseq.start(env.v_sqr);
  phase.drop_objection(this, "scenario complete");
endtask
systemverilog
// Optional utility for environment-level tuning
function void configure_drain(uvm_phase phase, time t);
  if (phase.get_name() == "run")
    phase.phase_done.set_drain_time(this, t);
endfunction
  • Drain time applies after objection total reaches zero.

  • If new objections are raised during the drain window, phase continues normally.

  • Use explicit comments describing why the selected delay exists (protocol latency, checker pipeline, etc.).


Timing diagram: drop, drain, end

The diagram below clarifies the event ordering that is frequently misunderstood.

diagram
[UVM] run-phase end timing

  time ───────────────────────────────────────────────────────────►

  objections:
    count=2        count=1           count=0
       │              │                 │
       ▼              ▼                 ▼
    work A done    work B done      start drain timer (D)
                                      │
                                      ├─ passive monitors/checkers still active
                                      │
                                      ▼
                                if no new raise by T0 + D
                                      │
                                      ▼
                                  phase ends

  If a new raise occurs during drain:
    drain wait is interrupted by active objections;
    end condition re-evaluates after those objections drop again.

Drift between protocol latency and drain settings

  • If drain is too small, intermittent end-of-test misses appear under high latency.

  • If drain is too large, every test pays unnecessary idle time.

  • Measure worst-case tail latency from logs/waves before finalizing value.


Tuning strategy for farm-scale regressions

Drain-time tuning is an economics problem: enough time for correctness, minimal overhead for throughput.

  1. Start with a conservative value in bring-up (e.g., 100ns or protocol-tail estimate).

  2. Instrument end-of-phase logs: when did final monitor/checker event occur after last drop?

  3. Gather distribution across stress tests; pick near worst-case percentile, not single run anecdote.

  4. Revisit when protocol timing changes (clock, queue depth, arbitration updates).

diagram
[CHECK] drain tuning worksheet

  observed tail (ns) across tests:
    8, 12, 9, 11, 35, 10, 14, 13, 42

  candidate drain:
    50ns  (covers spikes + margin)

  regression impact:
    +50ns * 10k tests = 0.5 ms simulated time
    wall-time impact depends on simulator speed and idle overhead

  if impact high:
    reduce fixed drain and move special cases to phase_ready_to_end logic

Key takeaways

  • set_drain_time protects trailing passive activity after final drop.

  • It is a fixed delay tool; keep value justified and measured.

  • Too small causes intermittent misses; too large wastes regression budget.

  • Use conditional extension for rare long tails rather than inflating global drain.

Common pitfalls

  • Using large drain as a blanket fix for unresolved scoreboard logic bugs.

  • Setting drain in multiple places with conflicting intentions.

  • Assuming drain alone guarantees functional completeness without checker-side conditions.