— Functions implemented in SystemVerilog and specified
in export declarations can be called from C; such functions are referred to as exported
functions.
— Tasks implemented in SystemVerilog and
specified in export declarations can be called from C; such functions are
referred to as exported tasks.
— Functions implemented in C which can be
called from SystemVerilog, and can in turn call exported tasks, are referred to
as imported tasks.
Depending on the data types
used for imported or exported tasks or
functions, the C code can be binary-level or source-level compatible.
Applications that do not use SystemVerilog packed types are always binary
compatible. Applications that don’t mix SystemVerilog packed and unpacked types
in the same data type can be written to guarantee binary compatibility. Open
arrays with both packed and unpacked parts are also binary compatible.
The values of SystemVerilog
packed types can be accessed via interface tasks or
functions using the canonical representation of 2-state and 4-state packed
arrays, or directly through pointers using the implementation representation.
The former mode assures binary level compatibility; the latter one allows for tool-specific,
performance-oriented tuning of an application, though it also requires
recompiling with the implementation-dependent definitions provided by the
vendor and shipped with the simulator.
Formal and
actual arguments of both imported tasks and functions
and exported tasks and functions are bound by the principle “What
You Specify Is What You Get.” This principle is binding both for the caller and
for the callee, in C code and in SystemVerilog code.
For the callee, it guarantees the actual arguments
are as specified for the formal ones. For the caller, it means the function
call arguments shall conform with the types of the
formal arguments, which might require type-coercion on the caller side.
Another way to
state this is that no compiler (either C or SystemVerilog) can make argument
coercions between a caller’s declared formals and the callee’s
declared the formals. This is because the callee’s
formal arguments are declared in a different language than the caller’s formal
arguments; hence there is no visible relationship between the two sets of
formals. Users are expected to understand all argument relationships and
provide properly matched types on both sides of the interface (see Annex
D.6.2).
In
SystemVerilog code, the compiler can change the formal arguments of a native
SystemVerilog tasks or function and modify its code accordingly,
because of optimizations, compiler pragmas, or
command line switches. The situation is different for imported tasks and functions. A SystemVerilog compiler cannot modify the C
code, perform any coercions, or make any changes
whatsoever to the formal arguments of an imported task or function.
A SystemVerilog
compiler shall provide any necessary coercions for the
actual arguments of every imported task and function
call. For example, a SystemVerilog compiler might truncate or extend bits of a
packed array if the widths of the actual and formal arguments are different.
Similarly, a C compiler can provide coercion for C types based on the
relationship of the arguments in the exported task and function’s
C prototype (formals) and the exported tasks and function’s
C call site (actuals). However, a C compiler cannot
provide such coercion for SystemVerilog types.
Thus, in each
case of an inter-language function call, either C to SystemVerilog or
SystemVerilog to C, the compilers expect but cannot enforce that the types on
either side are compatible. It is therefore the user’s responsibility to ensure
that the imported/exported task
or function types exactly
match the types of the corresponding functions in the foreign language.
D.5.5 context and non-context tasks and functions
Also refer to
Section 27.4.3.
Some DPI
imported tasks or functions, or other
interface's functions called from them, require that
the context of their call be known. It takes special instrumentation of their
call instances to provide such context; for example, a variable referring to
the “current instance” might need to be set. To avoid any unnecessary overhead,
imported task and function calls in SystemVerilog code are
not instrumented unless the imported task or function
is specified as context in its SystemVerilog import declaration.
All DPI export tasks and functions require that the context of their call is known.
This occurs since SystemVerilog task and function
declarations always occur in instantiable scopes,
hence allowing a multiplicity of unique task and function
instances in the simulator’s elaborated database. Thus, there is no such thing
as a non-context export task or function.
For the sake of
simulation performance, a non-context imported task or function
call shall not block SystemVerilog compiler optimizations. An imported task or function not specified as context shall not access any data
objects from SystemVerilog other then its actual arguments. Only the actual
arguments can be affected (read or written) by its call. Therefore, a call of
non-context imported task or function is not a barrier for
optimizations. A context imported task or function,
however, can access (read or write) any SystemVerilog data objects by calling
PLI/VPI, nor by calling an embedded export task or function.
Therefore, a call to a context task
or function is a barrier for
SystemVerilog compiler optimizations.
Only the calls
of context imported tasks and functions are properly instrumented and
cause conservative optimizations; therefore, only those task and functions can safely call all functions from other APIs,
including PLI and VPI functions or exported SystemVerilog functions. For
imported functions not specified as context, the effects of calling PLI, VPI,
or SystemVerilog tasks or functions can be unpredictable and such
calls can crash if the callee requires a context that
has not been properly set.
Special DPI
utility functions exist that allow imported tasks and functions
to retrieve and operate on their context. For example, the C implementation of
an imported task or function can use svGetScope() to retrieve an svScope
corresponding to the instance scope of its corresponding SystemVerilog import
declaration. See Annex D.8 for more details.
D.7.2 Calling SystemVerilog tasks and functions from C
There is no
difference in argument passing between calls from SystemVerilog to C and calls
from C to SystemVerilog. Functions
Tasks and functions exported from SystemVerilog cannot have
open arrays as arguments. Apart from this restriction, the same types of formal
arguments can be declared in SystemVerilog for exported tasks and functions and imported tasks and
functions. A task or function exported from SystemVerilog
shall have the same function header in C as would an imported function with the
same function result type and same formal argument list. In the case of
arguments passed by reference, an actual argument to SystemVerilog task and function called from C shall be allocated using the same layout
of data as SystemVerilog uses for that type of argument; the caller is
responsible for the allocation. It can be done while preserving the binary
compatibility, see Annex D.11.5 and Annex D.11.11.
Calling a SystemVerilog
task from C is the same as calling a SystemVerilog function from C with the
exception that the return type of an exported task is an int
value which has a special meaning related to disable statements. Please see
section 27.8 for details on disable processing by DPI imported tasks and
functions.
D.8
Context tasks and functions
Some DPI imported tasks and functions require that the context of their
call is known. For example, those calls can be associated with instances of C
models that have a one-to-one correspondence with instances of SystemVerilog
modules that are making the calls. Alternatively, a DPI imported task or function might need to access or modify
simulator data structures using PLI or VPI calls, or by making a call back into
SystemVerilog via an export task or function.
Context knowledge is required for such calls to function properly. It can take
special instrumentation of their call to provide such context.
To avoid any unnecessary
overhead, imported task and function calls in
SystemVerilog code are not instrumented unless the
task or imported function is specified as context in its SystemVerilog
import declaration. A small set of DPI utility functions are available to
assist programmers when working with context tasks or
functions (see Annex D.8.3). If those utility functions are used with any
non-context function, a system error shall result.
D.8.1 Overview of DPI and VPI context
Both DPI tasks and functions and VPI/PLI functions might need to understand
their context. However, the meaning of the term is different for the two
categories of tasks and functions.
DPI imported tasks and functions are essentially proxies for native SystemVerilog tasks and functions.
Native SystemVerilog tasks and functions always operate in the scope of their declaration
site. For example, a native SystemVerilog function f() can be declared in a module m which is
instantiated as top.i1_m. The top.i1_m instance of f() can be called via hierarchical reference from code in a distant
design region. Function f() is said to
execute in the context (aka. instantiated
scope) of top.i1_m,
since it has unqualified visibility only for variables local to that specific
instance of m. Function f() does not have unqualified visibility for any variables in
the calling code’s scope.
DPI imported tasks and functions follow the same model as native SystemVerilog tasks and functions. They execute in the context of their
surrounding declarative scope, rather than the context of their call sites.
This type of context is termed DPI context.
This is in
contrast to VPI and PLI functions. Such functions execute in a context
associated with their call sites. The VPI/PLI programming model relies on C
code’s ability to retrieve a context handle associated with the associated
system task’s call site, and then work with the context handle to glean
information about arguments, items in the call site’s surrounding declarative
scope, etc. This type of context is termed VPI context.
Note that all
DPI export tasks and functions require that the context of
their call is known. This occurs since SystemVerilog task and function declarations always occur in instantiable
scopes, hence giving rise to a multiplicity of associated task or function instances in the simulator’s database. Thus, there is no
such thing as a non-context export task or function.
All export task and function calls must have their execution
scope specified in advance by use of a context-setting API function.
D.8.2
Context of imported and export tasks and
functions
DPI imported and export tasks and functions can be declared anywhere a normal
SystemVerilog task or function can be declared.
Specifically, this means that they can be declared in module, program, interface,
or generate declarative scope.
A context imported task or function executes in the context of the
instantiated scope surrounding its declaration. This means that such tasks and functions can see other variables in that
scope without qualification. As explained in Annex D.8.1, this should not be
confused with the context of the task’s or
function’s call site, which can actually be anywhere in the SystemVerilog
design hierarchy. The context of an imported or exported task or function corresponds to the fully qualified
name of the task or function, minus the task or function name itself.
Note that context is
transitive through imported and export context tasks
and functions declared in the same scope. That is, if an imported task or function is running in a certain context, and
if it in turn calls an exported task or
function that is available in the same context, the exported task or function can be called without any use of svSetScope(). For example, consider a SystemVerilog call to
a native function f(), which in turn calls a native function g(). Now
replace the native function f() with an equivalent imported context C function, f’(). The system shall behave identically regardless if f() or f’() is in the call chain above g(). g() has the proper execution context in both cases.
D.8.3
Working with DPI context tasks and functions in
C code
DPI defines a small set of
functions to help programmers work with DPI context tasks
and functions. The term scope is used in the task
or function names for consistency with other SystemVerilog terminology.
The terms scope and context are equivalent for DPI tasks and functions.
There are functions that
allow the user to retrieve and manipulate the current operational scope. It is an
error to use these functions with any C code that is not executing under a call
to a DPI context imported task or function.
There are also functions
that provide users with the power to set data specific to C models into the
SystemVerilog simulator for later retrieval. These are the “put” and “get” user
data functions, which are similar to facilities provided in VPI and PLI.
The put and get user data
functions are flexible and allow for a number of use models. Users might wish
to share user data across multiple context imported functions defined in the
same SV scope. Users might wish to have unique data storage on a per function
basis. Shared or unique data storage is controllable by a user-defined key.
To achieve shared data
storage, a related set of context imported tasks and
functions should all use the same userKey. To achieve
unique data storage, a context import task or
function should use a unique key. Note that it is a requirement on the user
that such a key be truly unique from all other keys that could possibly be used
by C code. This includes completely unknown C code that could be running in the
same simulation. It is suggested that taking addresses of static C symbols
(such as a function pointer, or address of some static C data) always be done
for user key generation. Generating keys based on arbitrary integers is not a
safe practice.
Note that it is never
possible to share user data storage across different contexts. For example, if
a Verilog module m declares a context imported task
or function f, and m is instantiated more than once in the SystemVerilog
design, then f shall
execute under different values of svScope. No such executing instances of f can share user data with each other, at least not
using the system-provided user data storage area accessible via svPutUserData().
A user wanting to share a
data area across multiple contexts must do so by allocating the common data
area, then storing the pointer to it individually for each of the contexts in
question via multiple calls to svPutUserData(). This is because, although a common user key can be
used, the data must be associated with the individual scopes (denoted by svScope) of those
contexts.
D.8.5 Relationship between DPI and VPI/PLI interfaces
DPI allows C
code to run in the context of a SystemVerilog simulation, thus it is natural
for users to consider using VPI/PLI C code from within imported tasks and functions.
There is no
specific relationship defined between DPI and the existing Verilog
programming interfaces (VPI and PLI). Programmers must make no assumptions
about how DPI and the other interfaces interact. In particular, note that a vpiHandle is not equivalent to an
svOpenArrayHandle, and the two must not be interchanged and
passed between functions defined in two different interface standards.
If a user wants
to call VPI or PLI functions from within an imported task or
function, the imported task or function must be flagged with
the context qualifier.
Not all VPI or
PLI functionality is available from within DPI context imported tasks and functions. For example, a SystemVerilog
imported task or function is not a system task, and thus making the following call from
within an imported task or function would result in an error:
/* Get handle to system task call
site in preparation for argument scan */
vpiHandle myHandle = vpi_handle(vpiSysTfCall, NULL);
Similarly,
receiving misctf callbacks and other
activities associated with system tasks are not supported inside DPI imported tasks and functions. Users should use VPI or PLI if
they wish to accomplish such actions.
However, the
following kind of code is guaranteed to work from within DPI context imported tasks and functions: