Skip to main content

Virtual Sequence and Virtual Sequencer in UVM

Virtual sequences and sequencers in UVM are just virtual containers of multiple sequences and sequencers.
Using virtual sequencers and sequences can be done in these three ways:

  1. Using only virtual Sequence and handles of sequencers inside the virtual sequence.
  2. Using Virtual sequencer to hold the sequencers
  3. Same as step 2, by using uvm_declare_p_sequencer.
Virtual Sequencer does not create any sequencer objects, rather it points to other physical sequencers in different agents. 
Virtual Sequencer extends from uvm_sequencer.
Virtual sequence will create its sequences and start them. Running sequences can be controlled by user in the body() task.
virtual sequence extends from uvm_sequence.

METHOD-1:
In this method, we will have one base virtual sequence which will hold the handles to different sequencers. Here we dont use any virtual sequencer. We just use the nested seqeunces with handles ot sequencers inside sequence itself.


//This is the base virtual sequence.
class v_seq_base extends uvm_sequence;
  `uvm_object_utils(v_seq_base)
  //Declare the sequencer handles.
  //This is two types of sequencer declaration. 
  //One with using parameterized uvm_sequencer 
  uvm_sequencer#(seq_item_1)   seqr1;
  //Second with direct sequencer type. This may be less reusable when there are multiple sequencers in agent.
  seqr_2  seqr2;
  function new(string name='v_seq_base');
    super.new(name)
  endfunction
endclass

//Sequence class
class v_seq1 extends v_seq_base;
  `uvm_object_utils(v_seq1)
  function new(string name='v_seq1');
    super.new(name)
  endfunction
  //Body
  virtual task body();
    //Create child sequences.
    //They can be different type of sequences, that you chose to run on the sequencers inside the virtual sequencer.
    sequence1   seq1;
    sequence2  seq2;
    seq1 = sequence1::type_id::create('seq1');
    seq2 = sequence2::type_id::create('seq2');
    fork 
    begin
      seq1.start(seqr1);
      seq2.start(seqr2);
    end
  endtask  
endclass

//Connecting sequencers and starting sequences.
class base_test extends uvm_test;
 `uvm_component_utils(base_test);

  //Your Env.
  Test_env  env;

  function new(string name='base_test',uvm_component parent = null);
  endfunction
  
  function build_phase(uvm_phase phase);
    env = Test_env::type_id::create('env');
  endfunction

  function set_seqr(v_seq_base v_seq);
    v_seq.seqr1 = env.agent1.seqr_handle;
    v_seq.seqr2 = env.agent2.seqr_handle_x;
  endfunction

endclass

//Main Test 
class test1 extends base_test;
  `uvm_component_utils(test1)
  function new(string name='test1',uvm_component parent = null);
  endfunction

  //No build phase explicitly required as parent already has it.
  task run_phase(uvm_phase phase);
    //Create the sequence
    phase.raise_objection();
    v_seq1  v_seq;
    vseq = v_seq1::type_id::create('v_seq');
    //SEt the virtual sequencer handles.
    set_vseq(v_seq);
    //Start sequence.
    v_seq.start(null);
    phase.drop_objection();
    
  endtask

endclass


METHOD-2:
In this method we will use the virtual sequencer. A virtual sequencer is just a normal uvm_sequencer which holds the handles to different sequences. 

//Create a virtual sequencer
class v_seqr extends uvm_sequencer;
  //Create the sub seqeuncer handles.
  //This is two types of sequencer declaration. 
  //One with using parameterized uvm_sequencer 
  uvm_sequencer#(seq_item_1)   seqr1;
  //Second with direct sequencer type. This may be less reusable when there are multiple sequencers in agent.
  seqr_2  seqr2;

  //Constructor
  function new(string name='v_seqr', uvm_component parent);
  endfunction
endclass

//This is the base virtual sequence.
class v_seq_base extends uvm_sequence;
  `uvm_object_utils(v_seq_base)
  
  //create virtual sequencer handle
  v_seqr  v_seqr1;
  function new(string name='v_seq_base');
    super.new(name)
  endfunction
  
  //Body
  task body();
    //Assign the virtual sequencer..
    //Get the sequencer this is running on.(the actual object)
    if(!$cast(v_seqr1, m_sequencer)) begin
      `uvm_error(get_full_name(), "Virtual Sequencer casting Faled");
    end
  endtask
endclass

//Sequence class
class v_seq1 extends v_seq_base;
  `uvm_object_utils(v_seq1)
  function new(string name='v_seq1');
    super.new(name)
  endfunction
  //Body
  task body();

    //Call super.body to get the v_seqr handle.
    super.body();
    //Create child sequences.
    //They can be different type of sequences, that you chose to run on the sequencers inside the virtual sequencer.
    sequence1   seq1;
    sequence2  seq2;
    seq1 = sequence1::type_id::create('seq1');
    seq2 = sequence2::type_id::create('seq2');
    fork 
    begin
      seq1.start(v_seqr1.seqr1);
      seq2.start(v_seqr1.seqr2);
    end
  endtask  
endclass

//Connecting sequencers and starting sequences.
class base_test extends uvm_test;
 `uvm_component_utils(base_test);
  //Declare the virtual sequencer. You can chose to place the sequencer in the environment too.
  v_seqr  v_sqr;
  //Your Env.
  Test_env  env;

  function new(string name='base_test',uvm_component parent = null);
  endfunction
  
  function build_phase(uvm_phase phase);
    env = Test_env::type_id::create('env');
    v_sqr = v_seqr::type_id::create('v_sqr');
  endfunction

  function connect_phase(uvm_phase phase);
    v_sqr.seqr1 = env.agent1.seqr_handle;
    v_sqr.seqr2 = env.agent2.seqr_handle_x;
  endfunction

endclass

//Main Test 
class test1 extends base_test;
  `uvm_component_utils(test1)

  function new(string name='test1',uvm_component parent = null);
  endfunction

  //No build phase explicitly required as parent already has it.
  task run_phase(uvm_phase phase);
    //Create the sequence
    phase.raise_objection();
    v_seq1  v_seq;
    vseq = v_seq1::type_id::create('v_seq');
   
    //Start virtual sequence on virtual sequrncer
    v_seq.start(v_sqr);
    phase.drop_objection();
    
  endtask

endclass


METHOD-3:
In this method, is pretty much similar to the method-2, but we declared virtual_sequencer handle manually and then type casted it. But UVM provides a way to create handle of specified sequencer
uvm_declare_p_sequencer(X) will create handle of type X.
When that sequence is called, it will take the m_sequencer and casts it to p_sequencer. In short this is the code it executes when sequence is called.
$cast(p_sequencer, m_sequencer)



//Create a virtual sequencer
class v_seqr extends uvm_sequencer;
  //Create the sub seqeuncer handles.
  //This is two types of sequencer declaration. 
  //One with using parameterized uvm_sequencer 
  uvm_sequencer#(seq_item_1)   seqr1;
  //Second with direct sequencer type. This may be less reusable when there are multiple sequencers in agent.
  seqr_2  seqr2;

  //Constructor
  function new(string name='v_seqr', uvm_component parent);
  endfunction
endclass

//This is the base virtual sequence.
class v_seq_base extends uvm_sequence;
  `uvm_object_utils(v_seq_base)
  
  //Use uvm method to declare and get the virtual sequencer handle.
  //This will assign the sequencer on which this sequence is called to p_sequencer and casts it to the v_seqr.
  `uvm_declare_p_sequencer(v_seqr)

  function new(string name='v_seq_base');
    super.new(name)
  endfunction
  

  endtask
endclass

//Sequence class
class v_seq1 extends v_seq_base;
  `uvm_object_utils(v_seq1)
  function new(string name='v_seq1');
    super.new(name)
  endfunction
  //Body
  task body();

    //Create child sequences.
    //They can be different type of sequences, that you chose to run on the sequencers inside the virtual sequencer.
    sequence1   seq1;
    sequence2  seq2;
    seq1 = sequence1::type_id::create('seq1');
    seq2 = sequence2::type_id::create('seq2');
    fork 
    begin
      seq1.start(p_sequencer.seqr1);
      seq2.start(p_sequencer.seqr2);
    end
  endtask  
endclass

//Connecting sequencers and starting sequences.
class base_test extends uvm_test;
 `uvm_component_utils(base_test);
  //Declare the virtual sequencer. You can chose to place the sequencer in the environment too.
  v_seqr  v_sqr;
  //Your Env.
  Test_env  env;

  function new(string name='base_test',uvm_component parent = null);
  endfunction
  
  function build_phase(uvm_phase phase);
    env = Test_env::type_id::create('env');
    v_sqr = v_seqr::type_id::create('v_sqr');
  endfunction

  function connect_phase(uvm_phase phase);
    v_sqr.seqr1 = env.agent1.seqr_handle;
    v_sqr.seqr2 = env.agent2.seqr_handle_x;
  endfunction

endclass

//Main Test 
class test1 extends base_test;
  `uvm_component_utils(test1)

  function new(string name='test1',uvm_component parent = null);
  endfunction

  //No build phase explicitly required as parent already has it.
  task run_phase(uvm_phase phase);
    //Create the sequence
    phase.raise_objection();
    v_seq1  v_seq;
    vseq = v_seq1::type_id::create('v_seq');
   
    //Start virtual sequence on virtual sequrncer
    v_seq.start(v_sqr);
    phase.drop_objection();
    
  endtask

endclass


Hope this explains well about usage of virtual sequences and sequencers.


Comments

Post a Comment

Popular posts from this blog

Verdi Uses - I

COVERAGE While going through some of the blog posts, i found that the Verdi supports even coverage right out of the box, but from the 2014 Version. Well till now, we relied on the URG reports and the DVE to review the coverage and apply waivers and create exclusion files. Looks verdi supports all the features.. I did not get any hands on the features of this option, soon i will find some time to play around with this option of coverage in Verdi. How to launch the verdi with coverage. $> verdi -cov -covdir <PATH_TO_TB>.simv.vdb -covdir <PATH_TO_TEST1>.simv.vdb -covdir <PATH_TO_TEST2>.simv.vdb...  You can pass multiple covdirs, where in all the results will be merged. References: SYNOPSYS SITE Think Verification  

Sequence Layering in UVM

What is Sequence Layering? Sequence layering is refered to as running sequences inside sequences.  So whats the big deal. There are virtual sequences we will use to run multiple sequences. A generic example of sequence layering. you have two sequences  Interrupt sequence register read write sequence. Generally when you have interrupt, we generally enter into interrupt service routine, which can be implemented as an interrupt sequence. So when you run an interrupt sequence, you might end up running register read /write sequence like polling or clearing interrupts. Well thats easy, just run one sequence in another. class top_seq extends uvm_sequence#(txn_item); //Two sequencers say.. sub sequences. sub_seq1 seq1; sub_seq2 seq2; ... task body(); seq1.start(some_seqr); seq2.start(some_seqr2); endtask endclass Killing sequences: That was great you are running nested sequences. But how to kill them when needed. UVM provides method cal

SystemVerilog: Mailboxes and Queues

Mailboxes and queues are couple of basic data constructs of system verilog language. Lets get to the definition of them: Queue: A Queue in system verilog function as the name suggests. But with a twist. Queue in system verilog is a list of similar elements. Queue is built on top of an array. Delcaration of a queue. < TYPE > < que_name >[ $ ]; Default Behaviour: The default size of the queue in system verilog is " Infinite ". The above declaration will create a que of infinite length. You can add elements to the que until your simulator crashes :) What if you want to create a queue of finite length. Just look at the declaration below: < TYPE > < que_name >[ $ :< LIMIT >]; The above declaration will create a queue of size " LIMIT+1 " Initializing queue with elements Accessing elements of a queue Methods in queue Inserting elements into queue Reoving elements from queue http://www.project-veripage.com/queue_1.ph