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
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
    • Sequence Items and the Driver Handshake
    • Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
      • Sequence Lifecycle and Hooks: What start() Does, Step by Step
      • Inside the Sequencer: Arbitration, Priority and Relevance
      • Responses and Pipelined Drivers
      • Stopping Sequences and Reset
      • Sequences and Objections: Who Keeps the Phase Open
      • The uvm_do Macro Family: What It Expands To
    • Virtual Sequences and Coordination
  • 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

‹Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
  1. Topics
  2. UVM Verification
  3. UVM Sequences & Stimulus
  4. Sequence Mechanics: Lifecycle, Sequencer, Responses and Control
  5. Sequences and Objections: Who Keeps the Phase Open

Part 5 · Sequences · Intermediate

Sequences and Objections: Who Keeps the Phase Open

starting_phase (deprecated in UVM 1.2, removed in IEEE 1800.2) versus get_starting_phase / set_starting_phase versus set_automatic_phase_objection(1); default sequences through uvm_config_db #(uvm_object_wrapper); and why the DMA bench keeps objections in the test.

On this page

    A team moves its stimulus out of the test and into a default sequence, a sequence the sequencer starts by itself at the beginning of a phase, set up with one config_db line. The test's run_phase becomes empty. The first run finishes in no time at all:

    Diagram
    UVM_INFO seq_lab_tb.sv(515) @ 0: uvm_test_top [END] run phase over at 0, driver saw 0 items

    Nobody objected, so the phase ended at time 0 and the sequence was killed before it sent anything. A sequence can keep the phase open, but only if it is told which phase it belongs to and asked to object. This lesson covers both, across UVM 1.1, 1.2 and IEEE 1800.2, and explains why the DMA bench does not use any of it.

    The idea: whoever knows when the work ends holds the door

    An objection is a hand on the door: the phase cannot end while anyone is holding it (the rules are in Objections deep dive). The question is whose hand. A sequence knows when its own items are done. Only the test knows when the scenario is done: after the interrupt, after the last output word drained, after the status check. So a sequence holding the phase is right when the sequence is the scenario, as with a default sequence, and redundant or wrong when a test is already waiting for the real end.

    For the sequence to object, it needs a phase to object on. That is its starting phase: the phase in which it was started.

    The picture: three generations of the same idea

    Version

    How a sequence finds its phase

    How it objects

    UVM 1.1

    The public member starting_phase, set by the sequencer for default sequences

    By hand: starting_phase.raise_objection(this) in pre_body, drop in post_body

    UVM 1.2

    get_starting_phase() / set_starting_phase(); starting_phase still there but deprecated

    set_automatic_phase_objection(1), or by hand in pre_start / post_start

    IEEE 1800.2

    get_starting_phase() / set_starting_phase() only; starting_phase is removed

    Same as UVM 1.2

    The UVM 1.1 habit of objecting in pre_body has a trap: pre_body is skipped when a parent starts the sequence with call_pre_post set to 0 (see Sequence lifecycle and hooks). Code that must use starting_phase today should move to the getter, which works in UVM 1.2 and IEEE 1800.2 alike.

    Build it

    The automatic objection

    set_automatic_phase_objection(1) makes start() raise an objection on the starting phase just before pre_start and drop it just after post_start. If the sequence is killed, kill() drops it too. Set it in the constructor so that every instance behaves the same way, however it is started:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // snippet: seq_lab_tb.sv
    class lab_auto_seq extends uvm_sequence #(lab_item);
      `uvm_object_utils(lab_auto_seq)
      function new(string name = "lab_auto_seq");
        super.new(name);
        set_automatic_phase_objection(1);
      endfunction
      task body();
        for (int i = 0; i < 5; i++) begin
          lab_item it = lab_item::type_id::create("it");
          start_item(it);
          if (!it.randomize()) `uvm_fatal("RAND", "randomize failed")
          finish_item(it);
        end
        `uvm_info("AUTO", "5 items sent", UVM_LOW)
      endtask
    endclass

    If the starting phase is null, there is nothing to object on and the automatic objection silently does nothing. That is the normal case for a sequence started inside another sequence's body, and it is fine: the parent holds the phase.

    A default sequence: the sequencer starts it

    A default sequence is registered against the sequencer's path plus the phase name. At the start of that phase the sequencer creates it, calls set_starting_phase(phase), randomizes it unless randomization is disabled, and starts it. It does not raise any objection for it:

    design.sv
    // snippet: seq_lab_tb.sv, in the test's build_phase
    uvm_config_db#(uvm_object_wrapper)::set(this, "env.sqr.run_phase", "default_sequence",
                                            lab_auto_seq::type_id::get());

    The uvm_object_wrapper form passes a type and lets the factory build it, so a factory override applies. The other form, passing a configured sequence instance as uvm_sequence_base, and the way default sequences read their own knobs, are covered in Configuring sequences.

    A sequence you start yourself

    When a test or virtual sequence starts a sequence and does not wait for it, for example background traffic in a fork ... join_none, the starting phase is not set automatically. Set it before start():

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // snippet
    task run_phase(uvm_phase phase);
      bg_traffic_seq bg = bg_traffic_seq::type_id::create("bg");
      bg.set_starting_phase(phase);
      bg.set_automatic_phase_objection(1);
      fork bg.start(env.in_agt.sqr); join_none   // bg now holds the phase until it ends
      // ... the test's own work, under its own objection ...
    endtask

    Why the DMA bench keeps objections in the test

    Every DMA test raises on its first line and drops after its last check. None of its sequences object. The reasons are worth saying in an interview:

    • The test knows the real end. A packet sequence ends when the last beat is accepted at the input. The job ends when the last word leaves the output and the interrupt is serviced, which no stream sequence can see.

    • Sequences stay reusable. apb_rw_seq runs inside tests, inside dma_vseq, and from the base test's helpers. With no phase logic, it behaves the same everywhere.

    • One owner is easy to debug. An objection trace with one raiser has one place to look. When the end must wait for the DUT, the bench uses a scoreboard's phase_ready_to_end (dma_drain_test), not more raisers; see End-of-test strategy.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // snippet: from the course DMA bench
    // A virtual sequence drives a whole job across both agents, with background
    // polling and a grab-based interrupt handler on the APB sequencer.
    class dma_vseq_test extends dma_base_test;
      `uvm_component_utils(dma_vseq_test)
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      task run_phase(uvm_phase phase);
        dma_vseq vseq = dma_vseq::type_id::create("vseq");
        phase.raise_objection(this);
        vseq.apb_sqr  = env.apb.sqr;
        vseq.axis_sqr = env.in_agt.sqr;
        vseq.irq_vif  = irq_vif;
        if (!vseq.randomize()) `uvm_fatal("RAND", "dma_vseq randomize failed")
        vseq.start(null);
        if (vseq.status[2:1] !== 2'b01) `uvm_error("STATUS", $sformatf("expected DONE only, STATUS=%08h", vseq.status))
        #100ns;
        phase.drop_objection(this);
      endtask
    endclass

    Even the virtual-sequence test follows the rule: the test raises, starts dma_vseq with start(null), waits for it, and drops.

    Watch it run: with and without the automatic objection

    The lab test with lab_auto_seq as the run-phase default sequence and an empty test run_phase:

    Diagram
    UVM_INFO seq_lab_tb.sv(48) @ 22000: uvm_test_top.env.drv [DRV] got  lab_auto_seq WR 2c=204d826f
    ...
    UVM_INFO seq_lab_tb.sv(281) @ 115000: uvm_test_top.env.sqr@@lab_auto_seq [AUTO] 5 items sent
    UVM_INFO seq_lab_tb.sv(515) @ 115000: uvm_test_top [END] run phase over at 115000, driver saw 5 items

    The same default sequence with set_automatic_phase_objection(0), the opening of this lesson:

    Diagram
    UVM_INFO seq_lab_tb.sv(515) @ 0: uvm_test_top [END] run phase over at 0, driver saw 0 items
    UVM_ERROR :    0

    Note the second log's UVM_ERROR : 0. A test that sends nothing and checks nothing passes. In the DMA bench, the scoreboard's SB_EMPTY check is what turns that into a failure.

    What breaks: the background sequence that ended the test early

    A test forked a long background traffic sequence with join_none, ran a short directed scenario under its own objection, and dropped. The background sequence had set_automatic_phase_objection(1) in its constructor, so the author expected it to hold the phase until its traffic finished. It did not: the run phase ended as soon as the test dropped, and the background traffic was cut off after a fraction of its items, with coverage to match.

    The automatic objection had nothing to object on. The sequence was started by hand, so its starting phase was null, and a null phase makes the automatic objection a silent no-op. One line, bg.set_starting_phase(phase) before start(), fixed it. The broader lesson: any mechanism that depends on the starting phase works by itself only for default sequences; everywhere else, you set the phase or you object yourself.

    Interview

    "Should sequences raise objections?"

    A weak answer: "No, sequences are objects, so they cannot raise objections." They can, through their starting phase, and default sequences must.

    A strong answer: "A sequence can object on its starting phase: in UVM 1.2 and IEEE 1800.2 with set_automatic_phase_objection(1), which raises before pre_start and drops after post_start, or by hand with get_starting_phase() in pre_start and post_start. starting_phase itself is deprecated in 1.2 and removed in 1800.2. A default sequence needs it, because nobody else objects for it. For a sequence I start myself I must call set_starting_phase first. In most benches, though, I keep the objection in the test, because only the test knows when the scenario, not the stimulus, is over."

    Follow-ups: What does the sequencer do for a default sequence? (creates it through the factory, sets its starting phase, randomizes it, starts it, and kills it when the phase ends). Why not object in pre_body? (it is skipped when call_pre_post is 0).

    Check yourself A test raises, starts a sequence that has set_automatic_phase_objection(1) and was given set_starting_phase(phase), waits for it, and drops. How many objections are held during the sequence?

    Two: the test's and the sequence's, both on the run phase, so uvm_top's total is 2. It is harmless, because each is dropped by its owner, but it is redundant. An objection trace will show both, which is a reason to pick one owner.

    Check yourself Legacy code in a sequence writes starting_phase.raise_objection(this);. You move the bench to an IEEE 1800.2 library. What happens, and what do you write instead?

    It no longer compiles, because the starting_phase member is removed in IEEE 1800.2. Use set_automatic_phase_objection(1) in the constructor, or uvm_phase p = get_starting_phase(); if (p != null) p.raise_objection(this); in pre_start and the matching drop in post_start. Both also work in UVM 1.2.

    Check yourself A default sequence with an automatic objection runs forever (a forever loop of idle traffic). What happens to the test?

    The run phase never ends, because the sequence never reaches post_start to drop its objection. The test runs until the timeout and ends with PH_TIMEOUT. Endless background sequences must not object; let the test own the end, and the sequencer will kill the default sequence when the phase ends.

    Next: the last lesson in this topic opens up the macros that legacy sequences use everywhere: The uvm_do macro family.

    Related topics

    • Objections deep dive

    • End-of-test strategy

    • Configuring sequences

    Previous

    83 / 280

    Next: The uvm_do Macro Family: What It Expands To

    Up next

    Up nextThe uvm_do Macro Family: What It Expands To

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Sequence Lifecycle and Hooks: What start() Does, Step by Step
    • Inside the Sequencer: Arbitration, Priority and Relevance
    • Responses and Pipelined Drivers