Part 5 · Sequences · Intermediate
Test start() & run_phase Objections
How tests launch sequences — raise_objection, seq.start(sqr), blocking until body() returns, and starting phase.
Sequences do not run alone
A sequence class defines stimulus procedure, but nothing happens until something calls start() on a sequencer handle. In block-level tests, the test run_phase creates the sequence, configures knobs, raises an objection to keep simulation alive, calls start(), then drops the objection.
Without a raised objection, run_phase can end before the driver consumes items — the simulation stops while the sequence sits blocked at finish_item. Objections are the UVM mechanism that ties test lifetime to driver activity.
start() is blocking: it runs pre_body, body, post_body sequentially and returns only when body() completes. All finish_item calls inside body() must resolve before start() returns.
[STIM] test launch sequence
run_phase:
raise_objection ← keep sim alive
seq.start(sqr) ← blocks until body() done
drop_objection ← allow sim to end
Without objection: run_phase ends → driver killed → hangComplete apb_smoke_test
class apb_smoke_test extends uvm_test;
`uvm_component_utils(apb_smoke_test)
apb_env env;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = apb_env::type_id::create("env", this);
endfunction
task run_phase(uvm_phase phase);
apb_wr_seq seq;
phase.raise_objection(this);
seq = apb_wr_seq::type_id::create("seq");
seq.num_trans = 5;
`uvm_info("TEST", "starting apb_wr_seq", UVM_LOW)
seq.start(env.apb_agent.sqr); // blocks until body() completes
`uvm_info("TEST", "apb_wr_seq done", UVM_LOW)
phase.drop_objection(this);
endtask
endclassraise_objection before start — driver run_phase stays active.
num_trans configured on sequence object before start().
start() argument is the agent sequencer — env.apb_agent.sqr.
drop_objection after start returns — all items driven.
What start() does internally
start() is more than calling body(). It sets p_sequencer, optionally jumps starting phase, and runs the sequence lifecycle hooks:
[SEQ] seq.start(sqr) internal sequence
1. seq.m_sequencer = sqr ← p_sequencer now valid
2. pre_start() ← rarely overridden
3. pre_body()
4. body() ← your start_item/finish_item loop
5. post_body()
6. post_start()
7. return to test
[STIM] test blocked at step 4 until all finish_item complete[STIM] [SEQ] [DRV] [UVM] — test-time stack
[STIM] apb_smoke_test.run_phase
│ raise_objection
│ seq.start(env.apb_agent.sqr)
▼
[SEQ] apb_wr_seq.body() ──► start_item/finish_item loop
▼
[SEQ] apb_sequencer
▼
[DRV] apb_driver.run_phase (active because objection raised)
▼
DUT pinsForking sequences from a test
Parallel scenarios fork multiple start() calls. Each sequence arbitrates on its sequencer independently:
task run_phase(uvm_phase phase);
apb_wr_seq wr_seq = apb_wr_seq::type_id::create("wr");
apb_rd_seq rd_seq = apb_rd_seq::type_id::create("rd");
phase.raise_objection(this);
fork
wr_seq.num_trans = 50;
wr_seq.start(env.apb_agent.sqr);
rd_seq.num_trans = 50;
rd_seq.start(env.apb_agent.sqr);
join
phase.drop_objection(this);
endtask[SEQ] forked start() on same sqr
wr_seq and rd_seq interleave at start_item boundaries
[DRV] sees: wr item, rd item, wr item, ... (FIFO arbitration)
fork/join waits for BOTH body() to complete before drop_objectionSame sequencer — items interleave; use lock/grab for atomic bursts.
Different sequencers — fully parallel, no arbitration between agents.
join_any + disable fork for timeout or first-complete scenarios.
Virtual sequence start from test
Chip tests start one virtual sequence on env.v_sqr — the test stays thin, scenario logic lives in the vseq:
task run_phase(uvm_phase phase);
dma_xfer_vseq vseq = dma_xfer_vseq::type_id::create("vseq");
phase.raise_objection(this);
vseq.randomize();
vseq.start(env.v_sqr); // vseq starts sub-sequences on p_sequencer.apb_sqr etc.
phase.drop_objection(this);
endtaskVirtual sequences do not call start_item themselves — they call start() on sub-sequences. See Virtual Sequences topic.
Objection debug checklist
Log at raise and drop — confirm test run_phase reached start().
If sim ends instantly: objection dropped before start() or never raised.
If sim hangs: objection still raised — sequence or driver blocked.
set_drain_time on drop for in-flight monitor transactions.
phase.raise_objection(this);
`uvm_info("TEST", "objection raised", UVM_LOW)
seq.start(env.apb_agent.sqr);
`uvm_info("TEST", "seq.start returned", UVM_LOW)
phase.drop_objection(this);
`uvm_info("TEST", "objection dropped", UVM_LOW)Key takeaways
raise_objection before seq.start — driver must be running.
start() blocks until body() returns — all items must complete.
start(sqr) sets p_sequencer — required for lock/grab in body().
Common pitfalls
No raise_objection — run_phase ends, driver stops, sequence hangs.
drop_objection before start() returns — sim may end mid-sequence.
start() on wrong sequencer — passive agent or null handle.
Creating sequence but never calling start() — zero stimulus.