Connection Guide
Connection Guide
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.
UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com
SVCreationOrder
SVCreationOrder
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
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
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.
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).
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.
UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com
ParameterizedTests
11
Declaration
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
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
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.
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
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
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
// 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.
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.
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
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
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
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
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
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
36
UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com
BusFunctionalModels
37
BusFunctionalModels
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.
UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com
DualTop
40
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
// 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
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).
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
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.
UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com
50
UVM/OVM Online Methodology Cookbook - Copyright (c) 2011 Mentor Graphics Corporation - http://uvm.mentor.com
51
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
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