vunit-python-bridge 0.1.0__py3-none-any.whl → 0.2.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 +13 -0
- 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 +25 -2
- vunit_python_bridge/file_base.py +128 -0
- vunit_python_bridge/hdl/src/python_ffi_pkg_bridge.vhd +11 -0
- vunit_python_bridge/hdl/src/python_pkg.vhd +224 -30
- vunit_python_bridge/hdl/src/python_pkg_vhpi.vhd +15 -1
- vunit_python_bridge/hdl/tools/generate_python_pkg.py +10 -0
- vunit_python_bridge/hdl/tools/python_pkg.vhd.in +180 -30
- vunit_python_bridge/native/config.c +42 -7
- vunit_python_bridge/native/vhpi/python_pkg.c +1 -1
- vunit_python_bridge/native/vhpi/python_pkg.h +1 -1
- vunit_python_bridge/native/vhpi/python_pkg_vhpi.c +1 -1
- vunit_python_bridge/native_library.py +89 -4
- vunit_python_bridge/runtime.py +145 -8
- vunit_python_bridge/simulator_hooks.py +28 -6
- vunit_python_bridge/vunit_pkg.toml +1 -3
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.2.0.dist-info}/METADATA +16 -11
- vunit_python_bridge-0.2.0.dist-info/RECORD +36 -0
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.2.0.dist-info}/WHEEL +1 -1
- vunit_python_bridge-0.1.0.dist-info/RECORD +0 -34
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {vunit_python_bridge-0.1.0.dist-info → vunit_python_bridge-0.2.0.dist-info}/top_level.txt +0 -0
vunit_python_bridge/__init__.py
CHANGED
|
@@ -25,10 +25,21 @@ Modules:
|
|
|
25
25
|
* foreign_application: builds the VHPI application.
|
|
26
26
|
* simulator_hooks: makes NVC and GHDL find the library.
|
|
27
27
|
* runtime: runs inside the simulator's embedded interpreter.
|
|
28
|
+
* file_base: the base directory of ``base_path``, ``set_relative_file_base``.
|
|
29
|
+
|
|
30
|
+
``set_relative_file_base(directory)``, called in the run script before ``add_package``, sets the
|
|
31
|
+
directory that ``base_path`` of ``python_pkg`` returns, for VHDL to join file names to. The
|
|
32
|
+
environment variable ``VUNIT_PYTHON_BRIDGE_FILE_BASE`` sets it too, and takes precedence.
|
|
28
33
|
"""
|
|
29
34
|
|
|
30
35
|
from pathlib import Path
|
|
31
36
|
|
|
37
|
+
from .file_base import add_file_base_package
|
|
38
|
+
|
|
39
|
+
# Public: run scripts use them
|
|
40
|
+
from .file_base import FILE_BASE_VARIABLE as FILE_BASE_VARIABLE
|
|
41
|
+
from .file_base import set_relative_file_base as set_relative_file_base
|
|
42
|
+
|
|
32
43
|
VHDL_PATH = Path(__file__).parent.resolve() / "hdl" / "src"
|
|
33
44
|
|
|
34
45
|
# The foreign language interface implementing the package for a simulator. VUnit says which
|
|
@@ -73,6 +84,8 @@ def setup(context):
|
|
|
73
84
|
f"it has no foreign language interface for {simulator_name}"
|
|
74
85
|
)
|
|
75
86
|
|
|
87
|
+
add_file_base_package(context)
|
|
88
|
+
|
|
76
89
|
if interface == "VHPI":
|
|
77
90
|
# Riviera-PRO/Active-HDL, the simulators served by the VHPI application
|
|
78
91
|
context.add_source_files(context.library.name, [VHDL_PATH / "python_pkg_vhpi.vhd"])
|
|
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
|
)
|
|
@@ -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
|
|
|
@@ -88,9 +91,12 @@ def setup(
|
|
|
88
91
|
)
|
|
89
92
|
|
|
90
93
|
root = Path(output_path) / "python_bridge"
|
|
91
|
-
library_file =
|
|
94
|
+
library_file = _prepare_library(Path(simulator_prefix) if is_fli else None)
|
|
92
95
|
run_script_dir = str(Path.cwd() if run_script_path is None else Path(run_script_path).resolve().parent)
|
|
93
|
-
|
|
96
|
+
# The configuration belongs to the project, the library may be shared. The simulator
|
|
97
|
+
# hooks tell the library where the configuration is.
|
|
98
|
+
config_file = root / CONFIG_FILE_NAME
|
|
99
|
+
_write_if_changed(config_file, _config_text(run_script_dir))
|
|
94
100
|
|
|
95
101
|
# The foreign attribute string of an entry point: the name of its wrapper in native/fli.c
|
|
96
102
|
# and the library, by absolute path since Questa accepts it, for the FLI; the VHPIDIRECT
|
|
@@ -105,6 +111,7 @@ def setup(
|
|
|
105
111
|
|
|
106
112
|
return PythonBridge(
|
|
107
113
|
library_file,
|
|
114
|
+
config_file,
|
|
108
115
|
[
|
|
109
116
|
bridge_package,
|
|
110
117
|
VHDL_SOURCE_PATH / "python_ffi_pkg_bridge.vhd",
|
|
@@ -112,6 +119,22 @@ def setup(
|
|
|
112
119
|
)
|
|
113
120
|
|
|
114
121
|
|
|
122
|
+
def _prepare_library(fli_prefix: Optional[Path]) -> Path:
|
|
123
|
+
"""
|
|
124
|
+
Prepare the library in the library directory, failing with a pointer to the variable
|
|
125
|
+
selecting another one when it cannot be written, e.g. a read-only home directory.
|
|
126
|
+
"""
|
|
127
|
+
directory = library_directory()
|
|
128
|
+
try:
|
|
129
|
+
return prepare_library(directory, fli_prefix)
|
|
130
|
+
except OSError as exc:
|
|
131
|
+
source = f"set by {LIBRARY_DIR_VARIABLE}" if os.environ.get(LIBRARY_DIR_VARIABLE) else "the default"
|
|
132
|
+
raise PythonBridgeError(
|
|
133
|
+
f"Cannot build the Python bridge library in {directory!s} ({source}): {exc}. "
|
|
134
|
+
f"Set {LIBRARY_DIR_VARIABLE} to a writable directory."
|
|
135
|
+
) from exc
|
|
136
|
+
|
|
137
|
+
|
|
115
138
|
def _vhpidirect_token(simulator_name, simulator_backend: Optional[str], library_file: Path) -> str:
|
|
116
139
|
"""
|
|
117
140
|
The library token of the VHPIDIRECT attributes: the file name the simulator dlopen()s,
|
|
@@ -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
|
+
|
|
@@ -26,6 +26,8 @@ use vunit_lib.id_pkg.all;
|
|
|
26
26
|
use vunit_lib.integer_array_pkg.all;
|
|
27
27
|
use vunit_lib.integer_vector_ptr_pkg.all;
|
|
28
28
|
use vunit_lib.logger_pkg.all;
|
|
29
|
+
use vunit_lib.run_pkg.get_phase;
|
|
30
|
+
use vunit_lib.run_types_pkg.all;
|
|
29
31
|
use work.python_bridge_pkg.all;
|
|
30
32
|
|
|
31
33
|
package python_ffi_pkg is
|
|
@@ -121,6 +123,7 @@ package python_ffi_pkg is
|
|
|
121
123
|
constant p_kind_integer_array : integer := 8;
|
|
122
124
|
constant p_kind_integer_vector : integer := 9;
|
|
123
125
|
constant p_kind_real_vector : integer := 10;
|
|
126
|
+
constant p_kind_dict : integer := 11;
|
|
124
127
|
|
|
125
128
|
-- Names of the operations, used in the error messages
|
|
126
129
|
impure function p_exec_operation(session : python_session_t := default_session) return string;
|
|
@@ -412,6 +415,14 @@ package body python_ffi_pkg is
|
|
|
412
415
|
variable offset, n : natural := 0;
|
|
413
416
|
variable staged_id : integer;
|
|
414
417
|
begin
|
|
418
|
+
if get_phase < test_runner_setup then
|
|
419
|
+
failure(
|
|
420
|
+
python_logger,
|
|
421
|
+
operation & " failed:" & LF &
|
|
422
|
+
"An integer_array_t argument cannot be made before test_runner_setup."
|
|
423
|
+
);
|
|
424
|
+
return -1;
|
|
425
|
+
end if;
|
|
415
426
|
if not p_succeeded(
|
|
416
427
|
vpy_push_array(len, width(arr), height(arr), depth(arr), bit_width(arr), boolean'pos(is_signed(arr))),
|
|
417
428
|
operation
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
-- Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
|
|
8
8
|
|
|
9
9
|
use work.python_ffi_pkg.all;
|
|
10
|
+
use work.python_file_base_pkg.all;
|
|
10
11
|
library vunit_lib;
|
|
11
12
|
use vunit_lib.path.all;
|
|
12
13
|
use vunit_lib.run_pkg.all;
|
|
13
14
|
use vunit_lib.runner_pkg.all;
|
|
14
15
|
use vunit_lib.integer_vector_ptr_pkg.all;
|
|
15
16
|
use vunit_lib.string_ops.all;
|
|
17
|
+
use vunit_lib.dict_pkg.all;
|
|
18
|
+
use vunit_lib.dict_2008p_pkg.all;
|
|
19
|
+
use vunit_lib.data_types_private_pkg.all;
|
|
16
20
|
|
|
17
21
|
use std.textio.all;
|
|
18
22
|
|
|
@@ -28,6 +32,9 @@ use vunit_lib.logger_pkg.all;
|
|
|
28
32
|
------------------------------------------------------------------------------
|
|
29
33
|
|
|
30
34
|
package python_pkg is
|
|
35
|
+
-- Import a Python file as a module. A relative path is relative to the
|
|
36
|
+
-- directory of the testbench file, tb_path, so test_runner_setup must have
|
|
37
|
+
-- been called. An absolute path is used as it is given.
|
|
31
38
|
procedure import_module_from_file(
|
|
32
39
|
module_path, as_module_name : string; session : python_session_t := default_session
|
|
33
40
|
);
|
|
@@ -78,6 +85,12 @@ package python_pkg is
|
|
|
78
85
|
--
|
|
79
86
|
-- H and L are read as 1 and 0. Any other metavalue is an error.
|
|
80
87
|
--
|
|
88
|
+
-- A dict_t value becomes a Python dict, {"key": value, ...}, whose values
|
|
89
|
+
-- are written the same way: integer, real, string, boolean, std_ulogic,
|
|
90
|
+
-- integer_vector, real_vector, integer_vector_ptr_t, integer_array_t and
|
|
91
|
+
-- dict_t values. Any other value type is an error. An integer_array_t value
|
|
92
|
+
-- is transferred as described below.
|
|
93
|
+
--
|
|
81
94
|
-- An integer_array_t value is transferred to Python by the Python bridge,
|
|
82
95
|
-- which is only available for NVC, GHDL and Questa, and is referred to by
|
|
83
96
|
-- the expression, which means that it can be used in several calls.
|
|
@@ -93,6 +106,8 @@ package python_pkg is
|
|
|
93
106
|
impure function kwarg_signed(kw : string; value : signed) return arg_t;
|
|
94
107
|
impure function arg(value : integer_array_t) return arg_t;
|
|
95
108
|
impure function kwarg(kw : string; value : integer_array_t) return arg_t;
|
|
109
|
+
impure function arg(value : dict_t) return arg_t;
|
|
110
|
+
impure function kwarg(kw : string; value : dict_t) return arg_t;
|
|
96
111
|
|
|
97
112
|
-- The Python expression calling identifier with the given arguments, for
|
|
98
113
|
-- example to embed a call in a larger exec or eval string:
|
|
@@ -164,6 +179,11 @@ package python_pkg is
|
|
|
164
179
|
-----------------------------------------------------------------------------
|
|
165
180
|
-- Results of eval: boolean, std_ulogic, vectors and arrays
|
|
166
181
|
-----------------------------------------------------------------------------
|
|
182
|
+
-- The result of eval_dict and call_dict is a new dict_t that the caller owns
|
|
183
|
+
-- and deallocates. Its values are integer, real, string, boolean,
|
|
184
|
+
-- integer_vector_ptr_t (a list of int), integer_array_t (a NumPy array, with
|
|
185
|
+
-- the word size of an eval_integer_array result) and dict_t (a nested dict).
|
|
186
|
+
--
|
|
167
187
|
-- std_ulogic_vector and integer_array_t results are only available under
|
|
168
188
|
-- their explicit names, not as eval overloads, which keeps
|
|
169
189
|
-- check_equal(eval("17"), 17) and length(eval("[1, 2]")) unambiguous.
|
|
@@ -187,6 +207,11 @@ package python_pkg is
|
|
|
187
207
|
expr : string; session : python_session_t := default_session
|
|
188
208
|
) return integer_array_t;
|
|
189
209
|
|
|
210
|
+
impure function eval_dict(
|
|
211
|
+
expr : string; session : python_session_t := default_session
|
|
212
|
+
) return dict_t;
|
|
213
|
+
alias eval is eval_dict[string, python_session_t return dict_t];
|
|
214
|
+
|
|
190
215
|
procedure eval_std_ulogic_vector(
|
|
191
216
|
expr : string; result : out std_ulogic_vector; session : python_session_t := default_session
|
|
192
217
|
);
|
|
@@ -247,6 +272,13 @@ package python_pkg is
|
|
|
247
272
|
alias call is call_integer_array[
|
|
248
273
|
string, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, python_session_t return integer_array_t];
|
|
249
274
|
|
|
275
|
+
impure function call_dict(
|
|
276
|
+
identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
|
|
277
|
+
session : python_session_t := default_session
|
|
278
|
+
) return dict_t;
|
|
279
|
+
alias call is call_dict[
|
|
280
|
+
string, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, python_session_t return dict_t];
|
|
281
|
+
|
|
250
282
|
procedure call_std_ulogic_vector(
|
|
251
283
|
identifier : string; result : out std_ulogic_vector;
|
|
252
284
|
arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
|
|
@@ -271,24 +303,36 @@ package python_pkg is
|
|
|
271
303
|
-- is given. The file is executed with __file__ set and its own directory on
|
|
272
304
|
-- sys.path so that it can import its siblings.
|
|
273
305
|
procedure exec_file(file_name : string; session : python_session_t := default_session);
|
|
306
|
+
|
|
307
|
+
-- The absolute base directory, set by vunit_python_bridge.set_relative_file_base()
|
|
308
|
+
-- or VUNIT_PYTHON_BRIDGE_FILE_BASE. Without one, a failure is reported on
|
|
309
|
+
-- python_logger and "" is returned.
|
|
310
|
+
impure function base_path return string;
|
|
274
311
|
end package;
|
|
275
312
|
|
|
276
313
|
package body python_pkg is
|
|
314
|
+
impure function p_file_path(file_name, operation : string; session : python_session_t) return string;
|
|
315
|
+
|
|
277
316
|
-- @formatter:off
|
|
278
317
|
procedure import_module_from_file(
|
|
279
318
|
module_path, as_module_name : string; session : python_session_t := default_session
|
|
280
319
|
) is
|
|
281
320
|
constant spec_name : string := "__" & as_module_name & "_spec";
|
|
321
|
+
constant path : string := p_file_path(
|
|
322
|
+
module_path, "import_module_from_file(""" & module_path & """, """ & as_module_name & """)", session
|
|
323
|
+
);
|
|
282
324
|
constant code : string :=
|
|
283
325
|
"from importlib.util import spec_from_file_location, module_from_spec" & LF &
|
|
284
326
|
"from pathlib import Path" & LF &
|
|
285
327
|
"import sys" & LF &
|
|
286
|
-
spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" &
|
|
328
|
+
spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" & path & "')))" & LF &
|
|
287
329
|
as_module_name & " = module_from_spec(" & spec_name & ")" & LF &
|
|
288
330
|
"sys.modules['" & as_module_name & "'] = " & as_module_name & LF &
|
|
289
331
|
spec_name & ".loader.exec_module(" & as_module_name & ")";
|
|
290
332
|
begin
|
|
291
|
-
|
|
333
|
+
if path /= "" then
|
|
334
|
+
exec(code, session);
|
|
335
|
+
end if;
|
|
292
336
|
end;
|
|
293
337
|
-- @formatter:on
|
|
294
338
|
|
|
@@ -408,6 +452,31 @@ package body python_pkg is
|
|
|
408
452
|
return result;
|
|
409
453
|
end;
|
|
410
454
|
|
|
455
|
+
-- The Python source text of a message, escaped for a double quoted Python
|
|
456
|
+
-- string. Quote, backslash and control characters are escaped; bytes >= 128
|
|
457
|
+
-- are kept so that Python decodes UTF-8 text.
|
|
458
|
+
function p_quoted(message : string) return string is
|
|
459
|
+
variable result : line;
|
|
460
|
+
begin
|
|
461
|
+
swrite(result, """");
|
|
462
|
+
for idx in message'range loop
|
|
463
|
+
case message(idx) is
|
|
464
|
+
when '"' | '\' => swrite(result, "\" & message(idx));
|
|
465
|
+
when LF => swrite(result, "\n");
|
|
466
|
+
when CR => swrite(result, "\r");
|
|
467
|
+
when others =>
|
|
468
|
+
if character'pos(message(idx)) < 32 or message(idx) = DEL then
|
|
469
|
+
swrite(result, "\x" & to_hstring(to_unsigned(character'pos(message(idx)), 8)));
|
|
470
|
+
else
|
|
471
|
+
swrite(result, string'(1 => message(idx)));
|
|
472
|
+
end if;
|
|
473
|
+
end case;
|
|
474
|
+
end loop;
|
|
475
|
+
swrite(result, """");
|
|
476
|
+
|
|
477
|
+
return result.all;
|
|
478
|
+
end;
|
|
479
|
+
|
|
411
480
|
function arg(value : integer) return arg_t is
|
|
412
481
|
begin
|
|
413
482
|
return (p_positional_arg, to_string(value));
|
|
@@ -420,12 +489,12 @@ package body python_pkg is
|
|
|
420
489
|
|
|
421
490
|
function arg(value : string) return arg_t is
|
|
422
491
|
begin
|
|
423
|
-
return (p_positional_arg,
|
|
492
|
+
return (p_positional_arg, p_quoted(value));
|
|
424
493
|
end;
|
|
425
494
|
|
|
426
495
|
function kwarg(kw : string; value : string) return arg_t is
|
|
427
496
|
begin
|
|
428
|
-
return (kw,
|
|
497
|
+
return (kw, p_quoted(value));
|
|
429
498
|
end;
|
|
430
499
|
|
|
431
500
|
function arg(value : integer_vector) return arg_t is
|
|
@@ -466,26 +535,6 @@ package body python_pkg is
|
|
|
466
535
|
end if;
|
|
467
536
|
end;
|
|
468
537
|
|
|
469
|
-
-- The Python source text of a message, escaped for a double quoted Python
|
|
470
|
-
-- string. A quote, a backslash and a line break are the characters that
|
|
471
|
-
-- cannot be written as they are.
|
|
472
|
-
function p_quoted(message : string) return string is
|
|
473
|
-
variable result : line;
|
|
474
|
-
begin
|
|
475
|
-
swrite(result, """");
|
|
476
|
-
for idx in message'range loop
|
|
477
|
-
case message(idx) is
|
|
478
|
-
when '"' | '\' => swrite(result, "\" & message(idx));
|
|
479
|
-
when LF => swrite(result, "\n");
|
|
480
|
-
when CR => swrite(result, "\r");
|
|
481
|
-
when others => swrite(result, string'(1 => message(idx)));
|
|
482
|
-
end case;
|
|
483
|
-
end loop;
|
|
484
|
-
swrite(result, """");
|
|
485
|
-
|
|
486
|
-
return result.all;
|
|
487
|
-
end;
|
|
488
|
-
|
|
489
538
|
-- Report a value that could not be converted and give it a Python source
|
|
490
539
|
-- text raising the same message, so that the call the argument is used in
|
|
491
540
|
-- fails the same way rather than being made without the argument.
|
|
@@ -601,6 +650,45 @@ package body python_pkg is
|
|
|
601
650
|
return "__vunit__.staged(" & integer'image(staged_id) & ")";
|
|
602
651
|
end;
|
|
603
652
|
|
|
653
|
+
impure function p_arg_value(value : dict_t; operation : string) return string;
|
|
654
|
+
|
|
655
|
+
-- The Python source text of the value stored for a key of a dict
|
|
656
|
+
impure function p_dict_item(value : dict_t; key, operation : string) return string is
|
|
657
|
+
begin
|
|
658
|
+
case get_value_type(value, key) is
|
|
659
|
+
when vhdl_integer => return to_string(get_integer(value, key));
|
|
660
|
+
when vhdl_real => return to_string(get_real(value, key), "%.16e");
|
|
661
|
+
when vhdl_string => return p_quoted(get_string(value, key));
|
|
662
|
+
when vhdl_boolean => return arg(get_boolean(value, key)).value;
|
|
663
|
+
when ieee_std_ulogic => return p_arg_value(get_std_ulogic(value, key), operation);
|
|
664
|
+
when vhdl_integer_vector => return to_py_list_str(get_integer_vector(value, key));
|
|
665
|
+
when vhdl_real_vector => return to_py_list_str(get_real_vector(value, key));
|
|
666
|
+
when vunit_integer_vector_ptr_t => return p_arg_value(get_integer_vector_ptr_t_ref(value, key));
|
|
667
|
+
when vunit_integer_array_t => return p_arg_value(get_integer_array_t_ref(value, key), operation);
|
|
668
|
+
when vunit_dict_t => return p_arg_value(get_dict_t_ref(value, key), operation);
|
|
669
|
+
when others =>
|
|
670
|
+
return p_failed_value(
|
|
671
|
+
operation & " cannot convert the " & to_string(get_value_type(value, key)) &
|
|
672
|
+
" value of the dict key """ & key & """"
|
|
673
|
+
);
|
|
674
|
+
end case;
|
|
675
|
+
end;
|
|
676
|
+
|
|
677
|
+
impure function p_arg_value(value : dict_t; operation : string) return string is
|
|
678
|
+
variable result : line;
|
|
679
|
+
begin
|
|
680
|
+
swrite(result, "{");
|
|
681
|
+
for idx in 0 to num_keys(value) - 1 loop
|
|
682
|
+
if idx > 0 then
|
|
683
|
+
swrite(result, ", ");
|
|
684
|
+
end if;
|
|
685
|
+
swrite(result, p_quoted(get_key(value, idx)) & ": " & p_dict_item(value, get_key(value, idx), operation));
|
|
686
|
+
end loop;
|
|
687
|
+
swrite(result, "}");
|
|
688
|
+
|
|
689
|
+
return result.all;
|
|
690
|
+
end;
|
|
691
|
+
|
|
604
692
|
function arg(value : real_vector) return arg_t is
|
|
605
693
|
begin
|
|
606
694
|
return (p_positional_arg, p_arg_value(value));
|
|
@@ -661,6 +749,16 @@ package body python_pkg is
|
|
|
661
749
|
return (kw, p_arg_value(value, "kwarg"));
|
|
662
750
|
end;
|
|
663
751
|
|
|
752
|
+
impure function arg(value : dict_t) return arg_t is
|
|
753
|
+
begin
|
|
754
|
+
return (p_positional_arg, p_arg_value(value, "arg"));
|
|
755
|
+
end;
|
|
756
|
+
|
|
757
|
+
impure function kwarg(kw : string; value : dict_t) return arg_t is
|
|
758
|
+
begin
|
|
759
|
+
return (kw, p_arg_value(value, "kwarg"));
|
|
760
|
+
end;
|
|
761
|
+
|
|
664
762
|
-----------------------------------------------------------------------------
|
|
665
763
|
-- Argument groups
|
|
666
764
|
-----------------------------------------------------------------------------
|
|
@@ -875,6 +973,51 @@ package body python_pkg is
|
|
|
875
973
|
return result;
|
|
876
974
|
end;
|
|
877
975
|
|
|
976
|
+
-- The dict_t of a dict result. Python keeps the converted entries of the dict
|
|
977
|
+
-- under the handle, see dict_kinds, dict_key and dict_item of runtime.py, and
|
|
978
|
+
-- VHDL fetches them one at a time with the eval of the type of each value.
|
|
979
|
+
impure function p_to_dict(handle : integer; session : python_session_t) return dict_t;
|
|
980
|
+
|
|
981
|
+
procedure p_add_dict_item(dict : dict_t; handle, index, kind : integer; session : python_session_t) is
|
|
982
|
+
constant entry : string := "(" & integer'image(handle) & ", " & integer'image(index) & ")";
|
|
983
|
+
constant key : string := eval_string("__vunit__.dict_key" & entry, session);
|
|
984
|
+
constant item : string := "__vunit__.dict_item" & entry;
|
|
985
|
+
variable ptr : integer_vector_ptr_t;
|
|
986
|
+
variable arr : integer_array_t;
|
|
987
|
+
variable nested : dict_t;
|
|
988
|
+
begin
|
|
989
|
+
case kind is
|
|
990
|
+
when p_kind_integer => set_integer(dict, key, eval_integer(item, session));
|
|
991
|
+
when p_kind_real => set_real(dict, key, eval_real(item, session));
|
|
992
|
+
when p_kind_boolean => set_boolean(dict, key, eval_boolean(item, session));
|
|
993
|
+
when p_kind_string => set_string(dict, key, eval_string(item, session));
|
|
994
|
+
when p_kind_integer_vector =>
|
|
995
|
+
ptr := eval_integer_vector_ptr(item, session);
|
|
996
|
+
set_integer_vector_ptr_t_ref(dict, key, ptr);
|
|
997
|
+
when p_kind_integer_array =>
|
|
998
|
+
arr := eval_integer_array(item, session);
|
|
999
|
+
set_integer_array_t_ref(dict, key, arr);
|
|
1000
|
+
when others =>
|
|
1001
|
+
-- A nested dict, whose result is its handle
|
|
1002
|
+
if p_eval(item, p_kind_dict, -1, p_eval_operation(item, session), session) then
|
|
1003
|
+
nested := p_to_dict(p_result_integer, session);
|
|
1004
|
+
set_dict_t_ref(dict, key, nested);
|
|
1005
|
+
end if;
|
|
1006
|
+
end case;
|
|
1007
|
+
end;
|
|
1008
|
+
|
|
1009
|
+
impure function p_to_dict(handle : integer; session : python_session_t) return dict_t is
|
|
1010
|
+
constant kinds : integer_vector := eval_integer_vector(
|
|
1011
|
+
"__vunit__.dict_kinds(" & integer'image(handle) & ")", session
|
|
1012
|
+
);
|
|
1013
|
+
constant result : dict_t := new_dict;
|
|
1014
|
+
begin
|
|
1015
|
+
for idx in kinds'range loop
|
|
1016
|
+
p_add_dict_item(result, handle, idx - kinds'low, kinds(idx), session);
|
|
1017
|
+
end loop;
|
|
1018
|
+
return result;
|
|
1019
|
+
end;
|
|
1020
|
+
|
|
878
1021
|
-----------------------------------------------------------------------------
|
|
879
1022
|
-- exec
|
|
880
1023
|
-----------------------------------------------------------------------------
|
|
@@ -893,20 +1036,51 @@ package body python_pkg is
|
|
|
893
1036
|
return false;
|
|
894
1037
|
end;
|
|
895
1038
|
|
|
896
|
-
-- The file name of exec_file, a relative one taken
|
|
897
|
-
-- testbench file like the file names of the other
|
|
898
|
-
|
|
1039
|
+
-- The file name of exec_file and import_module_from_file, a relative one taken
|
|
1040
|
+
-- from the directory of the testbench file like the file names of the other
|
|
1041
|
+
-- VUnit subprograms
|
|
1042
|
+
impure function p_file_path(file_name, operation : string; session : python_session_t) return string is
|
|
1043
|
+
constant tb_dir : string := tb_path(get_cfg(runner_state));
|
|
899
1044
|
begin
|
|
900
|
-
if
|
|
1045
|
+
if file_name = "" then
|
|
1046
|
+
failure(get_logger(get_id(session)), operation & " failed:" & LF & "Empty file name.");
|
|
1047
|
+
return "";
|
|
1048
|
+
elsif p_is_absolute(file_name) then
|
|
901
1049
|
return file_name;
|
|
1050
|
+
elsif tb_dir = "" then
|
|
1051
|
+
-- Without a testbench path the file would silently be taken from the working directory
|
|
1052
|
+
failure(
|
|
1053
|
+
get_logger(get_id(session)),
|
|
1054
|
+
operation & " failed:" & LF &
|
|
1055
|
+
"A relative file name needs the testbench path set by test_runner_setup. " &
|
|
1056
|
+
"Call it after test_runner_setup, give an absolute path, " &
|
|
1057
|
+
"or join it to base_path with a base directory set in the run script."
|
|
1058
|
+
);
|
|
1059
|
+
return "";
|
|
902
1060
|
end if;
|
|
903
|
-
return join(
|
|
1061
|
+
return join(tb_dir, file_name);
|
|
1062
|
+
end;
|
|
1063
|
+
|
|
1064
|
+
impure function base_path return string is
|
|
1065
|
+
begin
|
|
1066
|
+
if p_relative_file_base = "" then
|
|
1067
|
+
failure(
|
|
1068
|
+
python_logger,
|
|
1069
|
+
"base_path failed: no base directory is set. " &
|
|
1070
|
+
"Call vunit_python_bridge.set_relative_file_base() in the run script before add_package, " &
|
|
1071
|
+
"or set the environment variable VUNIT_PYTHON_BRIDGE_FILE_BASE."
|
|
1072
|
+
);
|
|
1073
|
+
end if;
|
|
1074
|
+
return p_relative_file_base;
|
|
904
1075
|
end;
|
|
905
1076
|
|
|
906
1077
|
procedure exec_file(file_name : string; session : python_session_t := default_session) is
|
|
1078
|
+
constant path : string := p_file_path(file_name, p_exec_file_operation(file_name, session), session);
|
|
907
1079
|
variable ok : boolean;
|
|
908
1080
|
begin
|
|
909
|
-
|
|
1081
|
+
if path /= "" then
|
|
1082
|
+
ok := p_exec_file(path, session);
|
|
1083
|
+
end if;
|
|
910
1084
|
end;
|
|
911
1085
|
|
|
912
1086
|
-----------------------------------------------------------------------------
|
|
@@ -952,6 +1126,16 @@ package body python_pkg is
|
|
|
952
1126
|
return null_integer_array;
|
|
953
1127
|
end;
|
|
954
1128
|
|
|
1129
|
+
impure function eval_dict(
|
|
1130
|
+
expr : string; session : python_session_t := default_session
|
|
1131
|
+
) return dict_t is
|
|
1132
|
+
begin
|
|
1133
|
+
if p_eval(expr, p_kind_dict, -1, p_eval_operation(expr, session), session) then
|
|
1134
|
+
return p_to_dict(p_result_integer, session);
|
|
1135
|
+
end if;
|
|
1136
|
+
return new_dict;
|
|
1137
|
+
end;
|
|
1138
|
+
|
|
955
1139
|
procedure eval_std_ulogic_vector(
|
|
956
1140
|
expr : string; result : out std_ulogic_vector; session : python_session_t := default_session
|
|
957
1141
|
) is
|
|
@@ -1052,6 +1236,16 @@ package body python_pkg is
|
|
|
1052
1236
|
);
|
|
1053
1237
|
end;
|
|
1054
1238
|
|
|
1239
|
+
impure function call_dict(
|
|
1240
|
+
identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
|
|
1241
|
+
session : python_session_t := default_session
|
|
1242
|
+
) return dict_t is
|
|
1243
|
+
begin
|
|
1244
|
+
return eval_dict(
|
|
1245
|
+
to_call_str(identifier, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), session
|
|
1246
|
+
);
|
|
1247
|
+
end;
|
|
1248
|
+
|
|
1055
1249
|
procedure call_std_ulogic_vector(
|
|
1056
1250
|
identifier : string; result : out std_ulogic_vector;
|
|
1057
1251
|
arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
|