Raise/Drop objection Automatically with UVM

In short: A sequence normally has to raise an objection before it runs and drop it after, or the run phase ends too early. UVM can do this for you: call set_automatic_phase_objection(1) in the sequence, and UVM raises the objection just before the sequence starts and drops it after it finishes. It only works when the sequence is tied to a phase with set_starting_phase, and you must never use it on a sequence with a forever loop, because the objection would never drop.

Raising and dropping objections by hand is easy to get wrong: forget the raise and your stimulus never runs, forget the drop and the test hangs. For sequences that map cleanly onto a phase, UVM offers a tidier option. The automatic phase objection makes UVM handle the raise and drop around the sequence, so the body stays focused on stimulus. This guide explains the starting phase, the automatic objection bit, the exact timeline of when the objection goes up and down, a full runnable example, and the one case where you must not use it.

A simple way to picture it

Think of signing in and out at a front desk when you visit an office. Normally you must remember to sign the book on the way in and sign out on the way out; forget either and the records are wrong. The automatic phase objection is like a badge reader that logs you in the moment you enter and out the moment you leave, without you thinking about it. UVM watches the sequence start and finish and handles the raise and drop for you, the same way the reader stamps your entry and exit.

The starting phase

For UVM to raise an objection on your behalf, it must know which phase the sequence belongs to. That is the starting phase. You set it with set_starting_phase(phase) from the test before starting the sequence, and read it back with get_starting_phase(). If the starting phase is null, there is no phase to object to, and the automatic objection does nothing. The older starting_phase variable is deprecated in favour of these two methods, which stop the phase being changed midway.

// in the test run_phase, tie the sequence to this phase
my_seq seq = my_seq::type_id::create("seq");
seq.set_starting_phase(phase);   // now UVM knows which phase to object to
seq.start(agent.sqr);

Turning on the automatic objection

You switch the behaviour on with set_automatic_phase_objection(1), usually inside the sequence constructor so every instance carries it. From then on, UVM raises an objection to the starting phase just before pre_start runs, and drops it after post_start finishes. You read the current setting with get_automatic_phase_objection().

The timeline is worth seeing, because it explains exactly when the objection is up:

start() is called
  --> objection is raised automatically
  pre_start()  runs
  pre_body()   runs (optional)
  body()       runs   <-- your stimulus
  post_body()  runs (optional)
  post_start() runs
  --> objection is dropped automatically
start() returns

A full runnable example

Here is a small but complete testbench that uses the automatic objection. The sequence item carries an address and data with simple constraints. The sequence turns on the automatic objection in its constructor, then its body just creates, randomizes, and sends one item. Notice there is no raise_objection or drop_objection anywhere in the sequence.

class my_seq_item extends uvm_sequence_item;
  rand logic [7:0] addr;
  rand logic [7:0] data;
  constraint addr_range_cn { addr inside {[10:20]};  }
  constraint data_range_cn { data inside {[100:200]}; }

  `uvm_object_utils_begin(my_seq_item)
    `uvm_field_int(addr, UVM_ALL_ON | UVM_DEC)
    `uvm_field_int(data, UVM_ALL_ON | UVM_DEC)
  `uvm_object_utils_end

  function new(string name = "my_seq_item");
    super.new(name);
  endfunction

  virtual function string convert2string();
    return $sformatf("addr=%0d data=%0d", addr, data);
  endfunction
endclass
class my_seq extends uvm_sequence #(my_seq_item);
  `uvm_object_utils(my_seq)

  function new(string name = "my_seq");
    super.new(name);
    // raise/drop are now handled by UVM around this sequence.
    // NEVER set this to 1 if body() contains a forever loop:
    // the objection would never be dropped and the test would hang.
    set_automatic_phase_objection(1);
  endfunction

  task body();
    `uvm_create(req)
    if (!req.randomize())
      `uvm_fatal(get_name(), "randomize failed")
    `uvm_info(get_name(), $sformatf("sending %s", req.convert2string()), UVM_LOW)
    `uvm_send(req)
  endtask
endclass
class my_driver extends uvm_driver #(my_seq_item);
  `uvm_component_utils(my_driver)
  function new(string name, uvm_component parent); super.new(name, parent); endfunction

  task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      `uvm_info(get_name(), $sformatf("driving %s", req.convert2string()), UVM_LOW)
      #50;
      seq_item_port.item_done();
    end
  endtask
endclass
class my_test extends uvm_test;
  `uvm_component_utils(my_test)
  my_agent agent;
  function new(string name, uvm_component parent); super.new(name, parent); endfunction

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

  task run_phase(uvm_phase phase);
    my_seq seq = my_seq::type_id::create("seq");
    seq.set_starting_phase(phase); // required for the automatic objection
    seq.start(agent.sqr);          // no manual raise/drop here either
  endtask
endclass

Because the sequence sets its automatic objection and the test sets the starting phase, UVM keeps the run phase alive for exactly as long as seq runs, then ends it. The test run_phase stays clean, with no objection bookkeeping.

Manual versus automatic objection

PointManual raise/dropAutomatic phase objection
Where you code itInside body() or the testOne call in the sequence constructor
Needs a starting phaseNoYes, set with set_starting_phase
Safe with a forever bodyYes, you control the dropNo, the objection never drops
Risk of forgetting the dropHighNone, UVM handles it
Best forLoops, conditions, fine controlSimple sequences that map to a phase

The automatic option removes the most common objection bug, the missing drop, for the everyday case. Keep the manual pattern for sequences that run a loop or end on a condition, where you need to decide exactly when the objection drops.

The one rule you must not break

Never set the automatic phase objection on a sequence whose body() contains a forever loop or otherwise never returns. UVM drops the objection only after the sequence finishes, so a sequence that never finishes holds the objection forever and the phase can never end. For a continuous traffic sequence, use the manual pattern and drop the objection when a real end condition is met, as shown in the guide on how to end a UVM test with objections.

Common mistakes to avoid

  • Turning on the automatic objection but forgetting set_starting_phase, so the starting phase is null and no objection is raised.
  • Using it on a sequence with a forever loop, which holds the objection forever and hangs the test.
  • Mixing manual raise/drop and the automatic objection on the same sequence, which double-counts and confuses the phase.
  • Relying on the deprecated starting_phase variable instead of set_starting_phase and get_starting_phase.
  • Expecting the objection to cover work started in a fork that outlives the body, since the drop happens when the body returns.

Keep learning

This builds on the objection mechanism, so read how to end a UVM test with objections for the manual pattern and drain time, and UVM phasing for where the run phase sits. See how sequences reach the driver in sequencer and driver communication. More is on the UVM category page and in the interview questions.

Frequently asked questions

What does set_automatic_phase_objection do?

It tells UVM to raise an objection to the starting phase just before the sequence runs and drop it after the sequence finishes. This removes the need to call raise_objection and drop_objection by hand for a sequence that maps onto a phase.

Why do I also need to set the starting phase?

UVM can only object to a phase it knows about. set_starting_phase(phase) tells the sequence which phase it belongs to. If the starting phase is null, the automatic objection has nothing to object to and does nothing.

Can I use the automatic objection with a forever loop?

No. UVM drops the objection only after the sequence body returns. A forever loop never returns, so the objection is held forever and the phase can never end. Use the manual raise and drop for continuous sequences.

When is the objection raised and dropped exactly?

It is raised automatically just before pre_start runs and dropped automatically after post_start finishes. So it covers the whole sequence from just before it starts to just after it ends.

Is starting_phase the same as set_starting_phase?

They target the same idea, but the starting_phase variable is deprecated. Use set_starting_phase and get_starting_phase instead, which prevent the phase being changed in the middle of a phase and are the supported way in current UVM.

Should I use manual or automatic objections?

Use the automatic objection for simple sequences that run once and map to a phase, since it removes the risk of a missing drop. Use the manual pattern for sequences with loops or a condition-based end, where you need to control exactly when the objection drops.

Similar Posts