Skip to content
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

Closed
eine opened this issue Sep 11, 2018 · 19 comments
Closed

VHDL support in SpinalSim using GHDL as a backend #146

eine opened this issue Sep 11, 2018 · 19 comments
Labels
feature ✨ Feature idea with clear API defined

Comments

@eine
Copy link
Contributor

eine commented Sep 11, 2018

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.

@Dolu1990
Copy link
Member

Actualy, there is this interface in the spinal.sim sources :
https://github.com/SpinalHDL/SpinalHDL/blob/master/sim/src/main/scala/spinal/sim/SimRaw.scala#L15
which as to be implemented for a given target simulator
For Verilator it's implemented as following :
https://github.com/SpinalHDL/SpinalHDL/blob/master/sim/src/main/scala/spinal/sim/SimVerilator.scala
+
https://github.com/SpinalHDL/SpinalHDL/blob/master/sim/src/main/scala/spinal/sim/VerilatorBackend.scala

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 :)

@Dolu1990
Copy link
Member

the def eval() function is called by spinalHDL to ask the simulatior to do delta cycles until everything is stabilized.
maybe that's a tricky thing ?

@eine
Copy link
Contributor Author

eine commented Sep 12, 2018

Hi @Dolu1990! Thanks for the references.

I think that implementing something as eval() with GHDL might be possible with either the VPI or the VHPI interfaces. I have gathered some examples in 1138-4EB/hwd-ide/tree/vpi-ex-ghdl/examples, but I have not thoroughly learnt it yet.

I was looking for something 'easier' to start with. I tried the following for now:

  • Replace generateVerilog and SpinalVerilog with generateVhdl and SpinalVhdl in VexRiscv/src/main/scala/vexriscv/demo/*.scala (see eine/VexRiscv@f9c7643).
  • I created a VHDL testbench, using entity Murax in Murax.vhd as the UUT (see below).
  • I can successfully analyze, elaborate and run both Murax.vhd and tb_murax.vhd:
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 83.333 ns (12 MHz) and the baud rate 115200. Is this correct? Also, should I provide any default value to the unused input ports (io_jtag_tms, io_jtag_tdi, io_jtag_tck, io_gpioA_read and io_uart_rxd)?


Files

tb_murax.vhd:

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;

murax.gtkw:

[*]
[*] GTKWave Analyzer v3.3.95 (w)1999-2018 BSI
[*] Tue Sep 11 23:31:57 2018
[*]
[dumpfile] "/src/murax.vcd"
[dumpfile_mtime] "Tue Sep 11 23:06:04 2018"
[dumpfile_size] 195951776
[savefile] "/src/murax.gtkw"
[timestart] 0
[size] 1366 711
[pos] -1 -1
*-34.449421 25250000000 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
[treeopen] tb_murax.
[treeopen] tb_murax.murax.
[treeopen] tb_murax.murax.system_cpu.
[treeopen] tb_murax.murax.system_uartctrl.uartctrl_1.
[sst_width] 210
[signals_width] 250
[sst_expanded] 1
[sst_vpaned_height] 279
@200
-tb_murax
@28
tb_murax.io_asyncreset
@22
tb_murax.io_gpioa_read[31:0]
@23
tb_murax.io_gpioa_write[31:0]
@22
tb_murax.io_gpioa_writeenable[31:0]
@28
tb_murax.io_jtag_tck
tb_murax.io_jtag_tdi
tb_murax.io_jtag_tdo
tb_murax.io_jtag_tms
tb_murax.io_mainclk
tb_murax.io_uart_rxd
tb_murax.io_uart_txd
@200
-system_uartctrl
@28
tb_murax.murax.system_uartctrl.io_uart_rxd
tb_murax.murax.system_uartctrl.io_uart_txd
tb_murax.murax.system_uartctrl.io_interrupt
tb_murax.murax.system_cpu.csrplugin_mstatus_mie
@200
-streamfifo_2
@22
tb_murax.murax.system_uartctrl.streamfifo_2.io_push_payload[7:0]
@28
tb_murax.murax.system_uartctrl.streamfifo_2.io_push_valid
tb_murax.murax.system_uartctrl.streamfifo_2.io_pop_valid
tb_murax.murax.system_uartctrl.streamfifo_2.io_pop_ready
@22
tb_murax.murax.system_uartctrl.streamfifo_2.io_pop_payload[7:0]
@200
-uartctrl_1
@22
tb_murax.murax.system_uartctrl.uartctrl_1.rx.io_read_payload[7:0]
@28
tb_murax.murax.system_uartctrl.uartctrl_1.rx.io_read_valid
@22
tb_murax.murax.system_uartctrl.uartctrl_1.rx.statemachine_shifter[7:0]
@28
tb_murax.murax.system_uartctrl.uartctrl_1.rx.bittimer_tick
tb_murax.murax.system_uartctrl.uartctrl_1.rx.bittimer_counter[2:0]
tb_murax.murax.system_uartctrl.uartctrl_1.rx.io_rxd
tb_murax.murax.system_uartctrl.uartctrl_1.rx.sampler_value
tb_murax.murax.system_uartctrl.uartctrl_1.rx.sampler_tick
tb_murax.murax.system_uartctrl.uartctrl_1.rx.sampler_samples_1
tb_murax.murax.system_uartctrl.uartctrl_1.rx.sampler_samples_2
[pattern_trace] 1
[pattern_trace] 0

@eine
Copy link
Contributor Author

eine commented Sep 13, 2018

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;

@Dolu1990
Copy link
Member

@1138-4eb Sorry, i completly losed the track of this issue :/
Probably to interface other simulator, the good solution would be to do like cocotb, with VPI.

How things are going ?

@eine
Copy link
Contributor Author

eine commented Jan 15, 2019

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:

  • A module name Apb3Axis was added to Murax. These are two AXI Streams (a master and a slave) mapped to some registers in the VexRiscv memory space. See https://github.com/1138-4EB/VexRiscv/tree/add-axis.
  • All the generateVerilog and SpinalVerilog statements were replaced with generateVhdl and SpinalVhdl. See https://github.com/1138-4EB/VexRiscv/tree/murax-vhdl-axis.
  • A software routine was written in order to read data from the slave stream, do some arithmetic operations, and put the results back through the master stream.
  • A VHDL testbench was written including: VexRiscv, VUnit UART verification components (RX and TX) and VUnit AXI Stream verification components (slave and master). Also, a companion C wrapper was written and some VHDL procedures were bind to C functions.

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:

  • Some memory is allocated and test data is randomly generated.
  • GHDL is called internally as if it was any other function, and the simulation is started.
  • During simulation, callbacks in C are executed to get data from the app memory space and put it in the slave AXI Stream (input to the RISCV). Consequently, callbacks are executed to get data back from the master AXI Stream (output from the RISCV) to the app memory space.
  • When the simulation is finished, control is returned to the main app.

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:

the def eval() function is called by spinalHDL to ask the simulatior to do delta cycles until everything is stabilized.
maybe that's a tricky thing ?

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,

Probably to interface other simulator, the good solution would be to do like cocotb, with VPI.

yes, VPI would probably be the best fit here. Unfortunately, I have no experience with it.

Nevertheless, I might have misunderstood the requirements for eval(). So, please, let me know if any of the info in this comment lights any bulb.

EDIT

ghdl/ghdl#154

@Dolu1990 Dolu1990 added the feature ✨ Feature idea with clear API defined label Feb 5, 2020
@bellaz89
Copy link
Contributor

bellaz89 commented Mar 1, 2020

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.
C snippets are present in this thesis.

@bellaz89
Copy link
Contributor

@eine
I created a frontend C++ library for GHDL. See https://github.com/bellaz89/GHDLInteropREPL. It should be enough for SpinalSim.

@Dolu1990
I realized that my library completely misses dealing with signed values. Should all three kinds (Int, Long, BigInt) be sign-extended when they are written on signals wider than their intrinsic bit site and vice-versa?

@Dolu1990
Copy link
Member

@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 ^^

@eine
Copy link
Contributor Author

eine commented Mar 25, 2020

@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.

@bellaz89
Copy link
Contributor

bellaz89 commented Apr 7, 2020

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.
Such an interface is a much cleaner approach than the older one and it allows to simulate with ghdl-mcode and icarus verilog. With minimal effort It could be possible to add the support of other (commercial) simulators that use VPI. Since the interfacing code doesn't change from simulation to simulation we can think about caching the modules and libraries for later use to speed up the simulations.

@bellaz89
Copy link
Contributor

bellaz89 commented Apr 10, 2020

@Dolu1990

In #293 I did the following things:

  1. Added a generic VPI backend with shared memory communication. This is done using boost-interprocess
  2. Added support to GHDL and its three flavors (mcode/gcc/llvm)
  3. Added support to Icarus Verilog
  4. Added support to GHDL wave files
  5. Written SimVPI and modified Signal adding an initialization flag and extending the id to Long

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.
The backend code creates the JNI shared library and the VPI plugins in sim/simulation_plugins/.
Since both JNI and VPI objects don't change from simulation to simulation, they are re-used by the following simulations significantly speeding up the loading time.

Remaining tasks/ideas

  • Adding an user interface to manage the new simulators and their parameters.
  • Test/adapt the code on windows/macos machines
  • Stress test the backends (Vexriscv simulations? SpinalHDL test suite?)
  • Adding a SpinalSim flag to automatically pick the 'best' simulator using the installed simulators/rtl files extensions as information
  • Add support of IVerilator wave files. May this involve modification in how the top entity code is generated? See https://iverilog.fandom.com/wiki/GTKWAVE and Specify dumpfile via command line? steveicarus/iverilog#202
  • Might it be useful to run GHDL in parallel along with Verilator/ IVerilog to cross-check SpinalHDL's vhdl and verilog generators?
  • Documenting everything

Update
Due to incompatibilities between SpinalSim and IVerilog execution model, the IVerilog backend is not working properly anymore. See #295 .

Update

Now Icarus Verilog backend works as it should

@bellaz89
Copy link
Contributor

bellaz89 commented Apr 13, 2020

Updates #296

@umarcor
Copy link

umarcor commented Apr 16, 2020

I discovered that my previous approach didn't work because of the inability to link the entire GHDL runtime into a dynamic library.

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.

Test/adapt the code on windows/macos machines

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.

Stress test the backends (Vexriscv simulations? SpinalHDL test suite?)

I have simulated VexRISCV with GHDL, including co-simulation with C. It works.

Might it be useful to run GHDL in parallel along with Verilator/ IVerilog to cross-check SpinalHDL's vhdl and verilog generators?

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.

@bellaz89
Copy link
Contributor

bellaz89 commented Apr 16, 2020

I discovered that my previous approach didn't work because of the inability to link the entire GHDL runtime into a dynamic library.

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.

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 :) )

  • The VPI API/workflow is not designed to work as a library that can be used from other programs/libraries and it doesn't return the control until the simulation is finished. Therefore to be able to perform a simulation in a single process, I had to use nasty tricks like making the VPI code to jump at an address defined at compile time and use a coroutine-like approach (boost-fiber) to do the context-switch between GHDL and the JVM. Even if this works in principle, the result is not so elegant IMHO. Using an interprocess approach results in a more natural way of exchanging data between the JVM and GHDL.
  • VPI is a (enough) documented API that works on different simulators. Therefore there might be the chance to support other free/commercial simulators that use such API with minimal effort. Plus it has some features like iterating/accessing the simulation object hierarchy that is quite useful when eg. initial value randomization is needed or to access an arbitrary signal just by its name at runtime. I see you are doing some work in that way #1053
    , but, correct me if I am wrong, it is still in an early stage of development.

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.
Finally, the interprocess synchronization is done trough atomics. Therefore, even if the synchronization cost is higher than linking the runtime directly in the JVM, I think IMHO that the overhead is still quite low compared to the machine time required for nontrivial HDL simulations.

Test/adapt the code on windows/macos machines

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 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.

Stress test the backends (Vexriscv simulations? SpinalHDL test suite?)

I have simulated VexRISCV with GHDL, including co-simulation with C. It works.

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.

Might it be useful to run GHDL in parallel along with Verilator/ IVerilog to cross-check SpinalHDL's vhdl and verilog generators?

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 thinking 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.

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.

@bellaz89
Copy link
Contributor

PS: the above message is just my personal opinion that might not reflect the view of SpinalHDL's mantainers :) .

@umarcor
Copy link

umarcor commented Apr 16, 2020

(Disclaimer! I am not expert in VPI/GHDL/HDL simulations in general so my assumptions could be plain wrong :) )

None of us is! We are here to talk about it and hopefully learn something!

  • The VPI API/workflow is not designed to work as a library that can be used from other programs/libraries and it doesn't return the control until the simulation is finished.

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, ghdl_main will run from start to finish and all you can define are callbacks. When step execution is documented for GRT, it will be possible to use it from VPI/VHPI/VHPIDIRECT... See ghdl/ghdl#1053 (comment).

Therefore to be able to perform a simulation in a single process, I had to use nasty tricks like making the VPI code to jump at an address defined at compile time and use a coroutine-like approach

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...

  • VPI is a (enough) documented API that works on different simulators. Therefore there might be the chance to support other free/commercial simulators that use such API with minimal effort.

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.

Plus it has some features like iterating/accessing the simulation object hierarchy that is quite useful when eg. initial value randomization is needed or to access an arbitrary signal just by its name at runtime.

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.

I see you are doing some work in that way ghdl/ghdl#1053

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 vhpidirect_user.h header that allows to do so from C, because that would be duplicating what VPI/VHPI offer. Instead, I want to provide a C header that allows to access type my_t is array (natural range <>, natural range <>) of real; as double my_matrix [][] in C. ATM, it is easy to do it with constrained arrays type my_t is array (0 to n, 0 to m) of real; as double my_matrix [n][m] (see https://github.com/umarcor/ghdl-cosim/pull/10/files), but not with unconstrained.

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.

but, correct me if I am wrong, it is still in an early stage of development

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 ghdl -e -shared flag was added a few days ago. But doing gcc -shared -Wl,$(ghdl --list-link tb) -Wl,--version-script=./file.ver -Wl,-u,ghdl_main has been supported for years, albeit not documented. By the same token, VHPIDIRECT examples are being rewritten and published now, but the same interface has been available for many years (10, at leat, http://ygdes.com/GHDL/).

If you mean the step-by-step execution of GRT, yes that's in early stage of development.

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.

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).

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 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:

  • Being able to generate shared libs with either GCC or ghdl -e.
  • The management of the runtime.
  • The possibility to use VHPIDIRECT to share resources between the wrapper C/Java/Scala and the main testbench in the HDL sources. I.e., not to copy KBs of test data, but to pass a pointer/access instead.

The part that worries me most is the Scala part where paths/commands/options are glued together to bootstrap the simulation.

Note that GHDL is tested on MSYS2/MINGW on Windows. Hence, this should be handled automatically.

That is very nice to hear since I will need to simulate VexRiscv for work in VHDL :) .

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.

Mixed language cosim would be a very nice feature to have.

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.

PS: the above message is just my personal opinion that might not reflect the view of SpinalHDL's mantainers :) .

Sure. The same applies to mine and GHDL.

@umarcor
Copy link

umarcor commented Apr 19, 2020

@bellaz89, see ghdl/ghdl#237 and ghdl/ghdl#1249.

@numero-744
Copy link
Collaborator

The requested feature has been added: https://spinalhdl.github.io/SpinalDoc-RTD/master/SpinalHDL/Simulation/bootstraps.html#configuration

So this issue can be closed

@Dolu1990 Dolu1990 closed this as completed Jun 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature ✨ Feature idea with clear API defined
Projects
None yet
Development

No branches or pull requests

5 participants