ModelSim FLI (Foreign Language Interface) Tutorial
Introduction
ModelSim actually has a pretty simple way to extend your VHDL code with C. It’s called the Foreign Language Interface (FLI). It works by defining a dynamic library and it’s function symbols through VHDL. You don’t even have to use any libraries to make it work.
All information you need can be found in this PDF: https://web.archive.org/web/20130107142559/https://users.ece.cmu.edu/~kbiswas/modelsim/se_fli.pdf
However it covers a lot more than what’s useful or needed, which makes it almost confusing.
Below is a simple FLI program that opens a file and reads/writes bytes at arbitrary positions.
VHDL side
The easiest way to interface with the FLI is through procedures. It looks something like this:
package C_interface is
procedure C_interface_init;
attribute foreign of C_interface_init : procedure is "init ../../C/my_FLI_linux.so";
procedure C_interface_deinit;
attribute foreign of C_interface_deinit : procedure is "deinit ../../C/my_FLI_linux.so";
procedure C_interface_write(byte_pos : in integer; byte_value: in integer);
attribute foreign of C_interface_write : procedure is "write_to_file ../../C/my_FLI_linux.so";
procedure C_interface_read(byte_pos : in integer; byte_value: out integer);
attribute foreign of C_interface_read : procedure is "read_from_file ../../C/my_FLI_linux.so";
end;
package body C_interface is
procedure C_interface_init is begin end;
procedure C_interface_deinit is begin end;
procedure C_interface_write(byte_pos : in integer; byte_value: in integer) is begin end;
procedure C_interface_read(byte_pos : in integer; byte_value: out integer) is begin end;
end;
All we have to do is leave the bodies of the procedures empty. In the definition we add “attribute foreign,” where we specify symbol name and file path. That’s it, once bound with a dynamic library you can call this procerude like any other VHDL procedure.
C side
The C side doesn’t have to do much except making sure symbols are created with the correct name and prototype as defined in the VHDL code. I suggest primarily using integers, as VHDL integers are compatible with C’s. out
variables are simply pointers.
#include <stdio.h>
#include <assert.h>
static FILE* my_file;
#define MY_FILE_SIZE (10 << 10)
void init() {
my_file = fopen("./test.bin", "wb+"); // open with write, this makes a new empty file
assert(my_file);
// resize file
fseek(my_file, MY_FILE_SIZE-1, SEEK_SET);
fputc(0, my_file);
}
void deinit() {
fclose(my_file);
}
void write_to_file(int byte_pos, int byte_value) {
assert(my_file);
fseek(my_file, byte_pos, SEEK_SET);
fwrite(&byte_value, 1, 1, my_file);
}
void read_from_file(int byte_pos, int* byte_value) {
assert(my_file);
fseek(my_file, byte_pos, SEEK_SET);
fread(byte_value, 1, 1, my_file);
}
Compiling
We neede to compile to a dynamic library. Since ModelSim is 32-bit, we need to make sure we compile to 32-bit as well.
Using GCC and MINGW it can be done like so:
#!/usr/bin/env sh
set -e
gcc -fPIC -Wall -m32 -g3 my_FLI.c -shared -o my_FLI_linux.so
file my_FLI_linux.so
#!/usr/bin/env sh
set -e
i686-w64-mingw32-gcc -fPIC -Wall -m32 -g3 my_FLI.c -shared -o my_FLI_windows.dll
file my_FLI_windows.dll
Conclusion
Now you can run this in ModelSim and get the full utility of a C program. Note that the path provided in “attribute foreign” must be absolute or relative to the working directory inside ModelSim.
*Disclaimer: None of the code above is actually tested or compiled, I’m just writing from memory/reference. There might be typos or small errors.