Home / Resources / Hardware/software co-simulation between VHDL and C with ghdl and gcc
Introduction
GHDL is an open-source simulator for the VHDL language. GHDL allows you to compile and execute your VHDL code directly in your PC. GHDL fully supports the 1987, 1993, 2002 versions of the IEEE 1076 VHDL standard, and partially the latest 2008 revision. By using a code generator (llvm, GCC or a builtin one), GHDL is much faster than any interpreted simulator.
The GNU Compiler Collection (gcc) is an optimizing compiler produced by the GNU Project supporting various programming languages, hardware architectures and operating systems.
The VHDL Procedural Interface (VHPI) is an application-programming interface to VHDL tools that allows programmatic access to a VHDL model during its analysis, elaboration, and execution.
C code
Using the VHPI, we can write a user-defined VHDL functions and procedures in C language. We can also manipulate variables that are defined in C. To do so, we must write access functions to write the variable and access its pointer.
Here is a C variable with two access functions:
int foreign_myGlobalVar = 42;
// Write access function for a global variable:
void foreign_setMyGlobalVarFromC(int theValue) {
foreign_myGlobalVar = theValue;
}
// Read access function for a global variable:
int * foreign_getMyGlobalVarPtr(void) {
return &foreign_myGlobalVar;
}
VHPI wrapper
For the variable to be accessed from VHDL, we must define a shared variable and
define the read access function.
All C functions must be declared and we use VHPIDIRECT
keyword and their name
to declare a foreign attribute for the function.
The body of the functions must also be defined in VHDL, for compilation to
succeed, even if it remains unused.
Here is an example of a VHPI wrapper for our C functions:
package wrapper is
type int_ptr is access integer; -- defines type int *
-- VHDL prototype for C functions:
function foreign_getMyGlobalVarPtr return int_ptr;
attribute foreign of foreign_getMyGlobalVarPtr : function is
"VHPIDIRECT foreign_getMyGlobalVarPtr";
--
procedure foreign_setMyGlobalVarFromC(theValue : integer);
attribute foreign of foreign_setMyGlobalVarFromC : procedure is
"VHPIDIRECT foreign_setMyGlobalVarFromC";
-- sharing the C global variable:
shared variable foreign_myGlobalVar : int_ptr := foreign_getMyGlobalVarPtr;
end wrapper;
package body wrapper is
-- the functions must be declared for compilation to work:
function foreign_getMyGlobalVarPtr return int_ptr is
begin
assert false report "VHPI" severity failure; -- does nothing
end foreign_getMyGlobalVarPtr;
procedure foreign_setMyGlobalVarFromC(theValue : integer) is
begin
assert false report "VHPI" severity failure;
end foreign_setMyGlobalVarFromC;
end wrapper;
VHDL testbench
The shared variable can be accessed from a VHDL testbench as well as previously defined functions/procedures.
Here is an example where we change the value of our shared variable from VHDL or by using a procedure which body is defined in C.
use work.wrapper.all;
entity testbench is
end testbench;
architecture bhv of testbench is
begin
process
begin
report "foreign_myGlobalVar = " & integer'image(foreign_myGlobalVar.all);
foreign_myGlobalVar.all := 16;
report "foreign_myGlobalVar = " & integer'image(foreign_myGlobalVar.all);
foreign_setMyGlobalVarFromC(69);
report "foreign_myGlobalVar = " & integer'image(foreign_myGlobalVar.all);
wait;
end process;
end bhv;
Makefile
Now, we must call gcc to compile the C code and ghdl to build our testbench. Then we execute the resulting binary and observe the results.
Here is a Makefile that provides the recipe (make sure to indent with tabulations):
all: testbench
./$^
testbench: wrapper.vhdl testbench.vhd myForeignCode.o
ghdl-gcc -a wrapper.vhdl testbench.vhd
ghdl-gcc -e -Wl,myForeignCode.o testbench
myForeignCode.o: myForeignCode.c
gcc -c -Wall $^ -o $@
clean:
ghdl-gcc --remove
$(RM) myForeignCode.o
Associated resources
Here is an archive which contains the discussed example and its Makefile.