Objections & Drain Time

Mastering simulation termination. Understanding how raise_objection works and how to use set_drain_time to prevent premature exiting.

The Global Consensus Mechanism

UVM uses Objections to decide when to move from one task-based phase (like run_phase) to the next. It works like a distributed consensus: as long as any component "objects" to the phase ending, the simulation stays alive.

Objection Math:

  • raise_objection(): Active Objection +1
  • drop_objection(): Active Objection -1
  • Simulation End: Occurs only when the counter reaches exactly zero.

Drain Time: The Scoreboard's Best Friend

A common bug: After the last packet is driven, the Sequence drops its objection. The simulation immediately ends, but the packet is still trapped inside the DUT’s internal pipeline! The Monitor never sees it, and the Scoreboard never checks it.

The set_drain_time() Strategy:

Drain time is a "cool-down" period. It tells UVM: "Wait for X amount of time after the objection counter hits zero before actually closing the phase." This allows internal DUT transactions to reach the monitors.

Implementation: Phase Drain

task run_phase(uvm_phase phase);
    // 1. Set the cool-down (Only needs to be done once)
    phase.phase_done.set_drain_time(this, 100ns);
    // 2. Control simulation life
    phase.raise_objection(this);
    main_vseq.start(sqr);
    phase.drop_objection(this);
endtask
                            

Debugging Sticky Objections

Sometimes a simulation "hangs" because a component forgot to drop an objection. Instead of digging through logs, use the built-in UVM command-line switch:

+UVM_OBJECTION_TRACE

This prints every single raise and drop event, along with the component path and the new count, making it easy to spot the "stuck" component.

Performance Best Practices

  • Objection Spam: Never raise/drop objections inside a tight loop (e.g., in a Driver per-packet). This causes massive simulation overhead as UVM propagates the change up the hierarchy every time.
  • The Virtual Sequence Rule: In modern UVM, the best place to manage objections is the Virtual Sequence. Components (Drivers/Monitors) should be "slaves" to the stimulus lifespan.
  • Timeout: Use uvm_top.set_timeout() as a safety net to prevent infinite hangs.

How UVM Terminates Without $finish

Interview Essential: "How does UVM end simulation without calling $finish?"

The Answer:

UVM uses the run_test() task. When all time-consuming phases complete (all objections dropped), UVM automatically executes all post-run phases (extract, check, report, final) and then run_test() calls $finish internally.

The Complete Flow:

  1. run_test() is called from your testbench top module
  2. UVM creates the test component and runs all phases
  3. uvm_root tracks objection counts across all phases
  4. When run_phase objections hit zero, run_phase ends
  5. Post-simulation phases execute (extract → check → report → final)
  6. run_test() calls $finish and simulation ends
Testbench Top Pattern

module tb_top;
    // DUT and interface instantiation
    my_if   m_if();
    my_dut  dut(.clk(m_if.clk), .data(m_if.data));
    initial begin
        // Pass interface to UVM
        uvm_config_db#(virtual my_if)::set(null, "*", "vif", m_if);
        // Start UVM - this handles $finish internally
        run_test("my_test");
        // Code after run_test() never executes
    end
endmodule
                            

Pro Tip: Forcing Early Termination

If you need to end simulation early (e.g., on fatal error), you can call:
uvm_top.stop_request() - Graceful shutdown after completing current transactions
global_stop_request() - Legacy method, still works