blob: 61d99479e7b30765ddd3c0d27ead56dc790e9aa8 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Abstract class meant to hold arbitrary virtual interface handles.
//
// Written primarily for an interface which implements functional coverage, this could be used
// for other purposes as well. This abstract class provides utilities & macros to retrieve
// virtual interface handles that are bound to a DUT's sub-modules. These sub-module interfaces must
// self-register using the `DV_VIF_WRAP_SET_VIF()` macro (see details below). The extended class
// then implements the `get_vifs()` method using the `DV_VIF_WRAP_GET_VIF*` macros below to retrieve
// the sub-module interface handles it maintains.
virtual class dv_vif_wrap;
string hier; // Represents the hierarchy of the parent module or interface.
string name; // Name of the class instance.
function new(string hier, string name = "");
this.hier = hier;
this.name = name;
endfunction
// Abstract method implemented by the extended class. It is recommended to invoke the helper
// macros below rather than manually define it.
pure virtual task get_vifs();
endclass
// Helper macros.
//
// These are defined in the file itself since they are tightly coupled to the class definition
// above. These are scoped globally so that extended classes can invoke them.
// Enable an interface to register itself (set its handle into the config db).
//
// Meant to be invoked from an interface. The macros invocation causes the interface to register
// itself into the uvm_resource_db pool. The derived class of dv_vif_wrap retrieves the handle to
// that interface handle from the uvm_resource db pool.
//
// SV LRM does not yet allow referencing the instance of an interface within itself as one
// would in case of a class using the ~this~ keyword. However, most major simulators actually
// support this in their own custom way. On VCS, a reference to self within the interface can be set
// using `interface::self()` construct. On Xcelium, this is achieved by simply referencing the
// interface name.
//
// _IF: The SV interface
// _VIF: The corresponding virtual interface handle name
// _LEVEL: # of hierarchical levels the module to which the SV interface is bound, starting at the
// top level DUT instance.
`define DV_VIF_WRAP_SET_VIF(_IF, _VIF, _LEVEL = 0) \
import uvm_pkg::*; \
function automatic void self_register(); \
string path; \
virtual _IF vif; \
/* Initial block adds another level in the path hierarchy which needs to be split out. */ \
/* Add one more to go one level up the interface instance. */ \
/* Example: tb.dut.core.u_foo_if.self_register -> tb.dut.core. */ \
path = dv_utils_pkg::get_parent_hier(.hier($sformatf("%m")), .n_levels_up(2 + _LEVEL)); \
`ifdef VCS \
vif = interface::self(); \
`elsif XCELIUM \
vif = _IF; \
`else \
vif = _IF; \
`endif \
uvm_pkg::uvm_resource_db#(virtual _IF)::set(path, `"_VIF`", vif); \
endfunction \
initial self_register();
// Enables the retrieval of individual vifs.
//
// The three macros below go together to define the _get_vifs() task in the extended class.
`define DV_VIF_WRAP_GET_VIFS_BEGIN \
task get_vifs(); \
fork \
// To avoid race condition between the instant when an interface handle is set into the config db
// and the instant when it is retrieved (in the same time step, at t = 0), the macro below invokes
// uvm_config_db#(..)::wait_modified() to ensure that the retrieval is done only after the set.
`define DV_VIF_WRAP_GET_VIF(_IF, _VIF) \
begin \
bit vif_found; \
/* At most 2 retries. */ \
repeat (2) begin \
/* Force the evaluation at the end of the time step. */ \
#0; \
if (uvm_pkg::uvm_resource_db#(virtual _IF)::read_by_name(hier, `"_VIF`", _VIF)) begin \
vif_found = 1'b1; \
break; \
end \
end \
`DV_CHECK_FATAL(vif_found, {`"_VIF`", " not found in the resource db"}, hier) \
end
`define DV_VIF_WRAP_GET_VIFS_END \
join \
endtask