vunit
vunit copied to clipboard
[PoC] Interfacing GHDL to other languages: declaration of foreign C functions
This PR is an example of how users can use GHDL + VUnit + VHPIDIRECT without built-in types. For practical cases, using string_ptr
, byte_vector_ptr
and/or integer_vector_ptr
instead is recommended. See #507, #476.
Using 1138-4EB/hwd-ide/tree/examples-vhpi/examples/VHPI as a reference, this PR extends example array_axis_vcs
to use data defined in a foreign C function. Moreover, the entrypoint to the binary is a foreign main
function, instead of the (default) ghdl_main
.
-
tb_axis_loop.vhd
is renamed totb_py_axis_loop.vhd
.- The instantiation of the UUT and the VCs is moved to a separate file:
tb_vc_axis_loop.vhd
- The instantiation of the UUT and the VCs is moved to a separate file:
-
tb_c_axis_loop.vhd
is added. This is equivalent totb_py_axis_loop.vhd
but, instead of reading/writing data from/to CSV files, data is generated in some C function and the output is evaluated in another C function. - In file
pkg_c.vhd
, the equivalencies between VHDL functions/types and the foreign resources are declared. -
run.py
is modified in order to first build the C sources to an object and then pass the object to GHDL for elaboration.
These examples are functional ATM. However, there are some issues that can be improved:
- [x] The additional elaboration flags are being passed to both testbenches. This means that the
py
testbench is also being executed through the foreignmain
function. This is not required, and it should be avoided. - [x]
I find it ugly to execute GCC explicitly in the. How to provide different sets of C objects to several testbenches: some of the C sources are common, and some are specific for each testbench. This is implemented in #470.run.py
. I'd rather add the C sources as any other source (lib.add_source_files(join(root, "src/**/*.c"))
and let VUnit handle it. However, I don't know if this can work with other simulators that support VHPI, or is something specific to GHDL. @kraigher, is this something you would like to support? Note that this same approach can be used to, e.g., let the simulation draw images in a window during simulation (see xvga) - [x] If I execute
python3 run.py -v --elaborate
, the application (main
) is executed, but GHDL is skipped, so the check fails. I would expect the binary not to be executed at all. [VUnit/vunit#467] - [x] When the simulation is successful, but the C check fails, VUnit reports pass.
main.c
and/ortb_c_axis_loop.vhd
should be updated in order to fix this. See #469.
I'm now building an example that uses AXI Master, instead of AXI Stream, in order to try the alternative memory model as commented at #462. Is there any AXI Master IP in the VUnit codebase which I can use as a UUT? See https://github.com/VUnit/vunit/issues/462#issuecomment-480578535
Apart from the copyright issues, I think that the acceptance tests are failing because the GHDL version being used at appveyor is v0.35. I could successfully execute them with GHDL 0.36-dev (20181129-168-g50da90f5)
.
It is possible to set a sim_option for just one test case or test bench so you do not have to set it for all test benches.
Regarding adding support for invoking GCC from VUnit I do not think it makes sense. For building C-code there already lots of tools out there and it does not seem worth the effort to add dependency scanning, build rules, compiler flag settings etc for GCC to VUnit. You could just call an external makefile from the run.py file to build any C-code.
Regarding '--elaborate' the GHDL binary must be called for elaboration to be performed. I guess in this example since you are wrapping the GHDL main function with data generation and checking you need to check the ARGV and skip data generation and checking unless the run flag was given.
It is possible to set a sim_option for just one test case or test bench so you do not have to set it for all test benches.
Thanks. I'll look for the correct syntax to do so.
Regarding adding support for invoking GCC from VUnit I do not think it makes sense. For building C-code there already lots of tools out there and it does not seem worth the effort to add dependency scanning, build rules, compiler flag settings etc for GCC to VUnit. You could just call an external makefile from the run.py file to build any C-code.
I agree that it is possible, and desirale indeed, to rely on external makefiles, cmakes, etc. This is specially so because foreign declarations can be written not only in C, but in multiple other languages.
Hence, I did not explain myself properly. I meant adding -Wl,path/to/a/c/object
to sim_options. If I built all the sources externally and saved the objects to say vunit_out/ghdl/libraries/c
, it would be possible to add the directory (or all the objects in it) automatically, without having to manually add each of them as sim options. In the current example there is a single object file, but in a practical design there might be multiple. However, I don't know if vunit_out/ghdl/libraries/c/*.o
is the best location. I am using join(root, 'src/test/main.o')
for now.
Regarding '--elaborate' the GHDL binary must be called for elaboration to be performed.
I opened a new issue to discuss this. Please see #466.
Wouldnt the user want to specify unique .o files for each testbench/test. Then all of them cannot be automatically added.
Now, the elaboration flag -Wl,
is only added to the testbench that uses the external memory/buffer. This produces an error during elaboration. I opened an issue about it: ghdl/ghdl#793. As commented there, the alternatives now are to provide two versions of some VHDL sources or to provide a stubs.c
file. I'm going with this second option until I get some reply from Tristan.
Wouldnt the user want to specify unique .o files for each testbench/test. Then all of them cannot be automatically added.
In the current implementation of the external memory model, I provide different sets of .o
files to the testbenches, depending on what type(s) of model(s) are used. I'll try to adapt the AXI DMA example ASAP.
@kraigher, I think this is ready for review now. As you see, I replaced the protected shared variables with a memory_t
, which is similar to the one used in VUnit. Nevertheless, the main objective of this PR is to provide an example of how to use VUnit and GHDL to interact with foreign languages: https://www.diffchecker.com/ck5A79oZ
There are three implementation details wich I am not completely happy with, but I couldn't guess how to do it better:
-
The length of
buffer_t
needs to be defined. I don't know which are the effects of this size. Does the simulator allocate the array and ignore it when the pointer is changed? Or is this just a type, and no memory is allocated at all because we only use anaccess
to it? I tried to set it tointeger'high
(as in https://github.com/VUnit/vunit/blob/master/vunit/vhdl/data_types/src/integer_vector_ptr_pkg.vhd#L18), but it will crash. -
Each time a data element is to be set/get,get_addr(memory.p_id)
is executed. This is because it seems not to be possible to have a field inmemory_t
of typebuffer_p
(access tobuffer_t
). -
The need for
stubs.c
: ghdl/ghdl#793.
In the "traditional testbench", stubs.c
is used just to show how to wrap a GHDL + VUnit simulation in a C application. In the second testbench, a buffer is allocated in the C application and VHPIDIRECT callbacks are used to share data between the testbench and the application.