vunit-python-bridge 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,18 +12,16 @@ The VUnit package making Python callable from VHDL.
12
12
  ``python_pkg``/``python_context`` into the ``python_bridge`` library and sets up the foreign
13
13
  language interface implementing them for the selected simulator:
14
14
 
15
- * NVC, GHDL (VHPIDIRECT) and Questa/ModelSim (FLI) are served by the VUnit Python bridge, a
16
- small C library (native/*.c) embedding CPython in the simulator process.
17
- * Riviera-PRO/Active-HDL are served by a VHPI application built with the simulator's own
18
- compiler driver.
15
+ the VUnit Python bridge, a small C library (native/*.c) embedding CPython in the simulator
16
+ process, called through VHPIDIRECT (NVC, GHDL), the FLI (Questa/ModelSim) or VHPI
17
+ (Riviera-PRO).
19
18
 
20
19
  Modules:
21
20
 
22
21
  * bridge: setup of the bridge, its configuration and its generated VHDL.
23
22
  * native_library: compiles and caches the library on Linux and macOS, selects the prebuilt
24
23
  DLL on Windows.
25
- * foreign_application: builds the VHPI application.
26
- * simulator_hooks: makes NVC and GHDL find the library.
24
+ * simulator_hooks: makes the simulators find the library and its configuration.
27
25
  * runtime: runs inside the simulator's embedded interpreter.
28
26
  * file_base: the base directory of ``base_path``, ``set_relative_file_base``.
29
27
 
@@ -45,30 +43,27 @@ VHDL_PATH = Path(__file__).parent.resolve() / "hdl" / "src"
45
43
  # The foreign language interface implementing the package for a simulator. VUnit says which
46
44
  # simulator was selected, the package says how it is served: NVC and GHDL both go through
47
45
  # VHPIDIRECT but bind the library differently, Questa/ModelSim goes through the FLI and
48
- # Riviera-PRO/Active-HDL through a VHPI application of their own.
46
+ # Riviera-PRO through VHPI.
49
47
  FOREIGN_LANGUAGE_INTERFACES = {
50
48
  "nvc": "VHPIDIRECT_NVC",
51
49
  "ghdl": "VHPIDIRECT_GHDL",
52
50
  "modelsim": "FLI",
53
51
  "rivierapro": "VHPI",
54
- "activehdl": "VHPI",
55
52
  }
56
53
 
57
54
  # The simulators of the table, named as a user knows them
58
- SUPPORTED_SIMULATORS = "NVC, GHDL, Questa/ModelSim, Riviera-PRO or Active-HDL"
55
+ SUPPORTED_SIMULATORS = "NVC, GHDL, Questa/ModelSim or Riviera-PRO"
59
56
 
60
57
 
61
58
  def setup(context):
62
59
  """
63
60
  Complete the addition of the package.
64
61
 
65
- ``vunit_pkg.toml`` lists the sources both variants of the package share, this adds the
66
- foreign language interface of the selected simulator: the VHPI application for
67
- Riviera-PRO/Active-HDL, the VUnit Python bridge for everyone else.
62
+ ``vunit_pkg.toml`` lists the sources of the package, this adds the bridge for the
63
+ foreign language interface of the selected simulator.
68
64
  """
69
65
  # pylint: disable=import-outside-toplevel
70
66
  from .bridge import setup as setup_bridge
71
- from .foreign_application import setup_vhpi_application
72
67
  from . import simulator_hooks
73
68
 
74
69
  simulator_name = context.simulator_name
@@ -86,12 +81,6 @@ def setup(context):
86
81
 
87
82
  add_file_base_package(context)
88
83
 
89
- if interface == "VHPI":
90
- # Riviera-PRO/Active-HDL, the simulators served by the VHPI application
91
- context.add_source_files(context.library.name, [VHDL_PATH / "python_pkg_vhpi.vhd"])
92
- setup_vhpi_application(context.output_path, simulator_name, context.simulator_prefix)
93
- return
94
-
95
84
  bridge = setup_bridge(
96
85
  context.output_path,
97
86
  context.run_script_path,
@@ -31,10 +31,10 @@ VHDL_SOURCE_PATH = PACKAGE_PATH / "hdl" / "src"
31
31
  BRIDGE_PACKAGE_TEMPLATE = VHDL_SOURCE_PATH / "python_bridge_pkg.vhd.in"
32
32
  CONFIG_FILE_NAME = "vunit_python_bridge.cfg"
33
33
 
34
- # The simulators the bridge serves, reaching it through VHPIDIRECT and through the FLI.
35
- # The rest of the simulators of the package are served by the VHPI application.
34
+ # The simulators the bridge serves, reaching it through VHPIDIRECT, the FLI and VHPI.
36
35
  FLI_SIMULATORS = ("modelsim",)
37
- BRIDGE_SIMULATORS = ("nvc", "ghdl") + FLI_SIMULATORS
36
+ VHPI_SIMULATORS = ("rivierapro",)
37
+ BRIDGE_SIMULATORS = ("nvc", "ghdl") + FLI_SIMULATORS + VHPI_SIMULATORS
38
38
 
39
39
  # GHDL backends that link the design ahead of time. For these the library
40
40
  # token in the VHPIDIRECT attribute is passed to the linker instead of being
@@ -77,7 +77,7 @@ def setup(
77
77
  """
78
78
  if simulator_name is not None and simulator_name not in BRIDGE_SIMULATORS:
79
79
  raise PythonBridgeError(
80
- f"The vunit-python-bridge package requires NVC, GHDL or Questa/ModelSim, "
80
+ f"The vunit-python-bridge package requires NVC, GHDL, Questa/ModelSim or Riviera-PRO, "
81
81
  f"it is not supported for {simulator_name}"
82
82
  )
83
83
 
@@ -85,13 +85,16 @@ def setup(
85
85
  check_windows_64bit_simulator(simulator_name, simulator_prefix)
86
86
 
87
87
  is_fli = simulator_name in FLI_SIMULATORS
88
- if is_fli and simulator_prefix is None:
88
+ is_vhpi = simulator_name in VHPI_SIMULATORS
89
+ if (is_fli or is_vhpi) and simulator_prefix is None:
89
90
  raise PythonBridgeError(
90
- f"The FLI variant of the bridge is built against the {simulator_name} installation, but it was not found"
91
+ f"The {'FLI' if is_fli else 'VHPI'} variant of the bridge is built against the {simulator_name} "
92
+ "installation, but it was not found"
91
93
  )
92
94
 
93
95
  root = Path(output_path) / "python_bridge"
94
- library_file = _prepare_library(Path(simulator_prefix) if is_fli else None)
96
+ front_end = "fli" if is_fli else "vhpi" if is_vhpi else None
97
+ library_file = _prepare_library(Path(simulator_prefix) if front_end else None, front_end)
95
98
  run_script_dir = str(Path.cwd() if run_script_path is None else Path(run_script_path).resolve().parent)
96
99
  # The configuration belongs to the project, the library may be shared. The simulator
97
100
  # hooks tell the library where the configuration is.
@@ -99,13 +102,16 @@ def setup(
99
102
  _write_if_changed(config_file, _config_text(run_script_dir))
100
103
 
101
104
  # The foreign attribute string of an entry point: the name of its wrapper in native/fli.c
102
- # and the library, by absolute path since Questa accepts it, for the FLI; the VHPIDIRECT
103
- # library token and the entry point itself for NVC and GHDL.
104
- foreign = (
105
- f"fli_{{entry_point}} {library_file!s}"
106
- if is_fli
107
- else f"VHPIDIRECT {_vhpidirect_token(simulator_name, simulator_backend, library_file)} {{entry_point}}"
108
- )
105
+ # and the library, by absolute path since Questa accepts it, for the FLI; the library by
106
+ # absolute path without its suffix, which Riviera-PRO adds, and the entry point registered
107
+ # by native/vhpi.c for VHPI; the VHPIDIRECT library token and the entry point itself for
108
+ # NVC and GHDL.
109
+ if is_fli:
110
+ foreign = f"fli_{{entry_point}} {library_file!s}"
111
+ elif is_vhpi:
112
+ foreign = f"VHPI {_vhpi_library_token(library_file)} {{entry_point}}"
113
+ else:
114
+ foreign = f"VHPIDIRECT {_vhpidirect_token(simulator_name, simulator_backend, library_file)} {{entry_point}}"
109
115
  bridge_package = root / "vhdl" / "python_bridge_pkg.vhd"
110
116
  _write_if_changed(bridge_package, _render_bridge_package(foreign))
111
117
 
@@ -119,14 +125,14 @@ def setup(
119
125
  )
120
126
 
121
127
 
122
- def _prepare_library(fli_prefix: Optional[Path]) -> Path:
128
+ def _prepare_library(simulator_prefix: Optional[Path], front_end: Optional[str]) -> Path:
123
129
  """
124
130
  Prepare the library in the library directory, failing with a pointer to the variable
125
131
  selecting another one when it cannot be written, e.g. a read-only home directory.
126
132
  """
127
133
  directory = library_directory()
128
134
  try:
129
- return prepare_library(directory, fli_prefix)
135
+ return prepare_library(directory, simulator_prefix, front_end)
130
136
  except OSError as exc:
131
137
  source = f"set by {LIBRARY_DIR_VARIABLE}" if os.environ.get(LIBRARY_DIR_VARIABLE) else "the default"
132
138
  raise PythonBridgeError(
@@ -145,6 +151,20 @@ def _vhpidirect_token(simulator_name, simulator_backend: Optional[str], library_
145
151
  return library_file.name
146
152
 
147
153
 
154
+ def _vhpi_library_token(library_file: Path) -> str:
155
+ """
156
+ The library of the VHPI attributes: its path without the suffix the simulator adds. The
157
+ attribute separates its parts by spaces, so the path cannot have any.
158
+ """
159
+ token = str(library_file.with_suffix(""))
160
+ if any(character.isspace() for character in token):
161
+ raise PythonBridgeError(
162
+ f"The Python bridge library for VHPI is in {library_file.parent!s}, whose path has a space, which the "
163
+ f"VHPI foreign attribute cannot name. Set {LIBRARY_DIR_VARIABLE} to a directory without spaces."
164
+ )
165
+ return token
166
+
167
+
148
168
  def _render_bridge_package(foreign: str) -> str:
149
169
  """
150
170
  The generated python_bridge_pkg.vhd: the declarations of the template with the foreign
@@ -10,4 +10,5 @@ context python_context is
10
10
  library python_bridge;
11
11
  use python_bridge.python_pkg.all;
12
12
  use python_bridge.python_ffi_pkg.all;
13
+ use python_bridge.python_object_pkg.all;
13
14
  end context;
@@ -4,24 +4,18 @@
4
4
  --
5
5
  -- Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
6
6
  --
7
- -- The foreign language interface of the Python package for NVC and GHDL,
8
- -- implemented with the VUnit Python bridge (src/vunit_python_bridge). It provides
9
- -- the same subprograms as the FLI and VHPI variants but over the private
10
- -- python_bridge_pkg, which the setup function of the package generates.
11
- --
12
- -- Deviations from the other variants:
13
- -- * The subprograms are impure since they call impure foreign subprograms.
14
- -- * Errors are reported as failures on the logger of the session they were
15
- -- performed in rather than aborting the simulation directly, which makes
16
- -- them observable from VHDL.
17
- -- * Sessions other than the default one are supported.
7
+ -- The foreign language interface of the Python package for every simulator,
8
+ -- implemented with the VUnit Python bridge (src/vunit_python_bridge) over the
9
+ -- private python_bridge_pkg, which the setup function of the package generates.
10
+ -- The subprograms are impure since they call impure foreign subprograms. Errors
11
+ -- are reported as failures on the logger of the session they were performed in,
12
+ -- which makes them observable from VHDL.
18
13
  --
19
14
  -- The p_ prefixed declarations are the private primitives the bridge
20
- -- operations of python_pkg are built on. They are not part of the API. The FLI and VHPI
21
- -- variants of this package declare the same primitives and implement them by
22
- -- reporting a failure.
15
+ -- operations of python_pkg are built on. They are not part of the API.
23
16
 
24
17
  library vunit_lib;
18
+ use vunit_lib.dict_pkg.all;
25
19
  use vunit_lib.id_pkg.all;
26
20
  use vunit_lib.integer_array_pkg.all;
27
21
  use vunit_lib.integer_vector_ptr_pkg.all;
@@ -111,6 +105,9 @@ package python_ffi_pkg is
111
105
  -----------------------------------------------------------------------------
112
106
  -- Private, the primitives the bridge operations of python_pkg are built on
113
107
  -----------------------------------------------------------------------------
108
+ -- Whether new_session has made a session with the identity
109
+ impure function p_has_session(id : id_t) return boolean;
110
+
114
111
  -- Result kinds, must match src/vunit_python_bridge/runtime.py
115
112
  constant p_kind_integer : integer := 0;
116
113
  constant p_kind_real : integer := 1;
@@ -172,6 +169,7 @@ package python_ffi_pkg is
172
169
 
173
170
  -- The result of the last evaluation
174
171
  impure function p_result_integer return integer;
172
+ impure function p_result_real return real;
175
173
  impure function p_result_string return string;
176
174
  impure function p_result_integer_vector return integer_vector;
177
175
  impure function p_result_real_vector return real_vector;
@@ -192,11 +190,20 @@ package body python_ffi_pkg is
192
190
  return new_session(get_id(name, parent => p_python_id));
193
191
  end;
194
192
 
193
+ -- The full names of the identities new_session has made sessions with
194
+ constant p_session_ids : dict_t := new_dict;
195
+
195
196
  impure function new_session(id : id_t) return python_session_t is
196
197
  begin
198
+ set_string(p_session_ids, full_name(id), "");
197
199
  return (p_data => new_integer_vector_ptr(1, value => to_integer(id)));
198
200
  end;
199
201
 
202
+ impure function p_has_session(id : id_t) return boolean is
203
+ begin
204
+ return has_key(p_session_ids, full_name(id));
205
+ end;
206
+
200
207
  impure function get_id(session : python_session_t) return id_t is
201
208
  begin
202
209
  return to_id(get(session.p_data, 0));
@@ -338,6 +345,11 @@ package body python_ffi_pkg is
338
345
  return vpy_result_integer;
339
346
  end;
340
347
 
348
+ impure function p_result_real return real is
349
+ begin
350
+ return vpy_result_real;
351
+ end;
352
+
341
353
  impure function p_result_string return string is
342
354
  constant len : natural := vpy_result_meta(0);
343
355
  variable result : string(1 to len);