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 ovm_sequence class. ovm_sequencer does the generation of this sequence of transaction, ovm_driver takes the transaction from Sequencer and processes the packet/ drives to other component or to DUT.
Sequencer
A Sequencer is defined by extending ovm_sequencer. ovm_sequencer has a port seq_item_export which is used to connect to ovm_driver for transaction transfer.
1) Define a sequencer by extending ovm_sequence.
`ifndef GUARD_SEQUENCER
`define GUARD_SEQUENCER
class Sequencer extends ovm_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.
`ovm_sequencer_utils(Sequencer)
4) Define the constructor.
function new (string name, ovm_component parent);
super.new(name, parent);
`ovm_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 ovm_object, so using a temporary ovm_object and cast it to configuration object.
A sequence is defined by extending ovm_sequence class. This sequence of transactions should be defined in budy() method of ovm_sequence class. OVM 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 ovm_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 `ovm_do_with(item, {da == p_sequencer.cfg.device0_add;} );
`ovm_do_with(item, {da == p_sequencer.cfg.device1_add;} );
end endtask : body
(S)Sequence Source Code
class Seq_device0_and_device1 extends ovm_sequence #(Packet);
function new(string name = "Seq_device0_and_device1");
super.new(name);
endfunction : new