0% found this document useful (0 votes)
131 views

Connection Guide

Connection Guide
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
131 views

Connection Guide

Connection Guide
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 56

MENTOR GRAPHICS UVM/ OVM DOCUMENTATION

VERIFICATION METHODOLOGY ONLINE COOKBOOK

M ENTOR GRAPHICS VERIFICATION METHODOLOGY TEAM


CONTACT [email protected] HTTP://WWW.MENTOR.COM/PRODUCTS/FV/TECHPUBS

w w w.m e n t o r.c o m

W H I T E P A P E R

Connections Guide
learn about DUT Interface Connections, techniques for hookup and reuse

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com PDF generated at: Tue, 15 Mar 2011 04:59:56 PST

Table of Contents
Articles
Connect/Dut Interface SVCreationOrder Connect/SV Techniques ParameterizedTests Config/Container Config/Overview Config/Params Package Connect/Virtual Interface VirtInterfaceConfigOVMContainer VirtInterfaceConfigPkg VirtInterfacePackage VirtInterfaceFunctionCallChain BusFunctionalModels DualTop ProtocolModules Connect/AbstractConcrete Connect/AbstractConcreteOVMContainer 1 5 7 9 12 14 18 21 28 30 33 35 37 39 41 43 45

References
Article Sources and Contributors Image Sources, Licenses and Contributors 50 51

Article Licenses
License 52

Datestamp:
- This document is a snapshot of dynamic content from the Online Methodology Cookbook - Created from http://uvm.mentor.com on Mon, 14 Mar 2011 21:59:57 PDT

Connect/Dut Interface

Connect/Dut Interface

Introduction
The Device Under Test (DUT) is typically a Verilog module or a VHDL entity/architecture while the testbench is composed of SystemVerilog class objects. There are number of factors to consider in DUT Testbench (TB) connection and communication; module instance to class object communication mechanisms, configuration of the DUT, reuse, emulation, black box/white box testing and so forth. There are quite a number of different approaches and solutions for managing the different pieces of this puzzle. The challenge is to manage it in a way that addresses all these different factors.

DUT-TB Communication
The DUT and testbench belong to two different SystemVerilog instance worlds. The DUT belongs to the static instance world while the testbench belongs to the dynamic instance world. Because of this the DUT's ports can not be connected directly to the testbench class objects so a different SystemVerilog means of communication, which is virtual interfaces, is used. The DUT's ports are connected to an instance of an interface. The Testbench communicates with the DUT through the interface instance. Using a virtual interface as a reference or handle to the interface instance, the testbench can access the tasks, functions, ports, and internal variables of the SystemVerilog interface. As the interface instance is connected to the DUT pins, the testbench can monitor and control the DUT pins indirectly through the interface elements. Sometimes a virtual interface approach cannot be used. In which case there is a second or alternative approach to DUT-TB communication which is referred to as the abstract/concrete class approach that may be used. However, as long as it can be used, virtual interfaces is the preferred and recommended approach. Regardless of which approach is used instance information must be passed from the DUT to the testbench. When using virtual interfaces the location of the interface instance is supplied to the testbench so its virtual interface properties may be set to point to the interface instance. The recommended approach for passing this information to the testbench is to use either the configuration database using ovm_container or to use a package. The test class in the testbench receives the information on the location of the interface instance. After receiving this information it supplies this information to the agent transactors that actually need the information. The test class does this by placing the information in a configuration object which is provided to the appropriate agent.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Dut Interface

More detailed discussion and examples of passing virtual interface information to the testbench from the DUT and on setting virtual interfaces for DUT-TB communication is in the article on virtual interfaces.

DUT-TB Configuration
Parameter sharing between the DUT and Testbench When the DUT and/or the associated interface(s) are parameterized the parameter values are almost always used in the testbench as well. These common parameters should be defined in a single location and then shared with both the DUT and the testbench. The recommended way to do this is to place in a package the parameters that are used both in the DUT and testbench. This package is referred to as the test parameters package. The test parameter package may also be used to pass the location of the interface instance from the DUT to the testbench as explained earlier. There is an example and more detailed explanation in the article on "setting virtual interface properties in the testbench with the configuration dataset using the test parameter package"

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Dut Interface Parameterized Tests Another approach to passing parameters into the testbench is to parameterize the top level class in the testbench which is typically the test. There are a number of issues with parameterized tests that are discussed along with solutions. Note: this article is not available to publish at this time so a link is not made.

Encapsulation
A typical DUT-TB setup has a top level SystemVerilog module that is a container for both the testbench and the DUT with its associated connection and support logic (such as clock generation). This style setup is referred to as a single top

The top level module can become messy, complicated and hard to manage. When this occurs it is recommended to group items by encapsulating inside of wrapper modules. Encapsulation also provides for modularity for swapping and for reuse. Several different levels of encapsulation may be considered and are discussed below. Dual Top A level of encapsulation where two top modules are used is called dual top. One of the top modules is a DUT wrapper module that includes the DUT, interfaces, protocol modules, clock generation logic, DUT wires, registers etc. The other top module is a wrapper module which creates the testbench. When emulation is a consideration Dual top is a necessity. The DUT wrapper is the stuff that goes in the emulator. The testbench wrapper module stays running in the simulator. If the testbench is only going to be used in simulation dual top is not necessary but may however still provide a useful level of encapsulation for modularity, reuse etc. The passing of information from the DUT to the testbench is the same as described earlier. A more detailed explanation and example is in the article Dual Top.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Dut Interface Protocol Modules When emulation is a consideration another level of encapsulation called protocol modules is necessary to isolate the changes that occur in the agent and interface in moving between simulation and emulation. Protocol modules are wrapper modules that encapsulate a DUT interface, associated assertions, QVL instances (which are not allowed inside an interface), and so forth. If the testbench is only going to be used in simulation protocol modules are not necessary. They may however still provide a useful level of encapsulation for modularity, reuse etc.

Blackbox and Whitebox Testing


Blackbox testing Blackbox testing of the DUT is a method of testing that tests the functionality of the DUT at the interface or pins of the DUT without specific knowledge of or access to the DUT's internal structure. The writer of the test selects valid and invalid inputs and determines the correct response. Black box access to the DUT is provided typically by a virtual interface connection to an interface instance connected to the pins of the DUT. Whitebox testing Whitebox Testing of the DUT is a method of testing that tests the internal workings of the DUT. Access to the DUT's internal structure is required. Providing this access effects the structure of the DUT-TB communication and must be taken into account if white box testing is a requirement.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

SVCreationOrder

SVCreationOrder

SystemVerilog Instance Worlds


When generating an OVM testbench and in particular the DUT testbench communication it is helpful to understand the differences between the two different "instance worlds" of SystemVerilog and the order in which things are created.

SystemVerilog Simulation Steps or Phases


A SystemVerilog simulation consists of three steps or phases (not to be confused with OVM phases): compilation, elaboration and run-time.

Compilation is where the code is parsed and analyzed. Elaboration is the process of binding together the components of the design. Elaboration includes among other things creating instantiations, computing parameter values, resolving hierarchical names and connecting nets. Often when referring to the compilation and elaboration phases they are not distinguished but are generally referred to as compilation. In other words a "compile time error" may refer to an error at any time prior to run-time. Run-time is what is thought of as the simulation actually executing or running with processes executing, simulation time advances etc. This step or phase is often referred to as "simulation". Phrases such as "prior to simulation" or "before simulation" are often used to refer to the compilation and elaboration steps that happen before run-time or "during simulation" to refer to the run-time step or phase.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

SVCreationOrder

Static Instance World


Many SystemVerilog component instances are created during elaboration before the simulation begins. Once simulation begins instances of these components are neither created nor destroyed but remain throughout the simulation. We refer to this as the static instance world. Components that belong to this world are module instances, interface instance, checker instances, primitive instances and the top level of the design hierarchy.

Dynamic Instance World


Component instances that may be created and destroyed during simulation (the SystemVerilog run phase) belong to what is referred to as the dynamic instance world. Components that belong to this world are classes. There is an exception to this however. Class methods and properties that are declared as static are created prior to runtime. They are, however, created after the component instances of the static world. This exception is often used to create and initialize class properties (including class objects) before simulation. This is referred to as static initialization. The OVM factory is an example of an object that is statically initialized.

Order of Creation
The components of the two instance worlds are created in this order: During Elaboration: 1. Component instances of the static world 2. Static methods and static properties of classes During run-time: 1. Class instances

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/SV Techniques

Connect/SV Techniques

Introduction and Recommendations


SystemVerilog provides in general four different means of communication or connection between instances: ports, pointers, Verilog hierarchical paths, and shared variables. For class based testbenches ports may not be used. Hierarchical paths are not recommended. Pointers are the common means used. Shared variables may be used in limited areas.

Ports
Ports are connections between members of the Static Instance World such as module and interface instances. Therefore they may not be used in classes which are part of the Dynamic Instance World. OVM provides a notion of ports such as tlm_put_port etc. These are not SystemVerilog ports but rather are wrapper classes around pointers. Hence OVM ports is a pointer based communication scheme dressed up like ports to look familiar to Verilog and VHDL engineers.

Handles
A class handle is what points to a class object (instance). It is called a handle to differentiate it because it is what is considered a safe-pointer because of the restrictive rules of use compared to pointers in other languages such as C. A virtual interface is a variable that represents an interface instance. It may be thought of as a handle to an interface instance.

Verilog Hierarchical Path


Every identifier in SystemVerilog has a unique hierarchical path name. Any named SystemVerilog object can be referenced by this hierarchical path name from any other SystemVerilog object. While this is powerful as it allows for communication from anywhere to anywhere it is in general not desirable to use this technique. In modeling it is an absolute "no-no" because a connection is implicit in a hierarchical reference. This implicit connection won't be synthesized or created when the model is implemented, thus breaking the design. In verification it is to be avoided because portability and reuse are limited when it is used.

Shared Variables
Shared variables are sometimes referred to as global variables although generally speaking they are not truly global in scope. A shared variable is a variable declared in a scope that may be referenced by other scopes. In shared variable behavior the variable may be read and or written in these other scopes. The two most common examples of shared variables used in testbenches are variables declared in packages and static property declarations of classes. In packages a variable may be declared such as an int or virtual interface. These variables may be referenced (i.e. both read and written) within other scopes such as classes or modules either by a fully resolved name ( package_name::variable_name ) or by import. Static property declarations of classes may be referenced by a fully resolved name (class_name::static_property_name). Often a static method of a class may be provided for accessing the static property. It is recommended that shared variables only be used for initialization or status type communication where there is a

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/SV Techniques clear relationship between when information is written and read. Shared variables are not recommended for the transfer of data, such as transactions, between objects. Because of the nature of shared variables race conditions are inherent and so care must be taken or races will occur.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

ParameterizedTests

ParameterizedTests

Introduction
When configuring a test environment, there are two situations where SystemVerilog parameters are the only option available - type parameters and parameters used to specify bit vector sizes. Due to the nature of SystemVerilog parameters, the latest time that these values can be set is at elaboration time, which is usually at the point where you invoke the simulator (See regression test performance below).

Registering Parameterized classes with the String-based Factory


Parameterized classes use the `ovm_component_param_utils and `ovm_object_param_utils macros to register with the factory. There are actually two factories, however - one string-based and one type-based. The param_utils macros only register with the type-based factory. Occasionally, you might want to use the string-based factory to create a component or object. The most common case where the string-based factory is used is during the call to run_test(). run_test() uses either its string argument or the string value from the OVM_TESTNAME plusarg to request a component from the string-based factory. Since a parameterized component does not register with the string-based factory by default, you will need to create a string-based registration for your top-level test classes so that they can be instantiated by run_test(). To accomplish this, you need to manually implement the actions that the param_utils macro performs. For example, given a parameterized test class named alu_basic_test #(DATA_WIDTH), the macro call `ovm_component_param_utils(alu_basic_test #(DATA_WIDTH)) would expand to:
typedef ovm_component_registry #(alu_basic_test #(DATA_WIDTH)) type_id; static function type_id get_type(); return type_id::get(); endfunction virtual function ovm_object_wrapper get_object_type(); return type_id::get(); endfunction

The typedef in the code above creates a specialization of the ovm_component_registry type, but that type takes two parameter arguments - the first is the type being registered (alu_basic_test #(DATA_WIDTH) in this case) with the type-based factory, and the second is the string name that will be used to uniquely identify that type in the string-based registry. Since the param_utils macro does not provide a value for the second parameter, it defaults to the null string and no string-based registration is performed. To create a string-based registration, you need to provide a string for the second parameter argument that will be unique for each specialization of the test class. You can rewrite the typedef to look like:
typedef ovm_component_registry #(alu_basic_test #(DATA_WIDTH), "basic_test1") type_id;

In addition, you would need to declare a "dummy"specialization of the parameterized test class so that the string name specified above is tied to the particular parameter values.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

ParameterizedTests
module testbench #(DATA_WIDTH); alu_rtl #(DATA_WIDTH) dut ( /* port connections */ ); // Associate the string "basic_test1" with the value of DATA_WIDTH typedef alu_basic_test #(DATA_WIDTH) dummy; initial begin run_test("basic_test1"); endmodule

10

Note:instead of a name like "basic_test1", you could use the macro described below to generate a string name like "basic_test_#(8)" with the actual parameter values as part of the string.

Regression Test Performance with Parameters


In order to increase simulation performance, QuestaSim performs some elaboration tasks, including specifying top-level parameters, in a separate optimization step via the vopt tool. This tool takes top-level parameters and "bakes" them into the design in order to take full advantage of the design structure for optimization. Unfortunately, this means that if you want to change the values of these parameters, it requires a re-optimization of the entire design before invocation of the simulator. This could have a significant impact on regression test performance where many test runs are made with different parameter values. To avoid this, you can tell vopt that certain parameters should be considered "floating", and will be specified later, by using the command-line options +floatparameters (for Verilog) and +floatgenerics (for VHDL). Once the parameters have been specified as floating, you can use the -g option in vsim to set the parameter value. Subsequent changes to the parameter value only require re-invocation of vsim with a new value and do not require a re-optimization of the design. The trade-off, however, is that these parameter values will not be used to help optimize the design for the best run-time performance. So, it is recommended that you use this technique sparingly, only when necessary (e.g. when the time cost of optimization is a measurable percentage of the total simulation run time). If necessary, you can separately pre-optimize the design with several parameter values, then select the optimization to use at run time.

Maintaining Consistency in Parameter Lists


Many SystemVerilog parameters can naturally be grouped together in a conceptual "parameter list". These parameters tend to be declared together and are used in many places in the test environment. Any change to a parameter list (e.g. adding a new parameter) usually requires careful editing of many different classes in many different files, and is an error-prone process. An optional technique that can be employed is to create a set of macros that can reduce the chance of errors and enforce consistency.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

ParameterizedTests

11

Declaration

`define params_declare #(int BUS_WIDTH = 16, int ADDR_WIDTH = 8)

Instantiation / Mapping `define params_map #(.BUS_WIDTH(BUS_WIDTH), .ADDR_WIDTH(ADDR_WIDTH) ) String Value `define params_string $sformat("#(%1d, %1d)", BUS_WIDTH, ADDR_WIDTH)

These macros keep with the reuse philosophy of minimizing areas of change. By using the macros, there is one, well-defined place to make changes in parameter lists.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Container

12

Config/Container

The ovm_container class


OVM's configuration mechanism limits the type of data that can be stored to integral, string, and object (derived from ovm_object). For any other type, such as virtual interfaces, associative arrays, queues, mailboxes, the only way to get into the configuration database is to be a data member of a wrapper class that derives from ovm_object. Rather than creating several different wrapper classes for each type of data to be stored, a single parameterized, general-purpose class, ovm_container has been created by Mentor that wraps a single piece of data. Also, for convenience, the class has static methods to add the object to, and retrieve the object from the configuration database. The ovm_container class source code is available here [add link]. Here is the class definition:
class ovm_container #( type T = int ) extends ovm_object; typedef ovm_container #( T ) this_t; // // Variable: t, the data being wrapped // T t; // See below for the source to these functions static function void set_value_in_global_config( string config_id , T t ); static function T get_value_from_config( ovm_component c , string config_id ); endclass

Putting the Object Into the OVMConfiguration Database


You can use a static function to hide the details of creating a configuration object, placing the value into the object, and putting the object into the database:
static function void set_value_in_global_config( string config_id , T t ); this_t container = new(); container.t = t; set_config_object("*" , config_id , container , /* Do not make a clone */ 0 ); endfunction

This function Creates the ovm_container object Populates it with a piece of data Stores the container object into the configuration database with global scope - i.e. any component can retrieve the object from the database. It also does not clone the object.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Container

13

Getting an object out of the database


static function T get_value_from_config( ovm_component c , string config_id ); ovm_object o; ovm_container #(T) tmp; assert (c.get_config_object (config_id, o)) else c.ovm_report_error ( s_no_container_config_id , $sformatf( "component has no uvm_container associated with %s" , config_id ) ); assert ($cast (tmp, o)) else c.ovm_report_error ( s_container_config_type_error_id , $sformatf( "object associated with %s is not of the correct type" , config_id) );

return tmp.t; endfunction

This static method gets the ovm_container associated with the config_id using the local config in component c. If set_value_in_global_config has been used then the component c is in fact irrelevant since the value will be the same anywhere in the OVM component hierarchy. But passing a value for the component allows the theoretical possibiliy that different values are present at different places in the OVM hierarchy for the same config_id.

Example use
Here is a link to an example use of ovm_container to convey a virtual interface pointer to the testbench from the top module. In this example the top module wraps a virtual interface and places the ovm_container object in the configuration database. Inside the testbench it fetches the ovm_container object from the database and extracts the virtual interface from it.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Overview

14

Config/Overview

Introduction
One of the key tenets of designing reusable testbenches is to use configuration parameters whenever possible. Parameterization permits scalability as well as flexibility to adapt a testbench to changing circumstances. This article uses the generic term "parameter" to mean any value that can be used to establish a specific configuration for a testbench, as opposed to the term "SystemVerilog parameter", which refers to the syntactic element. In a testbench, there are any number of values that you might normally write as literals - values such as for-loop limits, string names, randomization weights and other constraint expression values, coverage bin values. These values can be represented by SystemVerilog variables, which can be set (and changed) at runtime, or SystemVerilog parameters, which must be set at compile time. Because of the flexibility they offer, variables should be the preferred way to set configuration parameters. There is a common situation, however, where SystemVerilog parameters are the only option available - bit size parameters. That situation is discussed in more detail in the Parameterized Tests document.

Types of configuration parameters


Any point in your testbench where you would place a literal value - most commonly integral values, but also occasionally strings - is a candidate for a configuration parameter. Examples include: for-loop limits array sizes values in randomization constraints values in SVA properties and sequences

Configuration Objects
Configuration objects are an efficient, reusable mechanism for organizing configuration parameters. They are described in detail in the whitepaper OVM Configuration and Virtual Interfaces. In a typical testbench, there can be several configuration objects, each tied to a component. They are created as a subclass of ovm_object and group together all related configuration parameters for a given branch of the test structural hierarchy. There can also be an additional, single configuration object that holds global configuration parameters. The OVM configuration database takes care of the scope and storage of the object. For convenience, a configuration object can have a static method that gets the object out of the database. Here is an example configuration object.
// configuration container class class wb_config extends ovm_object; `ovm_object_utils( wb_config ); // Configuration Parameters virtual wishbone_bus_syscon_if v_wb_bus_if; // virtual wb_bus_if int int int int bit bit m_wb_id; m_wb_master_id; m_mac_id; unsigned m_mac_wb_base_addr; [47:0] m_mac_eth_addr; [47:0] m_tb_eth_addr; // // // // // // Wishbone bus ID Wishbone bus master id for wishone agent id of MAC WB master Wishbone base address of MAC Ethernet address of MAC Ethernet address of testbench for sends/receives

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Overview
int int int int m_mem_slave_size; unsigned m_s_mem_wb_base_addr; m_mem_slave_wb_id; m_wb_verbosity; // // // // Size of slave memory in bytes base address of wb memory for MAC frame buffers Wishbone ID of slave memory verbosity level for wishbone messages

15

function new( string name = "" ); super.new( name ); endfunction // Convenience function that first gets the object out of the OVM database // and reports an error if the object is not present in the database, then // casts it to the correct config object type, again checking for errors static function wb_config get_config( ovm_component c ); ovm_object o; wb_config t; if( !c.get_config_object( "wb_config" , o , 0 ) ) begin c.ovm_report_error( "no config error ", "this component has no config associated with id wb_config"); return null; end if( !$cast( t , o ) ) begin c.ovm_report_error( "config type error" , $sformatf("the object associated with id %s is of type $s which is not the required type %s" , "wb_config" , o.get_type_name() , type_name ) ); return null; end return t; endfunction endclass

Using a Configuration Object


Any component that requires configuration should perform the following steps: get its own configuration create its own internal structure and behavior based on its configuration configure its children The test component, as the top-level component gets its configuration values from either a "params package" or from the OVM configuration database (e.g. for a virtual interface handle). It then sets test-specific configuration parameters for components in the environment.
class test_mac_simple_duplex extends ovm_test; ... wb_config wb_config_0; ... // config object for WISHBONE BUS

function void set_wishbone_config_params(); //set configuration info // NOTE The MAC is WISHBONE slave 0, mem_slave_0 is WISHBONE slave 1 // MAC is WISHBONE master 0, wb_master is WISHBONE master 1 wb_config_0 = new(); wb_config_0.v_wb_bus_if = ovm_container #(virtual wishbone_bus_syscon_if)::get_value_from_config(this, "WB_BUS_IF"); wb_config_0.m_wb_id = 0; // WISHBONE 0 wb_config_0.m_mac_id = 0; // the ID of the MAC master wb_config_0.m_mac_eth_addr = 48'h000BC0D0EF00; wb_config_0.m_mac_wb_base_addr = 32'h00100000; wb_config_0.m_wb_master_id = 1; // the ID of the wb master wb_config_0.m_tb_eth_addr = 48'h000203040506; wb_config_0.m_s_mem_wb_base_addr = 32'h00000000; wb_config_0.m_mem_slave_size = 32'h00100000; // 1 Mbyte wb_config_0.m_mem_slave_wb_id = 0; // the ID of slave mem

// virtual interface

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Overview
wb_config_0.m_wb_verbosity = 350; set_config_object("*","wb_config",wb_config_0, 0); // put in config endfunction ... function void build(); super.build(); set_wishbone_config_params(); ... endfunction ... endclass

16

The components that use the configuration object data get access via the static helper function. In this example, the drivers get the virtual interface handle, ID, and verbosity from the object.
class wb_m_bus_driver extends ovm_driver ... #(wb_txn, wb_txn);

virtual wishbone_bus_syscon_if m_v_wb_bus_if; bit [2:0] m_id; // Wishbone bus master ID wb_config m_config; ... function void build(); super.build(); m_config = wb_config::get_config(this); // get config object m_id = m_config.m_wb_master_id; ... endfunction function void connect(); super.connect(); m_v_wb_bus_if = m_config.v_wb_bus_if; // set local virtual if property endfunction function void end_of_elaboration(); int wb_verbosity; set_report_verbosity_level(m_config.m_wb_verbosity); _global_reporter.set_report_verbosity_level(wb_verbosity); endfunction ... endclass

Configuring sequences Asequence can use configuration data, but it must get the data from a component. Usually this should be done via its sequencer. Here, the MAC simple duplex sequence gets the configuration data from its sequencer and uses parameters to influence the data sent from the sequence:
class mac_simple_duplex_seq extends wb_mem_map_access_base_seq; ... wb_config m_config; task body; ... m_config = wb_config::get_config(m_sequencer); ... init_mac(); ... endtask // init the MAC's registers

// get config object

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Overview
virtual task init_mac(); // init the MAC ... //clear mode register in MAC wb_write_register("mode_reg", 0); //write MAC_ADDR0 register wb_write_register("mac_addr0_reg", m_config.m_mac_eth_addr[31:00]); //write MAC_ADDR1 register wb_write_register("mac_addr1_reg", m_config.m_mac_eth_addr[47:32]); ... endtask ... endclass

17

Configuring DUT connections


Setting up DUT-to-Testbench connections are one kind of configuration activity that is always necessary. It involves activity on the DUT side, most commonly using protocol modules and an ovm_container. On the testbench side the test component gets the virtual interface handle from the OVM config database and applies it to appropriate configuration objects:
class test_mac_simple_duplex extends ovm_test; ... function void set_wishbone_config_params(); wb_config_0 = new(); // Get the virtual interface handle that was set in the top module or protocol module wb_config_0.v_wb_bus_if = ovm_container #(virtual wishbone_bus_syscon_if)::get_value_from_config(this, "WB_BUS_IF"); ... set_config_object("*","wb_config",wb_config_0, 0); // put in config endfunction ... endclass

// virtual interface

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Params Package

18

Config/Params Package
When a DUT and/or interface is parameterized the parameter values are almost always used in the testbench as well. These parameters should not be specified with direct literal values in your instantiations. Instead define named parameter values in a package and use the named values in both the DUT side as well as the testbench side of the design. This helps avoid mistakes where a change is made to one side but not to the other. Or, if a test configuration parameter is some function of a DUT parameter, there is a chance that a miscalculation may when making a change. Note that this package is not a place for all test parameters. If you have test-specific parameters that are not used by the DUT, those values should be set directly in the test. The DUT parameters package should be used only for parameters that are shared between the DUT and the test.

Example use of a Parameter Package


In the WISHBONE bus based example below there are two WISHBONE bus devices. A slave memory and an Ethernet Media Access Controller (MAC). Parameters are placed in a package (test_params_pkg) and then used in instantiating the WISHBONE devices in the top level module and inside the test class of the testbench.

The test parameter package is shown below:


package test_params_pkg; import ovm_pkg::*; // WISHBONE general slave parameters parameter slave_addr_space_sz = 32'h00100000;

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Params Package

19

// WISHBONE slave memory parameters parameter mem_slave_size = 18; // 2**slave_mem_size = size in words(32 bits) of wb slave memory parameter mem_slave_wb_id = 0; // WISHBONE bus slave id of wb slave memory // MAC WISHBONE parameters parameter mac_m_wb_id = 0; parameter mac_slave_wb_id = 1; endpackage // WISHBONE bus master id of MAC // WISHBONE bus slave id of MAC

The parameter values (mem_slave_size, mem_slave_wb_id) usage in the top module to instantiate the WISHBONE bus slave memory module is shown below. Note the import of the test_params_pkg in the top_mac module:
module top_mac; ... import test_params_pkg::*; // WISHBONE interface instance // Supports up to 8 masters and up to 8 slaves wishbone_bus_syscon_if wb_bus_if(); //----------------------------------// WISHBONE 0, slave 0: 000000 - 0fffff // this is 1 Mbytes of memory wb_slave_mem #(mem_slave_size) wb_s_0 ( // inputs .clk ( wb_bus_if.clk ), .rst ( wb_bus_if.rst ), .adr ( wb_bus_if.s_addr ), .din ( wb_bus_if.s_wdata ), .cyc ( wb_bus_if.s_cyc ), .stb ( wb_bus_if.s_stb[mem_slave_wb_id] .sel ( wb_bus_if.s_sel[3:0] ), .we ( wb_bus_if.s_we ), // outputs .dout( wb_bus_if.s_rdata[mem_slave_wb_id] .ack ( wb_bus_if.s_ack[mem_slave_wb_id] .err ( wb_bus_if.s_err[mem_slave_wb_id] .rty ( wb_bus_if.s_rty[mem_slave_wb_id] ); ... endmodule

),

), ), ), )

Parameter usage inside the test class of the testbench to set the configuration object values for the WISHBONE bus slave memory is shown below. Note that instead of using the numeric literal of 32'h00100000 for the address value, the code uses an expression involving a DUT parameter (mem_slave_size).
package tests_pkg; ... import test_params_pkg::*; ... `include "test_mac_simple_duplex.svh" endpackage //----------------------------------------------------------------class test_mac_simple_duplex extends ovm_test; ... wb_config wb_config_0; // config object for WISHBONE BUS ... function void set_wishbone_config_params(); //set configuration info wb_config_0 = new(); wb_config_0.m_s_mem_wb_base_addr = mem_slave_wb_id * slave_addr_space_sz; // base address of slave mem wb_config_0.m_mem_slave_size = 2**(mem_slave_size+2); // default is 1 Mbyte wb_config_0.m_mem_slave_wb_id = mem_slave_wb_id; // WISHBONE bus slave id of slave mem

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Config/Params Package
... endfunction ... endclass

20

Multiple Instances
When you have multiple instances of parameter sets you can either create a naming convention for your parameters or you can use a prameterized class-based approach to organize your parameter sets on a per-instance basis. Create a parameterized class which specifies the parameters and their default values. Then for each instance set the parameter values by creating a specialization of the parameterized class using a typedef.
package test_params_pkg; import ovm_pkg::*; // WISHBONE general slave parameters parameter slave_addr_space_sz = 32'h00100000; // WISHBONE slave memory parameters class WISHBONE_SLAVE #(int mem_slave_size = 18, int mem_slave_wb_id = 0); endclass // Specializations for each slave memory instance typedef WISHBONE_SLAVE #(18, 0) WISHBONE_SLAVE_0; typedef WISHBONE_SLAVE #(18, 1) WISHBONE_SLAVE_1; // MAC WISHBONE parameters parameter mac_m_wb_id = 0; parameter mac_slave_wb_id = 2; endpackage // WISHBONE bus master id of MAC // WISHBONE bus slave id of MAC

To access or use the parameters, such as mem_slave_size or mem_slave_wb_id, specified in the specializations WISHBONE_SLAVE_0 or WISHBONE_SLAVE_1 in the above code, use the following syntax name_of_specialization::parameter_name as illustrated below.
module top_mac; ... import test_params_pkg::*; // WISHBONE interface instance // Supports up to 8 masters and up to 8 slaves wishbone_bus_syscon_if wb_bus_if(); //----------------------------------// WISHBONE 0, slave 0: 000000 - 0fffff // this is 1 Mbytes of memory wb_slave_mem #(WISHBONE_SLAVE_0::mem_slave_size) wb_s_0 ( // inputs .clk ( wb_bus_if.clk ), .rst ( wb_bus_if.rst ), .adr ( wb_bus_if.s_addr ), .din ( wb_bus_if.s_wdata ), .cyc ( wb_bus_if.s_cyc ), .stb ( wb_bus_if.s_stb [WISHBONE_SLAVE_0::mem_slave_wb_id] .sel ( wb_bus_if.s_sel[3:0] ), .we ( wb_bus_if.s_we ), // outputs .dout( wb_bus_if.s_rdata[WISHBONE_SLAVE_0::mem_slave_wb_id] .ack ( wb_bus_if.s_ack [WISHBONE_SLAVE_0::mem_slave_wb_id] .err ( wb_bus_if.s_err [WISHBONE_SLAVE_0::mem_slave_wb_id] .rty ( wb_bus_if.s_rty [WISHBONE_SLAVE_0::mem_slave_wb_id] ); ... endmodule

),

), ), ), )

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface

21

Connect/Virtual Interface

Virtual Interaces
A virtual interface is a dynamic variable that contains a reference to a static interface instance. For all intents and purposes, it can be thought of as a handle or reference to a SystemVerilog interface instance. Note that the use of the term "virtual" here is not the in the same sense as is conventionally used in object oriented programming but rather it is what the IEEE 1800 committee chose to call these references.

Virtual Interface Use


Virtual interfaces are used because a class based test bench may not have ports and hence cannot directly connect to a Verilog or VHDL DUT. Instead a SystemVerilog interface instance is connected to the ports of the DUT and the testbench communicates with the DUT through the interface instance. Using a virtual interface as a reference or handle to the interface instance, the testbench can access the tasks, functions, ports, and internal variables of the SystemVerilog interface. As the interface instance is connected to the DUT pins, then the testbench can monitor and control the DUT pins indirectly through the interface elements. The DUT may have one or more interfaces connected to its ports. If a DUT has multiple logical interfaces (different buses etc.) it is recommended to have a separate interface for each logical interface. Consideration should also be given to encapsulate the interface instances inside a protocol module.

An example DUT (WISHBONE bus slave memory in diagram) has the following ports:
module wb_slave_mem #(parameter mem_size = 13) (clk, rst, adr, din, dout, cyc, stb, sel, we, ack, err, rty); input input output input clk, rst; [31:0] adr, din; [31:0] dout; cyc, stb;

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface
input input output ... endmodule [3:0] sel; we; ack, err, rty;

22

In the WISHBONE bus environment there are a number of parameters that are shared between the DUTand the testbench. They are defined in a test parameters package ( test_params_pkg) shown below. Of interest here are the mem_slave_size and mem_slave_wb_id parameters. The mem_slave_size is used to set the size of the slave memory device. The WISHBONE bus has both masters and slaves with each having master and slave ids respectively. The mem_slave_wb_id is used to sets the WISHBONE slave id of the slave memory
package test_params_pkg; import ovm_pkg::*; // WISHBONE general slave parameters parameter slave_addr_space_sz = 32'h00100000; // WISHBONE slave memory parameters parameter mem_slave_size = 18; // 2**slave_mem_size = size in words(32 bits) of wb slave memory parameter mem_slave_wb_id = 0; // WISHBONE bus slave id of wb slave memory ... endpackage

A WISHBONE bus interconnect interface to connect to this DUT is below. This interconnect supports up to 8 masters and 8 slaves. Not shown here is the arbitration , clock , reset and slave decode logic. Only shown is the interconnect variables. A link to the full source is further down in this article.
// Wishbone bus system interconnect (syscon) // for multiple master, multiple slave bus // max 8 masters and 8 slaves interface wishbone_bus_syscon_if #(int num_masters = 8, int num_slaves = 8, int data_width = 32, int addr_width = 32) (); // WISHBONE common signals bit clk; bit rst; bit [7:0] irq; // WISHBONE master outputs logic [data_width-1:0] m_wdata[num_masters]; logic [addr_width-1:0] m_addr [num_masters]; bit m_cyc [num_masters]; bit m_lock[num_masters]; bit m_stb [num_masters]; bit m_we [num_masters]; bit m_ack [num_masters]; bit [7:0] m_sel[num_masters]; // WISHBONE master inputs bit m_err; bit m_rty; logic [data_width-1:0] m_rdata; // WISHBONE slave inputs logic [data_width-1:0] s_wdata; logic [addr_width-1:0] s_addr; bit [7:0] s_sel; bit s_cyc; bit s_stb[num_slaves]; //only input not shared since it is the select bit s_we; // WISHBONE slave outputs logic [data_width-1:0] s_rdata[num_slaves]; bit s_err[num_slaves]; bit s_rty[num_slaves];

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface
bit s_ack[num_slaves]; ... endinterface

23

To connect the interface to the DUT a hierarchical connection from the pins of the DUT to the variables in the interfaces is made as shown below. Note that the mem_slave_wb_id parameter from the test_params_pkg is used as a slave "slot id" to connect the slave memory to the correct signals in the interface.
module top_mac; import ovm_pkg::*; import tests_pkg::*; import ovm_container_pkg::*; import test_params_pkg::*; // WISHBONE interface instance // Supports up to 8 masters and up to 8 slaves wishbone_bus_syscon_if wb_bus_if(); //----------------------------------// WISHBONE 0, slave 0: 000000 - 0fffff // this is 1 Mbytes of memory wb_slave_mem #(18) wb_s_0 ( wb_slave_mem #(mem_slave_size) wb_s_0 ( // inputs .clk ( wb_bus_if.clk ), .rst ( wb_bus_if.rst ), .adr ( wb_bus_if.s_addr ), .din ( wb_bus_if.s_wdata ), .cyc ( wb_bus_if.s_cyc ), .stb ( wb_bus_if.s_stb[mem_slave_wb_id] .sel ( wb_bus_if.s_sel[3:0] ), .we ( wb_bus_if.s_we ), // outputs .dout( wb_bus_if.s_rdata[mem_slave_wb_id] .ack ( wb_bus_if.s_ack[mem_slave_wb_id] .err ( wb_bus_if.s_err[mem_slave_wb_id] .rty ( wb_bus_if.s_rty[mem_slave_wb_id] ); ... endmodule

),

), ), ), )

In the testbench access to the DUT is typically required in transactors such as drivers and monitors that reside in an agent. Assume in the code example below that the virtual interface property m_v_wb_bus_if points to the instance of the wishbone_bus_syscon_if connected to the DUT (the next section discusses setting the virtual interface property). Then in a WISHBONE bus driver the code might look like this. Note the use of the virtual interface property to access the interface variables:
class wb_m_bus_driver extends ovm_driver #(wb_txn, wb_txn); ... ovm_analysis_port #(wb_txn) wb_drv_ap; virtual wishbone_bus_syscon_if m_v_wb_bus_if; // Virtual Interface bit [2:0] m_id; // Wishbone bus master ID wb_config m_config; ... //WRITE 1 or more write cycles virtual task wb_write_cycle(ref wb_txn req_txn); wb_txn orig_req_txn; $cast(orig_req_txn, req_txn.clone()); //save off copy of original req transaction for(int i = 0; i<req_txn.count; i++) begin if(m_v_wb_bus_if.rst) begin reset(); // clear everything return; //exit if reset is asserted end m_v_wb_bus_if.m_wdata[m_id] = req_txn.data[i]; m_v_wb_bus_if.m_addr[m_id] = req_txn.adr; m_v_wb_bus_if.m_we[m_id] = 1; //write

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface
m_v_wb_bus_if.m_sel[m_id] = req_txn.byte_sel; m_v_wb_bus_if.m_cyc[m_id] = 1; m_v_wb_bus_if.m_stb[m_id] = 1; @ (posedge m_v_wb_bus_if.clk) while (!(m_v_wb_bus_if.m_ack[m_id] & m_v_wb_bus_if.gnt[m_id])) @ (posedge m_v_wb_bus_if.clk); req_txn.adr = req_txn.adr + 4; // byte address so increment by 4 for word addr end `ovm_info($sformatf("WB_M_DRVR_%0d",m_id), $sformatf("req_txn: %s",orig_req_txn.convert2string()), 351 ) wb_drv_ap.write(orig_req_txn); //broadcast orignal transaction m_v_wb_bus_if.m_cyc[m_id] = 0; m_v_wb_bus_if.m_stb[m_id] = 0; endtask ... endclass

24

Setting Virtual Interface Properties in the Testbench


There are two separate and distinct activities that occur in order to set a virtual interface property in the transactors (driver, monitor etc.) of the testbench to point to the interface instance connected to the DUT. First the DUT provides connection information to the testbench. For the purposes of modularity and reuse this should be done in such a way that the DUT knows nothing about the testbench. The DUT should provide the necessary connection information to the testbench in a manner that does not require additional connections or modifications to the DUT or its associated logic. Second the connection information must be distributed in the testbench to the appropriate agents with their transactors. Setting and distribution of configuration and connection information in the testbench ideally should be done in a single place the test class. The test class gets the information from the DUT and distributes it to the appropriate testbench components. The test class should be the only testbench component that has hard coded information about the DUT, and its interfaces. When DUT configuration changes occur then changes to the testbench can be limited to the test. This facilitates scalability and reusability for the rest of the testbench.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface

25

The questions may be asked: "Why not have the agents get the connection information directly from the DUT? Why have it distributed by the test? Doesnt that seem more complicated and extra work?" The approach of the agents getting the information direct effectively hard codes information in the agents or transactors about the DUT and reduces scalability and reuse. If a change is made in the DUT configuration it is likely that change would be required in the agent. One may think of the DUT connection and configuration information as a "pool" of information provided by the DUT to the testbench. In the recommended approach the test class gets information out of this pool and distributes it to the correct agents. If the information pool changes then appropriate changes are made in one location - the test. The agents are not affected because they get their information in the same manner from the test. If instead the agents each get information directly from the pool they need to know which information to fetch. If the information pool changes then changes would need to be made in the agents. There are two approaches to passing the location of the interface instance to the test class. The recommended approach is the first listed here which is using ovm_container.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface

26

Bus Functional Models (BFM)


Sometimes a the DUT connection is not directly to the ports of the DUT but rather is made through a BFM. Most often the BFM will have tasks for generating DUT transactions. DUT-TB communication with a BFMis discussed here.

Multiple Interface Instance Considerations


When you have multiple instances of an interface type, each instance needs to have some sort of unique ID so that the transactors may know which interface to be connected to. This may be done by simply appending a numeric value to the end of the instance name if the interface instances are declared in an array or at the same level. Often it is more convenient when there are multitple DUT instance and hence multiple interface instances to wrap the dut and its interfaces in a wrapper module. The wrapper module may be parameterized to form unique id's internal to the wrapper for multiple instances. In the wishbone wrapper module code shown above a WB_ID parameter is used to uniquefy the wishbone bus interface instance and facilitate the placement in the configuration database using ovm_container. The code below shows the top level module with wrapper module instances:
module top_mac; wb_bus_wrapper #(0) wb_bus_0(); wb_bus_wrapper #(1) wb_bus_1(); endmodule

In the test class the appropriate virtual interface is extracted and assigned to the appropriate wishbone configuration object. Wishbone environement 0 is then connected to wishbone bus wrapper 0 an
class test_mac_simple_duplex extends ovm_test; ... mac_env env_0; //environment for WISHBONE bus 0 mac_env env_1; //environment for WISHBONE bus 1 wb_config wb_config_0; // config object for WISHBONE BUS 0 wb_config wb_config_1; // config object for WISHBONE BUS 1 ... function void set_wishbone_config_params(); //set configuration info for WISHBONE 0 wb_config_0 = new(); wb_config_0.m_wb_id = 0; // WISHBONE 0 // Get the virtual interface handle that was set in the top module or protocol module wb_config_0.m_v_wb_bus_bfm_if = ovm_container #(virtual wishbone_bus_bfm_if)::get_value_from_config(this, "WB_BFM_IF_0"); set_config_object("env_0*","wb_config",wb_config_0, 0); // put in config ... wb_config_1 = new(); wb_config_1.m_wb_id = 1; // WISHBONE 1 wb_config_1.m_v_wb_bus_bfm_if = ovm_container #(virtual wishbone_bus_bfm_if)::get_value_from_config(this, "WB_BFM_IF_1"); set_config_object("env_1*","wb_config",wb_config_1, 0); // put in config ... endfunction ... endclass

// virtual interface

// virtual interface

If generate statements are used, the virtual interface assignment must also be done inside the generate block, because variable indexing of generate block instances is not allowed.
genvar i; for (i = 0; i<NUM_INTERFACES; i++) begin : gen // alu_if instance alu_if a_if(.clk(clk)); // DUT instance

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/Virtual Interface
alu_rtl alu ( .val1(a_if.val1), .val2(a_if.val2), .mode(a_if.mode), .clk(a_if.clk), .valid_i(a_if.valid_i), .valid_o(a_if.valid_o), .result(a_if.result) ); initial begin // Each virtual interface must have a unique name, so use $sformatf ovm_container #(virtual alu_if)::set_value_in_global_config($sformatf("ALU_IF_%0d",i),a_if); end end

27

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceConfigOVMContainer

28

VirtInterfaceConfigOVMContainer

Setting Virtual Interface Properties in the Testbench with the Configuration Database using ovm_container
This is the recommended approach in assigning the actual interface reference to the virtual interface handles inside the testbench. This approach in general has three steps. 1. Use ovm_container as a means to put a virtual interface, that points to a specific interface instance, into the configuration database. 2. The test class fetches the virtual interface from the configuration database and places it in a configuration object that is made available for the particular components (agents, drivers, monitors etc.) that communicate with the DUT through that particular interface. 3. The component that actually accesses the DUT via the virtual interface sets its virtual interface property from the virtual interface in the supplied configuration object. There is a discussion here as to why one would take the approach of the test class fetching and distributing the information to the agents and transactors instead of having the transactors or agents fetch the data directly. This approach supports scalability and reuse: Since the transactor receives the interface instance information from the configuration object it is not affected by changes in the DUT configuration. If you are using emulation, this method works with protocol modules in the "Dual Top" methodology. Placing a Virtual Interface into the Configuration Database using ovm_container Since the configuration database can only store integral values, strings, or objects that derive from ovm_object, in order to store a virtual interface handle in the database, a container, or "wrapper" object that holds the handle must be created and placed in the configuration database. Mentor has created a general-purpose container object named ovm_container that this is available to download from the OVMworld contributions site (link here??). The example code below shows calling the ovm_container convenience method set_value_in_global_config() which creates a configuration object, puts a virtual interface property inside the object and then places the object in the configuration database.
// Top level module for a wishbone system with bus connection // multiple masters and slaves module top_mac; ... // WISHBONE interface instance // Supports up to 8 masters and up to 8 slaves wishbone_bus_syscon_if wb_bus_if(); ... initial begin //set interfaces in config space ovm_container #(virtual wishbone_bus_syscon_if)::set_value_in_global_config("WB_BUS_IF", wb_bus_if); run_test("test_mac_simple_duplex"); end endmodule // create and start running test

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceConfigOVMContainer Making the Virtual Interface Available in the Testbench The test class creates a configuration object which has a virtual interface property. It then assigns this property by calling the ovm_container convenience method get_value_from_config() (link) which returns the virtual interface from the configuration database. The second argument to this method must be the same string that was used to place the virtual interface in the configuration database. The test then places the configuration object into the configuration database for providing access for the particular components (agents, drivers, monitors etc.) that communicate with the DUT through that particular interface.
class test_mac_simple_duplex extends ovm_test; ... wb_config wb_config_0; ... // config object for WISHBONE BUS

29

function void set_wishbone_config_params(); //set configuration info wb_config_0 = new(); wb_config_0.v_wb_bus_if = ovm_container #(virtual wishbone_bus_syscon_if)::get_value_from_config(this, "WB_BUS_IF"); ... // other WISHBONE bus configuration settings set_config_object("*","wb_config",wb_config_0, 0); // put in config endfunction function void build(); super.build(); set_wishbone_config_params(); ... endfunction ... endclass

// virtual interface

Setting Virtual Interface Property in Transactor The component that actually accesses the DUT via the virtual interface sets its virtual interface property from the virtual interface in the supplied configuration object.
// WISHBONE master driver class wb_m_bus_driver extends ovm_driver ...

#(wb_txn, wb_txn);

virtual wishbone_bus_syscon_if m_v_wb_bus_if; // Virtual Interface wb_config m_config; ... function void build(); super.build(); m_config = wb_config::get_config(this); // get config object ... endfunction function void connect(); super.connect(); m_v_wb_bus_if = m_config.v_wb_bus_if; // set local virtual if property endfunction ... endclass

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceConfigPkg

30

VirtInterfaceConfigPkg

Setting Virtual Interface Properties in the Tesbench using a Package


An alternative to using ovm_containerto provide virtual interface information to the test class is to use a package. The recommended approach is to sue the test parameters package. An alternate approach, which is not recommended but is in industry use is to use the agent package. This article will focus on the recomended approach.

Using the Test Parameters Package


This approach to assigning the actual interface reference to the virtual interface handles inside the testbench is very similar to the recommended approach using the configuration database with ovm_container. It is the same in that the test uses a configuration object and the configuration database to distribute the virtual interface . It differs in that a package is used to convey the virtual interface handle to the test class instead of using ovm_container. The disadvantage is this approach is it can not be used with protocol modules which means this approach can not easily support emulation. This approach in general has 4 steps. 1. A virtual interface is declared in a package. It is recommended that this package be the test parameters package. 2. In the top module containing the DUT and interface instance an assignment is made to the virtual interface in the package to point to the interface instance. 3. Test class gets the virtual interface information from the package and assigns the virtual interface in a configuration object that is distributed to the appropriate components (agents, drivers, monitors etc.) that communicate with the DUT through that particular interface. 4. The component that actually accesses the DUT via the virtual interface sets its virtual interface property from the virtual interface in the supplied configuration object. There is a discussion here as to why one would take the approach of the test class fetching and distributing the information to the agents and transactors instead of having the transactors or agents fetch the data directly. Declaring Virtual Interface in the Test Parameters Package In the test parameters package declare a virtual interface pointer.
package test_params_pkg; import ovm_pkg::*; // WISHBONE bus virtual interface virtual wishbone_bus_syscon_if v_wb_bus_if; ... endpackage

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceConfigPkg Assign Virtual Interface in the Test Parameters Package In the top module containing the DUT and the interface instance make an assignment in an initial block to point the virtual interface in the test parameters package to the interface instance. The virtual interface property may be imported or explicitly referenced.
module top_mac; ... import test_params_pkg::*; // WISHBONE interface instance // Supports up to 8 masters and up to 8 slaves wishbone_bus_syscon_if wb_bus_if(); ... initial begin //set WISHBONE virtual interface in test_params_pkg v_wb_bus_if = wb_bus_if; run_test("test_mac_simple_duplex"); end endmodule // create and start running test

31

Making the Virtual Interface available in the Testbench The test class creates a configuration object which has a virtual interface property. It then assigns this property from the virtual interface in the test parameters package. The test then places the configuration object into the configuration database for providing access for the particular components (agents, drivers, monitors etc.) that communicate with the DUT through that particular interface.
class test_mac_simple_duplex extends ovm_test; ... wb_config wb_config_0; ... // config object for WISHBONE BUS

function void set_wishbone_config_params(); //set configuration info wb_config_0 = new(); // Set WISHBONE bus virtual interface in config obj to virtual interface in test_params_pkg wb_config_0.v_wb_bus_if = v_wb_bus_if; ... set_config_object("*","wb_config",wb_config_0, 0); // put in config endfunction function void build(); super.build(); set_wishbone_config_params(); ... endfunction ... endclass

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceConfigPkg Setting Virtual Interface Property in Transactor The component that actually accesses the DUT via the virtual interface sets its virtual interface property from the virtual interface property in the supplied configuration object.
// WISHBONE master driver class wb_m_bus_driver extends ovm_driver ...

32

#(wb_txn, wb_txn);

virtual wishbone_bus_syscon_if m_v_wb_bus_if; // Virtual Interface wb_config m_config; ... function void build(); super.build(); m_config = wb_config::get_config(this); // get config object ... endfunction function void connect(); super.connect(); m_v_wb_bus_if = m_config.v_wb_bus_if; // set local virtual if property endfunction ... endclass

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfacePackage

33

VirtInterfacePackage

Setting Virtual Interface Properties in the Testbench with Packages


An easy way of assigning the actual interface reference to the virtual interface handles inside the testbench is by creating a virtual interface variable in a package. This method has the advantage of simplicity. Because of its disadvantages however this approach should only be considered for relative simple designs that do not have parameterized interfaces, or do not have multiple instances of an interface and is not recommended for general use. It has the following disadvantages that limit reusability: Parameterized interfaces cannot be declared in the package with generic parameter values - they must use actual values. Any changes to parameter values would then force a recompilation. It introduces an additional dependency on an external variable. So, for example, any change to the virtual interface name would require changes in any components that referenced the variable.

Virtual Interface Variable in Package


It is recommended that the virtual interface variable be created in the package of the agent that connects to the interface instance. From the package, the virtual interface variable becomes directly accessible to all components which include the drivers, monitors etc. of the agent.
package wishbone_pkg; ... virtual wishbone_bus_syscon_if v_wb_bus_if; ... `include "wb_m_bus_driver.svh" `include "wb_bus_monitor.svh" ... endpackage // virtual wishbone interface pointer

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfacePackage In the top level module, just assign the actual interface instance to the package variable:
module top_mac; ... // WISHBONE interface instance wishbone_bus_syscon_if wb_bus_if(); ... initial begin //set virtual interface to wishbone bus wishbone_pkg::v_wb_bus_if = wb_bus_if; ... end endmodule

34

Any component that uses the virtual interface should create a local handle and assign the package variable to the local handle in the connect() method.
// wishbone master driver class wb_m_bus_driver extends ovm_driver ...

#(wb_txn, wb_txn);

virtual wishbone_bus_syscon_if m_v_wb_bus_if; ... function void connect(); super.connect(); m_v_wb_bus_if = v_wb_bus_if; // set local virtual if property endfunction ... endclass

Strictly speaking, the use of a local virtual interface handle is not necessary, since the package variable is visible, but this step makes the code more reusable. For example, if the package variable name changes, there is only one line in the driver that would need to change.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceFunctionCallChain

35

VirtInterfaceFunctionCallChain

Function Call Chaining


It is unfortunate that this approach to assigningactual interface reference to the virtual interface handles inside the testbench is the one that is used in the xbus example that is prevalent in the OVM user's guide. Many users naturally assume that this is the recommended method because of its use in the example. This approach is not recommended. It involves creating a function (called assign_vi in the example) that takes a virtual interface handle as an argument, and calls an equivalent function (also named assign_vi) on one or more child components. This is repeated down the hierarchy until a leaf component is reached. Any components that need the virtual interface declare a local handle and assign the function argument to the local handle. In the connect() function of test env:
xbus0.assign_vi(xbus_tb_top.xi0);

In the xbus env:


function void assign_vi(virtual interface xbus_if xi); xi0 = xi; if( bus_monitor != null) begin bus_monitor.assign_vi(xi); end for(int i = 0; i < num_masters; i++) begin masters[i].assign_vi(xi); end for(int i = 0; i < num_slaves; i++) begin slaves[i].assign_vi(xi); end endfunction : assign_vi

In the agent:
function void assign_vi(virtual interface xbus_if xmi); monitor.assign_vi(xmi); if (is_active == OVM_ACTIVE) begin sequencer.assign_vi(xmi); driver.assign_vi(xmi); end endfunction : assign_vi

In the monitor:
function void assign_vi(virtual interface xbus_if xmi); this.xmi = xmi; endfunction

There are two main reasons why this method should not be used. It is not reusable - If the test environment hierarchy changes, these functions must be updated Unnecessary extra work - To reach leaf components in the environment, you must pass the virtual interface handle down through intermediate levels of the hierarchy that have no use for the virtual interface. Also, to make this method more reusable with respect to environment hierarchy changes, you would have to embed extra decision-making code (as in the examples above). or write each function to iterate over all children or and call the function on each child.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

VirtInterfaceFunctionCallChain This requires even more unnecessary work.

36

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

BusFunctionalModels

37

BusFunctionalModels

Bus Functional Models for DUTcommunication


Sometimes a the DUT connection is not directly to the ports of the DUT but rather is made through a BFM. As shown in the diagram below, typically the BFM will have tasks for generating DUT transactions. There are several different "flavors" of BFM:

Verilog Module BFM Can be modified


If the BFM is a Verilog module the first choice with this type of connection is to modify the BFM to be an interface. Then a standard virtual interface connection can be made to the BFM and the testbench transactors can directly access the BFM trasaction tasks.

Verilog Module BFM Can not be modified


The Verilog BFM may not be able to be modified for several reasons. It may have an instance of a module in which case it can not be changed to an interface. Or it simply can not be edited. In this case a wrapper module is introduced. The wrapper module contains the instance of the BFM, the DUT and an instance of an interface as shown in the diagram below. The interface acts as a "proxy" for the BFM to the testbench transactors. For example to do a wishbone bus write transaction the driver would call the write task in the interface which in turn would call the write task in the BFM. See the diagram below. Example code for the wrapper module is below:
module wb_bus_wrapper #(int WB_ID = 0); ... // WISHBONE BFM instance wishbone_bus_syscon_bfm wb_bfm(); // WISHBONE 0, slave 0: 000000 - 0fffff wb_slave_mem #(mem_slave_size) wb_s_0 (...); // MAC 0 eth_top mac_0(...); // Interface interface wishbone_bus_bfm_if #(int ID = WB_ID) (input bit clk); // Methods //WRITE 1 or more write cycles task wb_write_cycle(wb_txn req_txn, bit [2:0] m_id); wb_bfm.wb_write_cycle(req_txn, m_id); endtask // other tasks not shown ... endinterface // Interface instance wishbone_bus_bfm_if #(WB_ID) wb_bus_bfm_if(.clk(wb_bfm.clk)); initial //set interface in config space ovm_container #(virtual wishbone_bus_bfm_if)::set_value_in_global_config( $sformatf("WB_BFM_IF_%0d",WB_ID), wb_bus_bfm_if);

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

BusFunctionalModels

38

endmodule

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

DualTop

39

DualTop
Typically a DUT-TB setup has a single SystemVerilog module as the top level. This top level module contains the DUT and its associated interfaces, protocol modules, connection and support logic. It also contains the code to create the testbench. All this "stuff" can get messy and hard to manage. A different way to manage all this stuff is to encapsulate everything associated directly with the DUT in a wrapper module. The code to create the testbench is placed in its own module. Verilog allows for more than one top level module in a simulation. Neither of these two modules are instantiated but rather are treated as top level modules. This arrangement is referred to as dual top. Dual top is a necessity for emulation. The DUTwrapper is the stuff that goes in the emulator. The other top module containing the test bench stays running in the simulator. If the testbench is only going to be used in simulation dual top is not necessary but may however still provide a useful level of encapsulation for modularity, reuse etc. Communicating the virtual interface connection between the DUT wrapper module and the testbench is done using the configuration database with ovm_container approach.

DUT Wrapper module


In this example the MAC DUT and its associated interfaces and protocol module and the WISHBONE slave memory along with the WISHBONE bus logic are contained in DUT wrapper module top_mac
module top_mac; import ovm_pkg::*; import ovm_container_pkg::*; import test_params_pkg::*; // WISHBONE interface instance // Supports up to 8 masters and up to 8 slaves wishbone_bus_syscon_if wb_bus_if(); //----------------------------------// WISHBONE 0, slave 0: 000000 - 0fffff // this is 1 Mbytes of memory wb_slave_mem #(mem_slave_size) wb_s_0 ( ... ); // wires for MAC MII connection wire [3:0] MTxD; wire [3:0] MRxD; //----------------------------------// MAC 0 // It is WISHBONE slave 1: address range 100000 - 100fff // It is WISHBONE Master 0 eth_top mac_0 ( ... ); // protocol module for MAC MII interface mac_mii_protocol_module #(.INTERFACE_NAME("MIIM_IF")) mii_pm( ... ); initial //set WISHBONE virtual interface in config space ovm_container #(virtual wishbone_bus_syscon_if)::set_value_in_global_config("WB_BUS_IF", wb_bus_if); endmodule

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

DualTop

40

Testbench top module


In the top module that creates the testbench is an initial block that calls run_test(). Note the imports ovm_pkg::run_test and tests_pkg::test_mac_simple_duplex. These are necessary for compilation of the run_test() line of code.
// This the top for the tesbench in a dual top setup // The dut is in the top_mac dut wrapper module module top; import ovm_pkg::run_test; import tests_pkg::test_mac_simple_duplex; initial run_test("test_mac_simple_duplex"); endmodule // create and start running test

Questasim with more than one top level module


The call to vsim includes more than one top module
#Makefile ... normal: clean cmp vsim +nowarnTSCALE -c top top_mac -suppress 3829 -do "run -all" ...

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

ProtocolModules

41

ProtocolModules
Protocol modules are wrapper modules that encapsulate a DUT interface, associated assertions, QVL instances (which are not allowed inside an interface), and so forth. When emulation is a consideration protocol modules provide a level of encapsulation necessary to isolate the changes that occur in the agent and interface in moving between simulation and emulation. If the testbench is only going to be used in simulation protocol modules are not necessary. They may however still provide a useful level of encapsulation for modularity, reuse etc. While it is not required that protocol modules be used together with the dual top methodology it is likely to be used mainly in connection with the dual top approach since it is also required for emulation. By adopting encapsulation, protocol modules protect the top level from changes: Any re-mapping due to changes in the interface can be done inside the protocol module. The top level module is protected from changes to the virtual interface registration/connection technique. You can instantiate QVL instances (which would not be allowed inside the SV interface) as well as add other assertions that might not already be present in the interface.

Example:

In this example an Ethernet Media Access Controller (MAC) is the DUT. A MAC has multiple interfaces. The one shown in the example is the Media Independent Interface (MII) which is where Ethernet packets are transferred to the physical interface. In this example the protocol module contains the MIIinterface instance, a QVLMIImonitor and code for putting the interface instance location in the configuration database using ovm_container.
module mac_mii_protocol_module #(string input logic wb_rst_i, // Tx output logic mtx_clk_pad_o, // input logic[3:0] mtxd_pad_o, // input logic mtxen_pad_o, // input logic mtxerr_pad_o, // // Rx output output logic logic[3:0] INTERFACE_NAME = "") ( Transmit Transmit Transmit Transmit clock (from PHY) nibble (to PHY) enable (to PHY) error (to PHY)

mrx_clk_pad_o, // Receive clock (from PHY) mrxd_pad_i, // Receive nibble (from PHY)

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

ProtocolModules
output output logic logic mrxdv_pad_i, mrxerr_pad_i, mcoll_pad_i, mcrs_pad_i, // Receive data valid (from PHY) // Receive data error (from PHY) // Collision (from PHY) // Carrier sense (from PHY) // MII data input (from I/O cell) // MII Management data clock (to PHY) // MII data output (to I/O cell) // MII data output enable (to I/O cell)

42

// Common Tx and Rx output logic output logic

// MII Management interface output logic md_pad_i, input logic mdc_pad_o, input logic md_pad_o, input logic md_padoe_o ); import ovm_container_pkg::*; // Instantiate interface mii_if miim_if();

// Connect interface to protocol signals through module ports assign mtx_clk_pad_o = miim_if.mtx_clk; assign miim_if.MTxD = mtxd_pad_o; assign miim_if.MTxEn = mtxen_pad_o; assign miim_if.MTxErr = mtxerr_pad_o ; assign assign assign assign mrx_clk_pad_o = miim_if.mrx_clk; mrxd_pad_i = miim_if.MRxD; mrxdv_pad_i = miim_if.MRxDV ; mrxerr_pad_i = miim_if.MRxErr ; ; ; ; ; ; ;

assign mcoll_pad_i = miim_if.MColl assign mcrs_pad_i = miim_if.MCrs assign assign assign assign md_pad_i = miim_if.Mdi_I miim_if.Mdc_O = mdc_pad_o miim_if.Mdo_O = md_pad_o miim_if.Mdo_OE = md_padoe_o

// Instantiate QVL Checker qvl_gigabit_ethernet_mii_monitor mii_monitor( .areset(1'b0), .reset(wb_rst_i), .tx_clk(miim_if.mtx_clk), .txd(miim_if.MTxD), .tx_en(miim_if.MTxEn), .tx_er(miim_if.MTxErr), .rx_clk(miim_if.mrx_clk), .rxd(miim_if.MRxD), .rx_dv(miim_if.MRxDV), .rx_er(miim_if.MRxErr), .col(miim_if.MColl), .crs(miim_if.MCrs) , .half_duplex(1'b0) ); // Connect interface to testbench virtual interface string interface_name = (INTERFACE_NAME == "") ? $sformatf("%m") : INTERFACE_NAME; initial begin ovm_container #(virtual mii_if)::set_value_in_global_config(interface_name, miim_if); end endmodule

It should be noted that if the parameter INTERFACE _ NAME is not set, then the default value is%m (i.e., the hierarchical path of this module). This is guaranteed to be unique. If this parameter is explicitly set, then it is up to the designer to make sure that the names chosen are unique within the enclosing module.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcrete

43

Connect/AbstractConcrete

Abstract/Concrete Class approach to DUT-TB communication


A handle based approach to DUT-TB communication that does not use virtual interfaces is referred to in the OVM industry as the abstract/concrete class approach. There is also a form of this approach that is in use that is known within Mentor Graphics as Two Kingdoms. As with using virtual interfaces this approach may be set up for the transactors in the testbench to communicate with the pins of the DUT, with a Bus Functional Model (BFM) which drives transactions on the DUT or the internals of the DUT using the SystemVerilog bind construct. The most typical use is with BFMs. Virtual interfaces is the recommended approach for DUT-TB communication. The abstract/concrete class approach should only be considered when virtual interfaces can not be used or as a secondary approach in the case of legacy BFM's that can not be modified to be an interface. The discussion in this article going forward will focus only on use of the abstract/concrete class approach with BFMs.

Example BFM
Here is a diagram showing a BFM for the WISHBONE bus that will be used in the examples here. The wishbone bus BFM is connected to the WISHBONE bus and has the WISHBONE bus arbitration, clock, reset etc. logic along with tasks which generate WISHBONE bus transactions (read, write etc.). Here is code from the BFM. The full code may be downloaded with the other example code shown later.
module wishbone_bus_syscon_bfm #(int num_masters = 8, int num_slaves = 8, int data_width = 32, int addr_width = 32) ( // WISHBONE common signals output logic clk, output logic rst, ... ); // WISHBONE bus arbitration logic ... //Slave address decode ... // BFM tasks //WRITE 1 or more write cycles task wb_write_cycle(wb_txn req_txn, bit [2:0] m_id = 1); ... endtask //READ 1 or more cycles task wb_read_cycle(wb_txn req_txn, bit [2:0] m_id = 1, output wb_txn rsp_txn); ... endtask // Monitor bus transactions task monitor(output wb_txn txn); ... endtask endmodule

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcrete

44

Abstract/Concrete Classes
First an abstract class (SystemVerilog virtual class) is defined. The abstract class has pure virtual methods and properties which define a public interface for accessing information. The implementations of the methods are not in the abstract class but rather are in a derived class which is referred to as the concrete class. The concrete class is defined inside of a wrapper module which also instantiates the BFM.

DUT Connection
Since it is defined inside the wrapper module the scope of the concrete class, wb_bus_concr_c in the example, is the wrapper module and hence its methods can access anything defined inside of the wrapper module (wb_bus_protocol_module), including ports, variables, class handles, functions, tasks, module instances etc. In this diagram an instance of the BFM is created inside the wrapper module. The methods of the concrete class access the BFM methods by hierarchical reference through this instance (bmf_instance_name.method_name).

Two different use models


After declaration of the abstract class and the concrete class inside the wrapper module there are two use models. There is the recommended use model which follows the abstract/concrete class pattern. Then there is a use model that is called the two kingdoms that is not recommended. Abstract/concrete class use model This approach is recommended as it follows the recommended use model for passing information from the DUT to the testbench which is discussed in detail here in the article on virtual interfaces. In this use model an actual instance of the concrete class is created inside of the wrapper module where the concrete class is defined. Inside the testbench a base class handle inside of the transactors (drivers, monitors etc.) is made to point to this derived class instance, through use of ovm_container and the configuration database. More details, diagrams and an example using a BFM are here. Two Kingdoms use model This approach is what is known as the Two Kingdoms. It is not recommended as it does not follow the recommended use model for passing information from the DUT to the testbench which is discussed in detail here in the article on virtual interfaces, instead it uses the factory. Additionally it does not strictly follow the abstract/concrete class pattern with the use of a base class handle in the drivers, monitors etc to point to the concrete class instance but rather the drivers and monitors themselves are the abstract/concrete classes. A factory override is set up of the concrete class for the base class. Inside the testbench a base handle is created and then a factory create is called on the handle resulting in a concrete object being created. More details, diagrams and an example using a BFM are here.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcreteOVMContainer

45

Connect/AbstractConcreteOVMContainer

Abstract Class
The Abstract class is defined as part of the agent in the testbench and is included in the agent package. Below is the code for an example abstract class called wb_bus_abs_c. Note the pure virtual methods which define a public interface to this class. As part of the public interface too is an event to represent the posedge of the clock. Note too that the abstract class inherits from ovm_component and so inherits the phase methods etc.
// Abstract class for abstract/concrete class wishbone bus communication //---------------------------------------------virtual class wb_bus_abs_c extends ovm_component; function new(string name, ovm_component parent); super.new(name,parent); endfunction // API methods //WRITE 1 or more write cycles pure virtual task wb_write_cycle(wb_txn req_txn, bit [2:0] m_id); //READ 1 or more cycles pure virtual task wb_read_cycle(wb_txn req_txn, bit [2:0] m_id, output wb_txn rsp_txn); // wait for an interrupt pure virtual task wb_irq(wb_txn req_txn, output wb_txn rsp_txn); //Get a wb transaction from the bus pure virtual task monitor(output wb_txn txn); event pos_edge_clk; endclass

Concrete Class
The concrete class is derived from the abstract class. It is required to override any pure virtual methods, providing implementations. It may also provide code that writes/reads variables inherited from the abstract class. This class is defined inside of a wrapper module that includes an instance of the BFM. Since it is defined inside the wrapper module the scope of the concrete class, is the wrapper module and hence its methods can access anything defined inside of the wrapper module, including ports, variables, class handles, functions, tasks, and in particular the BFM module instance. Here is the code for the concrete class wb_bus_concr_c. It is defined inside the wrapper module wb_bus_protocol_module which is a protocol module. Note that this class inherits from the abstract class and provides implementations of the methods. These methods are straight forward in that they are "proxy" methods that simply call the corresponding method inside the BFM. For example the concrete driver class wb_write_cycle() task calls the wb_write_cycle() task inside the BFM. At the bottom of the wb_bus_protocol_module is the instance (wb_bfm) of the BFM (wishbone_bus_syscon_bfm).
module wb_bus_protocol_module #(int WB_ID = 0, int num_masters = 8, int num_slaves = 8, int data_width = 32, int addr_width = 32) ( // Port declarations // WISHBONE common signals output logic clk, output logic rst, ...

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcreteOVMContainer
); ... // Concrete class declaration class wb_bus_concr_c #(int ID = WB_ID) extends wb_bus_abs_c; function new(string name = "", ovm_component parent = null); super.new(name,parent); endfunction // API methods // simply call corresponding BFM methods //WRITE 1 or more write cycles task wb_write_cycle(wb_txn req_txn, bit [2:0] m_id); wb_bfm.wb_write_cycle(req_txn, m_id); endtask //READ 1 or more cycles task wb_read_cycle(wb_txn req_txn, bit [2:0] m_id, output wb_txn rsp_txn); wb_bfm.wb_read_cycle(req_txn, m_id, rsp_txn); endtask // wait for an interrupt task wb_irq(wb_txn req_txn, output wb_txn rsp_txn); wb_bfm.wb_irq(req_txn, rsp_txn); endtask task monitor(output wb_txn txn); wb_bfm.monitor(txn); endtask task run(); forever @ (posedge clk) -> pos_edge_clk; endtask endclass ... // WISHBONE BFM instance wishbone_bus_syscon_bfm wb_bfm( .clk( clk ), .rst( rst ), ... ); endmodule

46

Connecting the testbench to the DUT


The testbench DUT connection with the abstract and concrete classes is similar to virtual interface connections. With a virtual interface connection a virtual interface handle is placed inside the transactor, such as a driver or monitor, and made to point to an instance of an interface connected to the DUT. In the abstact/concrete class approach a handle of the abstract class type is placed inside the transactor. An instance of the concrete class is made inside the wrapper module(wb_bus_protocol_module) and the abstract class handle is made to point to the concrete class instance.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcreteOVMContainer

47

In the diagram above the DUTs and the wb_bus_protocol_module are wrapped in a wrapper module the wb_bus_wrapper. This is for modularity and convenience in instantiating multiple wishbone buses. The code below shows the instance of the concrete class inside the wrapper module and a method (a "lazy allocator" ie a method that doesn't allocate the instance until it is needed) used for creating the concrete class instance.
module wb_bus_protocol_module #(int WB_ID = 0, int num_masters = 8, int num_slaves = 8, int data_width = 32, int addr_width = 32) ( // Port declarations // WISHBONE common signals output logic clk, output logic rst, ... ); // Concrete class declaration class wb_bus_concr_c #(int ID = WB_ID) extends wb_bus_abs_c; ... endclass // instance of concrete class wb_bus_concr_c wb_bus_concr_c_inst; // lazy allocation of concrete class function wb_bus_abs_c get_wb_bus_concr_c_inst(); if(wb_bus_concr_c_inst == null) wb_bus_concr_c_inst = new(); return (wb_bus_concr_c_inst); endfunction initial //set concrete class object in config space ovm_container #(wb_bus_abs_c)::set_value_in_global_config( $sformatf("WB_BUS_CONCR_INST_%0d",WB_ID) , get_wb_bus_concr_c_inst()); // WISHBONE BFM instance wishbone_bus_syscon_bfm wb_bfm( .clk( clk ), .rst( rst ), ... ); endmodule

The location of the concrete class instance is provided to the transactor in the same manner as in virtual interface connections using ovm_container to pass a handle that points to the concrete class instance to the test class and then through a configuration object from the test class to the transactor. In the code above inside the initial block a handle to

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcreteOVMContainer the concrete instance is placed inside the configuration space using ovm_container. In the test class the handle to the concrete driver class instance is fetched from the configuration database and placed inside a configuration object which is made available to the wishbone agent. This approach is recommended as it follows the recommended use model for passing information from the DUT to the testbench which is discussed in detail here in the article on virtual interfaces.
class test_mac_simple_duplex extends ovm_test; ... mac_env env_0; wb_config wb_config_0; // config object for WISHBONE BUS 0 ... function void set_wishbone_config_params(); wb_config_0 = new(); wb_config_0.m_wb_bfm_driver = ovm_container #(wb_bus_bfm_driver_base)::get_value_from_config(this, $sformatf("WB_BUS_BFM_DRIVER_C_INST_%0h", wb_config_0.m_wb_id)); // concrete class object ... set_config_object("env_0*","wb_config",wb_config_0, 0); // put in config ... endfunction function void build(); super.build(); set_wishbone_config_params(); set_mii_config_params(); ... endfunction ... endclass

48

Inside the driver an abstract class handle is made to point to the concrete class instance by fetching the location from the configuration object provided by the test class.
// WISHBONE master driver class wb_bus_bfm_driver extends ovm_driver #(wb_txn, wb_txn); `ovm_component_utils(wb_bus_bfm_driver) ovm_analysis_port #(wb_txn) wb_drv_ap; bit [2:0] m_id; // Wishbone bus master ID wb_config m_config; wb_bus_abs_c m_wb_bus_abs_c; function new(string name, ovm_component parent); super.new(name,parent); endfunction function void build(); super.build(); m_config = wb_config::get_config(this); // get config object m_id = m_config.m_wb_master_id; wb_drv_ap = new("wb_drv_ap", this); // Assign abstract class handle to concrete object m_wb_bus_abs_c = m_config.m_wb_bus_abs_c; endfunction task run(); wb_txn req_txn; forever begin seq_item_port.get(req_txn); // get transaction @ ( m_wb_bus_abs_c.pos_edge_clk) #1; // sync to clock edge + 1 time step case(req_txn.txn_type) //what type of transaction? NONE: `ovm_info($sformatf("WB_M_DRVR_%0d",m_id), $sformatf("wb_txn %0d the wb_txn_type was type NONE", req_txn.get_transaction_id()),OVM_LOW ) WRITE: wb_write_cycle(req_txn); READ: wb_read_cycle(req_txn); RMW: wb_rmw_cycle(req_txn);

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Connect/AbstractConcreteOVMContainer
WAIT_IRQ: fork wb_irq(req_txn); join_none default: `ovm_error($sformatf("WB_M_DRVR_%0d",m_id), $sformatf("wb_txn %0d the wb_txn_type was type illegal", req_txn.get_transaction_id()) ) endcase end endtask //READ 1 or more cycles virtual task wb_read_cycle(wb_txn req_txn); wb_txn rsp_txn; m_wb_bus_abs_c.wb_read_cycle(req_txn, m_id, rsp_txn); seq_item_port.put(rsp_txn); // send rsp object back to sequence wb_drv_ap.write(rsp_txn); //broadcast read transaction with results endtask //WRITE 1 or more write cycles virtual task wb_write_cycle(wb_txn req_txn); wb_txn orig_req_txn; $cast(orig_req_txn, req_txn.clone()); //save off copy of original req transaction m_wb_bus_abs_c.wb_write_cycle(req_txn, m_id); wb_drv_ap.write(orig_req_txn); //broadcast orignal transaction endtask //RMW ( read-modify_write) virtual task wb_rmw_cycle(ref wb_txn req_txn); `ovm_info($sformatf("WB_M_DRVR_%0d",m_id), "Wishbone RMW instruction not implemented yet",OVM_LOW ) endtask // wait for an interrupt virtual task wb_irq(wb_txn req_txn); wb_txn rsp_txn; m_wb_bus_abs_c.wb_irq(req_txn, rsp_txn); seq_item_port.put(rsp_txn); // send rsp object back to sequence endtask endclass

49

When the driver receives a WISHBONEwrite transaction for example in the run task it calls its wb_write_cycle() task which uses the abstract class handle (m_wb_bus_abs_c) to call the wb_write_cycle() method in the concrete class which in turn calls the wb_write_cycle() method in the BFM.

Multiple instance considerations


When there are multiple instances of the DUT & BFM then each concrete class instance needs to be "uniquified" so the correct pairing can be made. A way to do this is to parameterize the wrapper class and the concrete class. In this example an integer value which is the WISHBONE bus id (WB_ID) is supplied to make a unique string with. This is done to make a correlation between bus id's and the concrete classes. Another approach to making the concrete class instance unique is described in the article on protocol modules (see at the bottom). The full source code in this article actually has two wishbone wrapper class instances inside of top_mac instead of the one shown in the diagrams and so ther are two WISHBONE buses, MACs etc. On the testbench side it has a wishbone environment for each of the WISHBONE buses instead of just one as shown in the diagrams. Separate configurtion objects are generated for each of the wishbone environments. The details for the multiple instances are not shown in this article but can be viewed in the the source code.

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Article Sources and Contributors

50

Article Sources and Contributors


Connect/Dut Interface Source: http://uvm.mentor.com/w/index.php?oldid=3968 Contributors: Mentor SVCreationOrder Source: http://uvm.mentor.com/w/index.php?oldid=3681 Contributors: Mentor Connect/SV Techniques Source: http://uvm.mentor.com/w/index.php?oldid=3683 Contributors: Mentor ParameterizedTests Source: http://uvm.mentor.com/w/index.php?oldid=3696 Contributors: Mentor Config/Container Source: http://uvm.mentor.com/w/index.php?oldid=3701 Contributors: Mentor Config/Overview Source: http://uvm.mentor.com/w/index.php?oldid=3962 Contributors: Mentor Config/Params Package Source: http://uvm.mentor.com/w/index.php?oldid=3970 Contributors: Mentor Connect/Virtual Interface Source: http://uvm.mentor.com/w/index.php?oldid=3721 Contributors: Mentor VirtInterfaceConfigOVMContainer Source: http://uvm.mentor.com/w/index.php?oldid=3973 Contributors: Mentor VirtInterfaceConfigPkg Source: http://uvm.mentor.com/w/index.php?oldid=3704 Contributors: Mentor VirtInterfacePackage Source: http://uvm.mentor.com/w/index.php?oldid=3694 Contributors: Mentor VirtInterfaceFunctionCallChain Source: http://uvm.mentor.com/w/index.php?oldid=3738 Contributors: Mentor BusFunctionalModels Source: http://uvm.mentor.com/w/index.php?oldid=3695 Contributors: Mentor DualTop Source: http://uvm.mentor.com/w/index.php?oldid=3723 Contributors: Mentor ProtocolModules Source: http://uvm.mentor.com/w/index.php?oldid=3703 Contributors: Mentor Connect/AbstractConcrete Source: http://uvm.mentor.com/w/index.php?oldid=3685 Contributors: Mentor Connect/AbstractConcreteOVMContainer Source: http://uvm.mentor.com/w/index.php?oldid=3705 Contributors: Mentor

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Image Sources, Licenses and Contributors

51

Image Sources, Licenses and Contributors


Image:Works With OVM 2.1.1.png Source: http://uvm.mentor.com/w/index.php?title=File:Works_With_OVM_2.1.1.png License: unknown Contributors: Mentor Image:Works With UVM 1.0.png Source: http://uvm.mentor.com/w/index.php?title=File:Works_With_UVM_1.0.png License: unknown Contributors: Mentor Image:DUT-TB diag OVM_5.jpg Source: http://uvm.mentor.com/w/index.php?title=File:DUT-TB_diag_OVM_5.jpg License: unknown Contributors: Mentor Image:DUT-TB diag 6.jpg Source: http://uvm.mentor.com/w/index.php?title=File:DUT-TB_diag_6.jpg License: unknown Contributors: Mentor Image:SV Creation Order diag 1.jpg Source: http://uvm.mentor.com/w/index.php?title=File:SV_Creation_Order_diag_1.jpg License: unknown Contributors: Mentor Image:Test params pkg diag 1.jpg Source: http://uvm.mentor.com/w/index.php?title=File:Test_params_pkg_diag_1.jpg License: unknown Contributors: Mentor Image:Virtual interface diag 1.jpg Source: http://uvm.mentor.com/w/index.php?title=File:Virtual_interface_diag_1.jpg License: unknown Contributors: Mentor Image:DUT-TB_diag_OVM_5.jpg Source: http://uvm.mentor.com/w/index.php?title=File:DUT-TB_diag_OVM_5.jpg License: unknown Contributors: Mentor Image:Protocol modules diag 2.jpg Source: http://uvm.mentor.com/w/index.php?title=File:Protocol_modules_diag_2.jpg License: unknown Contributors: Mentor Image:Two kingdoms diag 7.jpg Source: http://uvm.mentor.com/w/index.php?title=File:Two_kingdoms_diag_7.jpg License: unknown Contributors: Mentor

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

License

52

License
Copyright 2010 Mentor Graphics Corp

UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com

Mentor Graphics UVM/OVM Documentation / Methodology Cookbook

For the latest product information, call us or visit:

w w w.m e n t o r.c o m

2010 Mentor Graphics Corporation, all rights reserved. This document contains information that is proprietary to Mentor Graphics Corporation and may be duplicated in whole or in part by the original recipient for internal business purposes only, provided that this entire notice appears in all copies. In accepting this document, the recipient agrees to make every reasonable effort to prevent unauthorized use of this information. All trademarks mentioned are this document are trademarks of their respective owners.

MGC 06-10

TECH9050-w

You might also like