Use of sequence macros give the added advantage of inline constraints, however you lose the ability to control the invocation of pre_body
and post_body
methods in the executed sequence. It reduces the number of lines in our code by creating the item, randomizing it and automatically calling the required tasks to start the given sequence or sequence item.

In a previous article, we learned that all `uvm_do
macros ultimately call the code defined in `uvm_do_on_pri_with
. So, let's see how that is structured within UVM to get better clarity behind the process.
`define uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)
begin
uvm_sequence_base __seq;
`uvm_create_on(SEQ_OR_ITEM, SEQR)
if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);
if ((__seq == null || !__seq.do_not_randomize) && !SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin
`uvm_warning("RNDFLD", "Randomization failed in uvm_do_with action")
end
if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY);
else __seq.start(SEQR, this, PRIORITY, 0);
end
If the object passed to this macro is a sequence item, then it creates the item and starts executing the item with start_item
method call. The item will be randomized and end with finish_item
method call. If the object __seq is not a sequence item, then start
method of the sequence is called.
Note that call_pre_post
field of the start
method is set to 0 which means pre_body
and post_body
methods of a sequence will never be called when using these sequence macros. Execution flow is otherwise similar to when sequence is executed by the start
method.


Example
Let's take the same code we saw while using start()
, and now call `uvm_do_pri_with
instead. Below is the base_sequence from which three child sequences (seq1, seq2, seq3) are derived.
class base_sequence extends uvm_sequence #(my_data);
`uvm_object_utils (base_sequence)
`uvm_declare_p_sequencer (my_sequencer)
function new (string name = "base_sequence");
super.new (name);
endfunction
virtual function void mid_do (uvm_sequence_item this_item);
`uvm_info (get_type_name (), "Executing mid_do", UVM_MEDIUM)
endfunction
virtual task pre_do (bit is_item);
`uvm_info (get_type_name (), "Executing pre_do", UVM_MEDIUM)
endtask
virtual function void post_do (uvm_sequence_item this_item);
`uvm_info (get_type_name (), "Executing post_do", UVM_MEDIUM)
endfunction
virtual task body ();
starting_phase.raise_objection (this);
`uvm_info ("BASE_SEQ", $sformatf ("Starting body of %s", this.get_name()), UVM_MEDIUM)
`uvm_info ("BASE_SEQ", $sformatf ("Sequence %s is over", this.get_name()), UVM_MEDIUM)
starting_phase.drop_objection (this);
endtask
endclass
In seq1 below, notice that we have used `uvm_do_pri_with
macro with empty constraints and no priority. We expect this to call the stat()
method of seq2, and skip the pre_body( ) and post_body( ) tasks.
class seq1 extends base_sequence;
`uvm_object_utils (seq1)
seq2 m_seq2;
virtual task pre_start ();
`uvm_info (get_type_name (), "Executing pre_start()", UVM_MEDIUM)
endtask
function new (string name = "seq1");
super.new (name);
endfunction
virtual task body ();
starting_phase.raise_objection (this);
m_seq2 = seq2::type_id::create ("m_seq2");
`uvm_info ("SEQ1", "Starting seq1", UVM_MEDIUM)
#10;
`uvm_do_pri_with (m_seq2, ,{})
`uvm_info ("SEQ1", "Ending seq1", UVM_MEDIUM)
starting_phase.drop_objection (this);
endtask
endclass
In seq2, we kick off another sequence (seq3) using the same macro.
class seq2 extends base_sequence;
`uvm_object_utils (seq2)
seq3 m_seq3;
function new (string name = "seq2");
super.new (name);
endfunction
virtual task pre_body ();
`uvm_info (get_type_name(), "Executing pre_body", UVM_MEDIUM)
endtask
virtual task body ();
m_seq3 = seq3::type_id::create ("m_seq3");
`uvm_info ("SEQ2", "Starting seq2", UVM_MEDIUM)
#10;
`uvm_do_pri_with (m_seq3, ,{})
`uvm_info ("SEQ2", "Ending seq2", UVM_MEDIUM)
endtask
virtual task post_body ();
`uvm_info (get_type_name(), "Executing post_body", UVM_MEDIUM)
endtask
endclass
In our final sequence, we don't do anything.
class seq3 extends base_sequence;
`uvm_object_utils (seq3)
function new (string name = "seq3");
super.new (name);
endfunction
virtual task pre_body ();
`uvm_info (get_type_name(), "Executing pre_body", UVM_MEDIUM)
endtask
virtual task body ();
`uvm_info ("SEQ3", "Starting seq3", UVM_MEDIUM)
#10;
`uvm_info ("SEQ3", "Ending seq3", UVM_MEDIUM)
endtask
virtual task post_body ();
`uvm_info (get_type_name(), "Executing post_body", UVM_MEDIUM)
endtask
virtual task post_start ();
`uvm_info (get_type_name (), "Executing post_start", UVM_MEDIUM)
endtask
endclass
The simulation results might look a bit fuzzy. The important thing to note is that the pre_do/mid_do
methods are called before the body( )
task is called. Let's do a simple dry run.
- SEQ1 : pre_start() - go to the body() of SEQ1 - Display "[SEQ1] Starting seq1" - Start SEQ2 // there's nothing in pre_start() and pre_body() is not called - Call parent's (SEQ1) pre_do - Display "[seq1] Executing pre_do" - Call parent's (SEQ1) mid_do - Display "[seq1] Executing mid_do" - Call body() of SEQ2 - Display "[SEQ2] Starting seq2" - Start SEQ3 ...
CDNS-UVM-1.1d (14.10-s013) (C) 2007-2013 Mentor Graphics Corporation (C) 2007-2013 Cadence Design Systems, Inc. (C) 2006-2013 Synopsys, Inc. (C) 2011-2013 Cypress Semiconductor Corp. ---------------------------------------------------------------- UVM_INFO @ 0: reporter [RNTST] Running test base_test... UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology: ------------------------------------------------------------ Name Type Size Value ------------------------------------------------------------ uvm_test_top base_test - @2644 m_top_env my_env - @214 m_drv0 my_driver - @208 rsp_port uvm_analysis_port - @2843 seq_item_port uvm_seq_item_pull_port - @2792 m_seqr0 my_sequencer - @2743 rsp_export uvm_analysis_export - @2933 seq_item_export uvm_seq_item_pull_imp - @3481 arbitration_queue array 0 - lock_queue array 0 - num_last_reqs integral 32 'd1 num_last_rsps integral 32 'd1 ------------------------------------------------------------ UVM_INFO ./tb/my_pkg.sv(92) @ 0: uvm_test_top.m_top_env.m_drv0 [my_driver] Applying initial reset UVM_INFO ./tb/my_pkg.sv(96) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] DUT is now out of reset UVM_INFO ./tb/my_pkg.sv(103) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer UVM_INFO ./tb/my_pkg.sv(170) @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [seq1] Executing pre_start() UVM_INFO ./tb/my_pkg.sv(180) @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] Starting seq1 UVM_INFO ./tb/my_pkg.sv(143) @ 400000: uvm_test_top.m_top_env.m_seqr0@@seq1 [seq1] Executing pre_do UVM_INFO ./tb/my_pkg.sv(139) @ 400000: uvm_test_top.m_top_env.m_seqr0@@seq1 [seq1] Executing mid_do UVM_INFO ./tb/my_pkg.sv(207) @ 400000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] Starting seq2 UVM_INFO ./tb/my_pkg.sv(143) @ 410000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [seq2] Executing pre_do UVM_INFO ./tb/my_pkg.sv(139) @ 410000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [seq2] Executing mid_do UVM_INFO ./tb/my_pkg.sv(234) @ 410000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Starting seq3 UVM_INFO ./tb/my_pkg.sv(236) @ 420000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [SEQ3] Ending seq3 UVM_INFO ./tb/my_pkg.sv(147) @ 420000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [seq2] Executing post_do UVM_INFO ./tb/my_pkg.sv(244) @ 420000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2.m_seq3 [seq3] Executing post_start UVM_INFO ./tb/my_pkg.sv(210) @ 420000: uvm_test_top.m_top_env.m_seqr0@@seq1.m_seq2 [SEQ2] Ending seq2 UVM_INFO ./tb/my_pkg.sv(147) @ 420000: uvm_test_top.m_top_env.m_seqr0@@seq1 [seq1] Executing post_do UVM_INFO ./tb/my_pkg.sv(183) @ 420000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] Ending seq1 UVM_INFO ./tb/my_pkg.sv(122) @ 420000: uvm_test_top.m_top_env.m_drv0 [my_driver] Finished DUT simulation --- UVM Report catcher Summary ---