Direct Programming Interface

Proposal Details

  • Who Updates: PeterFlake
  • Date Proposed:
  • Date Last Updated:2020/8/13
  • Priority:
  • Complexity:
  • Focus:

Current Situation

The FOREIGN attribute allows foreign language subprograms to be called, but has two deficiencies which limit its use. It does not allow foreign language data types returned by one subprogram call to be passed to the next subprogram call, and it does not allow VHDL subprograms to be called from the foreign language.

Although section 4.3 states that the FOREIGN attribute does not specify the parameter-passing mechanism, and the format of the string is implementation dependent, these are specified for VHPI subprograms. The VHPI parameter-passing mechanism requires extensive code to extract the actual values.


It should be simple to import a subprogram, i.e. have VHDL calling a C function without code to manipulate the parameters. This requires that data types of parameters can be implicitly converted between languages.

It should be simple to export a subprogram, i.e. have a C function calling VHDL. This should take place in the context of an imported subprogram. The VHDL may be a procedure with a wait.

The DPI should support the Accellera SCE_MI interface to hardware emulators in a similar way to the SystemVerilog DPI. This requires not only the export of subprograms, but the ability to change context from one to another.

The VHDL code should work without modification with any tool supporting the DPI standard.

There has also been a request for VHDL to import a SystemVerilog subprogram, and for VHDL to export a subprogram to SystemVerilog.

There has also been a request to import Python, and dynamically determined subprograms.

Implementation details

New FOREIGN attributes are proposed. The attributes should include the language name, so that multiple languages can be supported by multiple attributes. A new attribute EXPORT is proposed, which has no effect on normal VHDL code but creates access for a foreign language. In the case of C the names IMPORT_C and EXPORT_C are proposed to minimize clashes with existing code. These are to be used with strings of C for subprogram import and export. In the case of SystemVerilog the names IMPORT_SV and EXPORT_SV are proposed.

When an IMPORT_C imported subprogram is called, the scope ( i.e. which declaration in which instance or package called it) is recorded. This scope can be accessed and used for for calling VHPI functions. This functionality requires a new VHPI data type vhContext, as well as new VHPI functions vhGetContext and vhSetContext.

The parameter passing is by copy in and copy out. Therefore the formal parameters of an imported or exported subprogram cannot be protected types. In the case of out or inout modes the corresponding C formal parameter must be a pointer. The actual must be a temporary variable provided by the tool. Note that the number and types of parameters are fixed, so that printf-style flexibility cannot be used (varargs).

The implicit mapping for basic VHDL types to C99 types is:


BIT _Bool


INTEGER long int

REAL double

TIME long long int

STRING char [ ]

The implicit mapping for basic VHDL types to SystemVerilog types is:


BIT bit



REAL real

TIME time

STRING string

The foreign language equivalents of other standard VHDL data types should be specified by the tool vendor as a C header or SystemVerilog include file. It is up to the tool to check the mapping between the types. User-defined types such as enumerations need to be re-specified in the foreign language. It is up to the user to ensure that the formals in the foreign languge match the formals in VHDL.

Exporting a VHDL function or procedure requires a unique linker name. If the export is declared in a package there is only one instance of the subprogram, so it can be called from anywhere. If the export is declared in an entity, there can be multiple instances of the subprogram. The tool has to select which one to use. The default is the instantiated scope (context) of the import declaration that called the C code. This means that the exported subprogram is used by imported subprograms from the same entity instance. Note that an exported procedure can only be called by an imported procedure. To provide more flexibility the context can be changed by calling vhSetContext with a parameter of type vhContext obtained from a previous call to vhGetContext.

[Note that in SystemVerilog the names are svSetScope, svScope and svGetScope. A useful diagnostic tool is svGetCallerInfo (char **filename, int *lineNumber)]

An imported C procedure, that calls an exported VHDL procedure containing a wait, may be called by several VHDL processes at the same time, and therefore should be written to be multi-thread safe. Rules for re-entrant C coding include care with static variables and library calls.

Code Examples

A function or procedure is imported as follows, with the string format standardized to give the linker name:

package P is

    procedure srand (seed:in NATURAL); -- C library void function
    attribute FOREIGN of srand: procedure is "IMPORT_C srand"; 

    function FFI_PUTS (s:STRING) return INTEGER; -- use Libffi to call C library
    attribute FOREIGN of FFI_PUTS: function is "IMPORT_C ffi_puts";

end package;

The C code is as follows, with thanks to the Red Hat Libffi documention:

#include <stdio.h>
#include <ffi.h>

int f( int x) { return x*x; }
int ffi_puts( char *s)

    ffi_cif cif;
    ffi_type *args[1];
    void *values[1];
    int rc = 0;

    /* Initialize the argument info vectors */

    args[0] = &ffi_type_pointer;
    values[0] = s;

    /* Initialize the call interface */

    if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_uint, args) == FFI_OK)

    ffi_call(&cif, puts, &rc, values); /* rc now holds the result of the call to puts */
    return rc;

A function or procedure is exported as follows, again with the attribute string to give the linker name. It must be exported from a point in the package where the function name is visible.

   package Q is
      function G (x:INTEGER) return INTEGER is
      beginend -- body
      attribute EXPORT of G: function is "EXPORT_C g";
   end package Q;

Use Cases

These enhancements will aid integration of a VHDL simulation with other tools and models, such as verification languages, instruction set simulators and hardware emulators. Libffi provides a means of integration with scripting languages.

Arguments FOR

These enhancements will make VHDL more useful as a simulation language because it will be easier and quicker to integrate it with other simulation and programming environments including operating systems and middleware.

Arguments AGAINST

The functionality can be provided in the foreign language using customised code for wrapping subprograms and for interacting with the simulation using the VHPI.

Might be better to have a way to specify the linker level interfaces for the VHDL rather than defining stuff at user level, e.g. you just tell you VHDL compiler to import/export according to a particular ABI for given functions outside the language itself. Handling plumbing is usually easier on the C/C++ side.

General Comments

  • JohnShields - 2011-06-09 - Comments from Collect Requirements page
  • develop a DPI for VHDL to support easy creation of foreign procedures
    • aids integration with other tools
    • simple export/import of subp declaration
    • subprogram calling C function and vice versa
    • basic types can be marshalled
    • any ref type that need to be read/updated when assigned, when time is consumed require API (and we may choose not to support)
    • ?Interface between VHDL & Verilog/SystemVerilog as well as C?
  • TristanGingold - 2015-08-07 - How do you make the difference between an imported subprogram and an exported subprogram ? Looks like for dpi_c, it's the presence of "extern" in the attribute. This is not very apparent, and I'd prefer to have two different attribute names (eg: C_IMPORT and C_EXPORT).
  • TristanGingold - 2015-08-07 - I don't see the issue with the FOREIGN attribute (in order to import a subprogram. The language name can appear in the attribute as it is certainly strange to import one subprogram from two different implementation. The last sentence in 'current situation' ('the single attribute FOREIGN does not allow more than one foreign language') doesn't seem correct.
  • TristanGingold - 2015-08-07 - It would be nice not to have to define the body (in vhdl) of an imported subprogram. Currently, even with FOREIGN, the user has to write a dummy body - which is stupid.
  • TristanGingold - 2015-08-07 - Allowing suspension (wait statement) in exported subprogram may not be a good idea, given the implementation burden.
  • TristanGingold - 2015-08-07 - The code example about file_open_kind is overly complex. I am not sure we want to map one type to a very different one.
  • TristanGingold - 2015-08-07 - The first line of 'code example' is not valid VHDL!
  • DavidKoontz - 2016-02-13 - int isn't a guaranteed 32 bit integer in C. Nor is VHDL integer guaranteed to be 32 bits (it's a minimum size). A long int isn't guaranteed to map to integer, either (and the same for unsigned long it). A VHDL string should map to an unsigned char[], and a character should map to an unsigned char. (the C char size is guaranteed to be at least 8 bits, VHDL uses ISO 8859-1, C 8 bit char is common).

I don't know that you have any business passing pointers across the C <=> VHDL interface. In SystemVerilog for instance the C memory space and VHDL memory space are separate (that would infer you'd have to use PLI/VPI, or in our case VHPI). Protecting the simulator and model memory resources sounds like a really good idea. C doesn't have what you could call a VHDL procedure equivalent with a parameter association list. Parameters of mode out imply an intermediary wrapper and passed pointers to a C function.

Note that VHDL can be otherwise implemented using interpretation rather than compiling and it's not outside the real of inserting foreign subprograms into a memory based model. That sort of implies worrying about implementation (e.g. IEEE Std 1800-2012 Annex J Inclusion of foreign language code) may be a bit out of bounds (which also makes it hard to claim a DPI-SV or the System C equivalent).

There's likely no easy (compact) way to pass VHDL context (scope and visibility based on where the FOREIGN declaration or specification is made) to a foreign language subprogram supplying the subprogram body (as much as Tristan hates the dummy bodies they could do that). I think Jim was commenting on something anecdotal from a particular vendor (starts with Mo) who wanted configurations to be analyzed into a different library than the entity and architecture they reference. This is the same issue, where does the context supplying scope and visibility come from? The way to recreate context is to re-analyze (even if you don't submit to a design library, the idea is that the design description is not updated nor is the reference library). In the case of DPI (or VHPI) how is that context conveyed? (like for a FOREIGN architecture, and ghdl doesn't do that yet.).

There are obvious issues with foreign subprograms containing wait statements which theoretically can cause an enclosing process to wait. That's real VHPI stuff instead, Signal parameters? What do signals look like? That's either implementation dependent or VHPI-ish and not Direct Programming Interface. Implementing signals likewise requires wrappers to do formal/actual translation between memory spaces for mode out (inout, buffer). Signals have different inward and outward references, read from an effective value, write to a particular driver's projected output waveform, for concurrent procedure calls there's an implicit sensitivity list. There's delay mechanism, pulse rejection limit, and after time expression plus some other out of band stuff (null, unaffected), and as Tristan notes do you really want call backs for waits in DPI?

There's bound to be a line you should draw between VHPI and DPI for feature support. Documenting could take more standard pages than SystemVerilog uses, VHDL is more complex. There's also an issue with supported VHDL types. You need something extensible beyond supported types. How much the types for packages standard, std_logic_1164, numeric_bit and numeric_std should be predefined? This stuff needs some cerebral attention. You really ought to have an implementation before standardization. If it ain't working now it may be too late for -2017, depending on supported features.

-- RyanHinton - 2020-02-17 -- Can we define distinct, meaningful levels of functionality? For example, I can imagine this facility without supporting wait statements in callbacks (e.g. only allowing function callbacks). Or a DPI without callbacks at all. Yes, this would be limiting relative to the current requirements, but is it sufficient? What are the actual use cases for the DPI?

-- PeterLadow - 2020-02-29 -- The specific use can I can think of is to drastically simplify the interface for these simple types of operations. Eliminating the wait behavior and limiting parameters to simple types (i.e. no composites, access, etc) is sufficient I think for a first run at things. Consider your case of "puts". Now consider the VHPI boilerplate necessary to pass arguments and extract them:


PLI_VOID myputs(const struct vhpiCbDataS *cb_data_p)
  // Get the call handle
  vhpiHandleT call_hdl = cb_data_p->obj;
  // Parameter handles and iterator
  vhpiHandleT param_hdl, param_iter;

  // Return value from the function
  vhpiValueT ret;
  ret.format = vhpiInt;
  ret.value.intg = -1; // Default to an error

  // Verify there are parameters
  if ((param_iter = vhpi_iterator(vhpiParamDecls, call_hdl)) == 0)
    // Do some error processing, perhaps vhpi_assert
    // Iterate over the parameters
    while ((param_hdl = vhpi_scan(param_iter)))
      // Verify the handle is of the correct type
      if (param_hdl.vValue->format != vhpiStrVal)
        // Do some error processing, perhaps vhpi_assert
        vhpiValueT val;
        // Zero out the value.  Note this will zero out val.bufsize, so that the call to vhpi_get_value will return the string length
        memset(&val, 0, sizeof(vhpiValueT));
        vhpi_get_value(param_hdl, &val);
        puts_retval = puts(val.value.str);
    // Release the handle
    vhpi_release_handle( param_hdl );
  // Not done yet!  Still need to set the return value.
  vhpi_put_value(call_hdl, &ret);

That's just for a simple puts() call. Imagine something like "function check_bits(constant vec : in std_logic_vector; const mask : in std_logic_vector) return boolean;" Accesses to vectors, especially std_logic_vector (which is an array of an enumerated type) is brutal. Or consider this case "procedure log_data(const vec : in std_logic_vector; fd : integer; echo_stdout : boolean);" One needs a TON of boilerplate to parse parameters, extract values.

Now, if these could be simple C functions (as per Verilog's DPI):

  int myputs(const char *str); int check_bits(const char vec[], int vec_len, const char mask, int mask_len); // Or something like this void log_data(const char vec[], int vec_len, int fd, int echo_stdout); // Or something like this  

Significantly simpler for the user.


Add your signature here to indicate your support for the proposal

-- DavidSmith - 2012-09-26 David Smith Synopsys, Inc. - I support this as long as it is coordinated with 1076.1. We have had this request from user's of the language and I am pleased to see 1076 starting to address it.

-- Brent Hayhoe - 2012-10-09

-- RadoslawNawrot - 2015-04-28

Topic attachments
I Attachment Action Size Date Who Comment
PDFpdf Direct_Programming_Interface_for_VHDL_201x_Proposal_Update2.pdf manage 93.6 K 2013-12-04 - 09:07 RadoslawNawrot Direct Programming Interface for VHDL 201x Proposal Update uploaded. Co-existance with Sv and SystemC, type mapping, OpenArrays
Topic revision: r35 - 2020-08-13 - 18:07:56 - PeterFlake
Copyright © 2008-2020 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback