vunit-python-bridge 0.1.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.
Files changed (34) hide show
  1. vunit_python_bridge/__init__.py +92 -0
  2. vunit_python_bridge/bin/vunit_python_bridge-cp310-win_amd64.dll +0 -0
  3. vunit_python_bridge/bin/vunit_python_bridge-cp311-win_amd64.dll +0 -0
  4. vunit_python_bridge/bin/vunit_python_bridge-cp312-win_amd64.dll +0 -0
  5. vunit_python_bridge/bin/vunit_python_bridge-cp313-win_amd64.dll +0 -0
  6. vunit_python_bridge/bin/vunit_python_bridge-cp314-win_amd64.dll +0 -0
  7. vunit_python_bridge/bridge.py +171 -0
  8. vunit_python_bridge/foreign_application.py +90 -0
  9. vunit_python_bridge/hdl/src/python_bridge_pkg.vhd.in +91 -0
  10. vunit_python_bridge/hdl/src/python_context.vhd +13 -0
  11. vunit_python_bridge/hdl/src/python_ffi_pkg_bridge.vhd +507 -0
  12. vunit_python_bridge/hdl/src/python_pkg.vhd +1087 -0
  13. vunit_python_bridge/hdl/src/python_pkg_vhpi.vhd +270 -0
  14. vunit_python_bridge/hdl/tools/generate_python_pkg.py +386 -0
  15. vunit_python_bridge/hdl/tools/python_pkg.vhd.in +765 -0
  16. vunit_python_bridge/native/arguments.c +191 -0
  17. vunit_python_bridge/native/bridge.h +117 -0
  18. vunit_python_bridge/native/config.c +201 -0
  19. vunit_python_bridge/native/error.c +120 -0
  20. vunit_python_bridge/native/fli.c +98 -0
  21. vunit_python_bridge/native/interpreter.c +271 -0
  22. vunit_python_bridge/native/operations.c +239 -0
  23. vunit_python_bridge/native/vhpi/python_pkg.c +125 -0
  24. vunit_python_bridge/native/vhpi/python_pkg.h +31 -0
  25. vunit_python_bridge/native/vhpi/python_pkg_vhpi.c +366 -0
  26. vunit_python_bridge/native_library.py +455 -0
  27. vunit_python_bridge/runtime.py +645 -0
  28. vunit_python_bridge/simulator_hooks.py +141 -0
  29. vunit_python_bridge/vunit_pkg.toml +17 -0
  30. vunit_python_bridge-0.1.0.dist-info/METADATA +116 -0
  31. vunit_python_bridge-0.1.0.dist-info/RECORD +34 -0
  32. vunit_python_bridge-0.1.0.dist-info/WHEEL +5 -0
  33. vunit_python_bridge-0.1.0.dist-info/licenses/LICENSE +373 -0
  34. vunit_python_bridge-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,270 @@
1
+ -- This package provides a dictionary types and operations
2
+ --
3
+ -- This Source Code Form is subject to the terms of the Mozilla Public
4
+ -- License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
+ -- You can obtain one at http://mozilla.org/MPL/2.0/.
6
+ --
7
+ -- Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
8
+
9
+ use std.textio.all;
10
+
11
+ library vunit_lib;
12
+ use vunit_lib.id_pkg.all;
13
+ use vunit_lib.integer_array_pkg.all;
14
+ use vunit_lib.integer_vector_ptr_pkg.all;
15
+ use vunit_lib.logger_pkg.all;
16
+
17
+ package python_ffi_pkg is
18
+ -- The identity of the Python interface, the identity of python_logger and
19
+ -- the parent of the identities of the sessions created from a name
20
+ constant p_python_id : id_t := get_id("python_bridge:python");
21
+
22
+ -- A session is a Python namespace with an identity, created from a name
23
+ -- under the identity of the Python interface or from an identity of its
24
+ -- own. Only the default session, the __main__ namespace, is supported by
25
+ -- this foreign language interface.
26
+ type python_session_t is record
27
+ p_data : integer_vector_ptr_t;
28
+ end record;
29
+
30
+ impure function new_session(name : string) return python_session_t;
31
+ impure function new_session(id : id_t) return python_session_t;
32
+
33
+ -- The identity of a session, which is what two sessions are compared by
34
+ impure function get_id(session : python_session_t) return id_t;
35
+
36
+ -- The name of the identity of a session, without hierarchy
37
+ impure function name(session : python_session_t) return string;
38
+
39
+ -- The session the operations are performed in when no other one is given.
40
+ -- It is created the way new_session does, which cannot be called here since
41
+ -- its body is not elaborated until the package body is.
42
+ constant default_session : python_session_t :=
43
+ (p_data => new_integer_vector_ptr(1, value => to_integer(get_id("default", parent => p_python_id))));
44
+
45
+ procedure python_setup;
46
+ -- TODO: Looks like Riviera-PRO requires the path to the shared library to be fixed at compile time
47
+ -- and that may become a bit limited. VHDL standard allow for expressions.
48
+ attribute foreign of python_setup : procedure is "VHPI libraries/python python_setup";
49
+ procedure python_cleanup;
50
+ attribute foreign of python_cleanup : procedure is "VHPI libraries/python python_cleanup";
51
+
52
+ function p_eval_integer(expr : string) return integer;
53
+ attribute foreign of p_eval_integer : function is "VHPI libraries/python eval_integer";
54
+ impure function eval_integer(expr : string; session : python_session_t := default_session) return integer;
55
+ alias eval is eval_integer[string, python_session_t return integer];
56
+
57
+ function p_eval_real(expr : string) return real;
58
+ attribute foreign of p_eval_real : function is "VHPI libraries/python eval_real";
59
+ impure function eval_real(expr : string; session : python_session_t := default_session) return real;
60
+ alias eval is eval_real[string, python_session_t return real];
61
+
62
+ function p_eval_integer_vector(expr : string) return integer_vector;
63
+ attribute foreign of p_eval_integer_vector : function is "VHPI libraries/python eval_integer_vector";
64
+ impure function eval_integer_vector(
65
+ expr : string; session : python_session_t := default_session
66
+ ) return integer_vector;
67
+ alias eval is eval_integer_vector[string, python_session_t return integer_vector];
68
+
69
+ function p_eval_real_vector(expr : string) return real_vector;
70
+ attribute foreign of p_eval_real_vector : function is "VHPI libraries/python eval_real_vector";
71
+ impure function eval_real_vector(
72
+ expr : string; session : python_session_t := default_session
73
+ ) return real_vector;
74
+ alias eval is eval_real_vector[string, python_session_t return real_vector];
75
+
76
+ function p_eval_string(expr : string) return string;
77
+ attribute foreign of p_eval_string : function is "VHPI libraries/python eval_string";
78
+ impure function eval_string(expr : string; session : python_session_t := default_session) return string;
79
+ alias eval is eval_string[string, python_session_t return string];
80
+
81
+ procedure p_exec(code : string);
82
+ attribute foreign of p_exec : procedure is "VHPI libraries/python exec";
83
+ procedure exec(code : string; session : python_session_t := default_session);
84
+
85
+ -----------------------------------------------------------------------------
86
+ -- Private, the primitives the bridge operations of python_pkg are built on
87
+ -----------------------------------------------------------------------------
88
+ -- Some operations of python_pkg are implemented by the VUnit Python
89
+ -- bridge, which is only available for NVC, GHDL and Questa. The primitives
90
+ -- below are declared so that python_pkg has one body for every simulator,
91
+ -- but they report a failure when they are used.
92
+
93
+ -- Logger of the Python interface, the parent of the session loggers
94
+ constant python_logger : logger_t := get_logger(p_python_id);
95
+
96
+ -- Result kinds, must match src/vunit_python_bridge/runtime.py
97
+ constant p_kind_integer : integer := 0;
98
+ constant p_kind_real : integer := 1;
99
+ constant p_kind_boolean : integer := 2;
100
+ constant p_kind_string : integer := 3;
101
+ constant p_kind_std_ulogic : integer := 4;
102
+ constant p_kind_std_ulogic_vector : integer := 5;
103
+ constant p_kind_signed : integer := 6;
104
+ constant p_kind_unsigned : integer := 7;
105
+ constant p_kind_integer_array : integer := 8;
106
+ constant p_kind_integer_vector : integer := 9;
107
+ constant p_kind_real_vector : integer := 10;
108
+
109
+ -- Name of the operation, used in the error messages
110
+ impure function p_eval_operation(expr : string; session : python_session_t := default_session) return string;
111
+
112
+ -- Execute the Python file with the given name
113
+ impure function p_exec_file(
114
+ file_name : string; session : python_session_t := default_session
115
+ ) return boolean;
116
+
117
+ -- Evaluate expr and convert the value to the VHDL type given by kind. width
118
+ -- is the length of a std_ulogic_vector, signed or unsigned result, -1 when
119
+ -- it is not known.
120
+ impure function p_eval(
121
+ expr : string;
122
+ kind : integer;
123
+ width : integer;
124
+ operation : string;
125
+ session : python_session_t := default_session
126
+ ) return boolean;
127
+
128
+ -- The result of the last evaluation
129
+ impure function p_result_integer return integer;
130
+ impure function p_result_string return string;
131
+ impure function p_result_integer_array return integer_array_t;
132
+
133
+ -- Transfer an integer_array_t to Python and return the id it is staged
134
+ -- under, -1 on failure.
135
+ impure function p_stage_array(arr : integer_array_t; operation : string) return integer;
136
+ end package;
137
+
138
+ package body python_ffi_pkg is
139
+ impure function new_session(name : string) return python_session_t is
140
+ begin
141
+ return new_session(get_id(name, parent => p_python_id));
142
+ end;
143
+
144
+ impure function new_session(id : id_t) return python_session_t is
145
+ begin
146
+ return (p_data => new_integer_vector_ptr(1, value => to_integer(id)));
147
+ end;
148
+
149
+ impure function get_id(session : python_session_t) return id_t is
150
+ begin
151
+ return to_id(get(session.p_data, 0));
152
+ end;
153
+
154
+ impure function name(session : python_session_t) return string is
155
+ begin
156
+ return name(get_id(session));
157
+ end;
158
+
159
+ impure function p_is_default(session : python_session_t) return boolean is
160
+ begin
161
+ return get_id(session) = get_id(default_session);
162
+ end;
163
+
164
+ procedure p_check_session(session : python_session_t) is
165
+ begin
166
+ if not p_is_default(session) then
167
+ report "Python sessions are only supported with NVC, GHDL and Questa" severity failure;
168
+ end if;
169
+ end;
170
+
171
+ impure function eval_integer(expr : string; session : python_session_t := default_session) return integer is
172
+ begin
173
+ p_check_session(session);
174
+ return p_eval_integer(expr);
175
+ end;
176
+
177
+ impure function eval_real(expr : string; session : python_session_t := default_session) return real is
178
+ begin
179
+ p_check_session(session);
180
+ return p_eval_real(expr);
181
+ end;
182
+
183
+ impure function eval_integer_vector(
184
+ expr : string; session : python_session_t := default_session
185
+ ) return integer_vector is
186
+ begin
187
+ p_check_session(session);
188
+ return p_eval_integer_vector(expr);
189
+ end;
190
+
191
+ impure function eval_real_vector(
192
+ expr : string; session : python_session_t := default_session
193
+ ) return real_vector is
194
+ begin
195
+ p_check_session(session);
196
+ return p_eval_real_vector(expr);
197
+ end;
198
+
199
+ impure function eval_string(expr : string; session : python_session_t := default_session) return string is
200
+ begin
201
+ p_check_session(session);
202
+ return p_eval_string(expr);
203
+ end;
204
+
205
+ procedure exec(code : string; session : python_session_t := default_session) is
206
+ begin
207
+ p_check_session(session);
208
+ p_exec(code);
209
+ end;
210
+
211
+ -----------------------------------------------------------------------------
212
+ -- Private, the primitives the bridge operations of python_pkg are built on
213
+ -----------------------------------------------------------------------------
214
+ procedure p_unsupported(name : string) is
215
+ begin
216
+ failure(python_logger, name & " requires NVC, GHDL or Questa");
217
+ end;
218
+
219
+ impure function p_eval_operation(expr : string; session : python_session_t := default_session) return string is
220
+ begin
221
+ if p_is_default(session) then
222
+ return "eval(""" & expr & """)";
223
+ end if;
224
+ return "eval(""" & expr & """, session => """ & name(session) & """)";
225
+ end;
226
+
227
+ impure function p_exec_file(
228
+ file_name : string; session : python_session_t := default_session
229
+ ) return boolean is
230
+ begin
231
+ p_unsupported("exec_file");
232
+ return false;
233
+ end;
234
+
235
+ impure function p_eval(
236
+ expr : string;
237
+ kind : integer;
238
+ width : integer;
239
+ operation : string;
240
+ session : python_session_t := default_session
241
+ ) return boolean is
242
+ begin
243
+ p_unsupported(operation);
244
+ return false;
245
+ end;
246
+
247
+ impure function p_result_integer return integer is
248
+ begin
249
+ p_unsupported("p_result_integer");
250
+ return integer'low;
251
+ end;
252
+
253
+ impure function p_result_string return string is
254
+ begin
255
+ p_unsupported("p_result_string");
256
+ return "";
257
+ end;
258
+
259
+ impure function p_result_integer_array return integer_array_t is
260
+ begin
261
+ p_unsupported("p_result_integer_array");
262
+ return null_integer_array;
263
+ end;
264
+
265
+ impure function p_stage_array(arr : integer_array_t; operation : string) return integer is
266
+ begin
267
+ p_unsupported(operation);
268
+ return -1;
269
+ end;
270
+ end package body;
@@ -0,0 +1,386 @@
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
+ Generate python_pkg.vhd from python_pkg.vhd.in in this directory.
9
+
10
+ The template holds the parts of python_pkg that are written by hand. The
11
+ generated parts are the argument value types of call, the result types of eval
12
+ and call and the execution of Python files. They are mostly one overload per
13
+ type, which is why they are generated. The operations that are implemented by
14
+ the Python bridge (NVC, GHDL and Questa) are built on the p_ prefixed
15
+ primitives of python_ffi_pkg, which the VHPI variant of that package
16
+ implements by reporting a failure.
17
+ """
18
+
19
+ from pathlib import Path
20
+ from string import Template
21
+
22
+ SRC_PATH = Path(__file__).parent.parent / "src"
23
+ TEMPLATE_PATH = Path(__file__).parent
24
+
25
+ # Like the operations of python_pkg, every operation takes the session it is
26
+ # performed in as its last parameter
27
+ SESSION = "session : python_session_t := default_session"
28
+
29
+ # The 10 arguments of call
30
+ ARGS = [f"arg{index}" for index in range(1, 11)]
31
+ ARG_PARAMETERS = f"{', '.join(ARGS)} : arg_t := null_arg"
32
+ ARG_ACTUALS = ", ".join(ARGS)
33
+ ARG_SIGNATURE = ", ".join(["string"] + ["arg_t"] * len(ARGS) + ["python_session_t"])
34
+
35
+ # Value types of arg and kwarg that are generated rather than declared in the
36
+ # template.
37
+ #
38
+ # vhdl: the VHDL type
39
+ # impure: true when the functions must be impure
40
+ # fails: true when the conversion can fail, which makes p_arg_value take the
41
+ # name of the operation to report the value it cannot convert
42
+ # suffix: appended to the arg and kwarg names, empty for an overload
43
+ #
44
+ # The integer_array_t value is transferred by the Python bridge, which makes it
45
+ # available for NVC, GHDL and Questa only. The other values are built from the
46
+ # Python source text of the value alone and work on any simulator.
47
+ #
48
+ # The unsigned and signed values are passed to typed names, arg_unsigned and
49
+ # arg_signed, on purpose: plain overloads would make a string literal
50
+ # argument, arg("Hello"), ambiguous since a string literal belongs to every
51
+ # character array type. std_ulogic needs no such name, being a scalar, and
52
+ # std_ulogic_vector is deliberately not among the types at all. Such values
53
+ # are passed as arg(to_string(slv)) and arg_unsigned(unsigned(slv)).
54
+ ARG_VALUES = [
55
+ dict(vhdl="real_vector", impure=False, fails=False, suffix=""),
56
+ dict(vhdl="integer_vector_ptr_t", impure=True, fails=False, suffix=""),
57
+ dict(vhdl="std_ulogic", impure=True, fails=True, suffix=""),
58
+ dict(vhdl="unsigned", impure=True, fails=True, suffix="_unsigned"),
59
+ dict(vhdl="signed", impure=True, fails=True, suffix="_signed"),
60
+ dict(vhdl="integer_array_t", impure=True, fails=True, suffix=""),
61
+ ]
62
+
63
+ # Result types of eval and call.
64
+ #
65
+ # name: suffix of eval_<name> and call_<name>
66
+ # vhdl: the VHDL type
67
+ # kind: result kind passed to the bridge, see src/vunit_python_bridge/runtime.py
68
+ # value: expression giving the result of a successful evaluation
69
+ # default: value returned after a failure
70
+ # hand_written_eval/hand_written_call: true when the eval/call function is
71
+ # declared in the template rather than generated. value and default
72
+ # are only needed when the eval function is generated.
73
+ RESULTS = [
74
+ dict(
75
+ name="integer",
76
+ vhdl="integer",
77
+ kind="p_kind_integer",
78
+ hand_written_eval=True,
79
+ hand_written_call=True,
80
+ ),
81
+ dict(
82
+ name="real",
83
+ vhdl="real",
84
+ kind="p_kind_real",
85
+ hand_written_eval=True,
86
+ hand_written_call=True,
87
+ ),
88
+ dict(
89
+ name="integer_vector",
90
+ vhdl="integer_vector",
91
+ kind="p_kind_integer_vector",
92
+ hand_written_eval=True,
93
+ hand_written_call=True,
94
+ ),
95
+ dict(
96
+ name="real_vector",
97
+ vhdl="real_vector",
98
+ kind="p_kind_real_vector",
99
+ hand_written_eval=True,
100
+ hand_written_call=False,
101
+ ),
102
+ dict(
103
+ name="string",
104
+ vhdl="string",
105
+ kind="p_kind_string",
106
+ hand_written_eval=True,
107
+ hand_written_call=False,
108
+ ),
109
+ dict(
110
+ name="integer_vector_ptr",
111
+ vhdl="integer_vector_ptr_t",
112
+ kind="p_kind_integer_vector",
113
+ hand_written_eval=True,
114
+ hand_written_call=False,
115
+ ),
116
+ dict(
117
+ name="boolean",
118
+ vhdl="boolean",
119
+ kind="p_kind_boolean",
120
+ value="p_result_integer /= 0",
121
+ default="false",
122
+ hand_written_eval=False,
123
+ hand_written_call=False,
124
+ ),
125
+ dict(
126
+ name="std_ulogic",
127
+ vhdl="std_ulogic",
128
+ kind="p_kind_std_ulogic",
129
+ value="p_to_std_ulogic(p_result_string(1))",
130
+ default="'U'",
131
+ hand_written_eval=False,
132
+ hand_written_call=False,
133
+ ),
134
+ dict(
135
+ name="std_ulogic_vector",
136
+ vhdl="std_ulogic_vector",
137
+ kind="p_kind_std_ulogic_vector",
138
+ value="p_to_std_ulogic_vector(p_result_string)",
139
+ default='""',
140
+ hand_written_eval=False,
141
+ hand_written_call=False,
142
+ # No eval/call alias: check_equal(eval("17"), 17) would otherwise be ambiguous
143
+ # with check_equal(std_ulogic_vector, natural). Use the explicit names.
144
+ alias_eval=False,
145
+ alias_call=False,
146
+ ),
147
+ dict(
148
+ name="integer_array",
149
+ vhdl="integer_array_t",
150
+ kind="p_kind_integer_array",
151
+ value="p_result_integer_array",
152
+ default="null_integer_array",
153
+ hand_written_eval=False,
154
+ hand_written_call=False,
155
+ # No eval alias: length(eval(...)) would otherwise be ambiguous between
156
+ # the integer_array_t and integer_vector_ptr_t results. Use eval_integer_array.
157
+ alias_eval=False,
158
+ ),
159
+ ]
160
+
161
+ # Result types whose width is given by the actual of the result parameter
162
+ PROCEDURE_RESULTS = [
163
+ dict(
164
+ name="std_ulogic_vector",
165
+ vhdl="std_ulogic_vector",
166
+ kind="p_kind_std_ulogic_vector",
167
+ value="p_to_std_ulogic_vector(p_result_string)",
168
+ ),
169
+ dict(
170
+ name="signed",
171
+ vhdl="signed",
172
+ kind="p_kind_signed",
173
+ value="signed(p_to_std_ulogic_vector(p_result_string))",
174
+ ),
175
+ dict(
176
+ name="unsigned",
177
+ vhdl="unsigned",
178
+ kind="p_kind_unsigned",
179
+ value="unsigned(p_to_std_ulogic_vector(p_result_string))",
180
+ ),
181
+ ]
182
+
183
+
184
+ def call_name(result):
185
+ """
186
+ Name of the call function of a result type. The integer one is named
187
+ call_integer_w_arg by python_pkg.
188
+ """
189
+ return "call_integer_w_arg" if result["name"] == "integer" else f"call_{result['name']}"
190
+
191
+
192
+ # -------------------------------------------------------------------------
193
+ # arg and kwarg
194
+ # -------------------------------------------------------------------------
195
+ def arg_declarations():
196
+ """
197
+ Declarations of the generated arg and kwarg subprograms.
198
+ """
199
+ lines = []
200
+ for value in ARG_VALUES:
201
+ purity = "impure " if value["impure"] else ""
202
+ suffix = value["suffix"]
203
+ lines.append(f" {purity}function arg{suffix}(value : {value['vhdl']}) return arg_t;")
204
+ lines.append(f" {purity}function kwarg{suffix}(kw : string; value : {value['vhdl']}) return arg_t;")
205
+ return "\n".join(lines)
206
+
207
+
208
+ def arg_functions():
209
+ """
210
+ Bodies of the generated arg and kwarg subprograms. p_arg_value reports a
211
+ value it cannot convert and gives it a Python source text raising the same
212
+ message, so that the call the argument is used in fails as well.
213
+ """
214
+ parts = []
215
+ for value in ARG_VALUES:
216
+ purity = "impure " if value["impure"] else ""
217
+ value_type = value["vhdl"]
218
+ suffix = value["suffix"]
219
+ for name, parameters, target in [
220
+ (f"arg{suffix}", f"value : {value_type}", "p_positional_arg"),
221
+ (f"kwarg{suffix}", f"kw : string; value : {value_type}", "kw"),
222
+ ]:
223
+ arg_value = f'p_arg_value(value, "{name}")' if value["fails"] else "p_arg_value(value)"
224
+ parts.append(
225
+ f"""\
226
+ {purity}function {name}({parameters}) return arg_t is
227
+ begin
228
+ return ({target}, {arg_value});
229
+ end;
230
+ """
231
+ )
232
+ return "\n".join(parts).rstrip("\n")
233
+
234
+
235
+ # -------------------------------------------------------------------------
236
+ # eval
237
+ # -------------------------------------------------------------------------
238
+ def eval_declarations():
239
+ """
240
+ Declarations of the generated eval functions and of the eval procedures.
241
+ """
242
+ lines = []
243
+ for result in RESULTS:
244
+ if result["hand_written_eval"]:
245
+ continue
246
+ lines.append(f" impure function eval_{result['name']}(")
247
+ lines.append(f" expr : string; {SESSION}")
248
+ lines.append(f" ) return {result['vhdl']};")
249
+ if result.get("alias_eval", True):
250
+ lines.append(f" alias eval is eval_{result['name']}[string, python_session_t return {result['vhdl']}];")
251
+ lines.append("")
252
+ for result in PROCEDURE_RESULTS:
253
+ lines.append(f" procedure eval_{result['name']}(")
254
+ lines.append(f" expr : string; result : out {result['vhdl']}; {SESSION}")
255
+ lines.append(" );")
256
+ return "\n".join(lines)
257
+
258
+
259
+ def eval_subprograms():
260
+ """
261
+ Bodies of the generated eval functions and of the eval procedures.
262
+ """
263
+ parts = []
264
+ for result in RESULTS:
265
+ if result["hand_written_eval"]:
266
+ continue
267
+ parts.append(
268
+ f"""\
269
+ impure function eval_{result['name']}(
270
+ expr : string; {SESSION}
271
+ ) return {result['vhdl']} is
272
+ begin
273
+ if p_eval(expr, {result['kind']}, -1, p_eval_operation(expr, session), session) then
274
+ return {result['value']};
275
+ end if;
276
+ return {result['default']};
277
+ end;
278
+ """
279
+ )
280
+ for result in PROCEDURE_RESULTS:
281
+ parts.append(
282
+ f"""\
283
+ procedure eval_{result['name']}(
284
+ expr : string; result : out {result['vhdl']}; {SESSION}
285
+ ) is
286
+ begin
287
+ if p_eval(expr, {result['kind']}, result'length, p_eval_operation(expr, session), session) then
288
+ result := {result['value']};
289
+ end if;
290
+ end;
291
+ """
292
+ )
293
+ return "\n".join(parts).rstrip("\n")
294
+
295
+
296
+ # -------------------------------------------------------------------------
297
+ # call
298
+ # -------------------------------------------------------------------------
299
+ def call_declarations():
300
+ """
301
+ Declarations of the generated call functions and of the call procedures.
302
+ """
303
+ lines = []
304
+ for result in RESULTS:
305
+ if result["hand_written_call"]:
306
+ continue
307
+ name = call_name(result)
308
+ lines.append(f" impure function {name}(")
309
+ lines.append(f" identifier : string; {ARG_PARAMETERS};")
310
+ lines.append(f" {SESSION}")
311
+ lines.append(f" ) return {result['vhdl']};")
312
+ if result.get("alias_call", True):
313
+ lines.append(f" alias call is {name}[")
314
+ lines.append(f" {ARG_SIGNATURE} return {result['vhdl']}];")
315
+ lines.append("")
316
+ for result in PROCEDURE_RESULTS:
317
+ lines.append(f" procedure call_{result['name']}(")
318
+ lines.append(f" identifier : string; result : out {result['vhdl']};")
319
+ lines.append(f" {ARG_PARAMETERS};")
320
+ lines.append(f" {SESSION}")
321
+ lines.append(" );")
322
+ return "\n".join(lines)
323
+
324
+
325
+ def call_subprograms():
326
+ """
327
+ Bodies of the generated call functions and of the call procedures. A call
328
+ is the evaluation of the Python expression calling the identifier with the
329
+ given arguments.
330
+ """
331
+ parts = []
332
+ for result in RESULTS:
333
+ if result["hand_written_call"]:
334
+ continue
335
+ parts.append(
336
+ f"""\
337
+ impure function {call_name(result)}(
338
+ identifier : string; {ARG_PARAMETERS};
339
+ {SESSION}
340
+ ) return {result['vhdl']} is
341
+ begin
342
+ return eval_{result['name']}(
343
+ to_call_str(identifier, {ARG_ACTUALS}), session
344
+ );
345
+ end;
346
+ """
347
+ )
348
+ for result in PROCEDURE_RESULTS:
349
+ parts.append(
350
+ f"""\
351
+ procedure call_{result['name']}(
352
+ identifier : string; result : out {result['vhdl']};
353
+ {ARG_PARAMETERS};
354
+ {SESSION}
355
+ ) is
356
+ begin
357
+ eval_{result['name']}(
358
+ to_call_str(identifier, {ARG_ACTUALS}), result, session
359
+ );
360
+ end;
361
+ """
362
+ )
363
+ return "\n".join(parts).rstrip("\n")
364
+
365
+
366
+ def generate_package():
367
+ """
368
+ Generate python_pkg from the template and the generated declarations.
369
+ """
370
+ template = (TEMPLATE_PATH / "python_pkg.vhd.in").read_text(encoding="utf-8")
371
+ return Template(template).substitute(
372
+ arg_declarations=arg_declarations(),
373
+ arg_functions=arg_functions(),
374
+ eval_declarations=eval_declarations(),
375
+ call_declarations=call_declarations(),
376
+ eval_subprograms=eval_subprograms(),
377
+ call_subprograms=call_subprograms(),
378
+ )
379
+
380
+
381
+ def main():
382
+ (SRC_PATH / "python_pkg.vhd").write_text(generate_package(), encoding="utf-8", newline="\n")
383
+
384
+
385
+ if __name__ == "__main__":
386
+ main()