Hi Friends, Welcome for the Part 2 of our discussion about “How Virtual Sequence Works?“. I believe you’ve gone through my post “How Virtual Sequence Works? – Part 1” where we got to know about Sequence Hierarchy, Virtual Sequence & different approaches of Virtual Sequence Implementation. My recommendation is to go through “Part 1” if you haven’t already done so.
As you know from Part 1 that we’re going to discuss the 2nd approach of the Virtual Sequence Implementation in this post, Let me restate here again the different Virtual Sequence Implementation approaches before jumping into the details of the 2nd approach.
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.
Now, lets dive deep into the 2nd approach.
Virtual Sequence Implementation (2nd approach):
To understand the 2nd approach in a better way, first its important to understand about Virtual Sequencer.
Virtual Sequencer is a Sequencer that is not connected to the UVM Driver itself, but contains the handles of the target Sequencer in the Testbench hierarchy.
In the Diagram 1 below, there is an example UVM Testbench environment to show the Virtual Sequencer’s application and 2nd approach of Virtual Sequence Implementation:
Diagram 1: Virtual Sequence Implementation (2nd approach)
As shown in the above diagram, Virtual Sequencer is the part of the Environment i.e. “Env”. Virtual Sequencer contains the handles of the target Sequencers i.e. & which are physically located inside the Agents i.e. AHB Agent & AXI Agent respectively. These target Sequencers handles assignment will be done during connect phase of the Environment class.
Virtual Sequence is located outside the Environment class & it is created in the run_phase() method of the Test. The Virtual Sequence is designed to run on the Virtual Sequencer & Virtual Sequence also gets the handles of the target Sequencers from the Virtual Sequencer.
Note: It’s a good practice to give names to the handles which test writer could understand easily.
Lets see the applicable UVM code for the Virtual Sequence Implementation, 2nd approach, as shown below:
///// Virtual Sequencer Class class virtual_seqr extend uvm_sequencer; `uvm_component_utils(virtual_seqr) /// Target Sequencer Handles ahb_seqr SQR_AHB; axi_seqr SQR_AXI; /// Constructor function new (string name = "virtual_seqr", uvm_component parent); super.new(name, parent); endfunction: new endclass: virtual_seqr
Virtual Sequencer i.e. “virtual_seqr” class is declared by extended the UVM base class uvm_sequencer. Target Sequencer handles are also declared inside it.
Now lets see the implementation of the Virtual Sequence. First a Base Virtual Sequence will be declared & later Virtual Sequence will be derived from the base virtual sequence. Lets see how its being done:
///// Base Virtual Sequence class base_vseq extends uvm_sequence #(uvm_sequence_item); `uvm_object_utils(base_vseq) /// Virtual Sequencer Handle virtual_seqr v_sqr; /// Target Sequencers Handle ahb_seqr SQR_AHB; axi_seqr SQR_AXI; /// Constructor function new (string name = "base_vseq"); super.new(name); endfunction: new /// Body Task (Assign target sequencers handle) task body(); if (!$cast(v_sqr, m_sequencer)) begin `uvm_error(get_full_name(), "Virtual Seqr pointer cast failed") end SQR_AHB = v_sqr.SQR_AHB; SQR_AXI = v_sqr.SQR_AXI; endtask: body endclass: base_vseq ///// Virtual Sequence class my_vseq extends base_vseq; `uvm_object_utils(my_vseq) /// Constructor function new (string name = "my_vseq"); super.new(name); endfunction: new /// Body Task(starting the sub-sequences) task body(); /// Assigning the Sub-Sequencer Handles super.body; /// Sub-Sequence Creation & Execution ahb_sequence ahb_seq; axi_sequence axi_seq; ahb_seq = ahb_sequence::type_id::create("ahb_seq"); axi_seq = axi_sequence::type_id::create("axi_seq"); repeat(30) begin ahb_seq.start(SQR_AHB); axi_seq.start(SQR_AXI); end endtask: body endclass: my_vseq
Now its turn to see the UVM code for the Environment class which instantiates Virtual Sequencer as well as both the Agents.
///// Environment Class class Environ extends uvm_env; `uvm_component_utils(Environ) /// Virtual Sequencer Handle virtual_seqr v_sqr; /// Agents Handles ahb_agent AHB_AGNT; axi_agent AXI_AGNT; /// Constructor function new (string name = "Environ", uvm_component parent); super.new(name, parent); endfunction: new /// Build Phase function void build_phase (uvm_phase phase); v_sqr = virtual_seqr::type_id::create("v_sqr"); AHB_AGNT = ahb_agent::type_id::create("AHB_AGNT"); AXI_AGNT = axi_agent::type_id::create("AXI_AGNT"); endfunction: build_phase /// Connect Phase function void connect_phase (uvm_phase phase); v_sqr.SQR_AHB = AHB_AGNT.m_sequencer; v_sqr.SQR_AXI = AXI_AGNT.m_sequencer; endfunction: connect_phase endclass: Envrion
In the above Environment class i.e. “Environ”, Virtual Sequencer is instantiated & built along with two Agents i.e. “AHB_AGNT” & “AXI_AGNT”. Target Sequencer handles are also assigned in the connect_phase(). Usage of a flexible & handy feature of UVM i.e. “m_sequencer” is being shown which by default points to the UVM Sequencer derived from the uvm_sequencer.
Finally lets see how the Virtual Sequence is started on the Virtual Sequencer from the Test:
///// Main Test class Test extends uvm_test; `uvm_component_utils(Test) /// Instantiations my_vseq vseq; Environ Env; /// Constructor function new (string name = "Test", uvm_component parent = null); super.new(name, parent); endfunction: new /// Build Phase function void build_phase (uvm_phase phase); Env = Environ::type_id::create("Env"); endfunction: build_phase /// Run Phase task run_phase (uvm_phase phase); /// Create the Virtual Sequence & Environment vseq = my_vseq::type_id::create("vseq"); phase.raise_objection(this); /// Start the Virtual Sequence vseq.start(Env.v_sqr); phase.drop_objection(this); endtask: run_phase endclass: Test
In the Test class i.e. “Test”, both Environment & Virtual Sequence i.e. “Environ” & “my_vseq” are instantiated and created. Finally Virtual Sequence is started on the Virtual Sequencer which exists inside the Environment.
So thats how, the Virtual Sequence can be implemented using the 2nd approach in UVM.
With this, We reached to the concluding part of the topic “How Virtual Sequence Works?”. In fact we covered “How Virtual Sequences can be Implemented?” as well. I hope it will provide you the value which I want to share with you. Please keep sharing your comments, inputs & suggestions. I feel happy hearing from you.
I’ll meet you with a new post soon. Keep visiting for other topics! Till then..take care, bye!