In this phase we will develop Sequence and Sequencer.
A sequence is series of transaction and sequencer is used to for controlling the flow of transaction generation.
A sequence of transaction (which we already developed in previous phase) is defined by extending uvm_sequence class. uvm_sequencer does the generation of this sequence of transaction, uvm_driver takes the transaction from Sequencer and processes the packet/ drives to other component or to DUT.
Sequencer
A Sequencer is defined by extending uvm_sequencer. uvm_sequencer has a port seq_item_export which is used to connect to uvm_driver for transaction transfer.
1) Define a sequencer by extending uvm_sequence.
`ifndef GUARD_SEQUENCER
`define GUARD_SEQUENCER
class Sequencer extends uvm_sequencer #(Packet);
endclass : Sequencer
`endif
2) We need Device port address, which are in configuration class. So declare a configuration class object.
Configuration cfg;
3) Declare Sequencer utility macros.
`uvm_sequencer_utils(Sequencer)
4) Define the constructor.
function new (string name, uvm_component parent);
super.new(name, parent);
`uvm_update_sequence_lib_and_item(Packet)
endfunction : new
5) In end_of_elaboration() method, using get_config_object(), get the configuration object which will be passed from testcase.
get_config_object() returns object of type uvm_object, so using a temporary uvm_object and cast it to configuration object.
A sequence is defined by extending uvm_sequence class. This sequence of transactions should be defined in budy() method of uvm_sequence class. UVM has macros and methods to define the transaction types. We will use macros in this example.
You can define as many sequences as you want. We will define 2 sequences.
1) Define sequence by extending
class Seq_device0_and_device1 extends uvm_sequence #(Packet);
endclass: Seq_device0_and_device1
2) Define constructor method.
function new(string name = "Seq_do");
super.new(name);
endfunction : new
3) Declare utilities macro. With this macro, this sequence is tied to Sequencer.
4) The algorithm for the transaction should be defined in body() method of the sequence. In this sequence we will define the algorithm such that alternate transactions for device port 0 and 1 are generated.
The device addresses are available in configuration object which is in sequencer. Every sequence has a handle to its sequence through p_sequencer. Using p_sequencer handle, access the device address.
virtualtask body();
foreverbegin `uvm_do_with(item, {da == p_sequencer.cfg.device_add[0];} );
`uvm_do_with(item, {da == p_sequencer.cfg.device_add[1];} );
end endtask : body
(S)Sequence Source Code
class Seq_device0_and_device1 extends uvm_sequence #(Packet);
function new(string name = "Seq_device0_and_device1");
super.new(name);
endfunction : new