vunit-python-bridge 0.1.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.
- vunit_python_bridge/__init__.py +20 -18
- vunit_python_bridge/bin/vunit_python_bridge-cp310-win_amd64.dll +0 -0
- vunit_python_bridge/bin/vunit_python_bridge-cp311-win_amd64.dll +0 -0
- vunit_python_bridge/bin/vunit_python_bridge-cp312-win_amd64.dll +0 -0
- vunit_python_bridge/bin/vunit_python_bridge-cp313-win_amd64.dll +0 -0
- vunit_python_bridge/bin/vunit_python_bridge-cp314-win_amd64.dll +0 -0
- vunit_python_bridge/bin/vunit_python_bridge-cp315-win_amd64.dll +0 -0
- vunit_python_bridge/bridge.py +58 -15
- vunit_python_bridge/file_base.py +128 -0
- vunit_python_bridge/hdl/src/python_context.vhd +1 -0
- vunit_python_bridge/hdl/src/python_ffi_pkg_bridge.vhd +37 -14
- vunit_python_bridge/hdl/src/python_object_pkg.vhd +916 -0
- vunit_python_bridge/hdl/src/python_pkg.vhd +421 -41
- vunit_python_bridge/hdl/tools/generate_python_pkg.py +292 -14
- vunit_python_bridge/hdl/tools/python_object_pkg.vhd.in +255 -0
- vunit_python_bridge/hdl/tools/python_pkg.vhd.in +193 -41
- vunit_python_bridge/native/config.c +42 -7
- vunit_python_bridge/native/vhpi.c +223 -0
- vunit_python_bridge/native_library.py +153 -41
- vunit_python_bridge/runtime.py +236 -13
- vunit_python_bridge/simulator_hooks.py +32 -6
- vunit_python_bridge/vunit_pkg.toml +2 -4
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.3.0.dist-info}/METADATA +21 -15
- vunit_python_bridge-0.3.0.dist-info/RECORD +34 -0
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.3.0.dist-info}/WHEEL +1 -1
- vunit_python_bridge/foreign_application.py +0 -90
- vunit_python_bridge/hdl/src/python_pkg_vhpi.vhd +0 -270
- vunit_python_bridge/native/vhpi/python_pkg.c +0 -125
- vunit_python_bridge/native/vhpi/python_pkg.h +0 -31
- vunit_python_bridge/native/vhpi/python_pkg_vhpi.c +0 -366
- vunit_python_bridge-0.1.0.dist-info/RECORD +0 -34
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.3.0.dist-info}/top_level.txt +0 -0
vunit_python_bridge/__init__.py
CHANGED
|
@@ -12,52 +12,58 @@ 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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
*
|
|
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.
|
|
26
|
+
* file_base: the base directory of ``base_path``, ``set_relative_file_base``.
|
|
27
|
+
|
|
28
|
+
``set_relative_file_base(directory)``, called in the run script before ``add_package``, sets the
|
|
29
|
+
directory that ``base_path`` of ``python_pkg`` returns, for VHDL to join file names to. The
|
|
30
|
+
environment variable ``VUNIT_PYTHON_BRIDGE_FILE_BASE`` sets it too, and takes precedence.
|
|
28
31
|
"""
|
|
29
32
|
|
|
30
33
|
from pathlib import Path
|
|
31
34
|
|
|
35
|
+
from .file_base import add_file_base_package
|
|
36
|
+
|
|
37
|
+
# Public: run scripts use them
|
|
38
|
+
from .file_base import FILE_BASE_VARIABLE as FILE_BASE_VARIABLE
|
|
39
|
+
from .file_base import set_relative_file_base as set_relative_file_base
|
|
40
|
+
|
|
32
41
|
VHDL_PATH = Path(__file__).parent.resolve() / "hdl" / "src"
|
|
33
42
|
|
|
34
43
|
# The foreign language interface implementing the package for a simulator. VUnit says which
|
|
35
44
|
# simulator was selected, the package says how it is served: NVC and GHDL both go through
|
|
36
45
|
# VHPIDIRECT but bind the library differently, Questa/ModelSim goes through the FLI and
|
|
37
|
-
# Riviera-PRO
|
|
46
|
+
# Riviera-PRO through VHPI.
|
|
38
47
|
FOREIGN_LANGUAGE_INTERFACES = {
|
|
39
48
|
"nvc": "VHPIDIRECT_NVC",
|
|
40
49
|
"ghdl": "VHPIDIRECT_GHDL",
|
|
41
50
|
"modelsim": "FLI",
|
|
42
51
|
"rivierapro": "VHPI",
|
|
43
|
-
"activehdl": "VHPI",
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
# The simulators of the table, named as a user knows them
|
|
47
|
-
SUPPORTED_SIMULATORS = "NVC, GHDL, Questa/ModelSim
|
|
55
|
+
SUPPORTED_SIMULATORS = "NVC, GHDL, Questa/ModelSim or Riviera-PRO"
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
def setup(context):
|
|
51
59
|
"""
|
|
52
60
|
Complete the addition of the package.
|
|
53
61
|
|
|
54
|
-
``vunit_pkg.toml`` lists the sources
|
|
55
|
-
foreign language interface of the selected simulator
|
|
56
|
-
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.
|
|
57
64
|
"""
|
|
58
65
|
# pylint: disable=import-outside-toplevel
|
|
59
66
|
from .bridge import setup as setup_bridge
|
|
60
|
-
from .foreign_application import setup_vhpi_application
|
|
61
67
|
from . import simulator_hooks
|
|
62
68
|
|
|
63
69
|
simulator_name = context.simulator_name
|
|
@@ -73,11 +79,7 @@ def setup(context):
|
|
|
73
79
|
f"it has no foreign language interface for {simulator_name}"
|
|
74
80
|
)
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
# Riviera-PRO/Active-HDL, the simulators served by the VHPI application
|
|
78
|
-
context.add_source_files(context.library.name, [VHDL_PATH / "python_pkg_vhpi.vhd"])
|
|
79
|
-
setup_vhpi_application(context.output_path, simulator_name, context.simulator_prefix)
|
|
80
|
-
return
|
|
82
|
+
add_file_base_package(context)
|
|
81
83
|
|
|
82
84
|
bridge = setup_bridge(
|
|
83
85
|
context.output_path,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
vunit_python_bridge/bridge.py
CHANGED
|
@@ -16,10 +16,12 @@ from pathlib import Path
|
|
|
16
16
|
from typing import List, NamedTuple, Optional
|
|
17
17
|
|
|
18
18
|
from .native_library import (
|
|
19
|
+
LIBRARY_DIR_VARIABLE,
|
|
19
20
|
PACKAGE_PATH,
|
|
20
21
|
PythonBridgeError,
|
|
21
22
|
check_python_build,
|
|
22
23
|
check_windows_64bit_simulator,
|
|
24
|
+
library_directory,
|
|
23
25
|
prepare_library,
|
|
24
26
|
windows_python_dll,
|
|
25
27
|
)
|
|
@@ -29,10 +31,10 @@ VHDL_SOURCE_PATH = PACKAGE_PATH / "hdl" / "src"
|
|
|
29
31
|
BRIDGE_PACKAGE_TEMPLATE = VHDL_SOURCE_PATH / "python_bridge_pkg.vhd.in"
|
|
30
32
|
CONFIG_FILE_NAME = "vunit_python_bridge.cfg"
|
|
31
33
|
|
|
32
|
-
# The simulators the bridge serves, reaching it through VHPIDIRECT
|
|
33
|
-
# 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.
|
|
34
35
|
FLI_SIMULATORS = ("modelsim",)
|
|
35
|
-
|
|
36
|
+
VHPI_SIMULATORS = ("rivierapro",)
|
|
37
|
+
BRIDGE_SIMULATORS = ("nvc", "ghdl") + FLI_SIMULATORS + VHPI_SIMULATORS
|
|
36
38
|
|
|
37
39
|
# GHDL backends that link the design ahead of time. For these the library
|
|
38
40
|
# token in the VHPIDIRECT attribute is passed to the linker instead of being
|
|
@@ -51,6 +53,7 @@ class PythonBridge(NamedTuple):
|
|
|
51
53
|
"""
|
|
52
54
|
|
|
53
55
|
library_file: Path
|
|
56
|
+
config_file: Path
|
|
54
57
|
vhdl_files: List[Path]
|
|
55
58
|
|
|
56
59
|
|
|
@@ -74,7 +77,7 @@ def setup(
|
|
|
74
77
|
"""
|
|
75
78
|
if simulator_name is not None and simulator_name not in BRIDGE_SIMULATORS:
|
|
76
79
|
raise PythonBridgeError(
|
|
77
|
-
f"The vunit-python-bridge package requires NVC, GHDL
|
|
80
|
+
f"The vunit-python-bridge package requires NVC, GHDL, Questa/ModelSim or Riviera-PRO, "
|
|
78
81
|
f"it is not supported for {simulator_name}"
|
|
79
82
|
)
|
|
80
83
|
|
|
@@ -82,29 +85,39 @@ def setup(
|
|
|
82
85
|
check_windows_64bit_simulator(simulator_name, simulator_prefix)
|
|
83
86
|
|
|
84
87
|
is_fli = simulator_name in FLI_SIMULATORS
|
|
85
|
-
|
|
88
|
+
is_vhpi = simulator_name in VHPI_SIMULATORS
|
|
89
|
+
if (is_fli or is_vhpi) and simulator_prefix is None:
|
|
86
90
|
raise PythonBridgeError(
|
|
87
|
-
f"The FLI variant of the bridge is built against the {simulator_name}
|
|
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"
|
|
88
93
|
)
|
|
89
94
|
|
|
90
95
|
root = Path(output_path) / "python_bridge"
|
|
91
|
-
|
|
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)
|
|
92
98
|
run_script_dir = str(Path.cwd() if run_script_path is None else Path(run_script_path).resolve().parent)
|
|
93
|
-
|
|
99
|
+
# The configuration belongs to the project, the library may be shared. The simulator
|
|
100
|
+
# hooks tell the library where the configuration is.
|
|
101
|
+
config_file = root / CONFIG_FILE_NAME
|
|
102
|
+
_write_if_changed(config_file, _config_text(run_script_dir))
|
|
94
103
|
|
|
95
104
|
# The foreign attribute string of an entry point: the name of its wrapper in native/fli.c
|
|
96
|
-
# and the library, by absolute path since Questa accepts it, for the FLI; the
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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}}"
|
|
103
115
|
bridge_package = root / "vhdl" / "python_bridge_pkg.vhd"
|
|
104
116
|
_write_if_changed(bridge_package, _render_bridge_package(foreign))
|
|
105
117
|
|
|
106
118
|
return PythonBridge(
|
|
107
119
|
library_file,
|
|
120
|
+
config_file,
|
|
108
121
|
[
|
|
109
122
|
bridge_package,
|
|
110
123
|
VHDL_SOURCE_PATH / "python_ffi_pkg_bridge.vhd",
|
|
@@ -112,6 +125,22 @@ def setup(
|
|
|
112
125
|
)
|
|
113
126
|
|
|
114
127
|
|
|
128
|
+
def _prepare_library(simulator_prefix: Optional[Path], front_end: Optional[str]) -> Path:
|
|
129
|
+
"""
|
|
130
|
+
Prepare the library in the library directory, failing with a pointer to the variable
|
|
131
|
+
selecting another one when it cannot be written, e.g. a read-only home directory.
|
|
132
|
+
"""
|
|
133
|
+
directory = library_directory()
|
|
134
|
+
try:
|
|
135
|
+
return prepare_library(directory, simulator_prefix, front_end)
|
|
136
|
+
except OSError as exc:
|
|
137
|
+
source = f"set by {LIBRARY_DIR_VARIABLE}" if os.environ.get(LIBRARY_DIR_VARIABLE) else "the default"
|
|
138
|
+
raise PythonBridgeError(
|
|
139
|
+
f"Cannot build the Python bridge library in {directory!s} ({source}): {exc}. "
|
|
140
|
+
f"Set {LIBRARY_DIR_VARIABLE} to a writable directory."
|
|
141
|
+
) from exc
|
|
142
|
+
|
|
143
|
+
|
|
115
144
|
def _vhpidirect_token(simulator_name, simulator_backend: Optional[str], library_file: Path) -> str:
|
|
116
145
|
"""
|
|
117
146
|
The library token of the VHPIDIRECT attributes: the file name the simulator dlopen()s,
|
|
@@ -122,6 +151,20 @@ def _vhpidirect_token(simulator_name, simulator_backend: Optional[str], library_
|
|
|
122
151
|
return library_file.name
|
|
123
152
|
|
|
124
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
|
+
|
|
125
168
|
def _render_bridge_package(foreign: str) -> str:
|
|
126
169
|
"""
|
|
127
170
|
The generated python_bridge_pkg.vhd: the declarations of the template with the foreign
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
3
|
+
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
The base directory that ``base_path`` of ``python_pkg`` returns: set by ``set_relative_file_base``
|
|
9
|
+
in the run script or by the environment variable ``VUNIT_PYTHON_BRIDGE_FILE_BASE``, and given to
|
|
10
|
+
VHDL in a generated package.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from .bridge import _write_if_changed
|
|
18
|
+
|
|
19
|
+
# Name of the generated VHDL package holding the base directory, used by python_pkg (base_path)
|
|
20
|
+
FILE_BASE_PACKAGE = "python_file_base_pkg"
|
|
21
|
+
|
|
22
|
+
# The environment variable setting the directory of base_path when the run script cannot. It takes
|
|
23
|
+
# precedence over set_relative_file_base so that whoever runs a shared run script can override it.
|
|
24
|
+
FILE_BASE_VARIABLE = "VUNIT_PYTHON_BRIDGE_FILE_BASE"
|
|
25
|
+
|
|
26
|
+
LOGGER = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# The directory set by set_relative_file_base, None when unset, and whether setup has run, after
|
|
29
|
+
# which the directory is in the generated package and can no longer be changed
|
|
30
|
+
_relative_file_base = None
|
|
31
|
+
_setup_done = False
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def set_relative_file_base(directory):
|
|
35
|
+
"""
|
|
36
|
+
Set the directory that ``base_path`` of ``python_pkg`` returns, for VHDL to join file names to.
|
|
37
|
+
|
|
38
|
+
Call it in the run script before ``vu.add_package("vunit-python-bridge", ...)``. A relative
|
|
39
|
+
directory is taken from the current working directory at the time of the call.
|
|
40
|
+
|
|
41
|
+
:param directory: An existing directory, a ``str`` or a ``Path``.
|
|
42
|
+
"""
|
|
43
|
+
global _relative_file_base # pylint: disable=global-statement
|
|
44
|
+
|
|
45
|
+
if _setup_done:
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
'set_relative_file_base() must be called before vu.add_package("vunit-python-bridge", ...), '
|
|
48
|
+
"the package has already been set up"
|
|
49
|
+
)
|
|
50
|
+
path = Path(directory).resolve()
|
|
51
|
+
if not path.is_dir():
|
|
52
|
+
raise ValueError(f"The relative file base {str(directory)!r} is not an existing directory")
|
|
53
|
+
_relative_file_base = path
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _vhdl_string_literal(text):
|
|
57
|
+
"""
|
|
58
|
+
A VHDL expression of type string for the UTF-8 bytes of a text, in ASCII: printable characters
|
|
59
|
+
in a literal with its quotes doubled, other bytes as character'val(n).
|
|
60
|
+
"""
|
|
61
|
+
parts = []
|
|
62
|
+
literal = ""
|
|
63
|
+
for byte in text.encode("utf-8"):
|
|
64
|
+
if 0x20 <= byte <= 0x7E:
|
|
65
|
+
literal += '""' if byte == ord('"') else chr(byte)
|
|
66
|
+
else:
|
|
67
|
+
if literal:
|
|
68
|
+
parts.append(f'"{literal}"')
|
|
69
|
+
literal = ""
|
|
70
|
+
parts.append(f"character'val({byte})")
|
|
71
|
+
if literal or not parts:
|
|
72
|
+
parts.append(f'"{literal}"')
|
|
73
|
+
if not parts[0].startswith('"'):
|
|
74
|
+
# A string expression rather than a lone character
|
|
75
|
+
parts.insert(0, '""')
|
|
76
|
+
return " & ".join(parts)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _file_base_package_text(directory):
|
|
80
|
+
"""
|
|
81
|
+
Content of the generated python_file_base_pkg.vhd.
|
|
82
|
+
"""
|
|
83
|
+
value = "" if directory is None else directory.as_posix()
|
|
84
|
+
return f"""\
|
|
85
|
+
-- Generated by vunit_python_bridge.setup. Do not edit.
|
|
86
|
+
--
|
|
87
|
+
-- The directory set by the environment variable {FILE_BASE_VARIABLE} or by
|
|
88
|
+
-- vunit_python_bridge.set_relative_file_base() in the run script, empty when neither is set.
|
|
89
|
+
-- base_path of python_pkg returns it.
|
|
90
|
+
package {FILE_BASE_PACKAGE} is
|
|
91
|
+
constant p_relative_file_base : string := {_vhdl_string_literal(value)};
|
|
92
|
+
end package;
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _file_base():
|
|
97
|
+
"""
|
|
98
|
+
The directory of base_path: that of the environment variable if it is set and not empty,
|
|
99
|
+
otherwise that of set_relative_file_base, None when neither is set.
|
|
100
|
+
"""
|
|
101
|
+
value = os.environ.get(FILE_BASE_VARIABLE, "")
|
|
102
|
+
if value == "":
|
|
103
|
+
return _relative_file_base
|
|
104
|
+
|
|
105
|
+
directory = Path(value).resolve()
|
|
106
|
+
if not directory.is_dir():
|
|
107
|
+
raise ValueError(f"The environment variable {FILE_BASE_VARIABLE}={value!r} is not an existing directory")
|
|
108
|
+
if _relative_file_base is not None and _relative_file_base != directory:
|
|
109
|
+
LOGGER.warning(
|
|
110
|
+
"The environment variable %s=%s takes precedence over set_relative_file_base(%r)",
|
|
111
|
+
FILE_BASE_VARIABLE,
|
|
112
|
+
value,
|
|
113
|
+
str(_relative_file_base),
|
|
114
|
+
)
|
|
115
|
+
return directory
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def add_file_base_package(context):
|
|
119
|
+
"""
|
|
120
|
+
Generate python_file_base_pkg and add it to the library of the package.
|
|
121
|
+
"""
|
|
122
|
+
global _setup_done # pylint: disable=global-statement
|
|
123
|
+
|
|
124
|
+
path = Path(context.output_path) / "python_bridge" / "vhdl" / f"{FILE_BASE_PACKAGE}.vhd"
|
|
125
|
+
_write_if_changed(path, _file_base_package_text(_file_base()))
|
|
126
|
+
context.add_source_files(context.library.name, [path])
|
|
127
|
+
_setup_done = True
|
|
128
|
+
|
|
@@ -4,28 +4,24 @@
|
|
|
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
|
|
8
|
-
-- implemented with the VUnit Python bridge (src/vunit_python_bridge)
|
|
9
|
-
--
|
|
10
|
-
--
|
|
11
|
-
--
|
|
12
|
-
--
|
|
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.
|
|
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;
|
|
28
22
|
use vunit_lib.logger_pkg.all;
|
|
23
|
+
use vunit_lib.run_pkg.get_phase;
|
|
24
|
+
use vunit_lib.run_types_pkg.all;
|
|
29
25
|
use work.python_bridge_pkg.all;
|
|
30
26
|
|
|
31
27
|
package python_ffi_pkg is
|
|
@@ -109,6 +105,9 @@ package python_ffi_pkg is
|
|
|
109
105
|
-----------------------------------------------------------------------------
|
|
110
106
|
-- Private, the primitives the bridge operations of python_pkg are built on
|
|
111
107
|
-----------------------------------------------------------------------------
|
|
108
|
+
-- Whether new_session has made a session with the identity
|
|
109
|
+
impure function p_has_session(id : id_t) return boolean;
|
|
110
|
+
|
|
112
111
|
-- Result kinds, must match src/vunit_python_bridge/runtime.py
|
|
113
112
|
constant p_kind_integer : integer := 0;
|
|
114
113
|
constant p_kind_real : integer := 1;
|
|
@@ -121,6 +120,7 @@ package python_ffi_pkg is
|
|
|
121
120
|
constant p_kind_integer_array : integer := 8;
|
|
122
121
|
constant p_kind_integer_vector : integer := 9;
|
|
123
122
|
constant p_kind_real_vector : integer := 10;
|
|
123
|
+
constant p_kind_dict : integer := 11;
|
|
124
124
|
|
|
125
125
|
-- Names of the operations, used in the error messages
|
|
126
126
|
impure function p_exec_operation(session : python_session_t := default_session) return string;
|
|
@@ -169,6 +169,7 @@ package python_ffi_pkg is
|
|
|
169
169
|
|
|
170
170
|
-- The result of the last evaluation
|
|
171
171
|
impure function p_result_integer return integer;
|
|
172
|
+
impure function p_result_real return real;
|
|
172
173
|
impure function p_result_string return string;
|
|
173
174
|
impure function p_result_integer_vector return integer_vector;
|
|
174
175
|
impure function p_result_real_vector return real_vector;
|
|
@@ -189,11 +190,20 @@ package body python_ffi_pkg is
|
|
|
189
190
|
return new_session(get_id(name, parent => p_python_id));
|
|
190
191
|
end;
|
|
191
192
|
|
|
193
|
+
-- The full names of the identities new_session has made sessions with
|
|
194
|
+
constant p_session_ids : dict_t := new_dict;
|
|
195
|
+
|
|
192
196
|
impure function new_session(id : id_t) return python_session_t is
|
|
193
197
|
begin
|
|
198
|
+
set_string(p_session_ids, full_name(id), "");
|
|
194
199
|
return (p_data => new_integer_vector_ptr(1, value => to_integer(id)));
|
|
195
200
|
end;
|
|
196
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
|
+
|
|
197
207
|
impure function get_id(session : python_session_t) return id_t is
|
|
198
208
|
begin
|
|
199
209
|
return to_id(get(session.p_data, 0));
|
|
@@ -335,6 +345,11 @@ package body python_ffi_pkg is
|
|
|
335
345
|
return vpy_result_integer;
|
|
336
346
|
end;
|
|
337
347
|
|
|
348
|
+
impure function p_result_real return real is
|
|
349
|
+
begin
|
|
350
|
+
return vpy_result_real;
|
|
351
|
+
end;
|
|
352
|
+
|
|
338
353
|
impure function p_result_string return string is
|
|
339
354
|
constant len : natural := vpy_result_meta(0);
|
|
340
355
|
variable result : string(1 to len);
|
|
@@ -412,6 +427,14 @@ package body python_ffi_pkg is
|
|
|
412
427
|
variable offset, n : natural := 0;
|
|
413
428
|
variable staged_id : integer;
|
|
414
429
|
begin
|
|
430
|
+
if get_phase < test_runner_setup then
|
|
431
|
+
failure(
|
|
432
|
+
python_logger,
|
|
433
|
+
operation & " failed:" & LF &
|
|
434
|
+
"An integer_array_t argument cannot be made before test_runner_setup."
|
|
435
|
+
);
|
|
436
|
+
return -1;
|
|
437
|
+
end if;
|
|
415
438
|
if not p_succeeded(
|
|
416
439
|
vpy_push_array(len, width(arr), height(arr), depth(arr), bit_width(arr), boolean'pos(is_signed(arr))),
|
|
417
440
|
operation
|