-
Notifications
You must be signed in to change notification settings - Fork 250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VUnitCoSim #568
Draft
umarcor
wants to merge
9
commits into
VUnit:master
Choose a base branch
from
dbhi:vunitcosim
base: master
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
VUnitCoSim #568
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
umarcor
force-pushed
the
vunitcosim
branch
5 times, most recently
from
November 17, 2019 01:54
09db0a9
to
fb2d746
Compare
umarcor
force-pushed
the
vunitcosim
branch
3 times, most recently
from
November 30, 2019 09:45
a799abb
to
bf7988b
Compare
This was referenced Mar 31, 2020
This was referenced Apr 12, 2020
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Based on #578 and #581.
This PR contains features to co-simulate a VHDL design from Python and inspect internal buffers in real-time with a JavaScript (Vue.js) web frontend. This content is work-in-progress and it might not fit in this repo; so it might end in a separate location.
Python-GHDL co-execution on any GNU/Linux platform is supported, as long as GHDL is built with the option in ghdl/ghdl#805. A C file is the 'middleware' between Python and VHDL. All the functionality is implemented either in the testbench or in the Python script. The main difference compared to regular examples, is that the binary is built with
ghdl.elab_e
. Then, the location is retrieved and it is loaded as a shared library in the python script. This allows to usectypes
to bind Python and C resources to each other, just as we do between VHDL and C with VHPIDIRECT.The example implements two executions:
It is further possible to set callbacks between C and Python, i.e., GHDL triggers a C function through VHPI, which itself triggers a Python method through ctypes. This can be useful to, e.g., allow a VHDL procedure push a value/message to a Python queue. Precisely, I think this is the one of the objectives of @LarsAsplund.
As commented in #469 (comment), these enhancements work only partially with VHDL 1993. It is possible to load some data to memory from Python and execute GHDL; but GHDL exits with
abort
, which prevents any Python post-check from being executed. This might be fixed in the future, since it is not an expected behaviour (see ghdl/ghdl#803). For now, I think it is enough to suggest using this feature with VHDL 2008 only.Some notes about this are added to
examples/vhdl/external_buffer/sigabrt.md
.A Python source is added,
vunit/cosim.py
which implements some utility functions to co-execute a GHDL binary that uses the external models. This includes functions to:main
orghdl_main
).Accordingly, a
cosim.py
file is added to exampleexternal_buffer
. This uses some of the functions above, along with the output ofghdl.elab_e
. It can be tested with:Note that the simulation is executed twice. First,
main
is executed, which relies on the code inmain.c
to allocate, initialize and check the content of the buffers. Second, those tasks are done in Python andghdl_main
is executed directly, by just ignoring thatmain
exists. This is a very interesting feature, because it is possible to use multiple entrypoints to the simulation (either in C or in Python), by using the same binary.The UUT is the
external_buffer
example explained above. It reads block_len bytes from a buffer and writes them back twice. Once in addressesblock_len to 2*^block_len-1
and another one in addresses2*block_len to 3*^block_len-1
. The first time the numbers are incremented by one, and the second time they are incremented by two.In the screencast, first, the design/test is compiled with
python3 run.py -v --build
. This is a normal VUnit execution with optionghdl.elab_e
(#467). The output is an executable binary generated by GHDL and the CLI args that VUnit would use, which are saved inargs.txt
.Then, with
cosim.py
, the binary is loaded in Python dynamically and the args are read from the txt file. First,main
is executed, which allocates data and prints it from C sources. Then, buffers are allocated in Python andghdl_main
is executed directly, effectively ignoring any C application code. You can see in the screencast that the C code print lines of type%i: %i
, while the python code printpy %i: %i
. Note that initial values are different: C starts with11, 22, 33, 44, 55
and Python starts with111, 122, 133, 144, 155
.In this design, one external string buffer is used for the data, and one external integer vector buffer is used to pass parameters such as the block length.
Everything above is already implemented in this PR.
Last, in the screencast a Flask server is started with
serve.py
. This provides a Vue.js frontend that allows to co-simulate interactively. The executable binary and args are loaded as incosim.py
, but the simulation is executed step-by-step, instead of running continuously until the end. It is possible to run the simulation for a given number of clock cycles only, or to have it re-triggered. Each time it is triggered, the data in the frontend is updated. Both modes are shown in the screencast: first 'for' is used a couple of times, and then 'every' is used until the simulation ends.Some notes:
vunit/vhdl/run/src
.examples/vhdl/vue
. For each of the examples/designs, only a subcomponent needs to be added, so it is easier for non-javascript developers to add views for their designs.This other example is based on an AXI Master core written in Vivado HLS and exported to VHDL. VUnit's verification components and the external memory from #470 are used. Furthermore, the
std_logic
signals in the top entity of the core are shown in the Vue frontend.Note that, the buttons/leds representing the
std_logic
signals show the status at the clock cycle when the content is updated. The status between updates is not recorded. However, it is possible to inspect it with GtkWave: since this is a VUnit simulation, after all, it is possible to generate a waveform by providing the corresponding CLI arg.Instead of a single table, the memory is split in two views. This is just an example to show the flexibility: it is possible to allocate a single large/huge buffer to be shared between Python and GHDL, but only show some blocks of it at certain pointed addresses.
This last example shares a large amount of data through an AXI Stream Slave and reads it from an AXI Stream Master, using VUnit's verification components. Precisely, a sintetic hyperspectral cube in BIP (band interleaved by pixel) format of size 256 x 128 x 20 x 16 bit is shared. With 'sintetic' I mean that the example is not a real hyperspectral image; instead I took 20 images, stacked them together and converted the cube to BIP format.
Since it is not possible to share 16 bit types betwen GHDL and a foreign language, actually an external integer vector is used (32 bits per value). Nonetheless, the point is that Python is used to read the shared buffer and encode each spatial image as a base64 encoded PNG. These PNG images are passed to the Vue frontend, which displays them directly. The user can interactively (during simulation) select which of the bands/layers to display.
Four images are shown, from left to right: the input buffer, the expected reference output, the buffer where GHDL is writing the results, and the difference between the reference and the results.
Once again, this is an example of the flexibility of the approach, to show that we can reuse the Vue project and common
cosim.py
functions for quite different designs. It is possible to show tables along with the images, for better inspection of the data.All of the examples above do read data from the simulation, but they also write some data back. Precisely, each time 'Run' is clicked, four integer values are modified in a shared integer vector. This means that it is possible to allow the frontend to modify the content of the tables and write it back to GHDL while the simulation is waiting to advance. This is not implemented, tho.
I am currently focused on providing structured access to internal buffers of the design. I want to use a single parameter to add some 'virtual copy' to internal BRAMs/FIFOs, so it is possible to inspect them during simulation. I find that all the simulators have problems recording large memories in waveforms, because they need to store lots of metadata. This approach would work around it by allowing to inspect them at different times of the simulation, but without recording everything.
For future work, I think it would be very interesting to further develop the Vue frontend, in order to provide multiple views for application specific cases. Since it is possible to match a VHDL peripheral with a Vue model, all kinds of possibilities arise: some VGA/HDMI receiver/monitor, a line follower robot with a circuit, the 3D model of a BLDC motor (with e.g. Three.js)... However, I won't be able to work on this. Hope some other finds it interesting enough.
Regarding VUnit, I think that this Vue frontend is just a fancy example of what might be possible with external strings and integer vectors. But I believe that the interesting part upstream are the features that allow
cosim.py
, notserve.py
. This is becausecosim.py
does not require any additional dependency, butserve.py
needs Flask, and optionally Pillow and numpy.To Do:
run.py
in example 'external_buffer' accepts argument--build
, to select between a regularelab_run
or the 'ghdl.elab_e' mode introduced in add sim_opt 'ghdl.elab_e' to run 'ghdl -e' instead of 'ghdl --elab-run --no-run' #467. When--build
is used, two artifacts are required: the executable binary and the list of args that VUnit would use to execute it (see Get run CLI args from Results #579, Save GHDL args to JSON file #581).