How Virtual Sequence Works? – Part 1

If you’ve visited my previous posts, you got to know by now – What is a Sequence in UVM and What is the role of a Sequence & How a Sequence works in UVM Testbench?

As a quick recap, Sequence is a dynamic key component of the UVM Testbench which is used to generate stimulus in an UVM environment. A Sequence is executed on a target Sequencer to generate usually the series of the sequence items & transact these sequence items with the UVM Driver to perform the simulation data transfer.

Before jumping to the Virtual Sequences, lets first understand the Sequence Hierarchy in UVM. It will help us to see the bigger picture of stimulus generation arrangement & process in UVM. For this, lets see the Diagram 1 below which shows the Sequence Hierarchy.

Sequence_Hierarchy

Diagram 1: Sequence Hierarchy

In this Diagram 1, we can see that there are 3 different levels of Sequences:

  • Top-level Sequence aka Virtual Sequence
  • Mid-level Sequences i.e. Sub-Sequences
  • Bottom-level Sequences i.e. Primitive Sequences

Top-level Sequence & Mid-level Sequences practically does not send any Transaction item to the UVM Driver. Their main role is to trigger the relevant Sub-Sequences or Primitive Sequences.

A Top-level Sequence is also called Virtual Sequence. It co-ordinates between the different Sub-Sequences. For example, lets consider the DUT is having 2 different interface ports, in this situation there would be 2 Agents serving each interface port inside the UVM Testbench. Virtual Sequence will co-ordinate & synchronize the Transactions for the 2 Agents to generate the simulation uses cases using the corresponding Sub-Sequences. Virtual Sequence decides which Agent’s Sequence will start first and the order of Sub-Sequences execution. We can say, Virtual Sequence acts like a Controller of the simulation data being generated  for the DUT.

Mid-level Sequences i.e. Sub-Sequences, usually, do not create transaction items. As per the sequence hierarchy scheme, Sub-Sequences provides modularity to the Sequence management. Sub-Sequences calls the Primitive Sequences in the defined order and control flow to create the required scenarios. Sub-Sequences can also do things like DUT configuration, Loading a Memory etc.

Low-level Sequences i.e. Primitive Sequences are the Sequences where the Transaction items are generated and sent to the UVM Driver and response from the Driver is received back. Function specific Sequences can be created to perform a specific function e.g. Read, Write, Read Modify & Write etc.

So by now, we’re clear that Virtual Sequence is a Top-level Sequence which controls the whole show of generating stimulus data/simulation data with the help of available Sub-Sequences and Primitive Sequences. Next, lets see the implementation & initialization approaches of the Virtual Sequence.

Virtual Sequence Implementation:

In UVM, Virtual Sequence can be implemented using 2 approaches.

  • In the 1st approach, Virtual Sequence will contain itself the handles of the Agent’s Sequencers on which the Sub-Sequences are to be executed.
  • In the 2nd approach, Virtual Sequence will run on a Virtual Sequencer which is of type uvm_sequencer. In this approach, Virtual Sequencer will contain the target Agent Sequencer’s handle.

In this post i.e. “How Virtual Sequence Works? – Part 1”, we’ll discuss the 1st approach.

“How Virtual Sequence Works? – Part 2” will cover 2nd approach in detail.

Virtual Sequence Implementation (1st approach):

Fundamental thing to understand in 1st approach is that the Agent’s target Sequencer handle are contained by the Virtual Sequence itself.

Apart from this, there are following points to focus upon in terms of Virtual Sequence implementation:

  • Virtual Sequence declaration which includes target Sequencers handles
  • The way a Virtual Sequence starts the Sub-Seqs on target Sequencers
  • The way a Virtual Sequence is started from a Test class

Diagram 2 below shows an example of 1st approach.

Virtual_Seq_1st_Approach

Diagram 2: Virtual Sequence Implementation (1st approach)

As shown in the diagram above, Virtual Sequence contains two Sequencer handles i.e. SQR_AHB & SQR_AXI. There are 2 Agents i.e. AHB Agent & AXI Agent which physically contains 2 Sequencers. These 2 Sequencers are assigned to the Sequencer handles inside Virtual Sequence in a Test. As per the shown diagram above, Virtual Sequence also creates two Sequences which are to be run on the Sequencers of the respective Agents.

Lets now dive deep into the points being mentioned above to understand the Virtual Sequence implementation using 1st approach.

Virtual Sequence Class:

To develop the Virtual Sequence, its a good idea to create a “Base Virtual Sequence” which may contain the handles of all the required target Sequencers. Later this Base Virtual Sequence can be extended to create the Virtual Sequence with the code to start the Sub-Sequences on the target Sequencers. Lets analyze all this by using the UVM code below:

 ///// Base Virtual Sequence Class
class base_vseq extends uvm_sequence #(uvm_sequence_item);
 `uvm_object_utils(base_vseq)
 
 /// Target Agent Sequencers
 uvm_sequencer #(ahb_txn) SQR_AHB;
 uvm_sequencer #(axi_txn) SQR_AXI;
 
 /// Constructor
 function new (string name = "base_vseq");
   super.new(name);
 endfunction: new
 
endclass: vseq_base

///// Virtual Sequence Class
class my_vseq extends base_vseq;
 `uvm_object_utils(my_vseq)
 
 /// Constructor
 function new (string name = "my_vseq");
   super.new(name);
 endfunction: new
 
 /// Sequence Body Task
 task body();
 
   ahb_seqeunce ahb_seq;
   axi_sequence axi_seq;
 
   ahb_seq = ahb_sequence::type_id::create("ahb_seq");
   axi_seq = axi_sequence::type_id::create("axi_seq");
 
   fork
    abh_seq.start(SQR_AHB);
    axi_seq.start(SQR_AXI);
   join
 
endclass: my_vseq

In the UVM code above, we got two classes i.e. & my_vseq classbase_vseq is the base virtual sequence class and my_vseq is the intended Virtual Sequence. Base virtual sequence contains the handle of the two taret Sequencers i.e. SQR_AHB & SQR_AXI.

Virtual Sequence class is extended from the Base Virtual Sequence class. It creates the two Sub-Sequences i.e. ahb_seq & axi_seq using the Factory mechanism. Later inside the body() task the Sub-Sequences are started on the target Agent’s Sequencer by the Virtual Sequence.

Starting the Virtual Sequence:

Next we need to see how the Sequencer handle assignment happens inside the Test and how the Virtual Sequence is started from the Test?

Lets further see the required UVM code for it:

///// Base Test Class
class base_test extends uvm_test;
 `uvm_component_utils(base_test);
 
 /// Environment Class Instantiation
 top_env Env;
 
 /// Constructor
 function new (string name = "base_test", uvm_component parent = null);
   super.new(name, parent);
 endfunction: new
 
 /// Build Phase
 function void build_phase (uvm_phase phase);
   Env = top_env::type_id::create("Env");
 endfunction: build_phase
 
 /// Method to Connect Sequencer Handles in VSEQ
 function void init_vseq (base_vseq vseq);
   vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB;
   vseq.SQR_AXI = test.env.axi_agent.SQR_AXI;
 endfunction: init_vseq
 
endclass: base_test

In the test base class i.e. base_test, shown UVM code above, a method i.e. init_vseq() is created which is used to assign the sequencer handles to the handles in classes derived from the virtual sequence base class.

///// Main Test
class test extends base_test;
 `uvm_component_utils(test)
 
 /// Constructor
 function new (string name = "test", uvm_component parent = null);
 super.new(name, parent);
 endfunction: new
 
 /// Run Phase
 task run_phase (uvm_phase phase);
  /// Create the Virtual Sequence
   my_vseq vseq = my_vseq::type_id::create("vseq");
   phase.raise_objection(this);
  /// Virtual Sequence Initialization
  init_vseq(vseq);
  /// Start the Virtual Sequence
  vseq.start(null);
  phase.drop_objection(this);
 endtask: run_phase
 
endclass: test

Inside the main test which is derived from the base test, Virtual Sequence is created using Factory. Later, the initialization method i.e. init_vseq() is being called to connect the Sequencers handle. Finally Virtual Sequence is started using “Null” since this Virtual Sequence is NOT started on any particular Sequencer.


With this, I would like to pen down for the Part 1 of “How Virtual Sequence Works?”. In Part 2, we’ll discuss about 2nd approach of the Virtual Sequence Implementation.

I hope this post will provide you the value I wanted to share with all of you. Please keep sharing your valuable comments, suggestions & inputs. I love to hear from you. Thank you for your time being spent with me via my blog. Keep visiting!

Till next time, take care..bye!


 

12 thoughts on “How Virtual Sequence Works? – Part 1

  1. in base_test code, what is “vseq” in vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB; , as we dont have any handle named “vseq”.
    is vseq = base_vseq?? or something else.

    Regards
    Tanajirao

    • vseq is the Virtual Sequence handle passed to the “init_vseq” function in “base_test”.
      vseq is declared and constructed inside the “test” “run_phase”.

      Thanks,
      Manish

  2. Thank you, Manish.
    It’s really useful and clear!!!!!!!!

    Just double confirm, looks like “endtask: body” is missed in the end of Sequence Body Task, right?

Leave a Reply

Your email address will not be published. Required fields are marked *