Professional Documents
Culture Documents
Ipcm SVM
Ipcm SVM
Product Incisive® Plan-to-Closure Methodology contains technology licensed from, and copyrighted by: Open SystemC
Initiative, Inc. and is © 1996-2006 by all contributors, all rights reserved; (c) 2007-2008 Cadence Design Systems, Inc., and
(c) 2007-2008 Mentor Graphics, Inc., all rights reserved. Associated Apache license terms may be found at
$SOCV_KIT_HOME/.
Trademarks: Trademarks and service marks of Cadence Design Systems, Inc. contained in this document are attributed to
Cadence with the appropriate symbol. For queries regarding Cadence’s trademarks, contact the corporate legal department at
the address shown above or call 800.862.4522. All other trademarks are the property of their respective holders.
Restricted Permission: This publication is protected by copyright law and international treaties and contains trade secrets and
proprietary information owned by Cadence. Unauthorized reproduction or distribution of this publication, or any portion of it,
may result in civil and criminal penalties. Except as specified in this permission statement, this publication may not be copied,
reproduced, modified, published, uploaded, posted, transmitted, or distributed in any way, without prior written permission
from Cadence. Unless otherwise agreed to by Cadence in writing, this statement grants Cadence customers permission to print
one (1) hard copy of this publication subject to the following conditions:
1. The publication may be used only in accordance with a written agreement between Cadence and its customer;
2. The publication may not be modified in any way;
3. Any authorized copy of the publication or portion thereof must include all original copyright, trademark, and other
proprietary notices and this permission statement;
4. The information contained in this document cannot be used in the development of like products or software, whether
for internal or external use, and shall not be used for the benefit of any other party, whether or not for consideration
Disclaimer: Information in this publication is subject to change without notice and does not represent a commitment on the
part of Cadence. Except as may be explicitly set forth in such agreement, Cadence does not make, and expressly disclaims, any
representations or warranties as to the completeness, accuracy or usefulness of the information contained in this document.
Cadence does not warrant that use of such information will not infringe any third party rights, nor does Cadence assume any
liability for damages or costs of any kind that may result from use of such information.
Restricted Rights: Use, duplication, or disclosure by the Government is subject to restrictions as set forth in FAR52.227-14
and DFAR252.227-7013 et seq. or its successor.
Terms of Use: Permission to internally use, modify, or distribute these source code files is hereby granted, provided that the
following conditions are met:
Customers may only modify or use this material for internal purposes related to integrated circuit design within Cadence's
design and verification environment. Customer shall not (1) copy this material except to the extent necessary to fulfill the
purpose stated above; (2) distribute this material to any third party without express written permission from Cadence; (3) allow
this material to be accessed or used by third parties or anyone other than customer's employees whose duties require such access
or use; or (4) distribute these materials for profit or commercial advantage.
Subject to compliance with the terms and conditions herein and the agreement under which this material is provided (the
“Agreement”), Cadence agrees to provide, or have provided, Maintenance Service as defined in the Agreement for the Term of
Use specified in the Agreement.
THIS SOFTWARE IS PROVIDED “AS IS” COMPLETELY WITHOUT ANY WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS ARE DISCLAIMED.
IN NO EVENT SHALL CADENCE BE LIABLE FOR ANY CLAIMS OR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, PROFITS, OR BUSINESS INTERRUPTION)
HOWEVER CAUSED, OR ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Contents
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Index-1
This book addresses all of the above requirements, describing the whole process of system verification
from planning to closure. This book focuses on system verification using e. For information about the
SystemVerilog implementation of system verification, refer to A Practical Guide to Adopting the
Universal Verification Methodology (UVM), available from the Cadence website.
Cadence also recommends reading Chapter 7, “Module-to-System Verification” in the UVM e User
Guide, which describes the methodology for creating e system universal verification components
(UVCs).
1. Create a feature-based verification plan that defines what features will be verified, independent of
how they will be verified.
2. Link the identified features to metrics that determine when the feature has been fully exercised.
For most verification environments, Cadence recommends using coverage as the measurement.
When planning a system verification environment, record what must be verified from the perspective of
each of the stakeholders, and identify how to measure progress towards each of the goals. In system
verification, the goals should be focused on the system level, assuming protocol- and module-level
verification were done at an earlier stage. For example, the vast majority of input should be valid
main-path data, without error injection.
For more information on plan-driven verification, see Chapter 1, “Introduction” in the Verification and
Planning (VPM) Manual.
Bugs in new modules Any new module created for the project is, of course, considered more
dangerous than trusty old modules.
Wrong assumptions The specs of the modules might be unclear or underspecified.
about some module
Bad module One of the supposedly pre-verified modules might not be well verified after
all. Suspect the following:
1.1.2 Checking
Here is a brief summary of checking considerations:
● Existing module checks should stay. Turn off only for performance.
● The system is more than the sum of its parts. Add checks for:
● End-to-end
● New semantics
● Monitors should broadcast the main events and the collected data items. You can use ports for this.
In that case, the monitor should broadcast on the ports, and the system integrator must bind the ports.
1.1.3 Coverage
Here is a brief summary of coverage considerations:
While looking into the details on performance enhancements, keep in mind the trade-off between
performance and detailed verification. After reaching a certain point, enhancing performance requires
giving up some detailed verification capabilities, and vice versa. For example, the more checks you
have, the slower your verification environment runs. The goal is to maximize performance with
minimum penalties. In other words, the goal is to find the best balance, accomplishing essential
verification while omitting those parts that are of little or no importance in the scope of system
verification. The basic guideline for boosting performance is to identify what is required for system
verification and what can be omitted.
Lean generation In system verification, you can assume that the low-level modules that
handle protocols are verified. There is no need to create exhaustive variations
of data items. On the other hand, you want to create valid input that exercises
all of the functionalities of the system. For that, the environment does not
need many variations of the data type, and test writers do not need detailed
and powerful control over generation. Just help them create the basic main
path input. For details, see “High-Performance Generation” on page 1-5.
Lean protocol checks Protocol checks require many accesses to the DUT. Those accesses are
costly in respect to performance. Check performance can be improved in
two ways:
● During the planning phase, decide which checks are relevant in the
context of the system and which can be eliminated from the system
verification environment.
● Prefer features with a relatively modest impact on performance. For
example, use assertions and transactions (created by the transaction based
collectors) as input to the checks instead of directly accessing the device.
Lean coverage During the planning phase, when determining the goals of the system
verification process, eliminate coverage points that are not part of your
system level metrics.
Note Begin with the end in mind. Before beginning module verification—and even while designing
your architecture—plan the system verification. Knowing in advance the goals of each phase in the
verification plan as well as the various types of data required in each phase will result in a better-designed
environment with maximum reuse capabilities. For more information, see also “UVC Reuse Over
Abstraction Levels” on page 1-11.
Note We recommend using Specman and IES profilers for recognizing performance bottlenecks in
the environment.
● “Simplified Data Items” on page 1-6: Data item definition and generation
● All physical fields of the item (that is, all fields written to the DUT)
● Only the very basic virtual fields
● Only the very basic constraints (most generation will be done procedurally)
For reuse, add to the data item a field indicating its level of complexity.
}; // cdn_ahb_burst
‘>
FULL Subtype
<‘
extend FULL cdn_ahb_burst {
// Virtual fields, allowing fine control on data item
// generation and injection
}; // FULL cdn_ahb_burst
‘>
BASIC Subtype:
<’
extend BASIC cdn_ahb_burst {
// Hard-constrain the basic fields to default or simple values
keep incr_size in [1..16];
};
’>
● The same instance is overwritten every time a data item is sent, hence memory allocation is reduced
● Values are assigned procedurally, and not by the constraint solver
A semaphore should be implemented in the BFM, synchronizing parallel sequences accessing the single
item.
For performance enhancements, sequences can call these methods instead of doing items.
1. Implement within the sequence methods the control the stimuli (read, write, send, etc.).
● In one mode, the BFM runs in an endless loop, getting items from the driver (a regular
PULL_MODE implementation).
● In the other mode (BYPASS_DRIVER mode), the loop is disabled.
3. Create tests that put the BFM in BYPASS_DRIVER mode. Then, instead of doing items, call the
read/write methods.
bfm.cur_burst_locker.lock();
bfm.cur_burst.size = size;
bfm.send_cur_burst();
bfm.cur_burst_locker.release();
};
};
’>
’>
’>
Documentation on the relationship between module and system e UVCs and the methodology of
constructing a system verification environment while reusing module-level components can be found in
Chapter 7, “Module-to-System Verification” in the UVM e User Guide.
Term Definition
Bidirectional Moving in two (opposite) directions; used with respect to data transfers.
Delay Model A representation of timing for a device. For example, the time it takes to execute a
function or algorithm.
Term Definition
Get In context to the TLM interface, consumes data. Similar to reading from a FIFO.
IP Intellectual property.
Peek In context to the TLM interfaces, reads most recent valid value. Similar to reading
of a variable or signal.
Poke In context to the TLM interfaces, overwrites data and can never block. Similar to
writing a variable or signal.
Protocol Layer Protocol-specific code, for example, USB or AHB. This layer adapts between the
user and transport layers. The protocol layer is typically defined and supplied by IP
vendors.
Put In context to the TLM interfaces, queues data. Similar to writing to a FIFO.
Slave Agent that passively waits for requests and returns a response.
TLM Platform A collection of TLMs used to model a system. Can provide mixed levels of TLM
abstraction views (for example, PV, PVT, CC).
Term Definition
Transport This layer uses the OSCI TLM generic data transport APIs. Models implement the
Layer transport interface.
Unidirectional Moving in only one direction. Used with respect to data transfers.
User Layer A protocol-specific API targeted for embedded software development. This layer is
typically defined and supplied by IP vendors.
Transaction-level models (TLMs) are architecture models that exchange transactions instead of signals.
Figure 3-1 on page 3-4 shows an example of a PV, programmers view, TLM bus platform. The example
includes transaction-level models for processor models (master0pv and master1pv), a bus model (xbuspv)
and some memory models, slave0pv and slave1pv. In this platform, the xbuspv bus model is connected to
two masters, master0pv and master1pv, and two slaves, slave0pv and slave1pv.
SystemC provides a standard library for modeling devices. Specialized constructs help define modules,
their processes and ports as well as channels used to communicate between modules. The OSCI TLM
Library defines a standard for interfacing these modules, by essentially providing a set of method
declarations to exchange transactions. The TLM developer will implement their models for a system
(using processes to model behavior), as well as the channels that implement the communication fabric
(for instance, the implementation of the TLM interface). In a bus platform the channel is the bus.
Different levels of detail can be incorporated into the transactions used to communicate over the bus (for
instance, channel). The model’s processes can also vary in the level of detail they utilize the transaction
information. The models use ports to access the interfaces implemented by the channel. Figure 3-1 uses
the term thread for a process that can be suspended and re-activated, has the ability to continuously run
for the lifetime of a simulation and can have a sensitivity list which activates it. sc_port and sc_export
are SystemC port constructs, and the xbuspv model implements a TLM interface, and is thus a channel.
Also noted are several typedefs used in the vr_xbus_sc_tlm package (in particular, the REQ_TYPE and
RSP_TYPE types, which are the transactions exchanged across this system). More details on the XBus
platform and these types can be found in “The vr_xbus_sc_tlm Package” on page 3-33 and throughout
this document. Essentially, the typedefs show in the figure below are used to simplify the syntax and
readability for the end user. For example, the masters are connected to an initiator port, which from the
legend is an sc_port. This port is bound to the bus’ target port, which is shown as an sc_export in both
the typedef’s and the legend. The bus’ r_port, also an sc_port, is bound to the slave target ports, which
are exports. sc_ports require a particular interface to communicate information through whereas
sc_exports provide the interface implementation in which to communicate. See also Section 3.2.4,
“Defining TLM Types,” on page 3-21 for more info on typedefs and other user defined types in the
vr_xbus_sc_tlm example.
XBUS_INITIATOR_PORT_TYPE
TARGET_PORT_TYPE
slave0pv
master0pv
TARGET_PORT_TYPE r_port
slave1pv
xbuspv
XBUS_INITIATOR_PORT_TYPE
master1pv
an sc_port
an sc_export port binds to channel
a thread
Identifying the data flow and synchronization details of your system with the PV models provides
insight on system bottlenecks, even without implementing the microarchitecture of the blocks. The
bottlenecks identified at this level provide insight into where you will have to do further architectural
analysis. Depending on the level of analysis required, you can develop more detailed models for critical
blocks or subsystems within the SoC. For the less critical areas of your system, you can use the
previously created PV models. The PV models can be reused for any other derivative SoC and for
mixed-abstraction simulations. In some cases, the PV models can be used to target other platforms.
By following the steps in “Creating Your Own TLM Platform” on page 3-13 and referring to “The
vr_xbus_sc_tlm Package” on page 3-33, you will be able to implement a high-level view of your
system’s functionality significantly faster than implementing RTL. A key feature of this high-level view
is that it is relatively simple and usable by several specialists using your overall design and verification
flow.
Transaction models should be defined in the context of the larger system design flow from algorithmic
development through RTL implementation.
Algorithmic development considers the behavior of the system without considering the architectural
implications of implementing it in hardware (HW) and software (SW). This modeling style assumes
infinite resources thus there is no model of time, only explicit dependencies typically specified as
semaphores.
When taking into account the implementation of the system in hardware and software, there are a fixed
set of resources (bus bandwidth, processor speed, memory, and so forth). Modeling the impact of
architecture implies modeling the time needed to run computations on a processor or on a peripheral. It
also needs to model transferring data between computational units (for example, buses, bridges, DMAs,
caches, interrupt controllers). This is what we mean by transaction-level modeling.
On the other end of the scale is an RTL design, which considers the details of the microarchitecture by
modeling every signal with full accuracy.
Table 3-2 on page 3-6 characterizes different abstraction levels for transaction-level models. The
abstraction level names used here are well known, however not standardized. Other abstraction level
names are being used by many companies. Other terms may have slightly varied definitions, but for the
most part are generally similar.
The fastest level is the programmer’s view (PV), which is appropriate for developing embedded
software as well as early full-system verification. The next level, programmer’s view + timing (PVT),
adds some timing to make it transaction accurate. The PVT level is appropriate for benchmarking
software and high-level architecture evaluation. The lowest level is cycle callable (CC), which is
appropriate for final HW/SW co-verification. This level is faster than RTL because it models data
communication as high-level data structures instead of individual signals thus reducing the number of
simulation events.
Development
Name Use Model Accuracy Speed Difficulty
Full-system
verification
3.1.3 PV Modeling
PV models like those in Figure 3-2 on page 3-7 capture the SoC architecture as seen by the embedded
software developer. This requires that the PV platform should accurately model the programmable
registers. Device drivers can be implemented to the register interface and diagnostic tests can be written
that read and write each register.
XBUS_INITIATOR_PORT_TYPE
TARGET_PORT_TYPE
slave0pv
master0pv
TARGET_PORT_TYPE r_port
slave1pv
xbuspv
XBUS_INITIATOR_PORT_TYPE
master1pv
an sc_port
an sc_export port binds to channel
a thread
To functionally run embedded software, the platform model does not need to model the performance of
the platform (bus contention, slave wait response, and so forth). All software that runs on a real device
is guaranteed to run on the PV model of the hardware platform. However, the reverse is not guaranteed.
Programmers may develop software on a PV platform, but the resulting code may be timing dependent
and thus fail on the real device. To minimize this problem, the programmer should not rely on timing
aspects of the platform. For example, the code shouldn't rely on a peripheral being slower than another
because in some circumstances this might not be true.
To reduce simulation time and make models easy to write, the PV model is defined with no clocking and
no explicit timing. Minimal synchronization is needed to enable correct functionality. For example, the
master must yield to the simulation scheduler in order to let interrupts be processed. How often the
master yields to the simulation scheduler should be user defined.
Supporting transactions that span multiple clock cycles is another implication of not modeling the
performance of the platform. For example, a burst bus request can be modeled as a single transaction
because the behavior only depends on the entire data burst being transferred. It is not concerned with
how long it takes to transfer the data to the slave; nor is it concerned with pipelining of the data. This
does have implications on visibility while debugging the hardware platform.
In a TLM platform, each of the architecture components (processors, busses, peripherals, and such) are
instantiated, connected in a netlist and configured as in Figure 3-1 on page 3-4. The masters (such as
processors) make read/write requests to the bus with address and data. The behavior of the bus decodes
the address and transports the read/write requests to the appropriate slave.
The function calls between components are blocking and reactive. A blocking function call is suitable
since a PV model isn't concerned with time and thus no parallel activity is explicitly modeled. The
processor can make a direct call to the bus which can immediately call the slave. Since this takes zero
time (no bus contention and no slave waiting), the simplest approach of a blocking function call is
appropriate for the PV master. Note also that the slave is reactive to the function call. It does not need to
wait on any event or clock.
Although this model is not cycle-accurate (because it does not model any clocking or timing), these
models are simple to write and the expected simulation speed is 100KHz-1MHz (almost real-time).
Until now, to obtain this speed, the software developer had to wait for a prototype board of the design.
Using the a TLM methodology, a PV model of an SoC can be provided six to nine months earlier in the
design with approximately 1/10th to 1/50th the amount of effort that it takes to create the RTL. This
facilitates software development in parallel with the hardware implementation and thus shortens the
overall project schedule.
The figure also shows the delay processing localized in the generic bus. Here we see arbitration, request,
response, and bus transfer delays centralized in the generic bus. Significant performance gains have
been shown with centralized delay processing, given the reduction in context switching. This
architecture allows for a specific bus platform, such as a specific processor, to implement a delay model
more exactly to is specific implementation. For example, if arbitration is required, a delay model can be
incorporated for different types of arbitration schemes. This enables higher levels of reuse of the
surrounding models across different platforms, while easing the modeling requirements for TLM
developers and users, while still increasing accuracy of the system delay model. This reflects the
vr_xbus_sc_tlm package delay processing implementation.
bus transfer
delay
With the addition of timing, programmers can benchmark their software application or find problematic
areas between hardware and software. For example, given different arbitration schemes or processor bus
delay models, the same master and slave code can be reused to determine a master’s average latency per
variant. This will give confidence that the specified architecture (for example, bus platform, model
architecture, and so forth) will support their application.
XBUS_INITIATOR_PORT_TYPE
TARGET_PORT_TYPE
slave0pvt
master0pvt
TARGET_PORT_TYPE r_port
slave1pvt
xbuspvt
master1pvt
an sc_port
an sc_export port binds to channel
a thread
Note This release does not address peripheral point-to-point delay processing.
The programmer’s view can be refined with a timing model by adding timing constructs, as depicted in
Figure 3-3 on page 3-9. The technique is also referred as trace based modeling. The PV model generates
a trace of activity in the model and then sometime later these traces are played out to determine the
timing. In the extreme case, the timing is executed after the programming model completes. In order to
debug the model it is better to intersperse behavior and timing, enabling the user to deduce the cause and
effect between the system behavior and its performance. The vr_xbus_sc_tlm package implements the
extreme case in this release. Thus, after the devices provide a request or response, a delay, that is, a
timing construct, is processed specific to the request or response.
Of course, the extra level of detail causes the simulation performance to decrease. Thus a second
important feature of separate timing constructs is that the timing can be turned on/off during dynamic
simulation. The configuration interface, set_timing_mode() can be used. If a problem is detected after
many minutes of simulation, the simulation can be run in PV mode (fast) until shortly before the
expected problem and then timing can be turned on to further inspect the timing related problem. After
running to the point of simulation that you are interested in, follow the steps in “Dynamic Timing Mode
(PV/PVT) and Delays” on page 3-53 to switch timing modes or processing delays.
In “PV Transaction Level Model Configuration” on page 3-4, the REQ_TYPE and RSP_TYPE definitions
are typedefs for the REQPV/PVT and RSPPV/PVT types being referred to in the descriptions above. The
section “XBus Compound and Scalar User Layers” on page 3-40 provides details on the request and
response classes defined in the vr_xbus_sc_tlm package source code example.
Note The PVT mode and delay data members can be set at construction or at run time. Command line
options can be provided at run time to avoid recompilation. This capability will be implemented in a
future release.
3.1.5 CC Modeling
A cycle-callable view of a TLM is an accurate representation of the microarchitecture. The platform
should model the performance impact of pipelining, communication protocols and structures,
out-of-order communications, and split-responses. By modeling these additional details of the
architecture, the platform can be used for detailed performance optimization through detailed platform
parameter tuning. Hardware and real-time software verification is also enabled at this level.
The accuracy of this model can be bus-accurate down to cycle-accurate. Bus-accurate models define
transactions at the level of a bus transfer which might span multiple cycles. For example, the model
supports bursts across the bus as a single transaction. Cycle-accurate models have transactions within a
single cycle, so a burst across the bus would be modeled as separate cycles, thus providing a more
implementation accurate representation. Transactor models can convert between bus-accurate and
cycle-accurate models.
In order to model pipelining, the interface between the models must be non-blocking. For example, the
processor should be able to make a bus request and continue to run additional instructions (those not
dependent on the result of the first bus request). This is in contrast to the PV technique of using function
call interface between models. In the CC model, the master makes a request and then the data is returned
sometime later. The model must be written to handle the asynchronous nature of the returned data which
also implies that data may be returned out-of-order and/or might be canceled en route.
At the CC level, models are sensitive to a clock, gated clock, or similar event. The model’s process is
“called” at every cycle (thus the name of this abstraction level). Once triggered, the model polls its
interfaces to see what is enabled. For bus-accurate platforms, slaves check for bus requests and masters
check for transfer completions or interrupts. For cycle-accurate platforms, slaves check for
address_ready and data ready signals. Note that calling the process every clock causes a context switch,
which impacts performance simulations. That is, the more context switches you have, the slower your
performance.
Figure 3-5 on page 3-12 shows an example of a message sequence chart of a CC model. A master
(processor) makes requests of the bus arbiter: polls for bus grant, requests a read request of a specified
address, and then polls for the slave response. This level of granularity is much finer than the PV model
and thus exhibits slower simulation speed as well as being a more difficult model to write. The positive
feature is that software can be completely validated at this level and be guaranteed to work on the final
SoC implementation.
This level of abstraction begins to approximate RTL by modeling multiple signals and more detail
relative to clocking. A single transaction structure provides higher level encapsulation, thus reducing the
number of ports on a model. This makes it easier to assemble the components into a platform (reduced
number of wires). In addition there are a reduced number of simulation objects and events thus gaining
performance over RTL. The processes themselves can execute every aspect of the model’s behavior.
A high-level example of a PV/CC Platform is provided in Figure 3-6 on page 3-13. In this example, the
PV master instance, master0pv, and the CC XBus instance, XBUSCC, are connected via an adaptor,
AdaptorCC/PV. This mixed level of abstraction shows reuse of PV models when refining other portions
of the system. The adaptor will translate data and control going in both directions. While a single
interface is utilized between the PV master and the adaptor, an interface per phase is shown in the
adaptor connection to the bus and with the slave peripheral. On the slave side, an adaptor would be
required to communicate with a non-CC model. Adaptors simply translate between the transaction
objects defined for each level of abstraction, including signals. Clocking is added at this level, thus
adding a high level of accuracy for model interaction. Processes utilize the clock and other events it is
sensitive to execute its behavior using the information provided by the adaptor.
Arbitration Phase
Data Phase
Data Phase
AdaptorCC/PV
XBUSCC
CLK
This section describes installation requirements, build procedures, and command line options available
for the current version of the TLM library. It discusses system requirements for running the example, the
build procedure to simulate the example, and how to determine if the results of the installation are
correct. Finally, it describes the directory structure of the library and included packages.
3.2.1.1 Requirements
To use the TLM library packages, you must install the following:
To configure your environment and run the demo for the tlm_lib vr_xbus_sc_tlm package:
● Set up your simulator environment variables correctly (see the simulator documentation).
To verify that the package and your environment are set up correctly:
4. Compare your output log file to the golden example log file provided (the following lines are all one
command):
diff outdir/ncsim.log \
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/golden/
RUN.XBUS.PVT.MAX.nc \
im.log
Standard C++ print utilities are being used, so there should be no differences in the outputs (the
version of the simulator might be different) unless the installation or build procedures were not
followed correctly.
Examples
To run the demo using the default settings:
`socv_which.sh vr_xbus_sc_tlm`/demo.sh
To run the demo specifying the XBus PVT model with minimum priority bus arbitration in interactive
mode:
`socv_which.sh vr_xbus_sc_tlm`/demo.sh -test RUN.XBUS.PVT.MIN -run_mode
interactive
At the top level, the directory structure includes a TLM library specific directory, tlm_docs, and the
relevant packages. Similar to eRM, a demo.sh and PACKAGE_README.txt exist. See the eRM
package information for details.
libraries
sc_tlm_design_lib tlm_lib
vr_xbus_sc_tlm
demo.sh
PACKAGE_README.txt
sysc
golden
results.log
src
docs
examples
Other directories within a package include sysc, src, docs, and examples. The sysc directory includes all
SystemC TLM models and top level source files. A golden directory is provided for several reasons.
First, to verify your installation is correct, log files for each of the configurations that are provided in this
package. Secondly, the golden directory can be used to store output from TLM models. Given a systems
function, for example, MP4 encoding, a simulation might provide a source AIFF file to the system,
which then generates an MP4 file. This output file may be used when doing comparisons in future RTL
simulations. The src directory is optionally used to store software that is specific to the package.
Examples include drivers or HCE, host code execution, or code that is being verified. The docs directory
contains package-specific documentation. The examples directory stores all simulation infrastructure
files, for example, scripts, for the package. These files are referenced by the demo.sh script.
For TLM model file naming, Cadence recommends incorporating the methodology/vendor prefix (for
example, vr_, tac_), protocol name (for example, xbus), model type (for example, master, slave, timer,
bus). Examples include:
● vr_xbus_master.{h,cc}
● vr_xbus_mem_slave.{h,cc}
● vr_xbus_router.{h,cc}
Note The optional src directory is not used for the vr_xbus_sc_tlm directory.
While reviewing the specification, some important pieces of information to note are:
From the specification, and from a PV perspective, the XBus protocol supports master read and write
requests of different sizes. The transfer of data includes arbitration, address, and data phases. The data
phase is the phase that peripherals respond to the master’s request. The responses include ERROR (and
SUCCESS) and can incorporate wait states.
The address, data, size, wait, read, and write signals in Table 1, Bus Signals, from the XBus
specification located at /vr_xbus/docs/xbus_spec.pdf., are being modeled in this TLM platform
example. Additionally, FIFO, MAX_PRIORITY and MIN_PRIORITY arbitration modes will be
analyzed to determine average master latency in several variant platform definitions.
This collection of information helps define class properties and behaviors, and variables to use when
implementing the system. See “Defining TLM Types” on page 3-21 for more information.
in memory. The details for accessing these memory locations are not required at this level of the
implementation. What is minimally required is the information to communicate the read and write
request and the peripherals response. Enabling communication of this information is what a user layer
provides. However, different users have different needs. Some users may be adept at using many C++
constructs like template classes and compound types (for example, user-defined class definitions with
data members) as part of the read/write requests. Others will prefer a very simple mechanism such as a
scalar type, like native ints and unsigned ints, for doing read and write requests. The user layer definition
in the vr_xbus_sc_tlm package takes this into consideration. One drawback in using a scalar interface is
in defining the entire type of request. Examples of this are determining whether the transfer should be a
burst transfer, or what type of metadata should be included. For example, how to track and relate
requests with responses to do an architectural analysis of the system’s latency requirements that is
required to track information in the environment correctly. The vr_xbus_sc_tlm package utilizes a
configuration interface to address this usage. It is up to the TLM modeler to implement the protocol and
transport layers.
Figure 3-9 on page 3-21 shows a basic example of the code structure of a simple PV system. At the
master level, the platform provides several interfaces, such as user layers. You can choose to use a scalar
interface with read() and write() methods in combination with a configuration interface. A second
option uses a compound interface to set up your fields for a read or write request.
For users less interested in C++ syntax, a scalar user layer might be preferred. For example, PV master1
can make a simple read request through an initiator port that implements the scalar user layer. This use
model has PV master1 configuration parameters initialized when the master is instantiated, such as via
constructor parameters. The arguments to the read function include the requested address as well as a
place holder for the value of the return data. The thread or method with the “read” request will be
blocked until the data is updated. For now just assume that the data will be updated at some point. A
status is provided for you based on the success of the request. If you would like to change the
configuration parameters defined at construction, these parameters can utilize the master provided
configuration interface. You can see a simple example in Figure 3-9 on page 3-21, where PV master1
sets the size of the read request to EIGHT.
Similarly, PV master2 makes a read request; however, a request object type, REQ_TYPE, is used. This
request type contains all of the user parameters required for defining a request. Copy and assignment
constructors are provided. The only argument to the read function is the request object. The function is
also blocking, and returns a response object type, RSP_TYPE, containing appropriate status, response
data, and metadata (among other types of information discussed later in this document). If you do not
know these terms, you might prefer the scalar interface discussed above. This approach lends itself more
to transaction-based communication and object-oriented programming. This use model is expected to be
preferred by more experienced programmers.
The user layer, including the compound, scalar, and configuration interface, used by the masters is
implemented in the initiator port shown in Figure 3-9 on page 3-21.
PV Master 1 PV Master 2
REQ_TYPE REQ;
xbus_status status; RSO_TYPE RSP;
initiator->set_size(EIGHT); REQ.a = a;
status = initiator->read(a, &d); REQ.size = EIGHT;
RSP = initiator->read(const
REQ);
initiator port
target port
router port
PV Memory Slave
read(a, &d) {
d = memory[a];
}
See Also
● “XBus Compound and Scalar User Layers” on page 3-40
A key to helping end users succeed in using a TLM model is make the use of it as simple as possible.
TLM developers can leverage language constructs to minimize unintended use of different types and to
hide complex C++ syntax. TLM developers should understand how end users must use the TLM model.
Developers should define use cases that the TLM model will be expected to meet.
xbus_protocol.h
48 enum xbus_timing_mode
49 {
50 PV, PVT
51 };
52
53 enum xbus_size // Number of bytes to transfer on the bus
54 {
55 ONE = 1,
56 TWO = 2
57 FOUR = 4,
58 EIGHT = 8
59 };
60
61 enum xbus_arbitration_mode
62 {
63 FIFO, ROUND_ROBIN, MIN_PRIORITY, MAX_PRIORITY, SLOT
64 };
65
66 enum xbus_request_type
67 {
68 READ , WRITE
69 };
71 enum xbus_status
72 {
73
74 ERROR , SUCCESS
75
76 };
xbus_master_base.h
typedef xbus_protocol::xbus_initiator_port
<ADDRESS,DATA,REQ,RSP,TLM_BUS_IF,STATUS,1> XBUS_INITIATOR_PORT_TYPE;
typedef xbus_master_base<ADDRESS_TYPE, DATA_TYPE, REQ_TYPE, RSP_TYPE,
TLM_BUS_IF_TYPE, xbus_status> XBUS_MASTER_BASE_TYPE;
router.h
typedef tlm_transport_if< REQ ,RSP > if_type
typedef router_port< if_type > r_port_t;
bus_types.h
typedef unsigned int ADDRESS_TYPE;
typedef std::vector<int> DATA_TYPE;
typedef xbus_request<ADDRESS_TYPE,DATA_TYPE> REQ_TYPE;
typedef xbus_response<ADDRESS_TYPE,DATA_TYPE> RSP_TYPE;
typedef tlm::tlm_transport_if< REQ_TYPE, RSP_TYPE > TLM_BUS_IF_TYPE;
typedef sc_export< TLM_BUS_IF_TYPE > TARGET_PORT_TYPE;
xbus_slave_base.h
typedef xbus_slave_base<ADDRESS_TYPE, DATA_TYPE, REQ_TYPE, RSP_TYPE,
TLM_BUS_IF_TYPE, xbus_status> XBUS_SLAVE_BASE_TYPE;
bus_types.h
#define MAX_MASTER_SIZE 20
Preprocessor macros are also helpful when concrete types are not available for a template definition.
The example vr_sc_tlm_xbus package provided in the TLM library provides Doxygen documentation.
It is located under
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/docs/doxygen.tar.Z. The
commenting styles used in this package followed guidelines and examples located at the following
locations
● http://www.cisst.org/resources/software/cis/doxygen-documentation.html
● http://www.stack.nl/~dimitri/doxygen/commands.html
A list of links representing the groups of modules provided in the example are displayed:
● MASTER
● SLAVE
● BUS
● CONVENIENCE_LAYER
● PROTOCOL_LAYER
● TRANSPORT_LAYER
● REQ_RSP_TYPE
4. Click any of the module links and traverse the code by clicking links or objects.
Inheritance, collaboration, and header dependency graphs are displayed. Comments for many types
and files are also included.
The OSCI TLM API focuses on SystemC interface classes. By defining a small set of generic, reusable
TLM interfaces, different components can implement the same interfaces enabling higher levels of
interoperability. These interfaces can be implemented as a C++ function in an interface class (derived
from sc_interface) or in other modules/channels in the system that implement the interface (for example,
a channel). To avoid memory management issues in a multi-threaded environment like SystemC, object
passing semantics are utilized, similar to sc_fifo, which effectively pass-by-value. This is a mechanism
which avoids susceptibility to problems associated with the use of raw C/C++ pointers (for example,
core dumping). Depending on the size of the modeled system, this type of semantic can impact
performance. Other techniques (not covered here) can be utilized to address memory management to
improve performance for these systems.
The TLM interface categories include unidirectional and bi-directional. Each include blocking and
non-blocking definitions. When calling a non-blocking method, you can assume that the method will
return at some point. In contrast, a blocking method may never return control to the calling thread or
method.
● tlm_poke_if<T>
● tlm_peek_if<T>
● tlm_put_if<T>
● tlm_get_if<T>
● tlm_master_if<REQ, RSP>
● tlm_slave_if<REQ, RSP>
● tlm_transport_if<REQ, RSP>
tlm_transport_if implies same HW as tlm_master_if, but also requests and responses are tightly coupled.
Figure 3-10 shows the primary TLM interfaces. This diagram provides lower-level representation of the
control that is intended to be modeled by the interfaces. This might be more useful for a hardware
designer. Understanding the typical usage of a valid control line with data in a read cycle and the
combination of read valid and data in a write cycle is reasonably straightforward. Mapping synchronous
and asynchronous to registered or combinational logic is also directly mapped, given that any of the
interfaces defined can be clocked or unclocked using SystemC semantics.
As noted, tlm_transport_if implies the same HW as the tlm_master_if, but the request and response are
tightly coupled (as in a bus platform). If you need to model loosely coupled requests and responses, do
not use tlm_transport_if. Instead, interface directly to request and response fifos using the
tlm_master_if. The tlm_master_if and tlm_transport_if can be implemented by a single function call,
OR by two fifos in each direction.
Figure 3-11 on page 3-27 provides an example of the relationships between the unidirectional interfaces.
Primary
Unidirectional
Interfaces
tlm_put_if tlm_get_if tlm_peek_if tlm_poke_if
tlm_get_peek_if
tlm_fifo_put_if tlm_fifo_get_if
Unidirectional interfaces send data in a single direction. Flow of control is in both directions. For
example, if a master needs to place a read address on the bus, this can be considered as a unidirectional
interface. Sending an IP packet can also be considered to be unidirectional. These two examples assume
that flow of control is in one direction. In many real world cases, however, both of these examples would
require interaction with another device (for example, arbiter or fabric) to gain control of the
communication median.
Bidirectional interfaces send data in both directions and the flow of control is in both directions. When
the previous unidirectional read address example is completely executed, the read transaction across the
bus is bidirectional. Control between the bus and master, and data from the bus to the master would
require a bidirectional TLM interface.
Any complex protocol can be broken down into a set of unidirectional and bidirectional accesses that
use the TLM API.
See Also
● “Implementing TLM Interfaces” on page 3-28
In SystemC, sc_ports can be bound to channels and sc_exports that provide more interface
implementations than are actually required. The mechanism that achieves this is C++ implicit
conversion to base classes. Having a hierarchy of interface classes is thus the key enabler for this
feature.
For example, Figure 3-11 on page 3-27 shows the class hierarchy relation for
tlm_non_blocking_get_if and tlm_get_if. This is because a child class has inherited all of its
parents’ interfaces. This would be the maximum interfaces you would be able to provide, for
example, as a channel that was exporting its interfaces. This would increase reuse and
inter-operability across platforms. Therefore, line 1 below is preferred to line 2, since it requires the
minimum set of interfaces.
1 sc_port<tlm_nonblocking_get_if<T> >
2 sc_port<tlm_get_if<T> >
Figure 3-11 on page 3-27 shows the class hierarchy relation for tlm_put_if and
tlm_blocking_put_if. For example, a module developer should require the minimum interfaces
from a channel in order to increase reuse and inter-operability across platforms. This is done by
using the parent class. Therefore, line 1 below is preferred to line 2 since it provides the maximum
set of interfaces.
1 sc_export<tlm_put_if<T> >
2 sc_export<tlm_blocking_put_if<T> >
● tlm_nonblocking_{put,get,peek}_if
● tlm_blocking_{put,get,peek}_if
● tlm_poke_if
● tlm_transport_if, tlm_master_if, tlm_slave_if
Notes
● Slightly prefer requiring non-blocking interfaces over blocking interfaces, especially if refining
to an implementation in which threading support is expensive or non-existent.
● Prefer to provide tlm_{put,poke,get,peek}_if over just the pure non-blocking or blocking
interfaces if it is not much additional modeling effort.
● For bidirectional communication with tightly coupled requests and responses, require and
provide tlm_transport_if. For non-tightly coupled requests and responses, require and provide
the tlm_master_if and tlm_slave_if.
● Only require the tlm_fifo_* interfaces in cases where the port can only be bound to tlm_fifo.
5. Derive from tlm_fifo interfaces (see Figure 3-11 on page 3-27) to connect incompatible interfaces.
tlm_fifo provides all of the put, get, and peek interfaces in blocking and non-blocking forms, so
tlm_fifo can be used to connect any two unidirectional TLM interfaces except tlm_poke_if. For
example, a TLM model can do blocking puts into a tlm_fifo, and non-blocking gets out of it.
Note An implemented TLM interface is a channel. A channel that implements an interface class by
inheriting from that class provides that interface to the outside world. An sc_export<IF> member within
a module or channel provides that interface to the outside world. An sc_port<IF> member within a
module or channel requires that interface from the outside world.
Example
The code snippet below shows part of the implementation of the scalar user layer that was defined in
“Defining the User Layer” on page 3-18. The user layer is the method signature defined. The code within
the user layer that updates specific protocol variables is the protocol layer. The first requirement is
addressed by the function, init_rsp_req(), invoked on line 127 below and defined from lines 245 to 260.
These sections of code address the need to translate a user layer type to a TLM interface type. For the
compound user layer, partially shown from lines 60–75, there is less of a need, since the same types are
used; however…
121 virtual STATUS write( const ADDRESS &a , const DATA &d )
122 {
124 REQ req;
125 RSP rsp;
127 init_rsp_req(rsp, req, WRITE, a, d);
129 rsp = (*this)->transport( req );
131 return rsp.status;
133 }
...
245 void init_rsp_req(RSP &rsp,
246 REQ &req,
247 const xbus_request_type & type, const ADDRESS &a, const DATA & d) {
249 req.type = type;
250 req.a = a;
251 req.d = d;
252 req.size = get_size();
253 req.master_id = get_id();
254 req.master_name = get_name();
255 rsp.master_name = get_name();
256 rsp.master_id = get_id();
257 rsp.size = get_size();
258 req.timing_mode = get_timing_mode();
259 req.delay = get_delay();
260 }
…there are cases where the response type will require updates by the actual peripheral that is
responding. In these cases, the model can use user layers or base classes to update user layer and
transport layer types. The slave memory code snippet shows the use of a copy constructor for this
purpose on line 63. The name of the responding slave is set on line 72. The status is set by either line 67
or 70 and returned on line 73.
60 RSP_TYPE xbus_mem_slave::
61 read( const REQ_TYPE &request ) {
63 RSP_TYPE rsp(request);
65 switch( request.type ) {
66 case READ :
67 rsp.status = read( request.a, rsp.d, request.size);
68 break;
69 default :
70 rsp.status = ERROR;
71 }
72 rsp.slave_name = name();
73 return rsp;
75 }
Several of the above snippets provide examples on how to treat request reference data appropriately and
how to return appropriate responses. Specifically, the examples provided in the vr_xbus_sc_tlm package
provide the same type of memory management as the TLM OSCI kit examples. In particular, argument
types are typically passed as const references, requiring each layer to allocate memory for request and
response types. Exceptions occur when the requesting device utilizes a scalar interface and requires
information from a peripheral. For example, the code snippet below shows, shows the reference DATA
being updated based on the slave response on line 160.
150 virtual STATUS read( const ADDRESS &a , DATA &d ) {
152 REQ req;
153 RSP rsp;
155 init_rsp_req(rsp, req, READ, a, d);
157 rsp = (*this)->transport( req );
159 //Update calling data reference
160 d = rsp.d;
162 return rsp.status;
164 }
1. Derive from the slave base class provided in the vr_xbus_sc_tlm package (see xbus_mem_slave.h).
4. Add types if required to the response types (see xbus_protocol.h and xbus_mem_slave.{h,cc}).
6. Update the transport layer if required (based on parameter types being added).
7. Add the slave to the memory map (see “Memory Map” on page 3-46).
The class hierarchy shown in Figure 3-15 on page 3-38 shows the slave xbus::my_xbus_slave. This is a
good class design example of how to implement another slave to add to a TLM platform.
1. Derive from the master base class provided in the vr_xbus_sc_tlm package (see xbus_master.h).
4. Add types if required to the request type (see xbus_protocol.h and xbus_master.{h,cc}).
The class hierarchy shown in Figure 3-13 on page 3-35 shows the slave xbus::my_xbus_master. This is
a good class design example of how to implement another slave to add to a TLM platform.
The following are examples of the infrastructure provided by the classes defined by the vr_xbus_sc_tlm
package:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/main.cc
See “Build Procedure” on page 3-14 and “Installing This Library” on page 3-14 for examples in
this package on building a simulation script and invoking it.
b. Verify the results.
c. Debug as needed.
This package was developed using the OSCI TLM kit 4.1 example and the
$SOCV_KIT_HOME/svm_lib/sc_tlm_verification_lib/vr_xbus_tlm package. The TLM router, master,
slave, and initiator port were modified as were files from the SVM package. The following sections
document the capabilities of the svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm package. The intention of
this package is for users to develop there own TLMs and platforms using this as a working example.
Where possible, instructions have been provided, using the perspective of what a user would have to
change if something needed customizing for their own environment. These changes are by no means
required to get the vr_xbus_sc_tlm package to compile or simulate. The XBus specification can be
found at designs/xbus/docs/xbus_spec.pdf.
● Mixed-abstraction configurations
● Template code implementation
● Utilization of bidirectional tlm_transport_if
● Compound and scalar user layers
● Arbitration
● Bus monitor synchronization for current request/response
The diagram above shows the usage of an abstract and concrete classes. The abstract classes have a
pattern enclosed with a dotted line. A concrete class will have a slight tint and will be enclosed in a solid
line. The class being discussed will have no fill and a solid line enclosing it. Contained classes will be
depicted with a black diamond and arrow symbol.
In this diagram, the class “Class being discussed” contains the template class
“my_name_space::Contained class<T1, T2>”, which is defined in the namespace my_name_space and
has two parameter, T1 and T2. Derivation is depicted by an arrow symbol. In the diagram, the class
“Class being discussed”, is derived from a concrete class, “Concrete class”, and an abstract class,
“Abstract class”.
The diagram above shows the multiple levels of inheritance for the xbus::xbus_master. At this highest
level is sc_module. This base class derived from sc_module which is the base class of all modules and
hierarchical channels in SystemC. The next level of inheritance is from xbus::xbus_base. It contains the
XBus configuration interface and register utilities for setting, clearing and getting values of bits of status
and control registers to enable users to define functionality of their model. The last level of inheritance
is that of a template base class, xbus::xbus_master_base. The class parameterizes the address, data,
request, response for scalar and compound requests, and the TLM bus interface that will be used. The
usage of template base classes increases the scope of usage of the code provided by enabling different
types to be used to specialize a specific platform as defined by a user. In the diagram we see the usage of
specific types defined in “Using Typedefs” on page 3-23. These concrete types have been defined to
specialize the template for a given set of types defined for this platform. The use of namespaces, e.g.
xbus::, has been used to avoid name conflicts. Users should theoretically be able to use the current set of
code to implement a multi-bus environment with variations in protocol. Of particular note, is the
xbus::xbus_master_base containment, shown by a black diamond and arrow, of an initiator port.
Following sections will discuss the importance of the initiator port for master communication to a bus or
other slave with a target port. Finally, the xbus::xbus_master. This class is used in all of the platforms
provided by this package. It defines several methods which in reality run regressions for each of the
protocol variations defined by the XBus using the user, protocol and transport layers implemented by the
package. The data flow through the system will be further discussed in “General Data Flow” on page
3-39.
Figure 3-13 also shows a class named xbus::my_xbus_master. This is shown only to show the
extensibility of the class hierarchy. A user can copy xbus::xbus_master (located at
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_master.{h,cc}) and
modify or delete the process definitions and add another process that does something completely
different. If the inheritance stays the same, all of the infrastructure code, including the initiator port,
configuration interface, register utilities, etc...., stays in tact, making much easier for the model
developer to create a model that can plug and play in mixed abstraction TLM platform.
Figure 3-14 shows the class diagram for the xbus::xbus_bus class. The XBus bus derives from the
abstract class tlm::tlm_transport_if, the OSCI TLM transport interface and from sc_module, the base of
all modules and hierarchical channels in SystemC. The next level of inheritance is from the template
utils::router class. This class parameterizes the address, request and response types. It routes requests to
a particular address based on a user provided file name, implemented by a string class and a utils::router
construction argument. Information from this file is stored in an address_map<ADDRESS> object and
is queried during simulation to route the requests to a slave within which the address request resides in.
The request is routed to the slave via the utils::router_port. The xbus_router_base is the last level of
inheritance. It redefines the transport layer, from that of utils::router, to include an arbitration phase,
processing delays and architectural analysis. Initiator ports get bound to the bus’ target port to access the
transport layer. The utils::router_port gets bound to slaves. The data flow through the system will be
further discussed in “General Data Flow” on page 3-39.
Figure 3-15 shows the class diagram for the xbus::xbus_mem_slave class. This XBus slave derives from
the abstract class tlm::tlm_transport_if, the OSCI TLM transport interface and from sc_module, the base
of all modules and hierarchical channels in SystemC. The next level of inheritance is from the template
utils::router class. This class parameterizes the address, request and response types. It routes requests to
a particular address based on a user provided file name, implemented by a string class and a utils::router
construction argument. Information from this file is stored in an address_map<ADDRESS> object and
is queried during simulation to route the requests to a slave within which the address request resides in.
The request is routed to the slave via the utils::router_port. The xbus_router_base is the last level of
inheritance. It redefines the transport layer, from that of utils::router, to include an arbitration phase,
processing delays and architectural analysis. Initiator ports get bound to the bus’ target port to access the
transport layer. The utils::router_port gets bound to slaves. The data flow through the system will be
further discussed in “General Data Flow” on page 3-39.
Figure 3-15 above shows the multiple levels of inheritance for the xbus::xbus_mem_slave. At this
highest level is sc_module. This base class derived from sc_module which is the base class of all
modules and hierarchical channels in SystemC. The next level of inheritance includes xbus::xbus_base,
TLM_BUS_IF_TYPE and bus_if. The xbus::xbus_base class provides the XBus configuration interface
and register utilities for setting, clearing and getting values of bits of status and control registers to
enable users to define functionality of their model. xbus::TLM_BUS_IF_TYPE is described in “Using
Typedefs” on page 3-23, and essentially is a specialization of the OSCI TLM transport interface,
typedef tlm::tlm_transport_if< REQ_TYPE, RSP_TYPE >. The slaves of this platform
implement a transport layer compatible with the OSCI TLM transport interface. The slaves also
implement the user layer defined by xbus_protocol::bus_if. The last level of inheritance is that of a
template base class, xbus::xbus_slave_base. The class parameterizes the address, data, request, response
for scalar and compound requests, and the TLM bus interface that will be used. The usage of template
base classes increases the scope of usage of the code provided by enabling different types to be used to
specialize a specific platform as defined by a user. In the diagram we see the usage of specific types
defined in “Using Typedefs” on page 3-23. These concrete types have been defined to specialize the
template for a given set of types defined for this platform. The use of namespaces, e.g. xbus::, has been
used to avoid name conflicts. Users should theoretically be able to use the current set of code to
implement a multi-bus environment with variations in protocol. Of particular note, is the
xbus::xbus_slave_base containment, shown by a black diamond and arrow, of TARGET_PORT_TYPE
target port. Following sections will discuss the importance of the target port for slave communication to
a bus or a master with an initiator port. Finally, the xbus::xbus_mem_slave. This class is used in all of
the platforms provided by this package. It implements memory functionality supporting protocol
variations defined by the XBus using the user, protocol and transport layers implemented by the
package. The memory contains a control register implemented as a std::unsigned int. The control
register controls writes to the memory. The control register is initialized to enable memory writing. The
data flow through the system will be further discussed in “General Data Flow” on page 3-39.
Figure 3-15 also shows a class named xbus::my_slave. This is shown only to show the extensibility of
the class hierarchy. A user can copy xbus::xbus_mem_slave (located at
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_mem_slave.{h,cc}) and
modify or delete the functionality to do something completely different. If the inheritance stays the
same, all of the infrastructure code, including the target port, configuration interface, register utilities,
etc...., stays in tact, easing the model developer’s process to create a model that can plug and play in
mixed abstraction TLM platform.
The OSCI TLM xbus defines the master as an sc_module which contains an sc_port/channel
implementation (initiator port). The port is a port/ channel, and implements the xbus_if user layer to the
master. The top level netlist, sc_main, binds the initiator port to the router/bus target port. The target port
is an sc_export type. The main difference between sc_port and sc_export (SystemC 2.1) is that sc_port
allow modules to call interface methods implemented by a channel that is not contained by the module;
whereas sc_export allows modules to call interface methods of a channel that the module contains.
The use of sc_export in this example is mainly for stylistic purposes. If users want pure module and
module containers (of channels) to have consistent implementation code structure, then the sc_export
should be included. Inclusion of the sc_export helps in the migration of a pure module to a module
container of channels.
In this example, the master models simple read-write operations executed from memory (for example,
ROM). Although, it is not directly mapped to embedded software like an HCE, host code execution, or
the model of a processor, it can be as effective in early software development if the register and memory
map definitions are accurate.
In a typical system, the processor loads opcodes and operands from memory, then executes the opcode
with its associated operands. This access can occur through a dedicated memory bus or through the a
shared bus architecture. This example uses a shared bus architecture. This example does not show
loading of memory for opcodes and operands, but takes the perspective of executing already loaded and
decoded instructions that result in read-write operations (equivalent to direct address load/store
operations).
The master invokes the user layer defined by xbus_if and implemented by the initiator port. The initiator
port, bound to the bus model’s target port, is a channel adaptor. That is, it takes information from the
user layer invoked by the master, and invokes the transport interface in the bus. The target port is
transparent in the initiator transport calls. The bus model is a module and a channel, that is, it is derived
from both sc_interface and sc_module. When the bus model transport method receives the request, it
processes three phases as defined by the XBus protocol: arbitration, address, and data. Address decoding
occurs providing a port index to a memory-mapped slave. The bus implementation of the transport
interface includes arbitration. During the data phase, the slave processes the data appropriately and
returns an updated response.
The XBus uses the transport layer for several purposes. tlm_transport_if implies the same hardware as
tlm_master_if, but with tightly coupled requests and responses. The requests and responses are tightly
coupled in the XBus protocol.
A key implementation step for developing a TLM platform and its models is defining interfaces. The
xbus_if class is an abstract class. An abstract class defines the interface, but not the actual
implementation of the interface. For example, lines 21 and 27 define how scalar and compound read
methods are called, but not what the method actually does when it is called. Likewise, lines 24 and 30 do
the same for the write methods.
15 template< typename ADDRESS , typename DATA, typename REQ, typename RSP,
typename STATUS >
16 class bus_if
17 {
18 public:
20 ///scalar read API
21 virtual STATUS read( const ADDRESS &a , DATA &d ) = 0;
23 ///scalar write API
24 virtual STATUS write( const ADDRESS &a , const DATA &d ) = 0;
26 ///compound read API
27 virtual RSP read( const REQ &req) = 0;
29 ///compound write API
30 virtual RSP write( const REQ &req) = 0;
31 };
Line 15 generalizes the interface and some of its template data members to be used by platforms that
utilize different (C++) types for address and data bus types. This increases the reusability of the class.
When an object of type bus_if is instantiated, users will define the ADDRESS, DATA, REQ, and RSP
types for the template class.
See Also
● “Defining TLM Types” on page 3-21
The bus and interface types defined for this platform are defined by the following files:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/bus_types.h
The specific enumeration types provided by the package code implementation include:
● enum xbus_timing_mode
● enum xbus_size
● enum xbus_arbitration_mode
● enum xbus_request_type
● enum xbus_status
Following is a code snippet of the enumerated types provided by the vr_xbus_sc_tlm package.
98 enum xbus_timing_mode
99 {
100 PV, PVT
101 };
102
104 enum xbus_size
105 {
106 ONE = 1,
107 TWO = 2,
108 FOUR = 4,
109 EIGHT = 8
110 };
112 enum xbus_arbitration_mode
113 {
114 FIFO, ROUND_ROBIN, MIN_PRIORITY, MAX_PRIORITY, SLOT
115 };
118 enum xbus_request_type
119 {
120 READ , WRITE
121 };
124 enum xbus_status
125 {
127 ERROR , SUCCESS
129 };
In addition to the enumerated types, the example uses parameters for address, data, request, response,
and status types for many template base classes. The specialization of these is done by derived classes.
ADDRESS_TYPE and DATA_TYPE types, such as unsigned int and int, will not require changes,
however, they can be changed to int or unsigned int, respectively, with no impact. Changing the
DATA_TYPE to a non-vector<> type will require changes. Cadence recommends leaving the vector
type in tact to support burst modes.
The request and response types defined for this platform are utilized by the following files:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_initiator_port.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_master.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_mem_slave.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_router.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_slave_base.h
It should be noted that many of the data members in the request and response classes generally reflect a
need for analysis of the overall request/response data and processing flow. Because of this, there are
associations between the request and response data members as well as with the requesting masters and
responding slaves. These data members are generally protocol-specific; however, function-specific data
members can be added.
xbus_request_type type Defines the bus request type (for example, read or
write).
xbus_size size Data size for the request. Use to process data bursts.
unsigned int delay Delay processed by the bus if the master timing_mode
is set to PVT.
unsigned int trans_id This value is updated by the bus model to track how
many transactions have been processed in the
simulation.
unsigned int master_id ID of the associated master that generated the request
associated with this response.
string master_name Name of the master that generated the request that this
response is associated with.
unsigned int master_trans_id ID of the master that generated the request that this response
is associated with.
sc_time start_time Simulation time when the master initially generated the
associated bus request. Used for architectural analysis.
sc_time end_time Simulation time when the bus completed processing the
response generated for an associated request.
Template Base
Class Location
xbus_initiator_port $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/
sysc/xbus_initiator_port.h
xbus_master_base $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
xbus_master_base.h
xbus_slave_base $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
xbus_slave_base.h
xbus_router_base $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
xbus_router_base.h
bus_if ● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
bus_if.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
xbus_initiator_port.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
xbus_mem_slave.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/main*.cc
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/examples/master.iport.map.xbus
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_router_base.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_bus.h
● $SOCV_KIT_HOME/util_lib/tlm_util/sc/utils/address_map.h
1. Instantiate your bus with a memory mapping definition file and arbitration mode.
3. Add an entry into the memory map definition file (master.iport.map.xbus) for your peripheral.
SC_XBUS_SLAVE0.iport 0 0x1000
Note The syntax for this entry is name.iport [space] [begin_address] [space] [end_address].
“iport” is the name of target_port instance instantiated in the xbus_mem_slave peripheral. The top
level netlist binds this port to the bus’ router port. These objects create address map entries based on
the memory map definition file. They provide the system memory mapping functionality during run
time. Its important that the name given used in the top level netlist is the same as that used in the
memory map definition file.
4. Repeat the last two steps for any additional peripherals required in the system.
3.3.8 Arbitration
The arbitration policy is implemented by the following files:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/main.cc
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_router.h
Although all of these modes are not implemented, they provide an example for adding additional
arbitration modes. In the current implementation of the arbitration phase method, modes not
implemented default to the FIFO arbitration policy.
The file xbus_router_base.h provides a method, void arbitration_phase(REQ req), that models the
arbitration phase for every request. Different configurations can construct the model differently during
architectural analysis phase either via construction or the bus’ configuration interface using the
arbitration_mode data member. The arbitration can be included in PV and PVT modes. In PV mode, the
system can be functionally analyzed using arbitration. In PVT mode, timing is incorporated.
Dynamically switching between the PV and PVT modes is essential for increasing performance and
efficiency during a debugging use model.
The file xbus_router_base.h provides a method, end_of_simulation(), which calculates the average,
maximum and minimum latency for each master in the system. For more information, see “Architectural
Analysis” on page 3-48.
The arbitration method is virtual. Derived types can override the functionality of virtual methods.
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_router_base.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h
The following code snippet from xbus_router_base.h shows items implemented in the vr_xbus_sc_tlm
package:
public:
// Environments can utilize this event and method to monitor the bus
sc_event transaction_complete;
// A master has completed a transaction
inline void xbus_tlm_mon_trans(REQ& req, RSP& rsp)
{
req = cur_req;
rsp = cur_rsp;
}
The bus monitoring capability is implemented in the bus. Therefore, an instantiation of the bus is
required. Notice that this example shows a public sc_event and an inline method that takes REQ and
RSP references. An external component or environment can refer to the transaction_complete event,
signifying a master request has been processed by the system relative to the bus. This synchronization
point allows other components or environments to sample the current request and response for further
analysis.
The REQ and RSP types are implemented in xbus_protocol.h as two template classes. These classes are
xbus_response and xbus_request. For more information, see “C++ Templates for Code Reuse” on page
3-43.
The vr_xbus_sc_tlm bus platform package provides a simple example of architectural analysis
implemented by the following files:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_router_base.h
The response and request classes incorporate request start and stop times and a master id. For more
information, see “C++ Templates for Code Reuse” on page 3-43, “Centralized Delay Processing” on
page 3-51, and “Dynamic Timing Mode (PV/PVT) and Delays” on page 3-53.
The bus model tracks the latency, via processing time of the request, for each master ID. The bus
assumes a finite number of masters (could be a construction parameter)
std::vector< sc_time > ms_list[MAX_MASTER_SIZE];
...
process_time = cur_rsp.end_time-cur_rsp.start_time;
ms_list[cur_rsp.master_id].push_back(process_time);
...
For each master ID, collect the processing time for each request and calculate the average based on
the total number of transactions that were processed for the given master ID.
end_of_simulation() {
...
// For each master m
num_m_process_times = ms_list[m].size();
...
//calculate average, maximum and minimum latency
for(size_t p = 0 ; p < num_m_process_times; p ++) {
current = ms_list[m][p];
average += current;
if ( maximum < current )
maximum = current;
if (!p )
minimum = current;
if ( minimum > current )
minimum = current;
}
average /= num_m_process_times;
With PVT and CC modeling styles, a platform can be evaluated for performance. Some interesting
performance analysis includes:
The bus model can be modified to provide enable/disable capability by the platform designer to avoid
unnecessary calculations. These analysis could be requested for the bus or for a particular (master to
slave) connection.
The following table was generated from the simulation results of the vr_xbus_sc_tlm package (see
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/golden/*.log).
From this table we can analyze how different master, bus, and slave configurations can affect average,
maximum and minimum latencies. The minimum latency of 0 ns for the
RUN.XBUS.MINXED.PV.PVT platform is due to the mix-abstraction capabilities. The timing
information provided by the architectural analysis is not indicative of the actual performance when PV
modes are being used. The value of 0 ns latency is due to a path of PV components.
4. cd $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/golden
● This will give you the info to put into a file, e.g. arch.txt, that can be read into an XL spreadsheet for
further analysis.
6. Copy the following line and insert as the first line of your new file, e.g. arch.txt. This will make your
headers in XL.
The vr_xbus_sc_tlm bus platform package provides a simple, centralized delay processing model
implemented by the following files:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_router_base.h
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_master.{h,cc}
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_mem_slave.{h,cc}
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h
a. An unsigned int delay data member was added to the xbus_request class defined in
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/xbus_protocol.h.
b. To support the entire data flow from master to slave (including the initiator port and bus model)
see how to add and remove data members to the request and response classes in section “C++
Templates for Code Reuse” on page 3-43 in order to understand how the infrastructure
incorporates the delay in the classes.
a. The master, bus and slave classes provide constructors that allow users to define a default delay
value to be processed by the bus during arbitration and data phases, respectively.
xbus_mem_slave.h
public:
xbus_mem_slave(sc_module_name name, unsigned int _id,
xbus_timing_mode _timing_mode = PV,
unsigned int _delay = 10, int _k = 10);
// Default PVT delay = 10
xbus_mem_slave.cc
public:
xbus_mem_slave(sc_module_name name, unsigned int _id,
xbus_timing_mode _timing_mode = PV,
unsigned int _delay = 10, int _k = 10);
// Default PVT delay = 10
a. Process the arbitration delay in the bus model if the bus model is in an appropriate timing mode
(for example, PVT).
// Arbitration delay
if(timing_mode == PVT)
wait(delay, SC_NS);
b. Process the request delay in the bus model if the requesting master is in an appropriate timing
mode (for example, PVT).
//Request delay
if(cur_req.timing_mode == PVT)
wait(cur_req.delay, SC_NS);
c. Process the burst delay in the bus model if the bus model is in an appropriate timing mode (for
example, PVT).
// Bus beat delay
if(timing_mode == PVT)
wait(cur_req.size*delay, SC_NS);
d. Process the response in the bus model if the responding peripheral is in an appropriate timing
mode (for example, PVT).
//Response delay
if(cur_rsp.timing_mode == PVT)
wait(cur_rsp.delay, SC_NS);
1. Use the configuration interface, set_timing_mode() with the appropriate timing mode (for example,
PV, PVT)
2. Invoke a simple read() or write() function using address and data argument types.
Compound Users
For users less interested in C++ syntax, the scalar user layer might be preferred. For example, PV
master1 can make a simple read request through an initiator port that implements the scalar user layer.
This use model has PV master1 configuration parameters initialized when the master is instantiated,
such as via constructor parameters. The arguments to the read function include the requested address as
well as a place holder for the value of the return data. This “read” request blocks until the data is
updated. A status is provided for the user based on the success of the request. If the user would like to
change configuration parameters defined at construction, you can utilize the master provided
configuration interface. Similar to the way size was set, the user can set PV master1 delay of the read
request to whatever the user would like, thus overriding the construction value of delay. Memory
management is handled by the layers outside of the master.
Similarly, PV master2 makes a read request, however a request object type, REQ_TYPE, is used. This
request type contains all of the user parameters required for defining a request. Copy and assignment
constructors are provided. Utilities for randomization and constraining requests can be created. This
approach lends itself more towards transaction based communication and object oriented programming.
This use model is expected to be preferred by more experienced programmers. The only argument to the
read function is the request object. The function is also blocking and returns a response object type,
RSP_TYPE, containing appropriate status and data. It is in this object type that the delay value can be
set.
The slave has the same user layers; however, it uses the RSP_TYPE for response parameters like delay.
● Some blocks require finer levels of abstraction (for example, PVT) in order to do architectural analysis
● Reuse of higher levels of abstraction (such as PV) can be accomplished when simulating other blocks
requiring architectural analysis.
● Debugging simulation can be run in the higher-performing PV mode and set to a more detailed mode
for design debug.
● Models are provided at certain levels of abstraction. Not all blocks will have every level of abstraction
implemented.
Each of the configuration execution files (RUN.*) for this package are located:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/examples
The top level netlists (main.*) associated with these configurations are located:
● $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc
Table 3-7 shows the different configuration execution files, their associated configuration netlist files,
and a brief comment describing the configurations. Sections “PVT Modeling” on page 3-8, “Centralized
Delay Processing” on page 3-51 and “Dynamic Timing Mode (PV/PVT) and Delays” on page 3-53 all
provide additional information for placing models in to different timing modes.
See Section 3.2.5.1, “Implementing TLM Interfaces,” on page 3-28 for guidelines on TLM interface
implementation.
3.4.1 Naming
SystemC is a class library for C++ and thus names must be valid C++ identifiers.
Class names (such as models) must have unique names for the entire design. It is manageable to create
unique names within a project team; but with the sharing of models between teams and companies name
conflicts can be a real problem. The C++ namespace is a good technique for avoiding name collisions.
Each project team should define a namespace and then define their models within that namespace. Of
course, if a single interface or block is being developed by several teams, a single namespace is
recommended. A reference to a model in a different namespace is accomplished by concatenating the
name of the namespace and the model name. One can minimize the overhead of typing the namespace in
the long name by declaring the namespace in a “use” clause. Once the namespace is declared in a use
clause the model name is sufficient (keeping the short names). Thus the overhead of referencing the
namespace is only needed when there is truly a conflict in naming.
The OSCI TLM library has defined several namespaces called “tlm” and “util”. The
vr_xbus_sc_tlm/sysc code defines several including xbus and xbus_protocol. xbus is used to define
components of the protocol including xbus_master and xbus_slave. xbus_protocol is used to define
types associated with communication between the components, for example, request and response types.
● Use const char* if desired, but only for fixed, unvarying strings.
● Use the class string for any string especially a string that will vary.
● Use the class vector instead of C-style arrays.
● Use a smart pointer class or shared pointer class instead of raw pointers.
Following the above rules will mean that you usually do not need to add to your class: a destructor, a
copy constructor, or an assignment operator.
Rather than using tools to find these bugs it is obviously better to not create them in the first place. In
C++ there are several techniques that can prevent memory bugs:
1. Smart pointers
2. Shared pointers
In addition these techniques can usually eliminate the need for a custom destructor, copy constructor and
assignment operator in a user's classes.
The benefits of using smart pointers however do not come for free. Smart pointers are slower and larger
than C-style, raw pointers. A paper available in the Boost Library kit shows the extra time taken by
various implementations of smart pointers and their application in pointer intensive applications. This
paper reported that filling and sorting a list of raw pointers was about twice as fast as filling and sorting
a list of smart pointers like the Boost shared_ptr class. Filling and sorting a vector was about three times
faster when raw pointers were used instead of smart pointers.
Likewise the string class is usually slower than char*. But you will write fewer lines of code, more
quickly and with many fewer bugs if you use string instead of char*.
This current version of the TLM Modeling library has incorporated the memory management use model
as provided by the TLM examples provided in the OSCI TLM library.
Further memory management may be required for large platforms. Care must be taken for
multi-threaded systems which are sharing the same objects (such as memory).
In choosing between smart pointers and raw pointers or between string and char* it is important to
consider the entire life cycle of a piece of software and not just the run time for a portion of it. Using
smart pointers and strings will:
A carefully debugged piece of code that uses raw pointers or char* can be easily broken by later
modifications to add features or to fix other bugs. Using string and containers protects your code from
accidents caused by subsequent modifications.
Choosing an error-prone programming technique (like C-style arrays, raw pointers or char*) over the
safer alternatives (vector, smart pointers and string) for the sake of increased run-time performance must
be done with great care. As Donald Knuth said (referring to a dictum from C.A.R. Hoare), “premature
optimization is the root of all evil in programming”. Optimizations often introduce subtle bugs and make
code harder to understand and maintain. So it is with the use of raw pointers, arrays and char*. Local
optimizations for the sake of performance are best added after a program is working and the actual
bottlenecks have been identified. It is hard to predict in advance where performance problems will lie.
On the other hand it is easy to predict that bugs will likely occur around the use of C-style arrays and raw
pointers.
vector<T>
The STL class vector is a sequence that supports random access. It performs automatic memory
management. Using the member function push_back and pop_back you can add or remove elements
from the vector and its size will change automatically. In addition the vector will be automatically
deleted when it goes out of scope -- that is when the vector is no longer accessible. Vector read and write
operations with operator[] do not do range checking. The 1998 C++ standard defines a range checking
member function vector<T>::at(size_type n) which will throw out_of_range when n is greater than the
size of the vector. Here is an example use of vector:
1 #include <vector>
2 function f() {
3 vector<int> int_vec(10);
4 for (int i = 0; i < 10; i++) {
5 int_vec.push_back(i);
6 }
...
7 int j = int_vec[9];
8 int k = int_vec.at(9);
...
9 j = int_vec[11]; // error
10 i = int_vec.at(11); // throws
11 int_vec.push_back(42);
At line 3 a vector<int> is declared. The argument to the constructor (10) is a hint to the vector class's
memory manager that 10 elements are expected to be needed. This constructor argument is not required
but can lead to faster code when you know ahead of time how many elements are likely needed. Note
that this memory allocation hint does not affect whether a vector access is in or out of range. Line 5 adds
elements to the vector. Line 7 reads the last element without range checking. By the time this line is
reached there have been 10 calls to push_back, so there are 10 elements in the vector int_vec. Line 8
reads with range checking. Line 9 is an unchecked out-of-bounds read and will have undesirable
consequences. Line 10 is a checked out-of-bounds read and will throw out_of_range. Line 11 adds
another element to the vector. Since this is the 11th element and the constructor hint was 10 this member
function call will likely lead to a reallocation of the vector in memory.
The code at lines 12 through 16 show how to access each element of the vector without having to worry
about going out of range. The code at line 17 shows a better way to perform a summation operation over
all the elements of the vector. There are numerous algorithms available in the C++ Standard Library for
operations on containers.
At line 18 the vector int_vec goes out of scope and all the memory it is consuming is freed. The user
does not need to worry about freeing the vector's memory. Likewise the user does not need to worry
about increasing the memory allocated to the vector as it grows during program execution.
For more information on vector and other STL containers see http://www.sgi.com/tech/stl, Scott
Meyers's book “Effective STL”, Nicolai Josuttis's “The C++ Standard Library: A Tutorial and
Reference”, and David Mussier’s “STL Tutorial and Reference Guide”.
String Classes
The 1998 C++ standard defines a string class that does automatic memory management and has
overloaded operators for various string operations. For example:
#include <string>
string message;
message = "initial stuff";
...
message += " more stuff";
...
if (message == "initial stuff more stuff") {...}
This class does not require you to allocate or free memory. Storage for the string is automatically
increased as required. This storage is freed when the object goes out of scope. Intuitive operators (like
+= and == shown above) are provided.
Consider how tedious it would be to write the above code with char*:
1 #include <string.h>
2 char string1[] = "initial stuff";
...
3 char string2[] = "more stuff";
...
4 char *m = new char[sizeof(string1) + sizeof(string2)];
5 if (strcmp(m, "initial stuff more stuff") == 0) {...}
6 delete [] m;
As lines 2 and 3 show, initialization of fixed char arrays is not too painful. But when you don't know
what the size of the char array is until runtime you have to make a careful calculation. The use of sizeof
in line 4 is off by one. Fortunately in this case it is too large by one rather than the too small by one
because sizeof includes the ''\0' at the end of a character string. In line 5 you have to remember to
compare the returned value of strcmp to 0. In line 6 is the most important thing you have to remember is
to free the memory, and don't use just delete. In this case you must use delete []. This complexity is
another reason to use the string class.
The SystemC library has traditionally provided a string class similar to the standard C++ one. SystemC's
string class is called sc_string. It is being deprecated in SystemC Version 2.1. This class has a simple
implementation with some of the features that are available in the C++ Standard Library string class.
Now that good implementations of the string class are provided with the newer versions HP’s aCC
compiler, Sun's C++ compiler and the GNU g++ compiler it makes sense to use the Standard Library
string class instead of sc_string. With the Standard Library string class your code will run just as fast
(maybe faster) than with sc_string; you will have access to many useful member functions for string
manipulation; and your code will be more reusable since it is based on a widely used standard.
The advice is then: use string, not sc_string. And use string instead of char* especially for strings that
are not constant.
● The automatically generated destructor will not free the allocated memory.
● The automatically generated copy constructor and assignment operator will copy the pointers but not
what they point to. This is often called a shallow copy.
However if the class uses smart pointers and C++ standard library strings and containers instead of
C-style pointers, strings or arrays, then the three automatically generated member functions should work
just fine with these objects. This is because the smart pointer and container classes define their own three
member functions and the automatically generated member functions for the user-defined class will call
the already provided smart pointer and container member functions for destruction, copy construction
and assignment.
Smart Pointers
Smart pointers are available in a variety of forms for the SystemC user:
The Boost Library contains five types of smart pointers. The most important one is shared_ptr. The
code for this class is included in the SystemC 2.1 distribution and in the Cadence Incisive product, from
version IUS5.3 onward.
A smart pointer class keeps track of which parts of your code are using the object pointed to. Smart
pointers usually work by means of reference counting. When the last reference goes out of scope the
memory pointed to is freed. For example:
#include "systemc/packages/boost/shared_ptr.hpp"
struct packet {
int src_addr;
int dst_addr;
... // more data members
};
for (int i = 0; i < 10; i++) { // line 1
shared_ptr<packet> packet_ptr(new packet); // line 2
// initialize object pointed to by packet_ptr
packet_ptr->src_addr = i; // line 3
packet_ptr->dst_addr = i + 0x10; // line 4
...
// Given elsewhere defined
This code shows a smart pointer being passed into an sc_fifo which likely is connected to a module
running in another thread. At line 2 a new object is allocated on the heap with the C++ built-in function
new. At line 6 the object is passed into the fifo by means of the smart pointer. As the loop continues,
when line 2 is executed for the second time another object is allocated. The smart pointer packet_ptr is
reused and now points to a new object. As a result this code no longer has access to the original object.
This code is finished with it. However, the original object that was passed into the fifo continues to exist
as long as the receiver is using it. When the receiver is finished with the object it will be automatically
deleted. For example imagine this code is connected to the other side (the receiver side) of the fifo:
while (1) {
shared_ptr<my_class> receive_ptr = fifo.read();
// process ptr
}
The first time the fifo read() method is called the first object that was sent through the fifo is received.
The smart pointer receive_ptr is used to access this object. When the fifo read() method is called a
second time, as the loop continues to run, the pointer receive_ptr is reused and points then to the second
object passed through the fifo. At this point the memory dedicated to the first object will be
automatically deleted since neither the sender nor the receiver still has access to it. They are then
finished with the first object.
If randomization, transaction recording or any other SystemC SCV feature based on introspection is
required then the class provided by SystemC, scv_smart_ptr, should be used instead of either shared_ptr
version. For example consider the code above modified to use scv_smart_ptr and randomization:
#include "scv.h"
for (int i = 0; i < 10; i++) {
scv_smart_ptr<packet> packet_ptr(new packet);
// randomize the object pointed to by packet_ptr:
packet_ptr->next();
// send packet to fifo:
fifo.write(packet_ptr);
}
Sharing a complete HW platform with a customer can be accomplished by distributing a SystemC linked
library and not the SystemC source code.
Sharing an IP component to be designed into a platform, the SystemC model can compiled into object
form. In order to instantiate this IP component into a platform, the class declaration, constructor, and
public interfaces needs to be distributed (for example, the header file). The SystemC implementation of
the interfaces does not need to be distributed. The heavy usage of templates blurs this line between
interface and implementation. You consider writing a concrete wrapper class around the template
version in an attempt to keep this protected.
Consideration should be given to the customer's visibility into the protected model for debugging
purposes. For example, the model/platform could support transaction recording and provide entry points
to turn on/off the recording. For debugging software, visibility into the memory architecture and register
banks is important. If the performance of the platform is being evaluated then additional performance
measurements should be made visible (average bus latency, cache misses, and so forth).
One note is that linked libraries and compiled objects are dependent on the machine configuration (OS
version, C++ compiler version and patch levels). Thus distribution may require building different
machine configurations depending on the requirements of the customer.
Another alternative for protection is encrypting the source code (standard algorithms are available on the
web). A partner or friendly customer is provided with the key for decryption. Once the model is
decrypted the customer has full visibility into the source code and can distribute the model to others. On
the positive side, the model is fully debuggable and avoids the issues of matching machine
configurations. This technique is useful for limiting access to a few friendly partners.
There are also a couple of techniques for licensing models. The module could require a password as an
argument to the constructor. Friendly customers can instantiate this model into their platform with the
correct password. The constructor would issue an error if the password is invalid. For platform
providers, the password could be a command line argument which is also checked inside the constructor
of the platform. A more robust licensing scheme includes checking for flexLM licenses within the
constructor. This adds the capability to support site licensing, sharing of licenses from a server as well as
temporary licenses. Both of these licensing techniques (passwords and flexLM) must be facilitated by
distributing the link library of compiled object form (thus limiting the debuggability of the model).
Generally, versioning of the source code is sufficient (you do not need to version compiled objects or
linked libraries). In addition to SystemC source code, the header files, makefiles, and other
dependencies (protected IP models, link libraries, and so forth) need to be versioned.
To facilitate sharing models between sites, all file references (include statements) should be relative
directory paths instead of absolute directory paths. Using absolute directory paths can lead to missing
files as well as problems due to different machine configurations.
Because compilation can be time-consuming, Cadence recommends that you keep file sizes to a
reasonable size (one class per file). For easy navigation, file names should reflect the name of the model
it describes.
Also note that many C++ debuggers are not very good at navigating files with the same name. For
example, naming each model “systemc.cpp” and “systemc.h” is difficult for the debugger to keep track
of which file should be displayed. An alternative solution is to name the files based on the classes to
which they declare/define. For example, name the DRAM files “dram.h” and “dram.cpp”.
Each TLM protocol should be managed in a separate directory. Each protocol directory contains the
declarations and definitions of transaction classes, port classes and additional utility classes like master
and slave classes.
IP models should be managed in separate directories for each component. Each IP directory contains the
SystemC header file declaring the class and the SystemC source file implementing the methods of the
class. The source code can reference the header file using local reference. This facilitates easy
checkin/out of the source and header file. By keeping each IP model separate they can be distributed or
copied more easily.
Common type definitions should be separated into a utility directory for easy sharing between IP
models.
Each HW platforms should be managed in a separate directory. Each platform contains the declaration
and definition of the platform. In addition, the makefile for building the platform should be included in
this directory.
Cadence recommends the directory structure for managing SystemC files provided in “Library Directory
and File Structure” on page 3-16.
Cadence provides a script called “ncsc_run” which enables easy building of a platform. One of the key
features is that it supports building a platform with a mixture of HDL (Verilog and VHDL) and SystemC
models. It also facilitates simulation within the Incisive environment by compiling and linking with the
appropriate compilers and linkers.
2. Create a script that invokes the simulator plus any other unique options or unix commands required:
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/examples/RUN.XBUS is a
simple example that invokes the PV only configuration of the TLM platform provided in the
vr_xbus_sc_tlm package
cp -f \
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/examples/
master.iport.map.xbus \
outdir/master.iport.map
ncsc_run $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/sysc/
main.cc -file \
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/examples/run.f
Note This file includes the top level netlist via the file main.cc. It includes and provides shared
options and information with the -file argument run.f (defined in the previous step). Also notice that
master.iport.map.xbus is getting copied into the simulation results directory. This is to define the
memory map of the system. See “Memory Map” on page 3-46 for more details.
$SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_sc_tlm/examples/test_list.f provides a
list of configurations included. Some are defined for architectural analysis comparisons. See
“Architectural Analysis” on page 3-48.
3. Create a script that invokes any or all of your configurations (such as regressions):
109 fi
112 if [ "${test_name}" = "" ]; then
113 echo "invalid test name ($test_name). "
114 usage
115 exit 1
116 fi
117 export VR_TLM_GUI
118 export VR_TLM_INTERACTIVE
119
120 echo "Running $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/vr_xbus_s
121 $SOCV_KIT_HOME/svm_lib/sc_tlm_design_lib/
vr_xbus_sc_tlm/examples/$test_name
4.1 Overview
There are three modes of connection between Specman and external agents (simulators, SystemC, and
so on).
Mode Description
Cycle accurate This is the typical mode for working with simulators. In this mode,
Specman monitors and reacts to changes that occur every clock
cycle. This is the most accurate connection mode, but it is also the
most time-consuming connection mode.
Decoupled timing This connection mode lets you divide the environment into levels
that run according to different clocks.
Table 4-1 Connection Modes between Specman and External Agents (continued)
Mode Description
e Reuse Methodology (eRM) Developer Manual describes how to write reusable e UVCs. This chapter
shows how to easily extend the e UVCs so that they can be used to verify TLM models.
Once an e UVC is built according to eRM, most of its parts are easily reusable over the different running
modes. If, for example, the data check is based on data items collected by the monitor, it does not matter
to the checker how and when the data items are collected. So, to use the e UVC with TLM models, you
only need to change the interface to the model—injection and collection of data.
This chapter uses as an example the vr_xbus_tlm package, which extends the vr_xbus eRM e UVC and
adds TLM mode to it. For the example, the TLM mode is created as a separate package, but it would also
have been valid to add it in the e UVC itself (in the e source directory).
Note This chapter does not explain how to model with SystemC. It also does not document the
Specman/SystemC interface. For the full documentation of Specman and the SystemC interface, refer to
the online help.
For module and system e UVCs (such as the vr_xcore), only a few changes are required. The system e
UVC monitor is based on events and data items emitted and collected by the monitors in the interface e
UVCs. The system monitor does not care how those data items were collected. The required changes in
the system e UVC are:
● The configuration file must define the abstraction level for each interface e UVC.
extend vr_xbus_env_u {
keep abstraction_level == TRANSFER;
};
● Events and checks that depend on internal information must be modified. For example, the XCore e
UVC has some events and checks based on an overflow indication from the DUT. The TLM model
of the XCore does not have such indications, so the overflow event is overridden when in TLM mode.
Units that must be subtyped on the basis of this type are the BFMs and monitors that get the additional
TLM functionality. Sometimes you also must subtype the env and agent units to propagate the value (for
example, when the agent controls the generation of the BFM and monitor under it).
For example, if the e UVC BFM expects to get a response but the model does not implement that, the
wrapper could create a fake response and pass it to the e UVC BFM.
For examples of wrapper code, see “TLM BFM” on page 4-3 and “STLM Monitor” on page 4-4.
Following are some parts of the vr_xbus_tlm_master_bfm.e file that describe how to write a TLM BFM
and connect it to the SystemC model.
The BFM has an instance of the output port of the transport method.
transport_transfer : out method_port of vr_xbus_transport_transfer is
instance;
The BFM driving TCM is extended so that instead of performing the bus protocol it passes the transfer
to the wrapper.
private drive_transfer(t : MASTER vr_xbus_trans_s) @synch.clock_rise is {
var cur_resp : vr_xbus_tlm_response_s;
//..
cur_resp = transport_transfer_wrapper(t);
//...
};
The wrapper transports the transfer to the model via the output method port.
transport_transfer_wrapper(t : MASTER vr_xbus_trans_s):
vr_xbus_tlm_response_s @synch.clock_rise is {
result = transport_transfer$(t);
return result;
};
The BFM output method port is bound to the SystemC model (bound to external), and its hdl_path is
constrained to the model API method name.
extend TRANSFER MASTER vr_xbus_bfm_u {
connect_ports() is also {
do_bind(transport_transfer, external);
};
};
The e UVC user guide should list the API provided by the monitor so that the verification engineer can
connect the model methods to the appropriate methods and events in the e UVC.
With the various Specman-C interface capabilities, other monitoring techniques can be used, based on
the API provided by the SystemC model. For example, the SystemC channel can provide events
indicating activities on the bus.
Following are some parts of the vr_xbus_bus_monitor_tlm.e file that describe how to write a TLM
monitor and connect it to the SystemC model.
A method_type is declared.
method_type vr_xbus_log_new_transfer(transfer : MONITOR vr_xbus_trans_s,
response: vr_xbus_tlm_response);
The e UVC monitor has an instance of an input method port of the logging method. This instance is a
wrapper. It gets the new transfer from the SystemC model and passes it to the monitor’s main logging
method.
log_new_transfer_wrapper : in method_port of vr_xbus_log_new_transfer
is instance;
The e UVC monitor defines and implements the main logging method.
private log_new_transfer(new_transfer : MONITOR vr_xbus_trans_s,
new_response: vr_xbus_tlm_response_s) is {
transfer = new_transfer;
//...
};
The monitor input method port is bound to the SystemC model (bound to external), and its hdl_path is
constrained to the model API method name.
extend TRANSFER vr_xbus_bus_monitor_u {
keep log_new_transfer_wrapper.hdl_path() == "xbus_log_new_transfer";
};
extend TRANSFER vr_xbus_bus_monitor_u {
connect_ports() is also {
do_bind(log_new_transfer_wrapper, external);
};
};
Add required code to the stub file, specman.cpp. It must include the model header file and the
convertors. It must also define a pointer to the top module in SystemC.
keep agent_code() == {"#include 'xcore_top.h'";
"#include 'conv_transfers.h'";
"#include 'conv_frames.h'";
"using namespace xbus;";
"using namespace xserial;";
"using namespace xcore;";
"extern Xcore_Top *top;" };
See Also
● vr_xbus_tlm/tlm_sve/vr_xbus_tlm_config.e
For example, these changes were made to the XCore SystemC model:
● Changes to sc_main(): the SVM golden e UVCs work in master mode. This means that the SystemC
design is fully responsible for time management and for running the simulation. In consequence, the
following changes were made to the SystemC code:
● src/snsc_user was added to the SystemC model so that it can call Specman methods.
● Before starting the run, sc_main() must activate Specman by calling
specman_init(started_mode).
● Data injection is now controlled by the e UVC BFM. The main SC_THREAD supplied with the
SystemC model is no longer required, and the file that contains the SystemC thread is no longer
included in the verification environment.
xbus_top* top;
// Bus
top = new xbus_top("Top");
top->xbus_top_clock.bind(clock);
specman_init(started_mode);
return 0;
}
When running in slave mode, the run is initiated and controlled by Specman. Therefore, sc_main() must
not issue sc_start(). For example:
#include <iostream>
#include <string>
#include "systemc.h"
#include "xbus_top.h"
#include "src/snsc_user.h"
xbus_top* top;
// Bus
top = new xbus_top("Top");
return 0;
}
e UVC BFM ● SystemC The e UVC BFM calls the TLM model method and
waits for the value-returning waits for its returned value. This methodology is
activity to end output method implemented in the SVM e UVCs. For example:
port
cur_resp =
transport_transfer$(cur_transfer);
e UVC BFM ● SystemC void The e UVC BFM calls a void method of the TLM. The
starts TLM output method TLM model emits an event when it completes the
model and port requested activity and the BFM can synchronize to that
continues ● SystemC events event to update its data. This methodology is required
when the model supports pipelining. In that case, the e
UVC BFM has to inject more than one data item in
parallel and hence cannot wait for the previous transfer
to complete before sending the next one.
TLM model controls ● Specman input The TLM model calls a method of the e UVC
monitoring method port monitor, passing to it all required data (for example,
the transfer struct after each transfer).
e UVC controls ● SystemC output The TLM model implements a monitor method.
monitoring method port This method waits for the transaction completion
● SystemC and then returns the current data. The e UVC
non-blocking monitor calls the monitor method in a while TRUE
method loop.
● Output method The TLM model emits an event when it has new
port data. The e UVC monitor connects an event port to
● SystemC events the event. When the event is emitted, the e UVC
monitor calls the monitor method. The method can
be blocking, as it immediately returns with the new
data.
In the SVM e UVCs, monitoring is implemented using input method ports. This is because currently it
is the safest methodology for synchronization. When working in PV mode, you must ensure that events
are handled in the e UVC in the same order as they are emitted by the model. If the model supports the
output method port API, you can use the e UVC-controlled approach, but make sure that the e UVC
monitors call the TLM model in the same order as the events are emitted.
The advantage of letting the e UVC control monitoring is that you can easily disable monitoring by
changing the e UVC configuration.
Whichever technique is chosen, the e UVC manual should document the supplied API so that the
verification engineer can connect the model methods to the appropriate methods and events in the e
UVC.
The XBus has a monitor that is sensitive to the event transaction_complete. When this event is emitted,
XBus::Monitor() is activated. XBus::Monitor() calls a method implemented in the e UVC.
// The constructor of Xbus
XBus(sc_module_name name)
: sc_module(name)
{
SC_THREAD(Monitor);
sensitive << transaction_complete;
}
e UVC code
The e UVC monitor has an input method port. It gets called by the wrapper, which has its own
implementation of this method. The wrapper’s method port HDL path matches the name of the method
called by the model.
// The e UVC monitor method
log_new_transfer(transfer : MONITOR vr_xbus_trans_s,
response: vr_xbus_tlm_response) is {
current_transfer = transfer;
current_response = response;
update_transfer();
};
The SystemC model implements a transaction-completion indicating event and a method that returns the
current transfer and its response.
sc_event transaction_complete; // A master has completed a transaction
void xbus_tlm_mon_trans(xbus_tlm_request& req, xbus_tlm_response& rsp)
{
req = cur_req;
rsp = cur_rsp;
}
e UVC
The e UVC monitor uses the SystemC event as a sampling event of its main method. Upon event
emission, the e UVC monitor calls the SystemC monitor method.
// Define an event and connect to SystemC event
package bus_transfer_end : in event_port is instance;
keep bus_transfer_end.hdl_path() == "transaction_complete";
update_transfer() @bus_transfer_end$ is {
while TRUE {
};
};
The SystemC model has a method that waits for the next transaction. It then returns the current
transaction and the response to it.
void xbus_tlm_mon_trans_blocking(xbus_tlm_request& req,
xbus_tlm_response& rsp) {
wait(transaction_complete);
req = cur_req;
rsp = cur_rsp;
}
e UVC code
poll_for_transfers() @sys.any is {
while TRUE {
poll_info_from_sc_bus$(current_transfer, current_response);
update_transfer();
};
};
In the XSerial e UVC, there are two monitors: RX and TX. Both are instances of the same unit
(vr_xserial_monitor_u). The HDL path of the monitor’s input method must be defined so that there is
no duplication of names.
The solution in the XSerial e UVC is adding an ID to each monitor. The integrator must verify that the
IDs of the agents in the SystemC model match the IDs of the e UVC monitors.
e UVC code
monitor_id : uint;
new_monitor_frame : in method_port of vr_xserial_log_new_frame
is instance;
keep soft new_monitor_frame.hdl_path() ==
append("xserial_log_new_frame", monitor_id);
SystemC code
void XSerialChannel::Monitor(){
while(true){
wait();
if (id == 0) {
xserial_log_new_frame0(cur_frame);
};
if (id == 1) {
xserial_log_new_frame1(cur_frame);
};
};
};
● The unit that implements the method (when you have an input method port)
● The unit that uses the method (when you have an output method port)
● A general unit at a higher level (typically, the env)
● A signal map
In the XBus and XSerial e UVCs, ports are instantiated in the unit that uses them: the output method port
instantiated in the BFMs and the input method port instantiated in the monitors.
In other packages, the output method ports are instantiated in the env unit.
● Artificial time: If the whole simulation runs in one cycle, all Specman events have the same time
stamp. This causes ordering issues, false DUT errors, and meaningless test information. To avoid
these problems, the TLM BFM has a wait cycle before each transmission to the TLM model. If there
are 10 transfers in the run, the test will last 10 Specman ticks.
● Use of events: When more than one event is emitted by the SystemC model, you risk a
synchronization problem among the various e UVC monitors. The system e UVC monitor uses events
emitted by the interface monitors. Correctness of the checks depends on the system monitor getting
the events in the same order emitted by the TLM SystemC model. But the order is not always
guaranteed. Events defined using event … is … are handled by the Specman Elite scheduler, and
their order is not predictable. For example, in the following code, if events frame_started and
frame_ended are emitted at the same time (which can happen in PV mode), the order of the events
if_frame_started and if_frame_ended is not predictable:
event if_frame_started is cycle @interface.frame_started;
event if_frame_ended is cycle @interface.frame_end_ed;
● Prefer use of methods over events
● Prefer use of on event rather than event is
● Data item buffering: To maintain the correct order, you might need to add buffering between the
model and the e UVC monitor. The buffer would verify that the monitor gets the next data item only
after completing analysis of the current item.
There are several approaches to these questions, depending on the e UVC architecture and the
complexity of the protocol.
Note In all of the following cases, coverage collected via the bus monitor represents activity on both
RTL and TLM. To see specific coverage for each agent, look at the coverage collected via the agent
monitor.
4.7.6.3 Bridge
This approach implements a bridge to connect several abstraction levels. The e UVC is defined to work
at one level, and any part of the system that is implemented at another level is connected to the e UVC
with a bridge.
Note As the XBus e UVC employs procedural binding, the demo requires Specman 5.0 or higher.
1. (Only when working with the OSCI simulator) Define the environment variables: SYSTEMC and
SYSTEMC_LIB_DIR.
2. Source Specman-home-dir/env.[c]sh.
keep count == 1
extend SEQ_WRITES_AND_READS vr_xbus_master_sequence {
keep seq_length == 10;
keep start_data == 0x10;
};
Note As the XCore e UVC employs method ports, the demo requires Specman 4.3 or higher.
1. (Only when working with the OSCI simulator) Define the environment variables: SYSTEMC and
SYSTEMC_LIB_DIR.
2. Source Specman-home-dir/env.[c]sh.
-- Write 5 frames
keep count == 5;
keep sequence.kind == VR_XCORE_XBUS_WRITE;
};
The first and most important task is verifying the low-level software drivers. As these are the software
methods that directly interface with the hardware, their cooperation must be verified thoroughly.
Occasionally, additional levels of the software or even full software applications might be co-verified
with the hardware.
Verification of software with hardware presents some additional challenges to the already complex task
of functional verification.
● Interfacing with the software requires additional infrastructure such as the GSA.
● The visibility of software internals is minimal, which makes monitoring difficult. There is no way
to generate events on method calls or value change without modifying the code.
● Software requires many simulation cycles, limiting the simulation to a few seconds of real time. To
overcome this limitation, the software is often run on the host or on an instruction set simulator.
● The memory associated with the software can be simulated with various memory models, and the
model could change over the life of a project.
● Additional complexity results when the software is a multi-tasking application running on top of an
operating system.
The ISX overcomes these difficulties by providing a generic interface between the verification
environment and the software. Figure 5-1 on page 5-2 shows a schematic depiction of a HW-SW
co-verification environment.
Bus SW
Multi Channel Sequence Driver
UVC UVC
main()
CPU Mem
f() g()
Periph Periph
Program h()
Periph Periph
UVC UVC
Some typical examples of what a HW-SW co-verification plan could include are:
● Was the software in all major states when specific hardware events occurred? For example:
● Buffer overflow
● Error data received on the communications port
● Interrupt from various sources
● Was the software driver reset while the hardware interface was dealing with:
● RX packet
● TX packet
● Error packet
For more information on plan-driven verification, see “Metric-Driven Verification” on page 1-2
● The device contains low-level software — either a hardware abstraction layer or software drivers.
● There is high performance of the software execution (rule of the thumb: more than 1kHz).
● The software API is so complex that covering all configurations with directed tests is difficult.
● Hardware-software corner cases are identifiable.
● Some software is essential for hardware verification — either the DUT software or the test software
created for driving the hardware to interesting cases.
See Also
● “Optimizing Performance of System Verification” on page 1-4.
Figure 5-2 shows the software UVC (in this case an eVC) connected to the software drivers with a GSA
connection. A virtual sequence driver activates concurrent sequences in the software UVC and other
UVCs.
The ports are defined in the env. The various components of the UVC use a parent pointer to access the
ports in the env.
method_type read_str (xcore: int) : string @sys.any;
extend vr_qsoc_sw_env_u {
// Define e method port and connect it to the "read_str" c routine
read_str: out method_port of read_str is instance;
keep bind(read_str, external);
keep read_str.hdl_path() == "read_str";
driver_call() @sys.any is {
var sitem: sw_sequence_item;
while TRUE {
sitem = driver.get_next_item();
sitem.do_me();
emit driver.item_done;
};
}; // driver_call()
run() is also {
start driver_call();
};
};
Using the BFM mechanism lets you add software access just by including another sequence and the
corresponding port in the env.
For example:
type sw_sequence_item_kind: [READ_STR, WRITE_STR, INIT_HW];
struct sw_sequence_item like any_sequence_item {
kind: sw_sequence_item_kind;
};
For example:
sequence sw_sequence using item=sw_sequence_item;
For example:
extend sw_sequence_item {
do_me() @driver.clock is undefined;
};
4. Add a parent pointer to the env that contains the method ports.
For example:
extend sw_sequence_driver {
!p_env: vr_qsoc_sw_env_u; -- access to ports in env
event clock is only @sys.any;
};
Activation Example
The sequence item encapsulates the knowledge of how to invoke the software in a method. The BFM is
responsible to activate that method when the item is sent to the BFM.
extend READ_STR sw_sequence_item {
str: string;
core: int;
do_me() @driver.clock is {
me.str = driver.p_env.read_str$(me.core);
};
nice_string(): string is also {
result = "read_str()";
};
};
The software sequence creates a sequence item and propagates the parameters to it.
extend sw_sequence_kind: [READ_STR];
extend READ_STR sw_sequence {
!dr_call: READ_STR sw_sequence_item;
str: string;
core: int;
};
Typically, information on method calls and their parameters is collected from the sequence driver.
Collecting this kind of information from the software side requires modifications to the software, which
is not recommended.
To collect the value of a software variable, you must access a port. You need a sampling event. For that
purpose, either the existing GSA trigger event or a specific new event emitted on the variable change
may be used. Then coverage can be collected as usual:
cover sw_api_called is {
item method_name using text="SW method called";
item POS_status using text="Status of POS application";
};
A F
Abstraction Level Use Models 3-5 figure
Adding IUS Features 3-67 PV transaction-level model configuration
Architectural Exploration 3-67 3-4, 3-7
PV/CC mixed abstraction message sequence
B 3-12
PV/CC mixed abstraction platform 3-13
BFM 4-3 PVT message sequence 3-9
TLM 4-3 TLM library directory structure 3-17
BFMs for software UVCs 5-5 TLM terminology 3-1
XBUS PVT platform 3-10
C -file argument to simulator 3-66
CC, definition 3-1 from module-level to system-level 2-1
Channel Refinement 3-67
Coding Guidelines 3-55 H
Configuration for Working with SystemC 4-6 hardware-software co-verification 5-1
Connection Modes between Specman Elite and
External Agents 4-1
Creating a SystemC wrapper for your algorithm
I
3-67 in-method ports, multiple instance 4-12
Installation and Configuration 3-14
D
Debug Techniques 3-67
L
delay model, definition 3-1 Layer, definition 3-2
E M
example software e UVC architecture 5-4 method port location 4-13
methodology
TLM BFM 4-8
O U
OSCI, definition 3-2 use models
abstraction level 3-5
P
Performance Considerations 3-67
V
ports for software UVCs 5-4 Verification 3-67
ports, in-method, multiple instances 4-12
PROXY 4-3 W
PV mode, synchronization issues 4-13
PV Modeling 3-6 write() 3-53
PV, definition 3-2
PVT Modeling 3-8 X
PVT, definition 3-2 XBus
TLM demo 4-15
S XCore
sc_main.cpp 4-7 TLM demo 4-16
sequences for software UVCs 5-5
SystemC
configuration for working with 4-6
interfacing with 4-6
model, modifying 4-6
T
TLM
BFM 4-3
BFM methodology 4-8
mode 4-3
modeling overview 3-3
monitor 4-4
monitor methodology 4-9
open issues 4-8
system UVC 4-2
XBus demo 4-15
XCore demo 4-16
TLM mode
BFM 4-3
monitor 4-4