FFI/DPI Header#

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 downto are reversed.

  • std_logic values correspond to an specific enumeration.

  • Etc.

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.

Attention

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). The 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.

demo#

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.

crypto#

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.

Hint

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 HDL_0 and HDL_1 is 2 and 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 (vfficharArr2bitArr and vffibitArr2charArr) defined in vffi_user.h. Furthermore, a reverseBitsInBytes function written in VHDL is used for reordering (see vffi_user-body.vhd).

Tip

This example is based on VHDL and C. However, as show in pycb, using Python modules/functions from VHDL is also possible. Projects such as pyca/cryptography provide equivalent features to OpenSSL. Are you up to writting a variant of this example using Python? Propose a PR!

xyce#

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.sandia.gov

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:

  • GenExt might 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.

  • MixedSignal provides 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 XyceCInterface are:

  • XyceCInterface is 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.

Note

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!

Note

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.

Note

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 (string).

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
);

Note

Given the similarity between void** and 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#

xyce package#

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;

xyce_xhdl package#

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;

Examples#

Xyce developers provide two versions of the same three examples (utils/XyceCInterface):

Python_examples

Use xyce_interface.py to manage a Xyce simulation from Python. No HDL is used.

VPI_examples

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 real_vector.

runACircuit#

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.

Note

Currently, 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#

runACircuitInSteps is based on Xyce: C interface and Xyce: VHDL interface. Precisely, xyce_t and from xyce package is used. This allows controlling the execution flow from VHDL, unlike runACircuit or the versions in Xyce’s repo; where all the relevant logic is implemented in C and HDL is only used as a trigger.

Note

See Section 7.1 of Mixed Signal Simulation with Xyce 6.11 for information regarding known issues with coordinated timestepping.

runWithDACs#

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 (runACircuit and 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.

Note

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.

Usage#

Two VUnit scripts are provided: run_minimal.py builds and executes runACircuit, and run.py takes care of the others.

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 (master), Python 3, VUnit (master) and Xyce.

Note

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.

Moreover, should vffi_user.h be standardized (see VHDL DPI/FFI based on GHDL), VUnit would allow testing multiple simulators easily.

Note

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.

GUI features#

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.

Analog modelling#

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.