As explained in Type declarations, dealing with some types directly as exported to C can be cumbersome:
Some VHDL types are exposed as fat pointers. Hence, dealing with unconstrained arrays and accesses is not straightforward.
Indexes of arrays with bounds of direction
std_logicvalues correspond to an specific enumeration.
vffi_user.h and vffi_user.vhd are utility headers/packages for easing the usage of those complex types. The examples in this section showcase the usage of those utilities. These examples are based on the foundations shown in previous examples. Hence, reading those is strongly suggested for understanding the implementation details.
As explained in the home page, GHDL’s implementation of VHPIDIRECT is not compliant with the standard,
and the standarization of a FFI/DPI is being discussed in the VASG (see [LCS-202x] VHDL DPI/FFI based on GHDL’s implementation of VHPIDIRECT).
vffi_user.h file available in this repo corresponds to the current implementation in GHDL. As the standarization process
goes forward, GHDL is expected to be adapted. Therefore, users should expect probably breaking changes in the header file.
This is a synthetic example that uses all the supported/defined data types and the helper functions in
vffi_user.h for converting
simple values, constrained arrays and multidimensional unconstrained arrays (fat pointers) to sets of C types. Hence, it is a
regression test for the header file.
In this example, a data value and key generated in VHDL are used to compute the AES cypher using OpenSSL.
The data, the key and the output are of type
std_logic_vector(0 to 127) and all of them are allocated in VHDL and passed to C
by reference, as shown in
vhdlallocarr from Sized in VHDL.
This example is a reduced version of the testing infrastructure of tmeissner/cryptocores. In that repository both encryption and decryption features of an AES IP core written in VHDL are tested using VHPIDIRECT and OpenSSL. See tmeissner/cryptocores: aes/sim/vhdl.
As explained in Restrictions on type declarations and shown in Vector of std_logic, VHDL
variables of types
std_logic``or ``std_logic_vector are not very usable in C being arrays of
char. That’s specially so because
the default encoding of
3, respectively. Hence, it is almost a requirement to convert them
into a format that is suitable for the target C function.
In C, the function provided by OpenSSL expects data as arrays of bits. Hence, variables need to be converted for the memory layout
of the data to match. This can be done in C only, and/or in VHDL too. Apart from the encryption function (encrypt.c),
an intermediate function (
cryptData) is used (caux.c). The intermediate
function uses enums and two helper functions (
vffibitArr2charArr) defined in vffi_user.h.
reverseBitsInBytes function written in VHDL is used for reordering (see vffi_user-body.vhd).
“Xyce is an open source, SPICE-compatible, high-performance analog circuit simulator, capable of solving extremely large circuit problems by supporting large-scale parallel computing platforms. It also supports serial execution on all common desktop platforms, and small-scale parallel runs on Unix-like systems”.
Xyce provides two mechanisms for external tools/simulation codes to use it:
- General External Interface (GenExt)
Comprenhensive interface aimed at developers who wish to couple other external codes written in C++. See AppNote Coupled Simulation with the Xyce General External Interface.
- Mixed Signal Interface (MixedSignal)
Interface to use Xyce as a shared library/object, either from C/C++ codes or through Python’s
ctypes. Class XyceCInterface provides methods to work with a pointer to the topmost object in a Xyce simulation; and corresponding Python bindings are provided. See AppNote Mixed Signal Simulation with Xyce 6.11 (other versions: October 2018, June 2020).
Regarding integration with GHDL, both interfaces might be suitable for different targets:
GenExtmight allow GHDL to provide VHDL-AMS support to Xyce, as the interface allows Xyce to execute callbacks defined in a foreign tool. However, VHDL-AMS support in GHDL is incomplete. Currently, the parsing stage is implemented only. See ghdl#1052.
MixedSignalprovides a higher abstraction level mechanism, which allows for co-execution of otherwise atomic modules.
In this examples, a simplified C API based on
MixedSignal is used. Moreover, VHDL bindings based on vffi_user.h
are used for driving the co-simulation from VHDL. The main differences between this simplified API and
XyceCInterfaceis designed to use a variable of type
void**as the handler of a simulation object. Unfortunately, such a type is not cleanly mapped to VHDL. Instead, identifiers of type string are used in VHDL, and this bridge provides a storage mechanism to keep track of the correspondence.
VHDL’s type system, which is based on Ada’s, avoids the requirement of using additional function/procedure parameters for passing array constraints.
This simplified API is targeted at developers who use VHDL as the main language to orchestrate the co-execution. Hence,
existence of C sources is expected to be transparent. xyce package provides the public VHDL API for end-users. Note that
it is possible to run multiple analog simulations by handling multiple instances of
xyce_t from different VHDL
modules. Find usage details in Examples.
Currently, the C implementation is based on vffi_user.h; hence, it is specific to GHDL. Nevertheless, it should be possible to adapt it for simulators that support FLI or XSI. Contributions are welcome!
Although the C implementation is not expected to be used directly, it can be useful to access it when the simulation executable is to be dynamically loaded. In these contexts, it might be handy to initialize and close the Xyce simulations from, say, Python, while VHDL is used to run steps and handle I/O. Even though specific examples are not available yet, this is a supported use case.
The current implementation of the storage mechanism is likely to be replaced as a result of the discussions in VUnit/vunit#603. Such a replacement should be transparent to end-users. However, since this is a very experimental project yet, note that disruptive changes might be required.
Xyce: C interface¶
This is a simplified API based on XyceCInterface. It
seems that binding
void** ptr from C to VHDL is not supported. So, unconstrained strings are used instead. Unconstrained
strings can be accesed as
char** id from C. A storage mechanism is used to manage actual pointers associated to each id
uint32_t xhdl_init( vffiNaturalDimArr_t* ptr, vffiNaturalDimArr_t* cir ); uint32_t xhdl_run( vffiNaturalDimArr_t* ptr, double requestedUntilTime ); uint32_t xhdl_run_1D( vffiNaturalDimArr_t* ptr, double requestedUntilTime, vffiNaturalDimArr_t* tArray, vffiNaturalDimArr_t* vArray ); uint32_t xhdl_run_2D( vffiNaturalDimArr_t* ptr, double requestedUntilTime, vffiNaturalDimArr_t* array2D ); double xhdl_read( vffiNaturalDimArr_t* ptr, vffiNaturalDimArr_t* name ); void xhdl_close( vffiNaturalDimArr_t* ptr );
Given the similarity between
char** (pointers), it might be possible to define a custom subtype of any
type and use an access type to it as a placeholder for managing C void pointers in VHDL. To be investigated…
Xyce: VHDL interface¶
Provides the user interface to co-execute Xyce simulations from VHDL. It is a protected type, which resembles OOP.
use work.xyce_xhdl_pkg.all; package xyce_pkg is alias arr2D_t is work.xyce_xhdl_pkg.arr2D_t; type xyce_t is protected procedure init ( id : string; circuit : string ); procedure run ( reqT : real ); procedure run ( reqT : real; arrTime : real_vector; arrVolt : real_vector ); procedure run ( reqT : real; arr2D : arr2D_t ); impure function reqT return real; impure function read ( name : string ) return real; procedure close; end protected; end xyce_pkg;
Provides bindings between VHDL and a foreign language through VHPIDIRECT. This source needs to be included in the design, but users are not expected to use it explicitly.
package xyce_xhdl_pkg is function xyce_init( id : string; circuit : string ) return integer; attribute foreign of xyce_init : function is "VHPIDIRECT xhdl_init"; function xyce_run( id : string; reqTime : real ) return integer; attribute foreign of xyce_run : function is "VHPIDIRECT xhdl_run"; function xyce_run( id : string; reqTime : real; arrTime : real_vector; arrVolt : real_vector ) return integer; attribute foreign of xyce_run[ string, real, real_vector, real_vector return integer ] : function is "VHPIDIRECT xhdl_run_1D"; type arr2D_t is array (natural range <>, natural range <>) of real; function xyce_run( id : string; reqTime : real; arr2D : arr2D_t ) return integer; attribute foreign of xyce_run[ string, real, arr2D_t return integer ] : function is "VHPIDIRECT xhdl_run_2D"; function xyce_read( id : string; name : string ) return real; attribute foreign of xyce_read : function is "VHPIDIRECT xhdl_read"; procedure xyce_close( id : string ); attribute foreign of xyce_close : procedure is "VHPIDIRECT xhdl_close"; end xyce_xhdl_pkg;
Xyce developers provide two versions of the same three examples (utils/XyceCInterface):
xyce_interface.pyto manage a Xyce simulation from Python. No HDL is used.
Use Verilog to trigger the Xyce simulations. However, all the logic is implemented in C. As explained in Section 7 of Mixed Signal Simulation with Xyce 6.11:
“The primary issue with the VPI capability is the lack of standards compliance. The example (…) uses the C++ features of the `XyceCInterface` directly. Wrapper functions, that only use ANSI C and the native PLI data-types in their function calls, still need to beimplemented.”
The examples in this repo are an adaptation of the VPI_examples from Xyce’s repo. Here, VHDL is used through VHPIDIRECT
bindings, instead of Verilog. The management of the execution flow is handled from VHDL, so that developers don’t need to
code in C/C++. Regarding data types, native VHDL types are used. Precisely, unconstrained strings and unconstrained
runACircuit is the most simple use case, where a single VHDL test bench and a single C file are used. It shows how to
call Xyce from VHDL, but no data is passed and neither Xyce: C interface nor Xyce: VHDL interface are used.
XyceCInterface requires circuit models to be passed as a path to a file. Providing a pointer to a
string or using
stdin is not supported yet. However, the feature has been requested to developers of Xyce and
it might be available in future versions.
runACircuitInSteps is based on Xyce: C interface and Xyce: VHDL interface. Precisely,
from xyce package is used. This allows controlling the execution flow from VHDL, unlike
or the versions in Xyce’s repo; where all the relevant logic is implemented in C and HDL is only used as a trigger.
See Section 7.1 of Mixed Signal Simulation with Xyce 6.11 for information regarding known issues with coordinated timestepping.
runWithDACs is also based on Xyce: C interface and Xyce: VHDL interface. On top of controlling the execution flow, in this example
DAC/ADC models provided by Xyce are used for passing data between the digital domain and the analog domain. So, the previous
two examples (
runACircuitInSteps) are not proper digital/analog co-simulations, because there
is no communication between digital and analog domains. Those examples are provided for illustrative purposes only, as an
introduction to this one. Here, vectors containing voltage and time need to be passed from VHDL to C. Then, an a value from
the analog circuit is read and passed to VHDL. Two equivalent implementations/tests are shown:
tb_xyce_eg_with_dacs_1D: two real_vector variables are used for passing an array of time values and an array of voltage values as separate arguments. Checking that the length of both arrays match is done explicitly.
tb_xyce_eg_with_dacs_2D: a single multidimensional array of real is used for passing time and voltage values in a single argument. As a result, all the vectors are guaranteed to have the same length.
The behaviour of both tests is exactly the same. However, the 2D variant makes it easier to extend the example for handling multiple DACs. Overall, both cases are provided for illustrative purposes, as both 1D and 2D unconstrained arrays are fat pointers and helper functions from vffi_user.h are used.
The YADC and YDAC device models in Xyce, are not realistic device models. Important issues like rise/fall times and drive/sink currents are not modelled. Hence, they are adequate for artificial transferring signal values between digital and analog domains, but those models need to be improved. See Section 5 of Mixed Signal Simulation with Xyce 6.11.
Since Xyce’s Mixed Signal Interface is an experimental feature yet, specific versions of the tools are required, and
configuration options need to be correctly chosen for producing shared libraries. In order to make getting a working
environment easier, a Dockerfile is provided. Moreover, an image is built
periodically and published as umarcor/cosim:xyce.
The image is based on ghdl/vunit:llvm-master and includes GHDL (
Python 3, VUnit (
master) and Xyce.
In these examples, VUnit is used for automatic dependency scanning, incremental builds and unit testing only. Other features, such as communication libraries and verification components, are not used.
As explained in AppNote Digital/Analog Cosimulation using CocoTB and Xyce, cocotb can be used to co-simulate GHDL and Xyce through VPI, as an alternative to using VHPIDIRECT. Such an approach might be preferred when Python is to be used as the orchestrator. Moreover, the report introduces an interesting use case where digital and analog versions of the same module are used. Unfortunately, sources of that example are not publicly available.
Neither GHDL nor Xyce provide built-in features for graphical schematic capture or plotting/viewing of simulation signals. Fortunately, both of them support generating traces that can be visualised with specific free and open source tools. If GHDL is used for generating waveforms, which might include digitalised analog signals, GTKWave can be used. Note that GTKWave provides an analog visualization type. Regarding Xyce, see AppNote Using Open Source Schematic Capture Tools With Xyce for information about schematic capture and plotting tools. See also Reading waveforms from HDL simulators with PulseView and Data type exploration and visualization in arithmetic algorithms/circuits.
Xyce supports Verilog-A through Automatic Device Model Synthesizer (ADMS), an open-source translator. Xyce/ADMS is a set of XML templates for use with ADMS, which allows to emit C++ for a device model. See Xyce/ADMS Users Guide.
Unfortunately, for VHDL-AMS there is neither built-in support nor a similar translation tool (yet). See related discussion in ghdl/ghdl#1052.