-
-
Notifications
You must be signed in to change notification settings - Fork 336
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
VHDL support in SpinalSim using GHDL as a backend #146
Comments
Actualy, there is this interface in the spinal.sim sources : So in the case of verilator it wasn't a crazy amount of work. Then i have no idea how the ghdl shared object interface appear :) |
the def eval() function is called by spinalHDL to ask the simulatior to do delta cycles until everything is stabilized. |
Hi @Dolu1990! Thanks for the references. I think that implementing something as I was looking for something 'easier' to start with. I tried the following for now:
ghdl_args="--std=08 -fPIC"
ghdl -a $ghdl_args Murax.vhd
ghdl -a $ghdl_args tb_murax.vhd
ghdl -e $ghdl_args tb_murax
./tb_murax --vcd=murax.vcd
gtkwave murax.vcd murax.gtkw However, I don't know how to check it. The next step is to add a UART RX in the testbench, in order to read the output from Murax. But I am not sure about the clk period and the baud rate. I saw https://github.com/SpinalHDL/VexRiscv/blob/master/src/test/cpp/murax/main.cpp#L13-L16, so I think that the expected period might be Files
library ieee;
context ieee.ieee_std_context;
entity tb_murax is
end entity;
architecture tb of tb_murax is
constant clk_period: time := 83333 ps;
signal io_asyncReset : std_logic;
signal io_mainClk : std_logic := '0';
signal io_jtag_tms : std_logic;
signal io_jtag_tdi : std_logic;
signal io_jtag_tdo : std_logic;
signal io_jtag_tck : std_logic;
signal io_gpioA_read : std_logic_vector(31 downto 0);
signal io_gpioA_write : std_logic_vector(31 downto 0);
signal io_gpioA_writeEnable : std_logic_vector(31 downto 0);
signal io_uart_txd : std_logic;
signal io_uart_rxd : std_logic;
begin
io_mainClk <= not io_mainClk after clk_period/2;
main: process begin
io_asyncReset <= '1';
wait for 15*clk_period;
io_asyncReset <= '0';
wait for 100 ms;
report "End of simulation" severity failure;
wait;
end process;
murax: entity work.Murax
port map (
io_asyncReset => io_asyncReset,
io_mainClk => io_mainClk,
io_jtag_tms => io_jtag_tms,
io_jtag_tdi => io_jtag_tdi,
io_jtag_tdo => io_jtag_tdo,
io_jtag_tck => io_jtag_tck,
io_gpioA_read => io_gpioA_read,
io_gpioA_write => io_gpioA_write,
io_gpioA_writeEnable => io_gpioA_writeEnable,
io_uart_txd => io_uart_txd,
io_uart_rxd => io_uart_rxd
);
end architecture;
|
I tried adding UART RX and TX verification components to a VUnit testbench. So far it works ok. I could successfully write and get the same content out (as explained in SpinalHDL/VexRiscv#murax-soc). See testbench below: I could also modify the demo program, rebuild it, and have it included when the SoC VHDL is generated. @Dolu1990 , I am now looking at how to replace the UART with either AXI4 Stream interfaces or FIFOs. However, I did only find AXI4 Full and AXI4 Lite in https://github.com/SpinalHDL/SpinalHDL/tree/master/lib/src/main/scala/spinal/lib/bus. Am I missing any other source? library ieee;
context ieee.ieee_std_context;
library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.vc_context;
entity tb_murax is
generic (
runner_cfg : string);
end entity;
architecture tb of tb_murax is
constant baud_rate : integer := 115200; -- bits / s
constant clk_period: time := 83333 ps;
signal asyncReset : std_logic;
signal mainClk : std_logic := '0';
signal jtag_tms : std_logic;
signal jtag_tdi : std_logic;
signal jtag_tdo : std_logic;
signal jtag_tck : std_logic;
signal gpioA_read : std_logic_vector(31 downto 0);
signal gpioA_write : std_logic_vector(31 downto 0);
signal gpioA_writeEnable : std_logic_vector(31 downto 0);
signal uart_txd : std_logic;
signal uart_rxd : std_logic;
constant tx_uart_bfm : uart_master_t := new_uart_master(initial_baud_rate => baud_rate);
constant tx_stream : stream_master_t := as_stream(tx_uart_bfm);
constant rx_uart_bfm : uart_slave_t := new_uart_slave(initial_baud_rate => baud_rate);
constant rx_stream : stream_slave_t := as_stream(rx_uart_bfm);
begin
mainClk <= not mainClk after (clk_period/2);
main : process
variable data : std_logic_vector(7 downto 0);
begin
test_runner_setup(runner, runner_cfg);
while test_suite loop
if run("test") then
info("Init test");
pop_stream(net, rx_stream, data);
info(to_string(data));
push_stream(net, tx_stream, x"77");
pop_stream(net, rx_stream, data);
info(to_string(data));
push_stream(net, tx_stream, x"55");
pop_stream(net, rx_stream, data);
info(to_string(data));
wait for 100 ms;
end if;
end loop;
test_runner_cleanup(runner);
wait;
end process;
test_runner_watchdog(runner, 100 ms);
tx_bfm : entity vunit_lib.uart_master generic map ( uart => tx_uart_bfm ) port map ( tx => uart_rxd );
rx_bfm : entity vunit_lib.uart_slave generic map ( uart => rx_uart_bfm ) port map ( rx => uart_txd );
---
murax: entity work.Murax
port map (
io_asyncReset => asyncReset,
io_mainClk => mainClk,
io_jtag_tms => jtag_tms,
io_jtag_tdi => jtag_tdi,
io_jtag_tdo => jtag_tdo,
io_jtag_tck => jtag_tck,
io_gpioA_read => gpioA_read,
io_gpioA_write => gpioA_write,
io_gpioA_writeEnable => gpioA_writeEnable,
io_uart_txd => uart_txd,
io_uart_rxd => uart_rxd
);
end architecture; |
@1138-4eb Sorry, i completly losed the track of this issue :/ How things are going ? |
Hi @Dolu1990, thanks for pinging back! In the end, I did nothing with neither SpinalSim nor VPI. But I have successfully used GHDL, VUnit, VexRiscv and VHPI to run some simulations. The scheme is as follows:
First, the RISCV software was built and the hex was generated. Then, the VHDL was generated with SpinalHDL. The VexRiscv VHDL and the testbench were compiled with GHDL. Last, a C app was built, adding the C wrapper and the compiled objects produced by GHDL. When the app is started:
Moreover, all VHDL/VUnit logging features are available during simulation. Hence, execution statistics can be saved to log/csv files. Also, waveforms can be exported in any of the formats supported by GHDL, for further inspection with GUI tools, such as GtkWave. Overall, it was some interesting investigation, as it is a useful setup to wrap GHDL in any other language that supports C-alike interfaces (say ada, golang, python, ruby, scala...). However, I did not guess how to run the simulator only for the delta cycles corresponding to a timestamp, as you asked:
ATM, you call GHDL and the simulation runs until it is finished due to some assertion, error or because all the processes are stalled. I.e., the flow of the simulation is handled by GHDL. Therefore, as you suggest,
yes, VPI would probably be the best fit here. Unfortunately, I have no experience with it. Nevertheless, I might have misunderstood the requirements for EDIT |
Surfing around I found a useful resource: https://essay.utwente.nl/70777/1/Verheij_MA_EEMCS.pdf This is a master thesis on how the co-simulation between Clash and GHDL/ModelSim/Icarus was done. |
@eine @Dolu1990 |
@bellaz89 Hmmm, so far i remember the SpinalSim API threat numbers as raw data, so there is no difference between signed and unsigned numbers. So that's already fine as far i can see ^^ |
@bellaz89, that's some interesting work. Thanks for the reference! If going through the VPI path, you might find cocotb interesting. It uses either VPI or VHPI to interact with a bunch of simulators, GHDL being one of them. For a more direct solution (with VHPIDIRECT/DPI), there is work in progress in VUnit/cosim. See also VUnit/vunit#603, ghdl/ghdl#1053 and ghdl/ghdl#1059. There are several comments about how to pass not only single values but also arrays or records/structs. |
I have some updates. I discovered that my previous approach didn't work because of the inability to link the entire GHDL runtime into a dynamic library. Therefore I rewrote it (https://github.com/bellaz89/VPIInteropREPL). This project uses boost-interprocess to communicate between the simulator and the client program. |
In #293 I did the following things:
I am able to run simulations nicely in scala with sbt "sim/test:runMain spinal.sim.PlayGhdl" You need to have Icarus Verilog or GHDL in your path and boost/interprocess headers in your include path. See https://github.com/bellaz89/SpinalHDL/blob/ee3bb11415b337bfb4614cffe0957d7ea070e9a8/sim/src/test/scala/spinal/sim/Test2.scala#L62 and https://github.com/bellaz89/SpinalHDL/blob/ee3bb11415b337bfb4614cffe0957d7ea070e9a8/sim/src/test/scala/spinal/sim/Test2.scala#L95 Either sim/yolo/adder.vhd or sim/yolo/adder.v are used for the test. Remaining tasks/ideas
Update Update Now Icarus Verilog backend works as it should |
Updates #296 |
Please, see https://ghdl.github.io/ghdl-cosim/vhpidirect/dynamic.html#generating-shared-libraries. That subsection was added/published less than 24h ago. It should allow you to pass pointers/accesses between VHDL and C, without having to resort to interprocesses. Nonetheless, it might still be useful to rely on them for different reasons.
The reference about shared libs above is still not tested on Windows or macOS. However, since GitHub Actions supports both of them, I'd like to add it to CI.
I have simulated VexRISCV with GHDL, including co-simulation with C. It works.
If possible, I think it would be so interesting to be able to co-simulate VHDL and Verilog sources (optionally with additional SpinalHDL). This is a very demanded feature, as mixed-language co-simulation with FOSS is not available yet: https://ghdl.readthedocs.io/en/latest/development/GSOC.html#mixed-language-vhdl-verilog. Precisely, there is a comment about GHDL and Verilator in https://ghdl.github.io/ghdl-cosim/#co-simulation-with-ghdl, because Tristan is thingking about adding a new backend to GHDL which would interact with Verilator (just as it interacts with yosys for synthesis). However, I don't know which would be the approach: to target an API or to generate C++/SystemC sources directly (which the user can then mix with Verilog->Verilator sources). An example (and presentation) about co-simulating VHDL and SystemC through GHDL is available https://github.com/ghdl/ghdl-cosim/tree/master/systemc. It has been recently discussed in the chat: March 26, 2020 10:54 AM. |
This is a great news! However as you pointed out it is not the only reason why I switched to a multiple-processes approach: (Disclaimer! I am not expert in VPI/GHDL/HDL simulations in general so my assumptions could be plain wrong :) )
Also, I decided not to use 'cutting edge' features of GHDL right now to allow a SpinalHDL user to easily setup its development environment using system packages without the need to recompile/install GHDL by hand. Once the API of #1053 is fixed, documented, with all the features the VPI has, then it is possible to evaluate a switch to that.
I think that the C++ side of the VPI interface code can be already run on different OSes since the only library dependency is boost-interprocess, (which is an header only lib). The part that worries me most is the Scala part where paths/commands/options are glued together to bootstrap the simulation.
That is very nice to hear since I will need to simulate VexRiscv for work in VHDL :) . @Dolu1990 has already written some code to make SpinalSim tests to work with the GHDL backend. The (his) idea is to try to run every SpinalHDL test, which actually runs on Verilator, on the GHDL backend too for testing purposes.
Mixed language cosim would be a very nice feature to have. Sadly I saw that the IVerilog simulator deprecated the VHDL simulation feature (steveicarus/iverilog#222) so right now I don't see a way to obtain this feature in the short term. From SpinalHDL perspective a way to perform mixed-lang would be to use the Verilator backend plus vhd2vl, but I never tested it so I am not able to say if it fits the common user needs. |
PS: the above message is just my personal opinion that might not reflect the view of SpinalHDL's mantainers :) . |
None of us is! We are here to talk about it and hopefully learn something!
I have not used VPI explicitly yet, so I'm not familiar with the details. However, I think that this limitation is not related to VPI, but to GHDL itself. No matter which cosimulation strategy you use,
I'm working around this in VUnit/vunit#568 with a 64bit counter that is shared between VHDL and C, and doing clock gating in the testbench to achieve a "stop the world" type of synch. Not elegant, but it works...
Actually, I'd suggest to have a look at cocotb for reference. They use VPI/VHPI to target multiple free/commercial simulators. E.g. ghdl/ghdl#1228. That's why I think that the work you are doing might be equivalent, as long as cocotb's and SpinalHDL's execution models are similar.
Precisely, this is the use case where I think that using VPI/VHPI pays off. For only sharing data in co-simulation contexts, it is not worth the hazzle. However, it can be really useful to peek deep in the hierarchy, such as swapping bits in space applications.
Not exactly. I'm mostly focused on VHPIDIRECT and on the C interface for the runtime library (GRT) that is embedded in the executables generated by GHDL. VHPIDIRECT is similar to a Direct Programming Interface (DPI), since it allows to map procedures/functions only, from C to VHDL. For many use cases, this is equivalent to how VPI callbacks are defined and used. However, the main difference is that with VHPIDIRECT you need to call those functions from VHDL explicitly. So, from the designer's point of view, VPI/VHPI are a C-only approach that does not require to modify the VHDL, while VHPIDIRECT is for building the UUT and the testbench mostly in VHDL only. The main advantage of VHPIDIRECT is that, for non-complex data types, you don't need any 1K header. You just write what you need. See https://github.com/ghdl/ghdl-cosim/tree/master/vhpidirect/quickstart/customc. Unfortunately, while sharing some data types between VHDL and C is straightforward, others (mainly unconstrained arrays) are fat pointers that are not documented. See https://ghdl.github.io/ghdl-cosim/vhpidirect/declarations.html#restrictions-on-type-declarations. Hence, the focus in ghdl/ghdl#1053 (now in ghdl/ghdl-cosim#3), is to document the fat pointers and to provide a helper header file that makes it easier to use them from C. Incidentaly, because of April 16, 2020 7:14 AM, I think that currently this restriction applies to VPI/VHPI too. Hence, documenting it for VHPIDIRECT might be useful to extend support in VPI. Let me elaborate: Currently, I think that you can set callback with VPI, and you will get access to variables/signals deep in the hierarchy. However, you are limited with regard to which variables/signals you can access/set/get. Precisely, I believe that the types that you can easily modify from VPI are the same that can be directly used in VHPIDIRECT. The point is that VPI provides you a poor-man's solution: iterating through arrays in the foreign language and setting/getting/modifying each item one by one. There is no API in VHPIDIRECT to do so. From my point of view, it doesn't make sense to write a Managing the C interface is a separate, but related issue. As a matter of fact, VHPIDIRECT does work with any of the backends (mcode, LLVM or GCC), but mcode does not support calling the simulation engine from C. See ghdl/ghdl#1215 (comment). As said in ghdl/ghdl-cosim#1, I believe that regardless of the approach for VHDL to execute C (VHPIDIRECT/VPI/VHPI), the API to handle GRT from C is common. This is what ghdl/ghdl#1053 (comment) is about.
Most of it is in early stage of documentation, but it was developed many years ago. Very few modifications have been done in the last years. For example, the If you mean the step-by-step execution of GRT, yes that's in early stage of development.
I believe there is no requirement to use cutting edge features. You can pick a mostly GCC based build approach. The requirement to recompile/install GHDL does not apply to many platforms, because fPIC is used by default. All available docker images do have it enabled by default. In fact, I don't know whether there is any real disadvantage in making this the default (as it was done with synthesis).
I would not suggest to change the VPI API, not now, not in the future. The work is already done and, as you said, it can be useful for other simulators too. As a matter of fact, a DPI interface similar to VHPIDIRECT might be added to future versions of the VHDL standard. However, that's out of scope for now. What I meant was to focus on:
Note that GHDL is tested on MSYS2/MINGW on Windows. Hence, this should be handled automatically.
I have not touched it in a while, but you can have a look at https://github.com/dbhi/spinalhdl-riscv. See also eine/VexRiscv@cb1b3fd.
I didn't mean mixed-language per se, but to simulate VHDL with GHDL, Verilog with Verilator and use SpinalHDL as a scheduler/manager in Scala to handle communication between both through VPI. I believe that "proper" mixed-language is out-of-scope of SpinalHDL.
Sure. The same applies to mine and GHDL. |
@bellaz89, see ghdl/ghdl#237 and ghdl/ghdl#1249. |
The requested feature has been added: https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Simulation/bootstraps.html#configuration So this issue can be closed |
While reading https://spinalhdl.github.io/SpinalDoc/spinal/sim/introduction/, I wondered whether any effort has been done in order to replicate the workflow with VHDL sources. ghdl/ghdl can be used to compile VHDL files to C (shared) objects, just as Verilator is used with Verilog sources.
I am actually seeking to generate and compile a VHDL version of Murax SoC, to then simulate the execution of some of the example programs.
The text was updated successfully, but these errors were encountered: