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

Simple sequence flow

To understand the sequence flow better, let us construct a simple sequence with a print statement in each of the above mentioned methods. First we'll try to understand how call_pre_post argument affects the output.


// Let's just declare a base sequence from which we can have a child sequence
// This will help to prove execution order of pre_do, mid_do and post_do tasks
class base_seq extends uvm_sequence;

  // Factory registration and new function is assumed to be written next
	...
	
  // For every method during the body() execution flow, we'll simply add a display statement to track 
  // how each method in this sequence is getting executed
  virtual task pre_body();
    `uvm_info (get_type_name(), "pre_body()", UVM_LOW)
  endtask

  virtual task pre_do(bit is_item);
    `uvm_info (get_type_name(), "pre_do()", UVM_LOW)
  endtask

  virtual function void mid_do(uvm_sequence_item this_item);
    `uvm_info (get_type_name(), "mid_do()", UVM_LOW)
  endfunction

  virtual task body();
    `uvm_info (get_type_name(), "body()", UVM_LOW)
  endtask

  virtual function void post_do(uvm_sequence_item this_item);
    `uvm_info (get_type_name(), "post_do()", UVM_LOW)
  endfunction

  virtual task post_body();
    `uvm_info (get_type_name(), "post_body()", UVM_LOW)
  endtask
endclass

Within our test case, we'll call the start method of our sequence base_seq. Note that the default value of call_pre_post argument of the start method is 1 and hence the pre_body and post_body tasks of that sequence will be called.


// Define a base test that will launch our base sequence
class base_test extends uvm_test;
	...
	
	// Instantiate the base_seq and start it on default sequencer
	virtual task run_phase (uvm_phase phase);
		base_seq bs = base_seq::type_id::create ("bs", this);
		bs.start (null);     // Default value of call_pre_post is 1
	endtask
endclass

As expected, the sequence executes the pre_body and post_body tasks in addition to the body method.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO ./tb/tb_top.sv(19) @ 0: reporter@@bs [base_seq] pre_body()
UVM_INFO ./tb/tb_top.sv(31) @ 0: reporter@@bs [base_seq] body()
UVM_INFO ./tb/tb_top.sv(39) @ 0: reporter@@bs [base_seq] post_body()

--- UVM Report catcher Summary ---

Now, let's supply 0 to call_pre_post argument and see that pre_body and post_body methods are not executed.


class base_test extends uvm_test;
	...
	
	// Call the same sequence with call_pre_post argument set to 0
	// This will show the difference in result compared to previous example
	virtual task run_phase (uvm_phase phase);
		base_seq bs = base_seq::type_id::create ("bs", this);
		bs.start (null, null, 0, 0);  // call_pre_post is given 0
	endtask
endclass	
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO ./tb/tb_top.sv(31) @ 0: reporter@@bs [base_seq] body()

--- UVM Report catcher Summary ---

Takeaway: if call_pre_post is 1, then pre_body and post_body methods of the started sequence will be executed.

Inherited sequence flow

The second argument to start method is parent which might cause a confusion regarding the term parent used in inheritance versus the parent sequence that starts the main sequence. To try out an example, we'll create a child sequence of base_seq with a print statement in each of its methods and spawn that from the test case.


// The child sequence will have its own pre_body task to see pre_body() method of which parent is called
class child1_seq extends base_seq;
	
  // Again, to track the message lets just print this into the log
  virtual task pre_body();
    `uvm_info (get_type_name(), "pre_body()", UVM_LOW)
  endtask
	
  // Definition of all other methods like base class should come here
	
endclass

class base_test extends uvm_test;
	...
	
	// Start the child sequence with the default value of call_pre_post
	virtual task run_phase (uvm_phase phase);
		child1_seq cs = child1_seq::type_id::create ("cs", this);
		cs.start (null);     // Default value of call_pre_post is 1
	endtask
endclass	

In this case also, we get a similar result as before where pre_body and post_body methods are invoked because of the default value of call_pre_post argument in the start method. So it is clear that parent argument simply specifies the parent sequence from where the main sequence is started.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO ./tb/tb_top.sv(19) @ 0: reporter@@bs [base_seq] pre_body()
UVM_INFO ./tb/tb_top.sv(31) @ 0: reporter@@bs [base_seq] body()
UVM_INFO ./tb/tb_top.sv(39) @ 0: reporter@@bs [base_seq] post_body()

--- UVM Report catcher Summary ---

Spawned sequence flow

Here, we'll start another sequence called child2_seq from the body method in child1_seq with rest of the code being the same. This example will demonstrate how the parent argument in start method can be used.


class child2_seq extends base_seq;
	...
  virtual task pre_body();
    `uvm_info (get_type_name(), "pre_body()", UVM_LOW)
  endtask
	
  // Definition of all other methods like base class should come here
endclass

class child1_seq extends base_seq;
	...
	
	virtual task body();
		child2_seq cs = child2_seq::type_id::create ("cs");
		cs.start (null);   // parent arg is by default null
	endtask
	...
endclass

It can be seen that child2_seq also executed its own methods after which child1_seq execution is resumed.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test child1_test...                                                                                                                  
UVM_INFO ./tb/tb_top.sv(52) @ 0: reporter@@cs [child1_seq] pre_body()                                                                                                       
UVM_INFO ./tb/tb_top.sv(65) @ 0: reporter@@cs [child1_seq] body()                                                                                                           
UVM_INFO ./tb/tb_top.sv(87) @ 0: reporter@@cs2 [child2_seq] pre_body()
UVM_INFO ./tb/tb_top.sv(99) @ 0: reporter@@cs2 [child2_seq] body()                                                                   
UVM_INFO ./tb/tb_top.sv(107) @ 0: reporter@@cs2 [child2_seq] post_body()
UVM_INFO ./tb/tb_top.sv(76) @ 0: reporter@@cs [child1_seq] post_body()                                                                                                      

--- UVM Report catcher Summary ---

Now we'll supply the pointer to child1_seq as an argument to the parent of the start method in child2_seq.


class child1_seq extends base_seq;
	...
	
	virtual task body();
		child2_seq cs = child2_seq::type_id::create ("cs");
		cs.start (null, this);     // Give handle of current seq as parent to child2
	endtask
	...
endclass

Now it can be seen that pre_do, mid_do and post_do methods of the parent sequence child1_seq has been called.

 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test child1_test...                                                                                                                  
UVM_INFO ./tb/tb_top.sv(52) @ 0: reporter@@cs [child1_seq] pre_body()                                                                                                       
UVM_INFO ./tb/tb_top.sv(65) @ 0: reporter@@cs [child1_seq] body()                                                                                                           
UVM_INFO ./tb/tb_top.sv(87) @ 0: reporter@@cs.cs2 [child2_seq] pre_body()                                                                                                   
UVM_INFO ./tb/tb_top.sv(56) @ 0: reporter@@cs [child1_seq] pre_do()                                                                                                         
UVM_INFO ./tb/tb_top.sv(60) @ 0: reporter@@cs [child1_seq] mid_do()                                                                                                        
UVM_INFO ./tb/tb_top.sv(99) @ 0: reporter@@cs.cs2 [child2_seq] body()                                                                                                       
UVM_INFO ./tb/tb_top.sv(72) @ 0: reporter@@cs [child1_seq] post_do()                                                                                                        
UVM_INFO ./tb/tb_top.sv(107) @ 0: reporter@@cs.cs2 [child2_seq] post_body()                                                                                                 
UVM_INFO ./tb/tb_top.sv(76) @ 0: reporter@@cs [child1_seq] post_body()                                                                                                      

--- UVM Report catcher Summary ---