27.1.1
Tasks and Functions
DPI allows direct
inter-language function calls between the languages on either side of the
interface. Specifically, functions implemented in a foreign language can be
called from SystemVerilog; such functions are referred to as imported
functions. SystemVerilog functions that are to be called from a foreign
code shall be specified in export declarations (see Section 27.6 for more
details). DPI allows for passing SystemVerilog data between the two domains
through function arguments and results. There is no intrinsic overhead in this
interface.
It is also
possible to perform task enables across the language boundary. Foreign code may
call SystemVerilog tasks, and native Verilog code may
call imported tasks. An imported task has the same
semantics as a native Verilog task: It never returns
a value, and it may block and consume simulation time.
All functions used in DPI
are assumed to complete their execution instantly and consume 0 (zero)
simulation time, just as normal SystemVerilog functions. DPI provides no means
of synchronization other than by data exchange and explicit transfer of
control.
Every imported task and function needs to be declared. A declaration
of an imported task or function is referred to
as an import declaration. Import declarations are very similar to
SystemVerilog task and function
declarations. Import declarations can occur anywhere where SystemVerilog task and function definitions are permitted. An
import declaration is considered to be a definition of a SystemVerilog task or function with a foreign language
implementation. The same foreign task or function
can be used to implement multiple SystemVerilog tasks
and functions (this can be a useful way of providing differing default
argument values for the same basic task or function),
but a given SystemVerilog name can only be defined once per scope. Imported tasks and functions can have zero or more formal input, output, and inout arguments. , and they Imported tasks always return a void value, and thus can only be used
in statement context. Imported functions can return a result or be
defined as void functions.
DPI is based entirely upon
SystemVerilog constructs. The usage of imported functions is identical as for
native SystemVerilog functions. With few exceptions imported functions and
native functions are mutually exchangeable. Calls of imported functions are
indistinguishable from calls of SystemVerilog functions. This facilitates
ease-of-use and minimizes the learning curve. Similar
interchangeable semantics exist between native SystemVerilog tasks and imported
tasks.
The SystemVerilog compiler
or simulator shall generate and/or use the function call protocol and argument
passing mechanisms required for the intended foreign language layer. The same
SystemVerilog code (compiled accordingly) shall be usable with different
foreign language layers, regardless of the data access method assumed in a
specific layer. Annex A
E defines DPI foreign language layer for the C
programming language.
27.3 Global name space of imported and exported tasks and functions
Every task or function imported to SystemVerilog must
eventually resolve to a global symbol. Similarly, every task or function exported from SystemVerilog defines a global
symbol. Thus the tasks and functions imported
to and exported from SystemVerilog have their own global name space of linkage
names, different from compilation-unit scope name space. Global names of
imported and exported tasks or functions must
be unique (no overloading is allowed) and shall follow C conventions for
naming; specifically, such names must start with a letter or underscore, and
can be followed by alphanumeric characters or underscores. Exported and
imported tasks and functions, however, can be
declared with local SystemVerilog names. Import and export declarations allow
users to specify a global name for a task or function
in addition to its declared name. Should a global name clash with a
SystemVerilog keyword or a reserved name, it shall take the form of an escaped
identifier. The leading backslash ( \ ) character and the trailing white space shall be
stripped off by the SystemVerilog tool to create the linkage identifier. Note
that after this stripping, the linkage identifier so
formed must comply with the normal rules for C identifier construction. If a
global name is not explicitly given, it shall be the same as the SystemVerilog task or function name. For
example:
export "DPI" foo_plus
= function \foo+ ; // "foo+" exported as "foo_plus"
export "DPI" function foo; // "foo" exported
under its own name
import "DPI" init_1 = function
void \init[1] (); // "init_1" is a linkage name
import "DPI" \begin = function
void \init[2] (); // "begin" is a linkage name
The same global
task or function can be referred to in multiple import
declarations in different scopes or/and with different SystemVerilog names, see
Section 27.4.4.
Multiple export
declarations are allowed with the same c_identifier,
explicit or implicit, as long as they are in different scopes and have the same
type signature (as defined in Section 27.4.4 for imported functions). Multiple
export declarations with the same c_identifier
in the same scope are forbidden.
27.4
Imported tasks and functions
27.4.1
Required properties of imported task or
functions calls
- semantic constraints
This section defines the
semantic constraints imposed on imported tasks or functions.
Some semantic restrictions are shared by all imported tasks
or functions. Other restrictions depend on whether the special
properties pure (see
Section 27.4.2) or context (see Section 27.4.3) are specified for an imported task
or function. A SystemVerilog compiler is not able to verify that those
restrictions are observed and if those restrictions are not satisfied, the
effects of such imported task or function calls
can be unpredictable.
27.4.1.1
Instant completion of imported functions
Imported functions shall
complete their execution instantly and consume zero-simulation time, similarly
to native functions.
Note that
imported tasks may consume time, similar to native SystemVerilog tasks.
27.4.1.3
Special properties pure and context
Special properties can be
specified for an imported task or function: as pure or as context (see also Section 27.4.2 or 27.4.3).
A function whose result depends
solely on the values of its input arguments and with no side effects can be
specified as pure.
This can usually allow for more optimizations and thus can result in improved
simulation performance. Section 27.4.2 details the rules that must be obeyed by
pure functions. An imported task can never be declared
pure.
An imported task or function that is intended to call exported tasks or functions or to access SystemVerilog data
objects other then its actual arguments (e.g. via VPI or PLI calls) must be
specified as context.
Calls of context tasks and functions are
specially instrumented and can impair SystemVerilog compiler optimizations;
therefore simulation performance can decrease if the context
property is specified when not
necessary. A task or function not specified as context
shall not read or write any data
objects from SystemVerilog other then its actual arguments. For tasks or functions not specified as context, the effects of calling PLI, VPI, or exported
SystemVerilog tasks or functions can be
unpredictable and can lead to unexpected behavior; such calls can even crash.
Section 27.4.3 details the restrictions that must be obeyed by non-context tasks or functions.
27.4.1.5 Reentrancy of imported tasks
Since imported
tasks may block (consume time), it is possible for an imported task’s C code to
be simultaneously active in multiple execution threads. Standard reentrancy
considerations must be made by the C programmer. Some examples of such
considerations include safe use of static variables, and ensuring that only
thread-safe C standard library calls (MT safe) are used.
27.4.1.6 C++ exceptions
It is possible
to implement DPI imported tasks and functions using C++, as long as C linkage
conventions are observed at the language boundary. If C++ is used, exceptions
must not propagate out of any imported task or function. Undefined behavior
will result if an exception crosses the language boundary from C++ into
SystemVerilog.
27.4.3
Context tasks or functions
Some DPI imported tasks or functions require that the context of their call
is known. It takes special instrumentation of their call instances to provide
such context; for example, an internal variable referring to the “current
instance” might need to be set. To avoid any unnecessary overhead, imported task or function calls in SystemVerilog code are not
instrumented unless the imported task or
function is specified as context.
All DPI exported tasks or functions require that the context of their
call is known. This occurs since SystemVerilog task or
function declarations always occur in instantiable
scopes, hence allowing a multiplicity of unique task
or function instances.
For the sake of simulation
performance, an 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 than its actual arguments. Only the actual arguments
can be affected (read or written) by its call. Therefore, a call of a
non-context 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, or by calling an export task or
function. Therefore, a call to a context task or
function is a barrier for SystemVerilog compiler optimizations.
Only calls of context
imported tasks or functions are properly
instrumented and cause conservative optimizations; therefore, only those tasks or functions can safely call all tasks or functions from other APIs, including PLI and
VPI tasks or functions or exported
SystemVerilog tasks or functions. For imported tasks or 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. However note that declaring an import context task or function does not automatically make any
other simulator interface automatically available. For VPI access (or any other
interface access) to be possible, the appropriate implementation defined
mechanism must still be used to enable these interface(s). Note also that DPI
calls do not automatically create or provide any handles or any special
environment that can be needed by those other interfaces. It is the user’s
responsibility to create, manage or otherwise manipulate the required
handles/environment(s) needed by the other interfaces.
Context imported tasks or functions are always implicitly supplied a
scope representing the fully qualified instance name within which the import
declaration was present. This scope defines which exported
SystemVerilog tasks or functions can be called
directly from the imported task or function;
only tasks or functions defined and exported
from the same scope as the import can be called directly. To call any other
exported SystemVerilog tasks or functions, the
imported task or function shall first have to
modify its current scope, in essence performing the foreign language equivalent
of a SystemVerilog hierarchical task or
function call.
Special DPI utility tasks and functions exist that allow imported tasks or functions to retrieve and operate on their
scope. See Annex D for more details.
Each imported task or function shall be declared. Such declarations are referred to as import declarations. The
syntax of an import declaration
is similar to the syntax of SystemVerilog task or function
prototypes (see Section 10.6).
Imported tasks or functions are similar to SystemVerilog tasks or functions. Imported tasks or functions can have zero or more formal input, output, and inout arguments. Imported functions can return a result or
be defined as void functions. Imported tasks never
return a result, and thus are always declared in foreign code as void
functions.
dpi_import_export ::= // from Annex A.2.6
import
"DPI" [ dpi_function_import_property
] [ c_identifier = ] dpi_function_proto ;
| import "DPI" [ dpi_task_import_property ] [ c_identifier = ] dpi_task_proto ;
| export "DPI" [ c_identifier = ] function function_identifier ;
| export "DPI" [ c_identifier = ] task task_identifier ;
dpi_function_import_property ::= context | pure
dpi_task_import_property ::= context
dpi_function_proto11,12 ::= function named_function_proto
dpi_task_proto12 ::= task named_task_proto
An import declaration
specifies the function or task name, function result
type, and types and directions of formal arguments. It can also provide
optional default values for formal arguments. Formal argument names are
optional unless argument passing by name is needed. An import declaration can
also specify an optional function or task
property: context or
pure. Imported functions can have properties context or pure;
imported tasks can have property context.
Note that an import
declaration is equivalent to defining a task or
function of that name in the SystemVerilog scope in which the import
declaration occurs, and thus multiple imports of the same task or function name into the same scope are
forbidden. Note that this declaration scope is particularly important in the
case of imported context tasks or functions,
see Section 27.4.3; for non-context imported tasks or
functions the declaration scope has no other implications other than defining
the visibility of the task or function.
c_identifier provides
the linkage name for this task or function in
the foreign language. If not provided, this defaults
to the same identifier as the SystemVerilog task or
function name. In either case, this linkage name must conform to C identifier
syntax. An error shall occur if the c_identifier,
either directly or indirectly, does not conform to these rules.
For any given c_identifier (whether explicitly defined with
c_identifier=, or automatically determined from the task or function name), all declarations, regardless
of scope, must have exactly the same type signature. The signature includes the
return type and the number, order, direction and types of each and every
argument. Type includes dimensions and bounds of any arrays or array
dimensions. Signature also includes the pure/context qualifiers that can be associated with an extern
definition.
Note that multiple
declarations of the same imported or exported task or
function in different scopes can vary argument names and default values,
provided the type compatibility constraints are met.
A formal argument name
is required to separate the packed and the unpacked dimensions of an array.
The qualifier ref cannot be used in import declarations. The actual
implementation of argument passing depends solely on the foreign language layer
and its implementation and shall be transparent to the SystemVerilog side of
the interface.
The following are
examples of external declarations.
// miscellanea
import "DPI"
function bit [15:0] getStimulus();
import "DPI”
context function void processTransaction(chandle elem,
output logic
[64:1] arr [0:63]);
import “DPI” task checkResults(input string s, bit [511:0]
packet);
A rich subset of SystemVerilog
data types is allowed for formal arguments of import and export tasks or functions. Generally, C compatible types,
packed types and user defined types built of types from these two categories
can be used for formal arguments of DPI tasks or
functions. The set of permitted types is defined inductively.
The following SystemVerilog
types are the only permitted types for formal arguments of import and export tasks or functions:
—
void, byte, shortint, int, longint, real, shortreal, chandle, and string
—
scalar values of
type bit and logic
—
packed one
dimensional arrays of type bit and logic
Note however, that every
packed type, whatever is its structure, is eventually equivalent to a packed
one dimensional array. Therefore practically all packed types are supported,
although their internal structure (individual fields of structs,
multiple dimensions of arrays) shall be transparent and irrelevant.
— enumeration types interpreted as the type associated
with that enumeration
—
types constructed
from the supported types with the help of the constructs:
—
struct
—
unpacked array
—
typedef
The following caveats apply
for the types permitted in DPI:
—
Enumerated data
types are not supported directly. Instead, an enumerated data type is
interpreted as the type associated with that enumerated type.
— SystemVerilog does not specify the actual memory
representation of packed structures or any arrays, packed or unpacked. Unpacked
structures have an implementation-dependent packing, normally matching the C
compiler.
— The actual memory representation of SystemVerilog data
types is transparent for SystemVerilog semantics and irrelevant for
SystemVerilog code. It can be relevant for the foreign language code on the
other side of the interface, however; a particular representation of the
SystemVerilog data types can be assumed. This shall not restrict the types of
formal arguments of imported tasks or
functions, with the exception of unpacked arrays. SystemVerilog implementation
can restrict which SystemVerilog unpacked arrays are passed as actual arguments
for a formal argument which is a sized array, although they can be always
passed for an unsized (i.e., open) array. Therefore,
the correctness of an actual argument might be implementation-dependent.
Nevertheless, an open array provides an implementation-independent solution.
27.7 Exported tasks
SystemVerilog
allows tasks to be called from a foreign language, similar to functions. Such tasks
are termed “exported tasks”.
All aspects of
exported functions described above in section 27.6 apply to exported tasks.
This includes legal declaration scopes as well as usage of the optional c_identifier.
It is never
legal to call an exported task from within an imported function. These
semantics are identical to native SystemVerilog semantics, in which it is
illegal for a function to perform a task enable.
It is legal for
an imported task to call an exported task only if the imported task is declared
with the context property. See section 27.4.3 (Context tasks and functions) for
more details.
One difference
between exported tasks and exported functions is that SystemVerilog tasks do
not have return value types. The return value of an exported task is an int value which indicates if a disable is active or not on
the current execution thread.
Similarly,
imported tasks return an int value which is used to
indicate that the imported task has acknowledged a disable. See section 27.8
for more detail on disables in DPI.
27.8 Disabling DPI tasks and functions
It is possible
for a disable statement to disable a block that is
currently executing a mixed language call chain. When a DPI import task or
function is disabled, the C code is required to follow a simple disable
protocol. The protocol gives the C code the opportunity to perform any
necessary resource cleanup, such as closing open file handles, closing open vpi handles, or freeing heap memory.
An imported task
or function is said to be in the disabled state when a disable statement
somewhere in the design targets either it or a parent for disabling. Note that
the only way for an imported task or function to enter the disabled state is
immediately after the return of a call to an exported task or function. An
important aspect of the protocol is that disabled import tasks and functions
must programmatically acknowledge that they have been disabled. A task or
function can determine that it is in the disabled state by calling API function
svIsDisabledState().
The protocol is
composed of the following items:
1. When an
exported task returns due to a disable, it must return a value of 1. Otherwise
it must return 0.
2. When an
imported task returns due to a disable, it must return a value of 1. Otherwise
it must return 0.
3. Before an
imported function returns due to a disable, it must call API function svAckDisabledState().
4. Once an imported
task or function enters the disabled state, it is illegal to make any further
calls to exported tasks or functions.
Items 2, 3, and
4 are mandatory behavior for imported DPI tasks and functions. It is the
responsibility of the DPI programmer to correctly implement the behavior.
Item 1 is
guaranteed by SystemVerilog simulators. In addition, simulators must implement
checks to ensure that items 2, 3, and 4 are correctly followed by imported
tasks and functions. If any protocol item is not correctly followed, a fatal
simulation error is issued.
Note that if an
exported task itself is the target of a disable, its parent imported task is
not considered to be in the disabled state when the exported task returns. In
such cases the exported task will return value 0, and calls to svIsDisabledState() will return 0 as well.
When a DPI
imported task or function returns due to a disable, the values of its output
and inout parameters are undefined. Similarly,
function return values are undefined when an imported function returns due to a
disable. C programmers may return values from disabled functions, and C
programmers may write values into the locations of output and inout parameters of imported tasks or functions. However, SystemVerilog
simulators are not obligated to propagate any such values to the calling
SystemVerilog code if a disable is in effect.