User defined sequences should be derived from uvm_sequence, which has many methods to control the execution, runtime phasing, and other controls. One of them is the start() task which will execute the sequence, and return upon completion. This is just another way of running a sequence by the sequencer. Let's take a quick look at the start( ) method.

virtual task start ( uvm_sequencer_base   sequencer,
                     uvm_sequence_base    parent_sequence = null,
                     int                  this_priority = -1,
                     bit                  call_pre_post = 1 );

Note that you have to always pass the handle to a sequencer which should execute this sequence, whereas the other arguments are optional. parent_sequence is often referred to the sequence from which the current one was called. this_priority specifies a priority for the sequence (by default it assumes priority of the parent sequence), and can be ignored for now. call_pre_post will call the pre_body() and post_body() of the current sequence. Here's an example of how the start() function has to be used, and what happens in the background.

seq.randomize (...); // optional
seq.start (m_sequencer, null, , 1);
// The following methods will be called in start()
seq.pre_start();            (task)       
seq.pre_body();             (task)  if call_pre_post == 1
   parent_seq.pre_do()      (task)   if parent_seq != null
   parent_seq.mid_do(this)  (func)   if parent_seq != null
seq.body()                  (task)   your code
   parent_seq.post_do(this) (func)   if parent_seq != null
seq.post_body()             (task)   if call_pre_post == 1
sub_seq.post_start()        (task)
sequence flow

Note that pre_start() and post_start() are always called. pre_do/mid_do/post_do methods are callbacks and are available in case you want to use them. Else you can ignore them if you don't have any code placed inside it. Also, remember that these belong to the parent sequence. It's quite useful in some scenarios where you want to execute something from the parent inside your sequence. Or, you can call the bare minimum seq.start(m_sequencer, null, , 0);, which will do the following:

  • pre_start( )
  • body( )
  • post_start( )


First we'll look at the definition of a base sequence from which three other sequences will be derived. Then we'll call them in a nested hierarchical fashion. seq1 -> seq2 -> seq3. pre_do/mid_do/post_do are included in the base sequencer to prove the flow of execution.

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"); (name);
   virtual task pre_do (bit is_item);
      `uvm_info (get_type_name (), "Executing pre_do", UVM_MEDIUM)
   virtual function void mid_do (uvm_sequence_item this_item);
      `uvm_info (get_type_name (), "Executing mid_do", UVM_MEDIUM)
   virtual function void post_do (uvm_sequence_item this_item);
      `uvm_info (get_type_name (), "Executing post_do", UVM_MEDIUM)
   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);
flow of execution in example

Now let's look at the derivatives starting with seq1. We are calling seq2 in the body( ) of seq1, and have opted to bypass the pre_body( ) and post_body( ) of seq2. And since parent_sequence is set as null, we don't expect anything else to be called, like in the bare minimum sequence.

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)
   function new (string name = "seq1"); (name);
   virtual task body ();
      starting_phase.raise_objection (this);
      m_seq2 = seq2::type_id::create ("m_seq2");
      `uvm_info ("SEQ1", "Starting seq1", UVM_MEDIUM)
      m_seq2.start (p_sequencer, null, , 0);
      `uvm_info ("SEQ1", "Ending seq1", UVM_MEDIUM)
      starting_phase.drop_objection (this);

Let's define seq2 next and notice that we are calling seq3 inside the body(). When this is passed as the argument to parent_sequence, it will refer to base_sequence for the pre_do/mid_do/post_do methods. By setting pre_post = 1, the pre_body() and post_body() of seq3 will be executed.

class seq2 extends base_sequence;
   `uvm_object_utils (seq2)
   seq3 m_seq3;
   function new (string name = "seq2"); (name);
   virtual task pre_body ();
      `uvm_info (get_type_name(), "Executing pre_body", UVM_MEDIUM)
   virtual task body ();
      m_seq3 = seq3::type_id::create ("m_seq3");
      `uvm_info ("SEQ2", "Starting seq2", UVM_MEDIUM)
      m_seq3.start (p_sequencer, this, , 1);
      `uvm_info ("SEQ2", "Ending seq2", UVM_MEDIUM)
   virtual task post_body ();
      `uvm_info (get_type_name(), "Executing post_body", UVM_MEDIUM)

Coming to the last sequence definition, we won't do anything here, except define post_start() method. This is to show that pre_start() and post_start() of a sequence are always called. Since by default, its empty nothing will be done. But, you can put something in there and it will be executed.

class seq3 extends base_sequence;
   `uvm_object_utils (seq3)
   function new (string name = "seq3"); (name);
   virtual task pre_body ();
      `uvm_info (get_type_name(), "Executing pre_body", UVM_MEDIUM)
   virtual task body ();
      `uvm_info ("SEQ3", "Starting seq3", UVM_MEDIUM)
      `uvm_info ("SEQ3", "Ending seq3", UVM_MEDIUM)
   virtual task post_body ();
      `uvm_info (get_type_name(), "Executing post_body", UVM_MEDIUM)
   virtual task post_start ();
      `uvm_info (get_type_name (), "Executing post_start", UVM_MEDIUM)

Simulation Results

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/ @ 0: uvm_test_top.m_top_env.m_drv0 [my_driver] Applying initial reset
UVM_INFO ./tb/ @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] DUT is now out of reset
UVM_INFO ./tb/ @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/ @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [seq1] Executing pre_start()
UVM_INFO ./tb/ @ 390000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] Starting seq1
UVM_INFO ./tb/ @ 390000: uvm_test_top.m_top_env.m_seqr0@@m_seq2 [SEQ2] Starting seq2
UVM_INFO ./tb/ @ 400000: uvm_test_top.m_top_env.m_seqr0@@m_seq2.m_seq3 [seq3] Executing pre_body
UVM_INFO ./tb/ @ 400000: uvm_test_top.m_top_env.m_seqr0@@m_seq2 [seq2] Executing pre_do
UVM_INFO ./tb/ @ 400000: uvm_test_top.m_top_env.m_seqr0@@m_seq2 [seq2] Executing mid_do
UVM_INFO ./tb/ @ 400000: uvm_test_top.m_top_env.m_seqr0@@m_seq2.m_seq3 [SEQ3] Starting seq3
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq2.m_seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq2 [seq2] Executing post_do
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq2.m_seq3 [seq3] Executing post_body
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq2.m_seq3 [seq3] Executing post_start
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_seqr0@@m_seq2 [SEQ2] Ending seq2
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_seqr0@@seq1 [SEQ1] Ending seq1
UVM_INFO ./tb/ @ 410000: uvm_test_top.m_top_env.m_drv0 [my_driver] Finished DUT simulation

--- UVM Report catcher Summary ---

Was this article helpful ?