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,765 @@
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 work.python_ffi_pkg.all;
10
+ library vunit_lib;
11
+ use vunit_lib.path.all;
12
+ use vunit_lib.run_pkg.all;
13
+ use vunit_lib.runner_pkg.all;
14
+ use vunit_lib.integer_vector_ptr_pkg.all;
15
+ use vunit_lib.string_ops.all;
16
+
17
+ use std.textio.all;
18
+
19
+ ------------------------------------------------------------------------------
20
+ -- This file is generated from tools/python_pkg.vhd.in by
21
+ -- src/vunit_python_bridge/hdl/tools/generate_python_pkg.py. Do not edit.
22
+ library ieee;
23
+ use ieee.std_logic_1164.all;
24
+ use ieee.numeric_std.all;
25
+
26
+ use vunit_lib.integer_array_pkg.all;
27
+ use vunit_lib.logger_pkg.all;
28
+ ------------------------------------------------------------------------------
29
+
30
+ package python_pkg is
31
+ procedure import_module_from_file(
32
+ module_path, as_module_name : string; session : python_session_t := default_session
33
+ );
34
+ procedure import_run_script(module_name : string := ""; session : python_session_t := default_session);
35
+
36
+ function to_py_list_str(vec : integer_vector) return string;
37
+ impure function to_py_list_str(vec : integer_vector_ptr_t) return string;
38
+ function to_py_list_str(vec : real_vector) return string;
39
+
40
+ function "+"(l, r : string) return string;
41
+
42
+ impure function eval_integer_vector_ptr(
43
+ expr : string; session : python_session_t := default_session
44
+ ) return integer_vector_ptr_t;
45
+ alias eval is eval_integer_vector_ptr[string, python_session_t return integer_vector_ptr_t];
46
+
47
+ type arg_t is record
48
+ name : string;
49
+ value : string;
50
+ end record;
51
+ constant p_positional_arg : string := ".";
52
+ constant p_ignore_arg : string := "-";
53
+ constant p_group_arg : string := "*";
54
+ constant null_arg : arg_t := (name => p_ignore_arg, value => "");
55
+
56
+ function arg(value : integer) return arg_t;
57
+ function kwarg(kw : string; value : integer) return arg_t;
58
+ function arg(value : string) return arg_t;
59
+ function kwarg(kw : string; value : string) return arg_t;
60
+ function arg(value : integer_vector) return arg_t;
61
+ function kwarg(kw : string; value : integer_vector) return arg_t;
62
+ function arg(value : real) return arg_t;
63
+ function kwarg(kw : string; value : real) return arg_t;
64
+ function arg(value : boolean) return arg_t;
65
+ function kwarg(kw : string; value : boolean) return arg_t;
66
+
67
+ -- real_vector and integer_vector_ptr_t values become Python lists, a
68
+ -- std_ulogic value becomes True or False, and an unsigned or signed value of
69
+ -- any width becomes a Python integer written as a hexadecimal literal, which
70
+ -- is therefore not limited to the range of a VHDL integer.
71
+ --
72
+ -- An aggregate or a literal needs a qualified expression to select the
73
+ -- overload, for example arg(real_vector'(1.0, 2.0)). A std_ulogic_vector is
74
+ -- passed as a string, arg(to_string(slv)), or as a number,
75
+ -- arg_unsigned(unsigned(slv)). The unsigned and signed values have names of
76
+ -- their own since an overload would make a string literal argument,
77
+ -- arg("Hello"), ambiguous.
78
+ --
79
+ -- H and L are read as 1 and 0. Any other metavalue is an error.
80
+ --
81
+ -- An integer_array_t value is transferred to Python by the Python bridge,
82
+ -- which is only available for NVC, GHDL and Questa, and is referred to by
83
+ -- the expression, which means that it can be used in several calls.
84
+ $arg_declarations
85
+
86
+ -- The Python expression calling identifier with the given arguments, for
87
+ -- example to embed a call in a larger exec or eval string:
88
+ --
89
+ -- exec("gcd = " & to_call_str("gcd", arg(35), arg(77)));
90
+ impure function to_call_str(
91
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg
92
+ ) return string;
93
+
94
+ impure function call_integer_w_arg(
95
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
96
+ session : python_session_t := default_session
97
+ ) return integer;
98
+ alias call is call_integer_w_arg[
99
+ 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];
100
+
101
+ procedure call(
102
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
103
+ session : python_session_t := default_session
104
+ );
105
+
106
+ impure function call_integer_vector(
107
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
108
+ session : python_session_t := default_session
109
+ ) return integer_vector;
110
+ alias call is call_integer_vector[
111
+ string, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, arg_t, python_session_t
112
+ return integer_vector];
113
+
114
+ impure function call_real(
115
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
116
+ session : python_session_t := default_session
117
+ ) return real;
118
+ alias call is call_real[
119
+ 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 real];
120
+
121
+ -----------------------------------------------------------------------------
122
+ -- Argument groups
123
+ -----------------------------------------------------------------------------
124
+ -- Arguments combined with & become a single argument, so that a call can
125
+ -- pass more than the 10 arguments it takes and can build its arguments in
126
+ -- steps:
127
+ --
128
+ -- call("f", arg(x), kwarg("a", 1) & kwarg("b", 2));
129
+ --
130
+ -- calls f(x, **dict(a=1, b=2)). A group of positional arguments becomes
131
+ -- *(1, 2,) and a group with both kinds *(1,), **dict(a=1). Python keeps its
132
+ -- own rules: a repeated keyword within a group is a syntax error and so is a
133
+ -- group followed by a positional argument of the call. Appending a
134
+ -- positional argument to a group that already has keyword arguments is an
135
+ -- error for the same reason.
136
+ --
137
+ -- null_arg is the identity of the operation, which is what makes a group
138
+ -- possible to build one argument at a time, ending with null_arg, and what
139
+ -- lets a helper that contributes an optional argument return null_arg when
140
+ -- it has none. An empty group is a valid call with no arguments rather than
141
+ -- an error.
142
+ impure function "&"(l, r : arg_t) return arg_t;
143
+
144
+ -----------------------------------------------------------------------------
145
+ -- Operations implemented by the Python bridge (NVC, GHDL and Questa)
146
+ -----------------------------------------------------------------------------
147
+ -- The operations below are implemented by the VUnit Python bridge and are
148
+ -- therefore only available for NVC, GHDL and Questa; the other simulators
149
+ -- report a failure when they are used. Like the other operations, every one
150
+ -- of them takes the session it is performed in as its last parameter,
151
+ -- defaulting to the default session.
152
+
153
+ -----------------------------------------------------------------------------
154
+ -- Results of eval: boolean, std_ulogic, vectors and arrays
155
+ -----------------------------------------------------------------------------
156
+ -- std_ulogic_vector and integer_array_t results are only available under
157
+ -- their explicit names, not as eval overloads, which keeps
158
+ -- check_equal(eval("17"), 17) and length(eval("[1, 2]")) unambiguous.
159
+ -- signed and unsigned results are only available in the procedure form
160
+ -- since a function cannot know the width of the result.
161
+ $eval_declarations
162
+
163
+ -----------------------------------------------------------------------------
164
+ -- Results of call: string, boolean, std_ulogic, vectors and arrays
165
+ -----------------------------------------------------------------------------
166
+ $call_declarations
167
+
168
+ -----------------------------------------------------------------------------
169
+ -- Execution of Python files
170
+ -----------------------------------------------------------------------------
171
+ -- Execute a Python file. A relative file name is relative to the directory
172
+ -- of the testbench file, tb_path, and an absolute file name is used as it
173
+ -- is given. The file is executed with __file__ set and its own directory on
174
+ -- sys.path so that it can import its siblings.
175
+ procedure exec_file(file_name : string; session : python_session_t := default_session);
176
+ end package;
177
+
178
+ package body python_pkg is
179
+ -- @formatter:off
180
+ procedure import_module_from_file(
181
+ module_path, as_module_name : string; session : python_session_t := default_session
182
+ ) is
183
+ constant spec_name : string := "__" & as_module_name & "_spec";
184
+ constant code : string :=
185
+ "from importlib.util import spec_from_file_location, module_from_spec" & LF &
186
+ "from pathlib import Path" & LF &
187
+ "import sys" & LF &
188
+ spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" & module_path & "')))" & LF &
189
+ as_module_name & " = module_from_spec(" & spec_name & ")" & LF &
190
+ "sys.modules['" & as_module_name & "'] = " & as_module_name & LF &
191
+ spec_name & ".loader.exec_module(" & as_module_name & ")";
192
+ begin
193
+ exec(code, session);
194
+ end;
195
+ -- @formatter:on
196
+
197
+ -- Importing a run script runs its module level code, so one without an
198
+ -- if __name__ == "__main__" guard would start VUnit again
199
+ procedure p_check_run_script_guard(script_path : string; session : python_session_t) is
200
+ begin
201
+ exec(
202
+ "from pathlib import Path" & LF &
203
+ "if '__main__' not in Path('" & script_path & "').read_text():" & LF &
204
+ " raise RuntimeError('The run script " & script_path & " has no if __name__ == ""__main__"" guard, " &
205
+ "importing it would run VUnit again')",
206
+ session);
207
+ end;
208
+
209
+ procedure import_run_script(module_name : string := ""; session : python_session_t := default_session) is
210
+ constant script_path : string := run_script_path(get_cfg(runner_state));
211
+ variable path_items : lines_t;
212
+ variable script_name : line;
213
+ begin
214
+ if script_path = "" then
215
+ exec("raise RuntimeError('import_run_script: VUnit was not started from a run script file')", session);
216
+ return;
217
+ end if;
218
+ p_check_run_script_guard(script_path, session);
219
+ if module_name = "" then
220
+ -- Extract the last item in the full path
221
+ path_items := split(script_path, "/");
222
+ for idx in path_items'range loop
223
+ if idx = path_items'right then
224
+ script_name := path_items(idx);
225
+ else
226
+ deallocate(path_items(idx));
227
+ end if;
228
+ end loop;
229
+ deallocate(path_items);
230
+
231
+ -- Set module name to script name minus its extension
232
+ path_items := split(script_name.all, ".");
233
+ deallocate(script_name);
234
+ for idx in path_items'range loop
235
+ if idx = path_items'left then
236
+ import_module_from_file(script_path, path_items(idx).all, session);
237
+ end if;
238
+ deallocate(path_items(idx));
239
+ end loop;
240
+ deallocate(path_items);
241
+ else
242
+ import_module_from_file(script_path, module_name, session);
243
+ end if;
244
+ end;
245
+
246
+ function to_py_list_str(vec : integer_vector) return string is
247
+ variable l : line;
248
+ begin
249
+ swrite(l, "[");
250
+ for idx in vec'range loop
251
+ swrite(l, to_string(vec(idx)));
252
+ if idx /= vec'right then
253
+ swrite(l, ",");
254
+ end if;
255
+ end loop;
256
+ swrite(l, "]");
257
+
258
+ return l.all;
259
+ end;
260
+
261
+ impure function to_py_list_str(vec : integer_vector_ptr_t) return string is
262
+ variable l : line;
263
+ begin
264
+ swrite(l, "[");
265
+ for idx in 0 to length(vec) - 1 loop
266
+ swrite(l, to_string(get(vec, idx)));
267
+ if idx /= length(vec) - 1 then
268
+ swrite(l, ",");
269
+ end if;
270
+ end loop;
271
+ swrite(l, "]");
272
+
273
+ return l.all;
274
+ end;
275
+
276
+ function to_py_list_str(vec : real_vector) return string is
277
+ variable l : line;
278
+ begin
279
+ swrite(l, "[");
280
+ for idx in vec'range loop
281
+ -- Use %.16e to ensure that the string representation of the real number is precise enough to avoid loss of
282
+ -- information when double-precision is used.
283
+ swrite(l, to_string(vec(idx), "%.16e"));
284
+ if idx /= vec'right then
285
+ swrite(l, ",");
286
+ end if;
287
+ end loop;
288
+ swrite(l, "]");
289
+
290
+ return l.all;
291
+ end;
292
+
293
+ function "+"(l, r : string) return string is
294
+ begin
295
+ return l & LF & r;
296
+ end;
297
+
298
+ impure function eval_integer_vector_ptr(
299
+ expr : string; session : python_session_t := default_session
300
+ ) return integer_vector_ptr_t is
301
+ constant result_integer_vector : integer_vector := eval(expr, session);
302
+ constant len : natural := result_integer_vector'length;
303
+ constant result_integer_vector_normalized : integer_vector(0 to len - 1) := result_integer_vector;
304
+ constant result : integer_vector_ptr_t := new_integer_vector_ptr(len);
305
+ begin
306
+ for idx in 0 to len - 1 loop
307
+ set(result, idx, result_integer_vector_normalized(idx));
308
+ end loop;
309
+
310
+ return result;
311
+ end;
312
+
313
+ function arg(value : integer) return arg_t is
314
+ begin
315
+ return (p_positional_arg, to_string(value));
316
+ end;
317
+
318
+ function kwarg(kw : string; value : integer) return arg_t is
319
+ begin
320
+ return (kw, to_string(value));
321
+ end;
322
+
323
+ function arg(value : string) return arg_t is
324
+ begin
325
+ return (p_positional_arg, '"' & value & '"');
326
+ end;
327
+
328
+ function kwarg(kw : string; value : string) return arg_t is
329
+ begin
330
+ return (kw, '"' & value & '"');
331
+ end;
332
+
333
+ function arg(value : integer_vector) return arg_t is
334
+ begin
335
+ return (p_positional_arg, to_py_list_str(value));
336
+ end;
337
+
338
+ function kwarg(kw : string; value : integer_vector) return arg_t is
339
+ begin
340
+ return (kw, to_py_list_str(value));
341
+ end;
342
+
343
+ function arg(value : real) return arg_t is
344
+ begin
345
+ return (p_positional_arg, to_string(value, "%.16e"));
346
+ end;
347
+
348
+ function kwarg(kw : string; value : real) return arg_t is
349
+ begin
350
+ return (kw, to_string(value, "%.16e"));
351
+ end;
352
+
353
+ function arg(value : boolean) return arg_t is
354
+ begin
355
+ if value then
356
+ return (p_positional_arg, "True");
357
+ else
358
+ return (p_positional_arg, "False");
359
+ end if;
360
+ end;
361
+
362
+ function kwarg(kw : string; value : boolean) return arg_t is
363
+ begin
364
+ if value then
365
+ return (kw, "True");
366
+ else
367
+ return (kw, "False");
368
+ end if;
369
+ end;
370
+
371
+ -- The Python source text of a message, escaped for a double quoted Python
372
+ -- string. A quote, a backslash and a line break are the characters that
373
+ -- cannot be written as they are.
374
+ function p_quoted(message : string) return string is
375
+ variable result : line;
376
+ begin
377
+ swrite(result, """");
378
+ for idx in message'range loop
379
+ case message(idx) is
380
+ when '"' | '\' => swrite(result, "\" & message(idx));
381
+ when LF => swrite(result, "\n");
382
+ when CR => swrite(result, "\r");
383
+ when others => swrite(result, string'(1 => message(idx)));
384
+ end case;
385
+ end loop;
386
+ swrite(result, """");
387
+
388
+ return result.all;
389
+ end;
390
+
391
+ -- Report a value that could not be converted and give it a Python source
392
+ -- text raising the same message, so that the call the argument is used in
393
+ -- fails the same way rather than being made without the argument.
394
+ impure function p_failed_value(message : string) return string is
395
+ begin
396
+ failure(python_logger, message);
397
+ return "__vunit__.error(" & p_quoted(message) & ")";
398
+ end;
399
+
400
+ -- The Python source text of an argument value. The overloads whose
401
+ -- conversion can fail take the name of the operation, used to report a value
402
+ -- that cannot be converted.
403
+ function p_arg_value(value : real_vector) return string is
404
+ begin
405
+ return to_py_list_str(value);
406
+ end;
407
+
408
+ impure function p_arg_value(value : integer_vector_ptr_t) return string is
409
+ begin
410
+ return to_py_list_str(value);
411
+ end;
412
+
413
+ impure function p_arg_value(value : std_ulogic; operation : string) return string is
414
+ begin
415
+ if to_x01(value) = '1' then
416
+ return "True";
417
+ elsif to_x01(value) = '0' then
418
+ return "False";
419
+ end if;
420
+ return p_failed_value(
421
+ operation & " cannot convert " & std_ulogic'image(value) & "; expected '0', '1', 'L' or 'H'"
422
+ );
423
+ end;
424
+
425
+ -- The Python hexadecimal literal of the magnitude given by the bits, for
426
+ -- example 0x1F or -0x80. A null range gives 0x0.
427
+ function p_to_hex_literal(bits : std_ulogic_vector; negative : boolean) return string is
428
+ constant hex_digits : string(1 to 16) := "0123456789ABCDEF";
429
+ constant padding : natural := (4 - bits'length mod 4) mod 4;
430
+ variable padded : std_ulogic_vector(bits'length + padding - 1 downto 0) := (others => '0');
431
+ variable result : line;
432
+ variable digit : natural;
433
+ variable leading : boolean := true;
434
+ begin
435
+ padded(bits'length - 1 downto 0) := bits;
436
+ swrite(result, "0x");
437
+ for nibble in padded'length / 4 - 1 downto 0 loop
438
+ digit := 0;
439
+ for idx in 3 downto 0 loop
440
+ digit := 2 * digit;
441
+ if padded(4 * nibble + idx) = '1' then
442
+ digit := digit + 1;
443
+ end if;
444
+ end loop;
445
+ if digit /= 0 then
446
+ leading := false;
447
+ end if;
448
+ if not leading then
449
+ swrite(result, string'(1 => hex_digits(digit + 1)));
450
+ end if;
451
+ end loop;
452
+ if leading then
453
+ swrite(result, "0");
454
+ end if;
455
+
456
+ if negative then
457
+ return "-" & result.all;
458
+ end if;
459
+ return result.all;
460
+ end;
461
+
462
+ -- The error reported for a vector value that cannot be converted
463
+ function p_metavalue_error(value : std_ulogic_vector; operation : string) return string is
464
+ begin
465
+ return operation & " cannot convert """ & to_string(value) & """; the value has metavalues";
466
+ end;
467
+
468
+ impure function p_arg_value(value : unsigned; operation : string) return string is
469
+ begin
470
+ if is_x(std_ulogic_vector(value)) then
471
+ return p_failed_value(p_metavalue_error(std_ulogic_vector(value), operation));
472
+ end if;
473
+ return p_to_hex_literal(to_x01(std_ulogic_vector(value)), false);
474
+ end;
475
+
476
+ impure function p_arg_value(value : signed; operation : string) return string is
477
+ variable folded : signed(value'length - 1 downto 0);
478
+ variable magnitude : signed(value'length downto 0);
479
+ begin
480
+ if is_x(std_ulogic_vector(value)) then
481
+ return p_failed_value(p_metavalue_error(std_ulogic_vector(value), operation));
482
+ end if;
483
+ if value'length = 0 then
484
+ return "0x0";
485
+ end if;
486
+
487
+ folded := signed(to_x01(std_ulogic_vector(value)));
488
+ if folded(folded'left) = '0' then
489
+ return p_to_hex_literal(std_ulogic_vector(folded), false);
490
+ end if;
491
+ -- Resizing before negating keeps the magnitude of signed'low representable
492
+ magnitude := -resize(folded, value'length + 1);
493
+ return p_to_hex_literal(std_ulogic_vector(magnitude), true);
494
+ end;
495
+
496
+ impure function p_arg_value(value : integer_array_t; operation : string) return string is
497
+ constant staged_id : integer := p_stage_array(value, operation);
498
+ begin
499
+ if staged_id < 1 then
500
+ -- p_stage_array reported the error of the transfer
501
+ return "__vunit__.error(" & p_quoted(operation & " failed") & ")";
502
+ end if;
503
+ return "__vunit__.staged(" & integer'image(staged_id) & ")";
504
+ end;
505
+
506
+ $arg_functions
507
+
508
+ -----------------------------------------------------------------------------
509
+ -- Argument groups
510
+ -----------------------------------------------------------------------------
511
+ -- A group is one argument whose Python source text is spliced into the call
512
+ -- like a positional argument: *(1, 2,) for positional arguments,
513
+ -- **dict(a=1, b=2) for keyword arguments and *(1,), **dict(a=1) for both.
514
+ -- Its name is the group marker followed by the length of the positional
515
+ -- part, which is what tells the two parts apart when more arguments are
516
+ -- appended to the group.
517
+ constant p_positional_group : string := "*(";
518
+ constant p_keyword_group : string := "**dict(";
519
+
520
+ function p_is_group(value : arg_t) return boolean is
521
+ alias name : string(1 to value.name'length) is value.name;
522
+ begin
523
+ if name'length <= p_group_arg'length then
524
+ return false;
525
+ end if;
526
+ return name(1 to p_group_arg'length) = p_group_arg;
527
+ end;
528
+
529
+ -- The length of the positional part of a group, 0 when it has none
530
+ function p_group_split(value : arg_t) return natural is
531
+ alias name : string(1 to value.name'length) is value.name;
532
+ begin
533
+ return natural'value(name(p_group_arg'length + 1 to name'length));
534
+ end;
535
+
536
+ -- The positional arguments of an operand as they are written within *(...).
537
+ -- Every argument is followed by a comma, which is what makes *(1,) a tuple.
538
+ function p_positional_items(value : arg_t) return string is
539
+ alias text : string(1 to value.value'length) is value.value;
540
+ begin
541
+ if value.name = p_positional_arg then
542
+ return text & ",";
543
+ elsif p_is_group(value) then
544
+ if p_group_split(value) = 0 then
545
+ return "";
546
+ end if;
547
+ -- Drop the *( prefix and the ) of the positional part
548
+ return text(p_positional_group'length + 1 to p_group_split(value) - 1);
549
+ end if;
550
+ return "";
551
+ end;
552
+
553
+ -- The keyword arguments of an operand as they are written within **dict(...)
554
+ function p_keyword_items(value : arg_t) return string is
555
+ alias text : string(1 to value.value'length) is value.value;
556
+ begin
557
+ if value.name = p_ignore_arg or value.name = p_positional_arg then
558
+ return "";
559
+ elsif not p_is_group(value) then
560
+ return value.name & "=" & value.value;
561
+ elsif p_group_split(value) = text'length then
562
+ return "";
563
+ elsif p_group_split(value) = 0 then
564
+ -- Keyword arguments only: drop the **dict( prefix and the trailing )
565
+ return text(p_keyword_group'length + 1 to text'length - 1);
566
+ end if;
567
+ -- Both kinds: the keyword part follows the positional part and ", "
568
+ return text(p_group_split(value) + 2 + p_keyword_group'length + 1 to text'length - 1);
569
+ end;
570
+
571
+ -- The arguments of both operands, separated
572
+ function p_join(l, r, separator : string) return string is
573
+ begin
574
+ if l = "" then
575
+ return r;
576
+ elsif r = "" then
577
+ return l;
578
+ end if;
579
+ return l & separator & r;
580
+ end;
581
+
582
+ -- *(...) or **dict(...) of the arguments, nothing when there are none
583
+ function p_group_text(prefix, items : string) return string is
584
+ begin
585
+ if items = "" then
586
+ return "";
587
+ end if;
588
+ return prefix & items & ")";
589
+ end;
590
+
591
+ -- The group holding the given positional and keyword arguments
592
+ function p_group(positional_items, keyword_items : string) return arg_t is
593
+ constant positional : string := p_group_text(p_positional_group, positional_items);
594
+ constant keywords : string := p_group_text(p_keyword_group, keyword_items);
595
+ begin
596
+ if positional = "" then
597
+ return (p_group_arg & integer'image(0), keywords);
598
+ elsif keywords = "" then
599
+ return (p_group_arg & integer'image(positional'length), positional);
600
+ end if;
601
+ return (p_group_arg & integer'image(positional'length), positional & ", " & keywords);
602
+ end;
603
+
604
+ impure function "&"(l, r : arg_t) return arg_t is
605
+ constant l_positional : string := p_positional_items(l);
606
+ constant l_keywords : string := p_keyword_items(l);
607
+ constant r_positional : string := p_positional_items(r);
608
+ constant r_keywords : string := p_keyword_items(r);
609
+ begin
610
+ if l.name = p_ignore_arg then
611
+ return r;
612
+ elsif r.name = p_ignore_arg then
613
+ return l;
614
+ elsif l_keywords /= "" and r_positional /= "" then
615
+ -- Python's own rule
616
+ failure(python_logger, "positional argument after keyword arguments");
617
+ return null_arg;
618
+ end if;
619
+
620
+ return p_group(p_join(l_positional, r_positional, " "), p_join(l_keywords, r_keywords, ", "));
621
+ end;
622
+
623
+ impure function to_call_str(
624
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg
625
+ ) return string is
626
+ variable result : line;
627
+ variable first : boolean := true;
628
+
629
+ procedure append(value : arg_t) is
630
+ begin
631
+ if value.name = p_ignore_arg then
632
+ return;
633
+ end if;
634
+ if not first then
635
+ swrite(result, ", ");
636
+ end if;
637
+ first := false;
638
+ if value.name = p_positional_arg or p_is_group(value) then
639
+ -- A group is spliced into the call like a positional argument
640
+ swrite(result, value.value);
641
+ else
642
+ swrite(result, value.name & "=" & value.value);
643
+ end if;
644
+ end;
645
+ begin
646
+ swrite(result, identifier & "(");
647
+ append(arg1);
648
+ append(arg2);
649
+ append(arg3);
650
+ append(arg4);
651
+ append(arg5);
652
+ append(arg6);
653
+ append(arg7);
654
+ append(arg8);
655
+ append(arg9);
656
+ append(arg10);
657
+ swrite(result, ")");
658
+
659
+ return result.all;
660
+ end;
661
+
662
+ impure function call_integer_w_arg(
663
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
664
+ session : python_session_t := default_session
665
+ ) return integer is
666
+ begin
667
+ return eval(to_call_str(identifier, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), session);
668
+ end;
669
+
670
+ procedure call(
671
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
672
+ session : python_session_t := default_session
673
+ ) is
674
+ begin
675
+ exec(to_call_str(identifier, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), session);
676
+ end;
677
+
678
+ impure function call_integer_vector(
679
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
680
+ session : python_session_t := default_session
681
+ ) return integer_vector is
682
+ begin
683
+ return eval(to_call_str(identifier, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), session);
684
+ end;
685
+
686
+ impure function call_real(
687
+ identifier : string; arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 : arg_t := null_arg;
688
+ session : python_session_t := default_session
689
+ ) return real is
690
+ begin
691
+ return eval(to_call_str(identifier, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), session);
692
+ end;
693
+
694
+ -----------------------------------------------------------------------------
695
+ -- Operations implemented by the Python bridge (NVC, GHDL and Questa)
696
+ -----------------------------------------------------------------------------
697
+ -- Python represents std_ulogic values by these characters
698
+ constant p_std_ulogic_characters : string(1 to 9) := "UX01ZWLH-";
699
+
700
+ function p_to_std_ulogic(value : character) return std_ulogic is
701
+ begin
702
+ for idx in p_std_ulogic_characters'range loop
703
+ if p_std_ulogic_characters(idx) = value then
704
+ return std_ulogic'val(idx - 1);
705
+ end if;
706
+ end loop;
707
+ -- The bridge only returns the characters above
708
+ return 'X';
709
+ end;
710
+
711
+ -- The elements of the characters, the first character becoming the leftmost element
712
+ function p_to_std_ulogic_vector(value : string) return std_ulogic_vector is
713
+ alias normalized : string(1 to value'length) is value;
714
+ variable result : std_ulogic_vector(value'length - 1 downto 0);
715
+ begin
716
+ for idx in normalized'range loop
717
+ result(value'length - idx) := p_to_std_ulogic(normalized(idx));
718
+ end loop;
719
+ return result;
720
+ end;
721
+
722
+ -----------------------------------------------------------------------------
723
+ -- exec
724
+ -----------------------------------------------------------------------------
725
+ -- True when the file name is absolute: /a/b, \\server\share\a, C:\a or C:/a
726
+ function p_is_absolute(file_name : string) return boolean is
727
+ alias name : string(1 to file_name'length) is file_name;
728
+ begin
729
+ if name'length = 0 then
730
+ return false;
731
+ elsif name(1) = '/' or name(1) = '\' then
732
+ return true;
733
+ elsif name'length >= 2 then
734
+ -- A Windows drive
735
+ return name(2) = ':';
736
+ end if;
737
+ return false;
738
+ end;
739
+
740
+ -- The file name of exec_file, a relative one taken from the directory of the
741
+ -- testbench file like the file names of the other VUnit subprograms
742
+ impure function p_file_path(file_name : string) return string is
743
+ begin
744
+ if p_is_absolute(file_name) then
745
+ return file_name;
746
+ end if;
747
+ return join(tb_path(get_cfg(runner_state)), file_name);
748
+ end;
749
+
750
+ procedure exec_file(file_name : string; session : python_session_t := default_session) is
751
+ variable ok : boolean;
752
+ begin
753
+ ok := p_exec_file(p_file_path(file_name), session);
754
+ end;
755
+
756
+ -----------------------------------------------------------------------------
757
+ -- eval
758
+ -----------------------------------------------------------------------------
759
+ $eval_subprograms
760
+
761
+ -----------------------------------------------------------------------------
762
+ -- call
763
+ -----------------------------------------------------------------------------
764
+ $call_subprograms
765
+ end package body;