«…Труд избавляет человека от трех великих зол: скуки, порока, нужды…»

UVM/UVM Cookbook/Sequences/Virtual

Материал из Wiki
< UVM
Перейти к: навигация, поиск
Проект Методология UVM

UVM Cookbook


Вебинары
Литература

* OVM *

Sequences/Virtual

Виртуальная последовательность представляет собой последовательность, которая управляет генерацией входных воздействий используя несколько sequencer`ов. Поскольку последовательности, sequencer`ы и driver`ы ориентированы на определённые интерфейсы, почти все testbench`и требуют виртуальную последовательность для координирования воздействий через разные интерфейсы и для взаимодействия между ними. Виртуальная последовательность часто является верхним уровшенм в иерархии последовательностей (sequence hierarchy). Виртуальная последовательность также может упоминаться как "ведущая-последовательность" ('master sequence') или "координационная/координирующая последовательность" 'co-ordinator sequence'.

Virtual sequence block level

Виртуальная последовательность отличается от обычной последовательности тем, что её основным назначением не является отправление транзакций (sequence items). Вместо этого она генерирует и выполняет последовательности на различных заданных агентах. Для этого она содержит указатели на классы (handles) для заданных sequencer`ов и они используются при запуске последовательностей.

// Creating a useful virtual sequence type:
typedef uvm_sequence #(uvm_sequence_item) uvm_virtual_sequence;
 
// Virtual sequence example:
class my_vseq extends uvm_virtual_sequence;
  ...
  // Handles for the target sequencers:
  a_sequencer_t a_sequencer;
  b_sequencer_t b_sequencer;
 
  task body();
    ...
    // Start interface specific sequences on the appropriate target sequencers:
    aseq.start( a_sequencer , this );
    bseq.start( b_sequencer , this );
  endtask
endclass

Для того, чтобы виртуальная последовательность работала, указатели на классы sequencer`ов должны быть назначены. Как правило, виртуальная последовательность создаётся в классе test в задаче (task) фазы run (run_phase) и назначения указателей классов sequencer`ов внутри объекта виртуальной последовательности выполняются с помощью теста. После того, как указатели на классы () sequencer`ов назначены, виртуальная последовательность запускается с помощью пустого (null) указателя на класс sequencer`а.

my_seq vseq = my_seq::type_id::create("vseq");
 
  vseq.a_sequencer = env.subenv1.bus_agent.sequencer;
  vseq.b_sequencer = env.subenv2.subsubenv1.bus_agent3.sequencer;
 
  vseq.start( null );

Есть несколько вариаций на тему виртуальной последовательности. Там нет ничего, чтобы остановить виртуальную последовательность Будучи запущенным на секвенсор и отправки элементы последовательность в этой секвенсор в то же время выполняя другие последовательности на их целевых секвенсоров. Виртуальный последовательность не должна быть выполнена с помощью теста, он может быть выполнен среде инкапсулирующего ряд агентах. Для большого испытательный стенд со многими агентами и несколько проблемных областей может быть несколько виртуальная последовательности, работающие одновременно.

In addition to target sequencer handles, a virtual sequence may also contain handles to other testbench resources such as register models which would be used by the sub-sequences.

Recommended Virtual Sequence Initialization Methodology

In order to use the (U)OVM effectively, many organisations separate the implementation of the testbench from the implementation of the test cases. This is either a conceptual separation or a organisational separation. The testbench implementor should provide a test base class and a base virtual sequence class from which test cases can be derived. The test base class is responsible for building and configuring the verification environment component hierarchy, and specifying which virtual sequence(s) will run. The test base class should also contain a method for assigning sequence handles to virtual sequences derived from the virtual sequence base class. With several layers of vertical reuse, the hierarchical paths to target sequencers can become quite long. Since the hierarchical paths to the target sequencers are known to the testbench writer, this information can be encapsulated for all future test case writers.

Virtual_seq_vertical_reuse.gif Virtual seq vertical reuse.gif

As an example consider the testbench illustrated in the diagram. To illustrate a degree of virtual reuse, there are four target agents organised in two sub-environments within a top-level environment. The virtual sequence base class contains handles for each of the target sequencers:

class top_vseq_base extends uvm_sequence #(uvm_sequence_item);
 
`uvm_object_utils(top_vseq_base)
 
uvm_sequencer #(a_seq_item) A1;
uvm_sequencer #(a_seq_item) A2;
uvm_sequencer #(b_seq_item) B;
uvm_sequencer #(c_seq_item) C;
 
function new(string name = "top_vseq_base");
  super.new(name);
endfunction
 
endclass: top_vseq_base

In the test base class a method is created which can be used to assign the sequencer handles to the handles in classes derived from the virtual sequence base class.

class test_top_base extends uvm_test;
 
`uvm_component_utils(test_top_base)
 
env_top m_env;
 
function new(string name = "test_top_base", uvm_component parent = null);
  super.new(name, parent);
endfunction
 
function void build_phase(uvm_phase phase);
  m_env = env_top::type_id::create("m_env", this);
endfunction: build_phase
 
// Method to initialise the virtual sequence handles
function void init_vseq(top_vseq_base vseq);
  vseq.A1 = m_env.m_env_1.m_agent_a.m_sequencer;
  vseq.C = m_env.m_env_1.m_agent_c.m_sequencer;
  vseq.A2 = m_env.m_env_2.m_agent_a.m_sequencer;
  vseq.B = m_env.m_env_2.m_agent_b.m_sequencer;
endfunction: init_vseq
 
endclass: test_top_base

In a test case derived from the test base class the virtual sequence initialisation method is called before the virtual sequence is started.

class init_vseq_from_test extends test_top_base;
 
`uvm_component_utils(init_vseq_from_test)
 
function new(string name = "init_vseq_from_test", uvm_component parent = null);
  super.new(name, parent);
endfunction
 
task run_phase(uvm_phase phase);
  vseq_A1_B_C vseq = vseq_A1_B_C::type_id::create("vseq");
 
  phase.raise_objection(this);
 
  init_vseq(vseq);  // Using method from test base class to assign sequence handles   
  vseq.start(null); // null because no target sequencer
 
  phase.drop_objection(this);
endtask: run_phase
 
endclass: init_vseq_from_test

The virtual sequence is derived from the virtual sequence base class and requires no initialisation code.

class vseq_A1_B_C extends top_vseq_base;
 
`uvm_object_utils(vseq_A1_B_C)
 
function new(string name = "vseq_A1_B_C");
  super.new(name);
endfunction
 
task body();
  a_seq a = a_seq::type_id::create("a");
  b_seq b = b_seq::type_id::create("b");
  c_seq c = c_seq::type_id::create("c");
 
  a.start(A1);
  fork
    b.start(B);
    c.start(C);
  join
 
endtask: body
 
endclass: vseq_A1_B_C

This example illustrates how the target sequencer handles can be assigned from the test case, but the same approach could be used for passing handles to other testbench resources such as register models and configuration objects which may be relevant to the operation of the virtual sequence or its sub-sequences.

Download-tarball.png Download-tarball.png Download a complete working example:
(tarball: test_init_uvm.tgz)

Alternative Methods For Initialising A Virtual Sequence

In the preceding example, the virtual sequence is initialised from the test class, this is the recommended approach since it is so simple to implement. However, there are several alternative approaches that could be used:

  • Putting the initialisation method in the test package rather than making it a member of a test base class. This allows several variants of the init method to exist without having to define different base classes.
  • Putting the initialisation method in a mapping package which imports the env package(s) and the virtual sequence package. This separates out the initialisation from the test. Several initialisation methods could be defined for different virtual sequences. The mapping package would be imported into the test package.
package my_virtual_sequence_mapping_pkg;
//
// This package is specific to the test env and to the virtual sequence
// 
import my_sequence_pkg::*;
import my_env_pkg:*;
 
function void init_my_virtual_sequence_from_my_env( my_virtual_sequence vseq , my_env env );
  vseq.fabric_ports[0] = env.env1.a_agent.sequencer;
  vseq.fabric_ports[1] = env.env2.a_agent.sequencer;
  vseq.data_port = env.env1.b_agent.sequencer;
  vseq.control_port = env.env2.c_agent.sequencer;
end
 
// Other virtual sequence initialisation methods could also be defined
 
endpackage
  • Using the uvm_config_db to pass sequencer handles from the env to the virtual sequence. This can be made to work in small scaled environments, but may breakdown in larger scale environments, especially when multiple instantiations of the same env are used since there is no way to uniquify the look-up key for the sequencer in the uvm_config_db
// Inside the env containing the target sequencers:
//
function void connect_phase(uvm_phase phase);
//
  uvm_config_db #(a_sequencer)::set(null, "Sequencers", "a_sqr", a_agent.m_sequencer);
  uvm_config_db #(b_sequencer)::set(null, "Sequencers", "b_sqr", b_agent.m_sequencer);
//
endfunction
 
// Inside the virtual sequence base class:
//
a_sequencer A;
b_sequencer B;
 
// Get the sequencer handles back from the config_db
//
task body();
  if(!uvm_config_db #(a_sequencer)::get(null, "Sequencers", "a_sqr", A)) begin
    `uvm_error("body", "a_sqr of type a_sequencer not found in the uvm_config_db")
  end
  if(!uvm_config_db #(b_sequencer)::get(null, "Sequencers", "b_sqr", B)) begin
    `uvm_error("body", "b_sqr of type b_sequencer not found in the uvm_config_db")
  end
  // ....
endtask
  • Using the find_all() method to find all the sequencers that match a search string in an environment. Again this relies on the sequencer paths being unique which is an assumption that will most likely break down in larger scale environments.
//
// A virtual sequence which runs stand-alone, but finds its own sequencers
class virtual_sequence_base extends uvm_sequence #(uvm_sequence_item);
 
`uvm_object_utils(virtual_sequence)
 
// Sub-Sequencer handles
bus_sequencer_a A;
gpio_sequencer_b B;
 
// This task would be called as super.body by inheriting classes
task body;
  get_sequencers();
endtask: body
 
//
function void get_sequencers;
  uvm_component tmp[$];
  //find the A sequencer in the testbench
  tmp.delete(); //Make sure the queue is empty
  uvm_top.find_all("*m_bus_agent_h.m_sequencer_h", tmp);
  if (tmp.size() == 0)
    `uvm_fatal(report_id, "Failed to find mem sequencer")
  else if (tmp.size() > 1)
    `uvm_fatal(report_id, "Matched too many components when looking for mem sequencer")  
  else
    $cast(A, tmp[0]);
  //find the B sequencer in the testbench
  tmp.delete(); //Make sure the queue is empty
  uvm_top.find_all("*m_gpio_agent_h.m_sequencer_h", tmp);
  if (tmp.size() == 0)
    `uvm_fatal(report_id, "Failed to find mem sequencer")
  else if (tmp.size() > 1)
    `uvm_fatal(report_id, "Matched too many components when looking for mem sequencer")  
  else
    $cast(B, tmp[0]);
endfunction: get_sequences
 
endclass: virtual_sequence_base

The Virtual Sequencer - An Alternative Methodology For Running Virtual Sequences

An alternative methodology for running virtual sequences is to use a virtual sequencer, which is a uvm_sequencer which contains the handles for the target sequencers. In this methodology, the virtual sequence is started on the virtual sequencer and it gets the handles for the target sequencers from the virtual sequencer. The limitation of this approach is that it is a fixed implementation which is very tightly coupled to the local hierarchy of an env and this adds complications with vertical reuse.