Quick Start#

‘rand’ from stdlib#

By default, GHDL includes the standard C library in the generated simulation models. Hence, resources from stdlib can be used without any modification to the build procedure.

This example shows how to import and use rand to generate and print 10 integer numbers. The VHDL code is equivalent to the following C snippet. However, note that this C source is NOT required, because stdlib is already built in.

#include <stdlib.h>
#include <stdio.h>

int main (void) {
  int i;
  for (i = 0; i < 10; i++)
    printf ("%d\n", rand ());
  return 0;
}

‘sin’ from libmath#

By the same token, it is possible to include functions from system library by just providing the corresponding linker flag.

In this example, function sin from the math library is used to compute 10 values. As in the previous example, no additional C sources are required, because the math library is already compiled and installed in the system.

custom C#

When the required functionality is not available in pre-built libraries, custom C sources and/or objects can be added to the elaboration and/or linking.

This example shows how to bind custom C functions in VHDL as either procedures or functions. Four cases are included: custom_procedure, custom_procedure_withargs, custom_function and custom_function_withargs. In all cases, the parameters are defined as integers, in order to keep it simple. See Type declarations for further details.

Since either C sources or pre-compiled .o objects can be added, in C/C++ projects of moderate complexity, it might be desirable to merge all the C sources in a single object before elaborating the design.

Wrapping ghdl_main#

basic#

Instead of using GHDL’s own entrypoint to the execution, it is possible to wrap it by providing a custom program entrypoint (main function), wherein the execution of the simulation is triggered by calling ghdl_main.

This example shows the most basic of such usage. ghdl_main is declared as extern in C, and arguments argc and argv are passed without modification. However, this sets the ground for custom prepocessing and postprocessing in a foreign language.

Other options are to just pass empty arguments (ghdl_main(0, NULL)) or to customize them:

char* args[] = {NULL, "--wave=wave.ghw"};
ghdl_main(2, args);

See Wrapping a simulation for further details about the constraints of argv. Furthermore, section Command-Line Arguments below shows argument parsing/manipulation strategies.

time#

Although most of the provided examples are written in C, VHPIDIRECT can be used with any language that supports a C-like compile and link model.

This example shows how to time the execution of a simulation from either C or Ada. In both cases, function clock is used to get the time before and after calling ghdl_main. Regarding the build procedure, it is to be noted that C sources are elaborated with -e, because GHDL allows passing parameters (in this case, additional C sources) to the compiler and/or linker. However, since it is not possible to do so with Ada, gnatmake, --bind and --list-link are used instead. See Linking object files for further info about custom linking setups.

Hint

Compared to the previous example, the declaration of ghdl_main includes three arguments in this example: int argc, void** argv, void** envp. This is done for illustration purposes only, as it has no real effect on the exercise.

exitcb#

When wrapped in a foreign language, calls to ghdl_main should return an exit code and allow the regular execution of the wrapper, so that users can handle simulation results in there. However, bugs might result in failures that exit immediately through an abortion signal. This example shows how to register an exit handler and a signal handler, to allow executing custom code regardless of crashes in the simulation.

In VHDL 1993, simulation termination statements and exit codes that simulators should produce were undefined. Therefore, it was common practice to termine the simulations through reports of severity failure. When wrapping GHDL, such strategies might result in undesired exit procedures. Using VHDL 2008 is suggested.

Linking#

bind#

Although GHDL’s elaborate command can compile and link C sources, it is sometimes preferred or required to call a compiler explicitly with custom arguments. This is useful, e.g., when a simulation is to be embedded in the build of an existing C/C++ application.

This example is equivalent to basic, but it shows how to use --bind and --list-link instead of -e. See Linking object files for further details.

Hint

Objects generated by --bind are created in the working directory. See GCC/LLVM only commands and ghdl#781.

package#

If the auxillary VHPIDIRECT subprograms need to be accessed in more than one entity, it is possible to package the subprograms. This also makes it very easy to reuse the VHPIDIRECT declarations in different projects.

In this example two different entities use a C defined c_printInt(val: integer) subprogram to print two different numbers. Subprogram declaration requirements are detailed under the Type declarations section.

sharedvar#

While sharing variables through packages in VHDL 1993 is flexible, in VHDL 2008 protected types need to be used. However, GHDL allows to relax some rules of the LRM through -frelaxed.

This example showcases multiple ways of sharing variables through packages, depending on the target version of the standard. Three different binaries are built from the same entity, using:

  • A VHDL 1993 package with --std=93.

  • A VHDL 1993 package with --std=08 -frelaxed.

  • A VHDL 2008 package with --std=08.

Note

Procedure setVar is not strictly required. It is used to allow the same descriptions of the entity/architectures to work with both VHDL 1993 and VHDL 2008. See the bodies of the procedure in pkg_93.vhd and pkg_08.vhd.

shint#

As an alternative to using a shared variable in VHDL, subdir shint contains an approach based on a helper record type which is used as a handle. Mimicking the concept of methods from Object Oriented (OO) programming, helper C functions are used to read/write the actual variables, instead of sharing data through an access/pointer. This approach is more verbose than others, but it works with either VHDL 1993 or VHDL 2008 without modification and without requiring -frelaxed. Moreover, it enhances encapsulation, as it provides a user-defined API between VHDL and C, which can improve maintainability when sources are reused. As a matter of fact, this approach is found in verification projects such as VUnit and OSVVM.

shrecord#

Records are contiguous collections of types in VHDL, analogous to struct in C. This subexample quickly showcases:

  • sharing a C declared struct between VHDL entities

  • sharing a VHDL declared record with C functions

This example only uses two subprograms, and does not have a globally shared variable like the other sharedvar examples. This is in order to keep the package compatible with both VHDL 93 and 08, and keep the focus on sharing a record variable.

Note

The records/structs have a field of type std_logic_vector/char[], which is a variable that is more complicated than the integer’s in previous examples. The Vector of std_logic example fully explains this variable.

As mentioned in Restrictions on type declarations, both records and accesses to records are passed by reference. This means that the functions in C receive and return struct *, regardless of the VHDL type being an access.

Command-Line Arguments#

Top-level generics#

As explained in Simulation options, there is no standard method in VHDL to obtain command-line arguments. However, GHDL allows to override top-level generics, with certain restrictions. See -gGENERIC and ghdl#1388 for further details.

In this example, two top-level generics of types string and integer are used. First, default values are used. Then, both are overriden through CLI arguments. Note that top-level generics cannot be undefined; hence, the user must provide either default values or CLI arguments.

Parsing/customizing argv#

By the same token, when wrapping a simulation, ghdl_main receives argc, argv and env as any regular main function in C. Hence, when GHDL is wrapped as explained in Wrapping a simulation, it is possible to either pass raw arguments or to process them before calling ghdl_main. As a result, overrides for top-level generics can be defined in C sources. Otherwise, GHDL will complain by producing an error.

These examples showcase multiple approaches to manipulate top-level generics when GHDL is wrapped:

  • rawargs: pass arguments without modification. This, which is equivalent to basic above, is the minimal requirement for default CLI features to be available when wrapping GHDL.

  • procargs: pass arguments and set/add some. First, no argument is provided and genStr is assigned a value in C. Then, genInt is provided and genStr is added in C.

    Note

    This is a naive approach without any specific library. getopt and/or Argp are popular ways to parse arguments in C. A complete example that uses getopt or Argp would be very welcome, since it would allow non trivial arguments ([c options] -- [ghdl options] or [ghdl options] -- [c options]). Feel free to open a PR!

  • fcnargs: pass arguments without modification, but use a function call to set the default of top-level generics. That is, when no arguments are provided, the value defined in C is used. However, when -ggenInt= is provided, it overrides the value of the top-level generic.

Setting parameters in C through VHDL generics#

This example is the opposite of fcnargs. A VHDL generic is passed to C when calling an external subprogram. Although this might feel as a rare use case, it is common when adapting designs that are not aware of VHPIDIRECT features, to enhance them with external snippets/libraries in a testbench.

JSON-for-VHDL#

JSON-for-VHDL is a synthesizable VHDL library that allows to provide configuration parameters through either a JSON file or an stringified (and optionally base16 encoded) top-level generic. Together with jq or the libraries available for almost any language, it is a very powerful resource to pass large amounts of params with minimal maintenance effort.

Examples are available at Paebbels/JSON-for-VHDL:tests.