Part 2 · Phases & Lifecycle · Intermediate

Canonical run_phase Objection Patterns: Test, Sequence, and Background

Production-ready patterns for test-owned objections, sequence self-management, virtual sequence coordination, and multi-source duration control.

Pattern 1: Test-owned top-level objection

The most common pattern — the test raises before starting the main virtual sequence and drops when it completes:

systemverilog
class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  my_env env;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = my_env::type_id::create("env", this);
  endfunction

  task run_phase(uvm_phase phase);
    my_vseq vseq = my_vseq::type_id::create("vseq");

    fork super.run_phase(phase); join_none  // spawn children

    phase.raise_objection(this, "main virtual sequence");
    vseq.start(env.v_sqr);
    phase.drop_objection(this, "main virtual sequence done");
  endtask
endclass
diagram
[PHASE][RUN] test-owned pattern

  test raises ──► vseq.start() ──► subseqs run on agents ──► test drops
  driver/monitor: passive forever loops (no objection)
  duration owner: test
  • Test owns duration — clear, single owner for traces.

  • fork super.run_phase join_none ensures children start before stimulus.

  • Virtual sequence orchestrates sub-sequences without its own objection.


Pattern 2: Sequence self-managed objections

Long-running or reusable sequences manage their own duration via starting_phase:

systemverilog
class long_running_seq extends uvm_sequence #(item_t);
  `uvm_object_utils(long_running_seq)

  task pre_body();
    if (starting_phase != null)
      starting_phase.raise_objection(this, "long_running_seq");
  endtask

  task body();
    repeat (num_iterations) begin
      `uvm_do_with(req, { addr inside {[0:255]}; })
    end
  endtask

  task post_body();
    if (starting_phase != null)
      starting_phase.drop_objection(this, "long_running_seq done");
  endtask
endclass

// Test simply starts it — no manual raise/drop needed
task run_phase(uvm_phase phase);
  fork super.run_phase(phase); join_none
  long_running_seq::type_id::create("lrs").start(env.agent.sqr);
  // phase stays alive until sequence post_body drops
endtask
diagram
[RUN][UVM] sequence-owned pattern

  pre_body   raise
  body       stimulus
  post_body  drop (even on uvm_error in body, post_body still runs)

  benefit: reusable sequence carries its own duration contract
  • pre_body/post_body pairing is the safest automatic raise/drop.

  • Reusable sequences should self-manage when started from multiple tests.

  • starting_phase is set by the sequencer when sequence starts in a phase context.


Pattern 3: Test + background traffic

When main and background sequences run concurrently, each manages its own objection — the phase ends only when both complete:

systemverilog
task run_phase(uvm_phase phase);
  main_seq    m_seq;
  bg_traffic  bg_seq;

  fork super.run_phase(phase); join_none

  // Main test flow
  phase.raise_objection(this, "main flow");
  m_seq = main_seq::type_id::create("m_seq");
  m_seq.start(env.v_sqr);
  phase.drop_objection(this, "main flow done");

  // Background: self-managed via pre_body/post_body
  bg_seq = bg_traffic::type_id::create("bg_seq");
  bg_seq.start(env.bg_sqr);
  // bg_traffic raises in pre_body — phase still alive after main drops
endtask
diagram
[PHASE][RUN] dual-source duration

  t=0    test raises (main)     count=1
  t=0    bg_seq raises          count=2
  t=500  test drops (main done) count=1  ← phase STILL alive
  t=900  bg_seq drops           count=0  ← phase ENDS
  • Multiple independent duration sources are normal and supported.

  • Do not drop the test objection expecting bg traffic to keep sim alive unless bg raised.

  • Document which sequences self-manage vs test-managed.


Pattern 4: Coordinated stop with event

For controlled shutdown (drain in-flight transactions before drop), use an event or flag:

systemverilog
class coordinated_test extends uvm_test;
  uvm_event stop_ev;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    stop_ev = uvm_event_pool::get_global("stop_ev");
  endfunction

  task run_phase(uvm_phase phase);
    fork super.run_phase(phase); join_none

    phase.raise_objection(this, "coordinated run");
    run_stimulus();
    stop_ev.trigger();                    // signal agents to stop accepting new items
    #(drain_ns);                          // wait for in-flight to complete
    phase.drop_objection(this, "drained");
  endtask
endclass

// Driver checks stop event
task run_phase(uvm_phase phase);
  forever begin
    if (stop_ev.is_on()) break;
    seq_item_port.get_next_item(req);
    drive(req);
    seq_item_port.item_done();
  end
endtask

Key takeaways

  • Test-owned objection is the default — simple and traceable.

  • Sequence pre_body/post_body is the safest self-managed pattern.

  • Multiple concurrent raisers are normal; phase ends when all drop.

  • Use events/flags for coordinated graceful shutdown before drop.

Common pitfalls

  • Test raises but sequence also raises — double-counting if not intentional.

  • Starting bg sequence without pre_body raise — sim ends when main test drops.

  • Coordinated stop without drain delay — in-flight transactions lost.

  • virtual sequence raises objection but sub-sequence also raises — inflated count.