Adapter Guide
Adapter Guide
Adapter Guide
14 October, 2013
Copyright © 2013 Advanced Micro Devices, Inc. (AMD). All rights reserved.
Advanced Micro Devices, Inc. , One AMD Place, P.O. Box 3453, Sunnyvale, CA 94088, USA.
This product is licensed under the Apache Software Foundation’s Apache License, Version 2.0 (the
"License") January 2004. The full license is available at: http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions and limitations under the
License.
Notices
Questions or suggestions relating to this document or product can be sent to:
uvm_contributions@cadence.com
Table of Diagrams
Overview
UVM-ML is an extension of UVM methodology, intended to provide UVM like capabilities in the multi-
language environment. It is implemented in the UVM-ML release package UVM-ML-1.3.tar which
includes the implementation of the UVM-ML backplane and several adapters which connect various
frameworks through the backplane.
This guide is intended for the adapter developer. It explains the requirements from the adapter and
shows examples of how to use it. Additional relevant information can be found in
The examples included in this guide are based on the UVM-SV and UVM-SC adapters included in the
UVM-ML release package mentioned above.
Adapters
Every framework that participates in the UVM-ML testbench needs an adapter. Adapters bridge
between the user code in the framework and the backplane. The main role of the adapter is to:
implement user requests related to actions in other frameworks and propagate the necessary
information to the backplane
implement backplane calls and propagate the relevant information to the framework
Internally the adapters contain various capabilities for the use of the integrator and the end users. The
integrator uses the adapters to construct the multi-language environment by using the TLM connection
and unified hierarchy capabilities. The end user uses the TLM interfaces to transfer transactions
between frameworks. The diagram below shows how the adapter provides serialization and other
services.
The ml/adapters directory in the release package contains two examples, a UVM-SV adapter under
ml/adapters/uvm_sv and a UVM-SC adapter under ml/adapters/uvm_sc. These are used below to
demonstrate the various capabilities of adapters.
Adapter Interfaces
The adapter has two interfaces, the required and provided API to communicate with the backplane and
the user facing API to be used by the framework. The end user communicates through the user facing
API and the adapter implements these API requests either internally in the adapter or through the
backplane API.
Provided API
The provided API is defined in the backplane in bp_api_struct in ml/backplane/bp_provided.h. This is a
struct that contains a list of pointers to the methods that constitute the backplane API and are available
to be used by the adapters. This API is discussed in detail in “Using the Provided API” below.
Framework Modifications
Occasionally there is need to do some small adjustments in the framework itself to be able to support
the required functionality. These changes should be kept minimal and if possible should be implemented
in a way that will be forward compatible i.e. will apply to future versions of the framework.
Typical examples of such modifications are gaining control over time management in a simulator that is
not designed to run in slave mode, or avoiding undesired race conditions between the testbench
construction and other initialization code.
The modifications to UVM-SV can be found in ml/frameworks/uvm/sv/uvm-1.1c. In this case the entire
UVM-SV package is replaced using the –uvmhome command line flag which points to the desired
version of UVM-SV. The ML specific changes are labeled “ML enabling fix”.
The UVM-SC required API is established when uvm_ml_bootstrap() is called, using the following function
which creates a new struct of type bp_frmw_c_api_struct and returns its pointer to the calling
backplane. This function also sets the pointers in the required_api struct to the appropriate
implementation functions.
The full list of API can be found in ml/backplane/bp_required.h. The implementation in the UVM-SC
adapter is in class uvm_ml_tlm_rec in ml/adapters/uvm_sc/common/uvm_ml_adapter.cpp. The
implementation in the UVM-SV adapter is in package uvm_ml in
ml/adapters/uvm_sv/uvm_ml_adapter.sv.
The adapter instantiates a parent proxy. The constructor of the parent proxy creates the requested
child component with the provided arguments.
int uvm_ml_tlm_rec::create_child_junction_node(
const char * component_type_name, // type of the child component
const char * instance_name, // instance name of the child component
const char * parent_full_name, // full name of the remote parent component
int parent_framework_id, // unique ID of the initiator framework
int parent_junction_node_id // unique ID of the parent component
);
Pre, post and runtime phases must be propagated top down on the unified hierarchy tree. The
parent proxy indicates the start of a new phase through the appropriate API in the backplane. The
backplane propagates the phase change to the appropriate parent proxy. The adapter is responsible
for triggering the phase in the appropriate parent proxy.
int uvm_ml_phase::transmit_phase(
int target_id, // unique ID of the parent proxy
const char * phase_group, // name of the phase group
const char * phase_name, // name of the phase
uvm_ml_phase_action phase_action // STARTED, EXECUTING or ENDED
);
TLM Communication
Implementation of non-blocking put from a remote port in some other framework.
The adapter unpacks the bit-stream into the appropriate type and then sends it using nb_put of the
appropriate interface to the target export in the user code.
int uvm_ml_tlm_rec::nb_put(
unsigned connector_id, // unique ID of the connector
unsigned stream_size, // size of data stream
uvm_ml_stream_t stream, // data stream sent to initiator
uvm_ml_time_unit time_unit, // current time unit
double time_value // current time value
);
Note: In the UVM-SV adapter there is no use for the time stamp, so the time parameters are
discarded when the API is called.
Framework Synchronization
Synchronize the framework to the time broadcasted from the master simulator.
void uvm_ml_tlm_rec::synchronize(
uvm_ml_time_unit time_unit, // current time unit
double time_value // current time value
);
The full list of API can be found in ml/backplane/bp_provided.h. Below is a sample of interfaces to
demonstrate the various capabilities.
Each framework must register and receive a unique ID which is used later on to identify this
framework. While registering, the adapter publishes its implementation of required API in the form
of the pointer to the “tray” of API pointers.
Note: The framework may have multiple IDs. If it is the only SystemC framework it can be identified
simply as “SC”, but if there are multiple SystemC frameworks than a more specific name can be used
like “UVMSC”, to differentiate from another framework that is not UVM-SC based.
int uvm_ml_sv_register_framework()
{
...
char * frmw_ids[3] = {(char *)"UVMSV", (char *)"SV", (char *)""};
uvm_sv_framework_id = BP(register_framework)((char *) "UVM SV",
frmw_ids,
sv_get_required_api());
...
return uvm_sv_framework_id;
}
The adapter instantiates a child proxy when the user facing API requests a remote child created. The
constructor of the child proxy calls this API in the backplane, passing the necessary parameters to
the target framework where the adapter creates the matching parent proxy.
The UVM-SC adapter calls this API in the constructor of the child proxy as follows:
The UVM-SV adapter calls this API in a function of the child_component_proxy as follows:
Pre, post and runtime phases must be propagated top down on the unified hierarchy tree. The child
proxy indicates the start of a new phase through the appropriate API in the backplane. The
backplane propagates the phase change to the appropriate parent proxy.
The UVM-SC adapter propagates the build phase from the child proxy as follows:
The UVM-SV adapter propagates the build phase from the child proxy as follows:
TLM Communication
Connect port to export or initiator to target socket.
This call to the backplane registers the connection in the backplane repository. The ports and
sockets are identified by their unified hierarchy path.
The UVM-SC adapter propagates the connect command from the user facing API as follows:
The UVM-SV adapter propagates the connect command from the user facing API as follows:
The transaction data initiated in the framework is serialized to a bit-stream and then sent to the
backplane. The backplane uses its repository to find the target of this interface and deliver the bit-
stream. In addition, every data transaction carries also the current time to facilitate time
synchronization between the frameworks.
The UVM-SC adapter forwards the call from the user facing API as follows:
The UVM-SV adapter forwards the call from the user facing API as follows:
Framework Synchronization
Broadcast time advancement through the backplane to slave simulators.
For example when the OSCI SystemC simulator is running in slave mode under some other master
simulator, the master simulator must synchronize the slave simulator using this API.
task synchronize();
uvm_ml_adapter_imp::uvm_ml_synchronize();
endtask : synchronize
This section shows examples of the main features that may be provided by the adapter.
Since each framework has rules about what must be connected, the adapter must obey these rules. For
example if a TLM1 put port is intended to be connected to a put export in another framework, the
adapter must provide a local “proxy” export to satisfy the need for matching export, and implement the
Port Registration
The “proxy” ports are created when the user registers a port as an ML port. In the UVM-SV adapter this
is done by a templated method for example for analysis port of T:
void uvm_ml_tlm_trans::write(
unsigned local_port_id,
MLUPO * val
) {
BP(write)(
framework_id,
local_port_id,
void uvm_ml_tlm_rec::write(
unsigned connector_id,
unsigned stream_size,
uvm_ml_stream_t stream,
uvm_ml_time_unit time_unit,
double time_value
) {
uvm_ml_tlm_receiver_base* rec = 0;
try {
ENTER_CO_SIMULATION_CONTEXT();
rec = get_receiver_base(connector_id);
...
rec->write_bitstream(stream_size,stream);
EXIT_CO_SIMULATION_CONTEXT();
}
CATCH_KERNEL_EXCEPTION_IN_RECEIVER_2_VOID
}
Blocking Interface
Supporting blocking calls between frameworks require special consideration because different
frameworks may have different stacks. The UVM-SC and UVM-SV adapters implement “fake” blocking by
separating the call to initiating, waiting and completion steps.
Initiating a blocking call is done by sending the transaction to the target framework using the request
interface. For example the TLM1 blocking get uses the following backplane interface:
Next the calling adapter registers an event to be triggered when the call is done and waits. When the
target framework implementation is done, the backplane indicates “notify_end_blocking” which triggers
the appropriate event to release the calling adapter. Then the result is obtained through the backplane
by the get_requested API.
arg->nblocks = BP(get_requested)(
framework_id,
port_connector_id,
call_id,
arg->val
);
Serialization
Data transferred between frameworks is passed through the backplane as a bit-stream. The adapters
are responsible for serializing and de-serializing the data based on the matching type definitions in the
frameworks. The serialization algorithm is aligned with the existing UVM mechanisms, so both the UVM-
SV and UVM-SC adapters make use the existing mechanism.
The UVM-SC adapter uses the uvm_packer provided in UVM-SC. The code for the packer can be found in
ml/frameworks/uvm/sc/base/uvm_packer.*. The code for the UVM-SC serializer is in
ml/adapters/uvm_sc/common/uvm_ml_packer.*.
The UVM-SV adapter uses the built in mechanism in UVM-SV that provides packing for uvm_object and
uvm_component when declared by the uvm_object_utils macro. The code for the UVM-SV adapter
serializer is in ml/adapters/uvm_sv/uvm_ml_serializer.*.
To support unified hierarchy the adapter must define and implement the parent_proxy and child_proxy
classes. An example can be found in ml/adapters/uvm_sc/common/uvm_ml_hierarchy.*.
Parent Proxy
The parent proxy is implemented as a uvm_component created by the required API to create a sub-tree.
Its create method instantiates the requested child component under the parent proxy.