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

UVM — различия между версиями

Материал из Wiki
Перейти к: навигация, поиск
м (Содержимое страницы заменено на «{{UVM TOC}}»)
 
Строка 1: Строка 1:
 
{{UVM TOC}}
 
{{UVM TOC}}
 
===Sequences/Virtual===
 
 
A virtual sequence is a sequence which controls stimulus generation using several sequencers. Since sequences, sequencers and drivers are focused on point interfaces, almost all testbenches require a virtual sequence to co-ordinate the stimulus across different interfaces and the interactions between them. A virtual sequence is often the top level of the [https://verificationacademy.com/cookbook/Sequences/Hierarchy '''sequence hierarchy''']. A virtual sequence might also be referred to as a 'master sequence' or a 'co-ordinator sequence'.
 
 
[[Файл:Virtual_sequence_block_level.gif|761px|Virtual sequence block level.gif]]
 
 
A virtual sequence differs from a normal sequence in that its primary purpose is not to send sequence items. Instead, it generates and executes sequences on different target agents. To do this it contains handles for the target sequencers and these are used when the sequences are started.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
|
 
<source lang="verilog">
 
// 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
 
</source>
 
|}
 
 
In order for the virtual sequence to work, the sequencer handles have to be assigned. Typically, a virtual sequence is created in a test class in the run phase and the assignments to the sequencer handles within the virtual sequence object are made by the test. Once the sequencer handles are assigned, the virtual sequence is started using a null for the sequencer handle.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>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 );</pre></div></div>
 
|}
 
 
There are several variations on the virtual sequence theme. There is nothing to stop the virtual sequence being started on a sequencer and sending sequence items to that sequencer whilst also executing other sequences on their target sequencers. The virtual sequence does not have to be executed by the test, it may be executed by an environment encapsulating a number of agents. For a large testbench with many agents and several areas of concern there may be several virtual sequences running concurrently.
 
 
In addition to target sequencer handles, a virtual sequence may also contain handles to other testbench resources such as [https://verificationacademy.com/cookbook/Registers '''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|697px|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:
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>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</pre></div></div>
 
|}
 
 
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.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>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</pre></div></div>
 
|}
 
 
In a test case derived from the test base class the virtual sequence initialisation method is called before the virtual sequence is started.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>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</pre></div></div>
 
|}
 
 
The virtual sequence is derived from the virtual sequence base class and requires no initialisation code.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>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</pre></div></div>
 
|}
 
 
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|40px|Download-tarball.png]]
 
| Download a complete working example:<br />(tarball: [https://verificationacademy.com/cookbook/download?file=/w/images/7/73/Test_init_uvm.tgz 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.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>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</pre></div></div>
 
|}
 
 
* 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
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>// 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</pre></div></div>
 
|}
 
 
* 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.
 
 
{| cellspacing="0" cellpadding="10" border="0"
 
|-
 
| <div dir="ltr"><div><pre>//
 
// 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</pre></div></div>
 
|}
 
 
The Virtual Sequencer - An Alternative Methodology For Running Virtual Sequences
 
 
An alternative methodology for running virtual sequences is to use a [https://verificationacademy.com/cookbook/Sequences/VirtualSequencer '''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.
 
 
<br>
 

Текущая версия на 14:04, 25 июня 2014

Проект Методология UVM

UVM Cookbook


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

* OVM *