Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ vunit-python-bridge embeds a Python interpreter in the simulator process so that
can execute Python code and call Python functions — a NumPy reference model, a constraint solver, a
plot of what the design just produced — without leaving the simulation. The VHDL API,
`python_pkg`/`python_context`, is compiled into the `python_bridge` library and is implemented for
the selected simulator by a foreign language interface the package builds itself: a small C library
called through VHPIDIRECT (NVC, GHDL) or the FLI (Questa/ModelSim), or a VHPI application built with
the simulator's own compiler driver (Riviera-PRO/Active-HDL). Values cross the interface with their
the selected simulator by a small C library the package builds itself, called through VHPIDIRECT
(NVC, GHDL), the FLI (Questa/ModelSim) or VHPI (Riviera-PRO). Values cross the interface with their
VHDL types: `integer`, `real`, `string`, `boolean`, `std_ulogic`, `unsigned`/`signed`, the vector
types, and `integer_array_t` as a NumPy array.

Expand Down Expand Up @@ -74,7 +73,7 @@ check(call_integer_vector("gain", arg(integer_vector'(1, 2))) = integer_vector'(
| NVC | VHPIDIRECT | Tested on Linux, macOS and Windows |
| GHDL (mcode, llvm-jit, llvm, gcc) | VHPIDIRECT | Tested on Linux, macOS and Windows |
| Questa/ModelSim | FLI | Tested manually on Linux; the Windows build is untested |
| Riviera-PRO, Active-HDL | VHPI | Untested; a subset of the API, see the documentation |
| Riviera-PRO | VHPI | Tested manually on Linux and Windows |

## Requirements

Expand Down
45 changes: 31 additions & 14 deletions docs/how_it_works.rst
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
.. _python_bridge:native:

How it works (NVC, GHDL and Questa)
------------------------------------
How it works
------------

For NVC, GHDL and Questa, the interpreter is embedded in the simulator process
by a small C library, the VUnit Python bridge (`src/vunit_python_bridge/native
The interpreter is embedded in the simulator process by a small C library, the
VUnit Python bridge (`src/vunit_python_bridge/native
<https://github.com/VUnit/vunit-python-bridge/tree/main/src/vunit_python_bridge/native>`__).
NVC and GHDL call it through VHPIDIRECT; Questa/ModelSim calls it through the
FLI, using the front end in `native/fli.c
<https://github.com/VUnit/vunit-python-bridge/blob/main/src/vunit_python_bridge/native/fli.c>`__
that converts the FLI parameters of one foreign subprogram per entry point. The generated VHDL
and everything above it is the same for all three. The interpreter is started
that converts the FLI parameters of one foreign subprogram per entry point.
Riviera-PRO, which has no VHPIDIRECT, calls it through VHPI, using
the front end in `native/vhpi.c
<https://github.com/VUnit/vunit-python-bridge/blob/main/src/vunit_python_bridge/native/vhpi.c>`__
that registers a foreign subprogram per entry point. The generated VHDL and
everything above it is the same for all simulators. The interpreter is started
on first use, is never restarted within a simulation, and uses no signal
handlers of its own.

Expand All @@ -20,7 +24,9 @@ Linux
below. It is rebuilt automatically when the
source, the Python version or the Python installation changes. For Questa
the FLI front end is compiled in too, against the ``mti.h`` of the
simulator, and the simulator installation is part of the cache key. The
simulator, and for Riviera-PRO the VHPI front end, against the
``vhpi_user.h`` of the simulator. The simulator installation is then part of
the cache key. The
package ships no prebuilt Linux library.

Windows
Expand All @@ -29,15 +35,16 @@ Windows
copied to the library directory, see below, and nothing is compiled. A development checkout of
the package does not contain the DLLs; they can be built with
``tools/build_python_bridge.py`` from an MSVC developer prompt, and without
them the library is built with gcc like for Questa. For Questa the library
must be linked against the simulator's ``mtipli``, so it is always built on
them the library is built with gcc like for Questa. For Questa and
Riviera-PRO the library must be linked against the simulator's
``mtipli`` or ``aldecpli``, so it is always built on
first use with gcc, against the headers and the import library of the
Python running VUnit. The compiler is ``CC`` if set, else the MinGW gcc
bundled with the simulator (``gcc-*-mingw64*`` next to the Questa
executables' directory), else ``gcc`` on ``PATH``. Many Questa installations
on Windows bundle none. A gcc build imports the Python DLL directly, so the
package adds its directory to ``PATH`` for the simulator processes. These
builds are untested.
package adds its directory to ``PATH`` for the simulator processes. The
Questa build is untested.

Library directory
The libraries are built or copied to a directory of their own rather than to the
Expand All @@ -57,7 +64,10 @@ Library directory
simulators leave out. The directory can be shared by projects since every
build has a directory named after what it was built from. The configuration
of a project stays in ``<output path>/python_bridge`` and the simulator hooks
pass its path to the library in ``VUNIT_PYTHON_BRIDGE_CONFIG``.
pass its path to the library in ``VUNIT_PYTHON_BRIDGE_CONFIG``. The VUnit
interface of Riviera-PRO has no hook for the environment of a
simulation, so for them the setup sets the variable in the environment of
the VUnit process, which the simulator inherits.

The bridge uses the full (version specific) CPython ABI rather than the
Stable ABI since embedding the interpreter in the environment VUnit runs in
Expand Down Expand Up @@ -90,6 +100,13 @@ starts, so it has to go on the command line of the process rather than on the
``vsim`` listing it in ``vsim -help all``. No simulator options need to be set
by the user.

The VHPI attributes generated for Riviera-PRO name the library by
absolute path without its suffix, which the simulator adds. The attribute
separates its parts by spaces, so the setup fails, asking for
``VUNIT_PYTHON_BRIDGE_LIBRARY_DIR`` to be set, when the path of the library
directory has a space. The front end transfers strings as vectors of character
positions, since Riviera-PRO ends a string value at a line feed.

Tested configurations
~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -102,6 +119,7 @@ GHDL llvm-jit 6.0.0, 7.0.0-dev
GHDL llvm 6.0.0, 7.0.0-dev
GHDL gcc 6.0.0, 7.0.0-dev
Questa (FLI) Altera Starter FPGA Edition 2025.3
Riviera-PRO (VHPI) 2026.04
========================== ========================================================

On Windows, CI tests NVC 1.22 and GHDL mcode (nightly) with Python 3.10, 3.12,
Expand All @@ -110,5 +128,4 @@ Windows machines without a compiler, there with every supported Python, so
each of its prebuilt DLLs is run.

Questa is tested manually on Linux and is not covered by CI; the Windows build
of its bridge library is untested. Riviera-PRO/Active-HDL (VHPI) is not tested
at all.
of its bridge library is untested.
3 changes: 1 addition & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Simulator Interface Status
NVC VHPIDIRECT Tested on Linux, macOS and Windows
GHDL (mcode, llvm-jit, llvm, gcc) VHPIDIRECT Tested on Linux, macOS and Windows
Questa/ModelSim FLI Tested manually on Linux; the Windows build is untested
Riviera-PRO, Active-HDL VHPI Untested; a subset of the API, see :doc:`other_simulators`
Riviera-PRO VHPI Tested manually on Linux and Windows
================================= ========== ========================================================

Getting started
Expand Down Expand Up @@ -127,7 +127,6 @@ become available through ``python_context``:
:hidden:

how_it_works
other_simulators

.. toctree::
:caption: About
Expand Down
42 changes: 0 additions & 42 deletions docs/other_simulators.rst

This file was deleted.

3 changes: 1 addition & 2 deletions docs/python_files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ Python files and modules
exec_file
---------

``exec_file`` executes a Python file (where the Python bridge implements the
API: NVC, GHDL and Questa), with the equivalent of
``exec_file`` executes a Python file, with the equivalent of

.. code-block:: python

Expand Down
9 changes: 3 additions & 6 deletions docs/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ Requirements

* A VUnit with support for packages and simulator hooks, see the `README
<https://github.com/VUnit/vunit-python-bridge#readme>`__.
* NVC, GHDL or Questa/ModelSim, through the VUnit Python bridge, see
:ref:`python_bridge:native`, or Riviera-PRO/Active-HDL (VHPI) through the VHPI application, which
the package builds in the same way, see
:ref:`python_bridge:other_simulators`. Any other simulator raises a
``RuntimeError``.
* NVC, GHDL, Questa/ModelSim or Riviera-PRO, see :ref:`python_bridge:native`.
Any other simulator raises a ``RuntimeError``.
* VHDL-2008 or later.
* CPython 3.10 or later with the standard (GIL) build. Free-threaded builds
are rejected with an error.
Expand All @@ -20,7 +17,7 @@ Requirements
* Windows: a 64-bit CPython from python.org (or compatible, such as
``actions/setup-python`` and ``uv``). No compiler is needed for NVC and GHDL
when the package includes the prebuilt DLLs. Otherwise, and always for
Questa and Riviera-PRO/Active-HDL, a MinGW-w64 ``gcc`` is needed: ``CC``, the
Questa and Riviera-PRO, a MinGW-w64 ``gcc`` is needed: ``CC``, the
one bundled with the simulator if there is one, or ``gcc`` on ``PATH``.
MSYS2/MinGW Pythons are not supported. The simulator must be a 64-bit (x64)
build too, since the Python DLL is loaded into its process; the package
Expand Down
29 changes: 25 additions & 4 deletions docs/running_code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ name selects the overload:

info("pi = " & to_string(eval_real("pi")));

On NVC, GHDL and Questa, where the API is implemented by the VUnit Python
bridge, ``eval`` has more result types:
``eval`` has more result types:

* ``eval_boolean``, returning ``boolean``. Only ``bool``/``numpy.bool_`` is
accepted. It also makes the result of ``eval`` usable as a condition:
Expand Down Expand Up @@ -163,7 +162,7 @@ VHDL Python
``std_ulogic`` ``bool``, 1 and H give ``True``, 0 and L give ``False``
``unsigned`` ``int``, passed as ``arg_unsigned``/``kwarg_unsigned``
``signed`` ``int``, passed as ``arg_signed``/``kwarg_signed``
``integer_array_t`` NumPy array, not on Riviera-PRO/Active-HDL
``integer_array_t`` NumPy array
========================== ========================================================

An ``unsigned`` or ``signed`` value of any width becomes an exact Python
Expand Down Expand Up @@ -199,7 +198,7 @@ passed as a number or as the string of its characters:
call("model.scale", arg_unsigned(unsigned(gain)));
call("model.push", arg(to_string(slv)));

On NVC, GHDL and Questa, ``call`` returns the same types as ``eval``:
``call`` returns the same types as ``eval``:
``call_boolean``, ``call_std_ulogic``, ``call_std_ulogic_vector``,
``call_integer_array``, ``call_string``, ``call_real_vector`` and
``call_integer_vector_ptr``, plus the procedures ``call_std_ulogic_vector``,
Expand Down Expand Up @@ -262,3 +261,25 @@ rather than by assigning to a variable in a loop:
...

call("model.configure", registers(0));

The example
~~~~~~~~~~~

The ``embedded_python`` example of this repository,
`examples/embedded_python
<https://github.com/VUnit/vunit-python-bridge/tree/main/examples/embedded_python>`__,
is a complete example that runs on every supported simulator.

Its ``tb_example.vhd`` has a test case for each part of the API: ``exec`` and
``eval`` with the types they convert, calls with positional, keyword and group
arguments, a 20 register status dump, wide
``arg_unsigned``/``arg_signed`` and ``std_ulogic`` argument values, a 2-D
``integer_array_t`` image transposed by NumPy, the result types of ``eval`` and
``call``, Python files executed with ``exec_file`` or imported with
``import_module_from_file``, two models loaded into a session each, and a
Python model failing with the logger of the default session mocked. Its last
test case drives ``python_model``, a verification component whose behaviour is
the Python function in ``python_model.py`` rather than VHDL. The component finds
that file by joining its name to ``base_path``, the directory of the run script, which
``run.py`` sets as the base directory, while the testbench gives the files it
loads itself relative to its own directory. See :ref:`python_bridge:file_paths`.
6 changes: 1 addition & 5 deletions docs/sessions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,6 @@ included:

mock(golden_logger, failure);

On Riviera-PRO/Active-HDL (VHPI), only the default session is supported:
passing any other session fails with a clear error.

.. _python_bridge:instance_sessions:

Independent models of component instances
Expand Down Expand Up @@ -134,8 +131,7 @@ session, keeping the mutable model state on ``self`` rather than in globals:
y <= call("model.accumulate", arg(x), session => session);

The module, and so the class, is shared by all sessions (see the caveats
below), while every session has a ``model`` object of its own. Like all
non-default sessions, this needs the Python bridge: NVC, GHDL or Questa.
below), while every session has a ``model`` object of its own.

Caveats
~~~~~~~
Expand Down
15 changes: 3 additions & 12 deletions docs/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ Type mapping
* ``real_vector`` ↔ ``list`` of ``float`` (``call_real_vector`` needs the bridge).
* ``integer_vector_ptr_t`` ↔ ``list`` of ``int``.
* ``std_ulogic``/``std_ulogic_vector`` ↔ ``str``, one character per element
out of ``U X 0 1 Z W L H -``, left to right (results only, Python bridge).
* ``signed``/``unsigned`` ↔ ``int`` (procedure results only, Python bridge).
* ``integer_array_t`` ↔ ``numpy.ndarray`` (Python bridge), see
out of ``U X 0 1 Z W L H -``, left to right (results only).
* ``signed``/``unsigned`` ↔ ``int`` (procedure results only).
* ``integer_array_t`` ↔ ``numpy.ndarray``, see
:ref:`python_bridge:integer_array`.

The types marked as needing the Python bridge are available on NVC, GHDL and
Questa, but not on Riviera-PRO/Active-HDL.

Results are strict: a value that does not fit the VHDL type, or is of the
wrong Python type, is an error. Values are never silently truncated or
wrapped.
Expand All @@ -29,12 +26,6 @@ wrapped.
integer_array_t and NumPy
--------------------------

.. note::

``integer_array_t`` values are transferred by the VUnit Python bridge and
are therefore available on NVC, GHDL and Questa, but not on
Riviera-PRO/Active-HDL.

An ``integer_array_t`` argument is transferred to Python and referred to by
the expression as a NumPy array of dtype ``int32``, so it can be reused in
several calls. It is indexed like VUnit, so that elements correspond
Expand Down
6 changes: 2 additions & 4 deletions docs/vhdl_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@
(
"python_ffi_pkg",
"python_ffi_pkg_bridge.vhd",
"Sessions, ``exec`` and the basic ``eval`` result types. The package is implemented for "
"the selected simulator: by the Python bridge for NVC, GHDL and Questa and by a VHPI "
"application for Riviera-PRO/Active-HDL, which declares the same API, see "
":doc:`../other_simulators`.",
"Sessions, ``exec`` and the basic ``eval`` result types, implemented by the Python bridge, "
"see :doc:`../how_it_works`.",
),
(
"python_pkg",
Expand Down
3 changes: 1 addition & 2 deletions examples/embedded_python/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ def main():
vunit_python_bridge.set_relative_file_base(root)
vu.add_package("vunit-python-bridge", allow_setup=True)
vu.add_random()
# The setup function of the package builds the foreign language application of the simulator
# (the Python bridge, or the VHPI application) under the output path.
# The setup function of the package builds the Python bridge for the simulator.

lib = vu.add_library("lib")
lib.add_source_files(root / "*.vhd")
Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ vunit_python_bridge = [
"hdl/tools/*.py",
"native/*.c",
"native/*.h",
"native/vhpi/*.c",
"native/vhpi/*.h",
"bin/*.dll",
]

Expand Down
1 change: 1 addition & 0 deletions release_notes/news.d/21.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Riviera-PRO is a supported simulator.
Loading
Loading