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,98 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
7
+ *
8
+ * FLI front end of the bridge, compiled into the library only for
9
+ * Questa/ModelSim. Every entry point of bridge.h gets an fli_ prefixed
10
+ * foreign subprogram that converts the FLI parameters and delegates, so that
11
+ * the bridge itself, the generated VHDL and python_ffi_pkg_bridge.vhd are the
12
+ * same for all simulators.
13
+ *
14
+ * Parameter and return type mapping, from the tables of "Matching VHDL
15
+ * Parameters with C Parameters" and "Matching VHDL Return Types with C Return
16
+ * Types" in the Questa Foreign Language Interface Manual:
17
+ * - A VHDL integer IN parameter of class constant is passed by value as int.
18
+ * - An array parameter, IN or OUT, is passed as an mtiVariableIdT handle.
19
+ * mti_GetArrayVarValue(id, NULL) returns a pointer to the storage of the
20
+ * variable itself, which is both read and written in place. The element
21
+ * type decides what it points to: char for string (an enumeration with at
22
+ * most 256 values), mtiInt32T for integer_vector and double for
23
+ * real_vector, which is exactly the ABI of bridge.h.
24
+ * - A function returning integer returns int, one returning real returns
25
+ * the mtiRealT union, assigned with MTI_ASSIGN_TO_REAL.
26
+ * Arrays are not null terminated and the handles are only valid during the
27
+ * call, neither of which matters here: every chunk carries its length and
28
+ * nothing is kept after the call returns.
29
+ */
30
+
31
+ #include "bridge.h"
32
+
33
+ #include "mti.h"
34
+
35
+ /* ------------------------------------------------------------------------ */
36
+ /* Scalars only: passed by value and returned as int */
37
+ /* ------------------------------------------------------------------------ */
38
+
39
+ VPY_EXPORT int fli_vpy_setup(void) { return (int)vpy_setup(); }
40
+
41
+ VPY_EXPORT int fli_vpy_cleanup(void) { return (int)vpy_cleanup(); }
42
+
43
+ VPY_EXPORT int fli_vpy_buffer_clear(void) { return (int)vpy_buffer_clear(); }
44
+
45
+ VPY_EXPORT int fli_vpy_begin(void) { return (int)vpy_begin(); }
46
+
47
+ VPY_EXPORT int fli_vpy_execute(int is_file) { return (int)vpy_execute((int32_t)is_file); }
48
+
49
+ VPY_EXPORT int fli_vpy_eval(int kind, int width) { return (int)vpy_eval((int32_t)kind, (int32_t)width); }
50
+
51
+ VPY_EXPORT int fli_vpy_push_array(int length, int width, int height, int depth, int bit_width, int is_signed) {
52
+ return (int)vpy_push_array((int32_t)length, (int32_t)width, (int32_t)height, (int32_t)depth, (int32_t)bit_width,
53
+ (int32_t)is_signed);
54
+ }
55
+
56
+ VPY_EXPORT int fli_vpy_stage(void) { return (int)vpy_stage(); }
57
+
58
+ VPY_EXPORT int fli_vpy_result_integer(void) { return (int)vpy_result_integer(); }
59
+
60
+ VPY_EXPORT int fli_vpy_result_meta(int index) { return (int)vpy_result_meta((int32_t)index); }
61
+
62
+ VPY_EXPORT int fli_vpy_error_length(void) { return (int)vpy_error_length(); }
63
+
64
+ /* A VHDL function returning real returns the mtiRealT union, not a double. */
65
+ VPY_EXPORT mtiRealT fli_vpy_result_real(void) {
66
+ mtiRealT result;
67
+
68
+ MTI_ASSIGN_TO_REAL(result, vpy_result_real());
69
+ return result;
70
+ }
71
+
72
+ /* ------------------------------------------------------------------------ */
73
+ /* Chunks: constrained arrays passed as mtiVariableIdT */
74
+ /* ------------------------------------------------------------------------ */
75
+
76
+ VPY_EXPORT int fli_vpy_buffer_append(mtiVariableIdT chunk, int length) {
77
+ return (int)vpy_buffer_append((const char *)mti_GetArrayVarValue(chunk, NULL), (int32_t)length);
78
+ }
79
+
80
+ VPY_EXPORT int fli_vpy_array_write(mtiVariableIdT chunk, int length) {
81
+ return (int)vpy_array_write((const int32_t *)mti_GetArrayVarValue(chunk, NULL), (int32_t)length);
82
+ }
83
+
84
+ VPY_EXPORT void fli_vpy_result_read_string(mtiVariableIdT chunk, int offset, int length) {
85
+ vpy_result_read_string((char *)mti_GetArrayVarValue(chunk, NULL), (int32_t)offset, (int32_t)length);
86
+ }
87
+
88
+ VPY_EXPORT void fli_vpy_result_read_integers(mtiVariableIdT chunk, int offset, int length) {
89
+ vpy_result_read_integers((int32_t *)mti_GetArrayVarValue(chunk, NULL), (int32_t)offset, (int32_t)length);
90
+ }
91
+
92
+ VPY_EXPORT void fli_vpy_result_read_reals(mtiVariableIdT chunk, int offset, int length) {
93
+ vpy_result_read_reals((double *)mti_GetArrayVarValue(chunk, NULL), (int32_t)offset, (int32_t)length);
94
+ }
95
+
96
+ VPY_EXPORT void fli_vpy_error_read(mtiVariableIdT chunk, int offset, int length) {
97
+ vpy_error_read((char *)mti_GetArrayVarValue(chunk, NULL), (int32_t)offset, (int32_t)length);
98
+ }
@@ -0,0 +1,271 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
7
+ *
8
+ * The embedded interpreter. It is initialized lazily on first use, in the
9
+ * Python environment that runs VUnit, and kept alive for the lifetime of the
10
+ * simulator process (it is never finalized).
11
+ */
12
+
13
+ #include "bridge.h"
14
+
15
+ #include <stdio.h>
16
+ #include <stdlib.h>
17
+ #include <string.h>
18
+
19
+ #ifdef _WIN32
20
+ #include <windows.h>
21
+ #else
22
+ #include <dlfcn.h>
23
+ #endif
24
+
25
+ typedef enum { STATE_UNINITIALIZED, STATE_READY, STATE_FAILED } state_t;
26
+
27
+ static state_t g_state = STATE_UNINITIALIZED;
28
+ static vpy_config_t g_config;
29
+ static PyObject *g_runtime = NULL; /* runtime.Runtime instance, strong reference */
30
+
31
+ PyObject *vpy_runtime(void) { return g_runtime; }
32
+
33
+ #ifdef _WIN32
34
+ /* malloc'ed wide string of a UTF-8 string, NULL on failure. */
35
+ static wchar_t *to_wide(const char *text) {
36
+ int length = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
37
+ wchar_t *wide;
38
+
39
+ if (length <= 0) {
40
+ return NULL;
41
+ }
42
+ wide = (wchar_t *)malloc(sizeof(wchar_t) * (size_t)length);
43
+ if (wide != NULL) {
44
+ MultiByteToWideChar(CP_UTF8, 0, text, -1, wide, length);
45
+ }
46
+ return wide;
47
+ }
48
+ #endif
49
+
50
+ /*
51
+ * Make the Python runtime library usable by extension modules.
52
+ *
53
+ * Windows: this library delay-loads pythonXY.dll. Load it by absolute path
54
+ * before the first Python API call so the delay-load helper finds it no
55
+ * matter how the simulator's DLL search path looks.
56
+ *
57
+ * POSIX: simulators dlopen() this library with RTLD_LOCAL, which makes
58
+ * libpython (our dependency) invisible to extension modules such as NumPy
59
+ * that expect the Python symbols to be globally available. Promote libpython
60
+ * to RTLD_GLOBAL.
61
+ */
62
+ static int load_python_library(void) {
63
+ #ifdef _WIN32
64
+ wchar_t *wpath = to_wide(g_config.python_dll);
65
+ HMODULE module;
66
+
67
+ if (wpath == NULL) {
68
+ vpy_set_error("Invalid Python DLL path in the Python bridge configuration");
69
+ return VPY_ERROR;
70
+ }
71
+ module = LoadLibraryExW(wpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
72
+ free(wpath);
73
+ if (module == NULL) {
74
+ char message[4096];
75
+
76
+ snprintf(message, sizeof(message), "Failed to load the Python DLL %s (Windows error %lu)", g_config.python_dll,
77
+ (unsigned long)GetLastError());
78
+ vpy_set_error(message);
79
+ return VPY_ERROR;
80
+ }
81
+ /* Intentionally never freed: the interpreter lives until process exit. */
82
+ return VPY_OK;
83
+ #else
84
+ Dl_info info;
85
+
86
+ if (dladdr((void *)&Py_InitializeFromConfig, &info) != 0 && info.dli_fname != NULL) {
87
+ /* Intentionally never closed: the interpreter lives until process exit. */
88
+ if (dlopen(info.dli_fname, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD) == NULL) {
89
+ vpy_set_error2("Failed to make the Python library globally visible: ", dlerror());
90
+ return VPY_ERROR;
91
+ }
92
+ }
93
+ return VPY_OK;
94
+ #endif
95
+ }
96
+
97
+ static int set_config_string(PyConfig *config, wchar_t **field, const char *value) {
98
+ PyStatus status;
99
+ #ifdef _WIN32
100
+ wchar_t *wvalue = to_wide(value);
101
+
102
+ if (wvalue == NULL) {
103
+ vpy_set_error("Invalid path in the Python bridge configuration");
104
+ return VPY_ERROR;
105
+ }
106
+ status = PyConfig_SetString(config, field, wvalue);
107
+ free(wvalue);
108
+ #else
109
+ status = PyConfig_SetBytesString(config, field, value);
110
+ #endif
111
+ if (PyStatus_Exception(status)) {
112
+ vpy_set_error2("Failed to configure Python: ", status.err_msg);
113
+ return VPY_ERROR;
114
+ }
115
+ return VPY_OK;
116
+ }
117
+
118
+ /* Create the interpreter. On success this thread holds the GIL. */
119
+ static int initialize_interpreter(void) {
120
+ PyConfig config;
121
+ PyStatus status;
122
+
123
+ PyConfig_InitPythonConfig(&config);
124
+ /* The simulator owns the process: no signal handlers, no argv parsing. */
125
+ config.install_signal_handlers = 0;
126
+ config.parse_argv = 0;
127
+
128
+ /* Make path configuration behave as if the interpreter that launched VUnit
129
+ * was started, which selects the same installation, virtual environment
130
+ * and site-packages. */
131
+ if (set_config_string(&config, &config.program_name, g_config.executable) != VPY_OK ||
132
+ set_config_string(&config, &config.executable, g_config.executable) != VPY_OK) {
133
+ PyConfig_Clear(&config);
134
+ return VPY_ERROR;
135
+ }
136
+
137
+ status = Py_InitializeFromConfig(&config);
138
+ PyConfig_Clear(&config);
139
+ if (PyStatus_Exception(status)) {
140
+ vpy_set_error2("Failed to initialize Python: ", status.err_msg != NULL ? status.err_msg : "unknown error");
141
+ return VPY_ERROR;
142
+ }
143
+ return VPY_OK;
144
+ }
145
+
146
+ /* Add a UTF-8 configuration string to a dict. GIL held. */
147
+ static int set_item(PyObject *dict, const char *key, const char *value) {
148
+ PyObject *object = PyUnicode_DecodeUTF8(value, (Py_ssize_t)strlen(value), "surrogateescape");
149
+ int status;
150
+
151
+ if (object == NULL) {
152
+ return -1;
153
+ }
154
+ status = PyDict_SetItemString(dict, key, object);
155
+ Py_DECREF(object);
156
+ return status;
157
+ }
158
+
159
+ /* Load ../runtime.py and create the Runtime instance. GIL held. */
160
+ static int create_runtime(void) {
161
+ static const char *bootstrap =
162
+ "import importlib.util, sys\n"
163
+ "_spec = importlib.util.spec_from_file_location('_vunit_python_runtime', runtime_path)\n"
164
+ "_module = importlib.util.module_from_spec(_spec)\n"
165
+ "sys.modules[_spec.name] = _module\n"
166
+ "_spec.loader.exec_module(_module)\n"
167
+ "runtime = _module.Runtime(run_script_dir=run_script_dir, prefix=prefix)\n";
168
+ PyObject *globals = PyDict_New();
169
+ PyObject *ret = NULL;
170
+ int status = VPY_ERROR;
171
+
172
+ if (globals == NULL || PyDict_SetItemString(globals, "__builtins__", PyEval_GetBuiltins()) < 0 ||
173
+ set_item(globals, "runtime_path", g_config.runtime) < 0 ||
174
+ set_item(globals, "run_script_dir", g_config.run_script_dir) < 0 ||
175
+ set_item(globals, "prefix", g_config.prefix) < 0) {
176
+ vpy_set_error_from_python();
177
+ goto done;
178
+ }
179
+
180
+ ret = PyRun_String(bootstrap, Py_file_input, globals, globals);
181
+ if (ret == NULL) {
182
+ vpy_set_error_from_python();
183
+ goto done;
184
+ }
185
+
186
+ g_runtime = PyDict_GetItemString(globals, "runtime"); /* borrowed */
187
+ if (g_runtime == NULL) {
188
+ vpy_set_error("Failed to create the Python bridge runtime");
189
+ goto done;
190
+ }
191
+ Py_INCREF(g_runtime);
192
+ status = VPY_OK;
193
+
194
+ done:
195
+ Py_XDECREF(ret);
196
+ Py_XDECREF(globals);
197
+ return status;
198
+ }
199
+
200
+ /*
201
+ * Initialize on first use. Failure is sticky: the error text keeps the reason
202
+ * and every later operation fails with it.
203
+ */
204
+ int vpy_initialize(void) {
205
+ int we_initialized;
206
+ PyGILState_STATE gil = PyGILState_UNLOCKED;
207
+ int status;
208
+
209
+ if (g_state != STATE_UNINITIALIZED) {
210
+ return g_state == STATE_READY ? VPY_OK : VPY_ERROR;
211
+ }
212
+ g_state = STATE_FAILED;
213
+
214
+ if (vpy_read_config(&g_config) != VPY_OK || load_python_library() != VPY_OK) {
215
+ return VPY_ERROR;
216
+ }
217
+
218
+ /* Reuse an interpreter embedded by someone else in this process. */
219
+ we_initialized = !Py_IsInitialized();
220
+ if (we_initialized) {
221
+ if (initialize_interpreter() != VPY_OK) {
222
+ return VPY_ERROR;
223
+ }
224
+ } else {
225
+ gil = PyGILState_Ensure();
226
+ }
227
+
228
+ status = create_runtime();
229
+
230
+ if (we_initialized) {
231
+ /* Release the GIL taken by initialization. Every entry point acquires it
232
+ * with PyGILState_Ensure, from whatever thread the simulator happens to
233
+ * call us on. The thread state is kept forever. */
234
+ PyEval_SaveThread();
235
+ } else {
236
+ PyGILState_Release(gil);
237
+ }
238
+
239
+ if (status == VPY_OK) {
240
+ g_state = STATE_READY;
241
+ }
242
+ return status;
243
+ }
244
+
245
+ /* Whether the interpreter and the runtime are ready to be used. */
246
+ int vpy_is_initialized(void) { return g_state == STATE_READY; }
247
+
248
+ /*
249
+ * Common start of the entry points that use Python after vpy_begin: fail if
250
+ * the bridge is not initialized, else take the GIL.
251
+ */
252
+ int vpy_enter(PyGILState_STATE *gil) {
253
+ if (g_state != STATE_READY) {
254
+ if (!vpy_has_error()) {
255
+ vpy_set_error("Internal error: the Python bridge is not initialized");
256
+ }
257
+ return VPY_ERROR;
258
+ }
259
+ *gil = PyGILState_Ensure();
260
+ return VPY_OK;
261
+ }
262
+
263
+ /* Status of a Python call returning ret (a new reference or NULL), which is consumed. */
264
+ int vpy_finish_call(PyObject *ret) {
265
+ if (ret == NULL) {
266
+ vpy_set_error_from_python();
267
+ return VPY_ERROR;
268
+ }
269
+ Py_DECREF(ret);
270
+ return VPY_OK;
271
+ }
@@ -0,0 +1,239 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
7
+ *
8
+ * The operations: setup, cleanup, begin, exec, eval, and the conversion and
9
+ * transfer of the result back to VHDL.
10
+ */
11
+
12
+ #include "bridge.h"
13
+
14
+ #include <stdio.h>
15
+ #include <string.h>
16
+
17
+ #define NUM_META 8
18
+
19
+ /* Result of the last evaluation, converted to a VHDL kind by vpy_eval. */
20
+ static PyObject *g_result = NULL; /* bytes of a string/array result, strong reference */
21
+ static const char *g_result_data = NULL; /* view into g_result, immutable */
22
+ static size_t g_result_size = 0;
23
+ static int32_t g_result_integer = 0;
24
+ static double g_result_real = 0.0;
25
+ static int32_t g_result_meta[NUM_META];
26
+
27
+ /* Drop the result of the previous operation. GIL held. */
28
+ static void clear_result(void) {
29
+ Py_CLEAR(g_result);
30
+ g_result_data = NULL;
31
+ g_result_size = 0;
32
+ g_result_integer = 0;
33
+ g_result_real = 0.0;
34
+ memset(g_result_meta, 0, sizeof(g_result_meta));
35
+ }
36
+
37
+ /* Keep the simulator's and Python's output in order. */
38
+ static void flush_c_streams(void) {
39
+ fflush(stdout);
40
+ fflush(stderr);
41
+ }
42
+
43
+ /*
44
+ * Start the interpreter. Eager and idempotent. Optional: every operation
45
+ * starts the interpreter if it is not running, see vpy_begin.
46
+ */
47
+ VPY_EXPORT int32_t vpy_setup(void) {
48
+ flush_c_streams();
49
+ return vpy_initialize();
50
+ }
51
+
52
+ /*
53
+ * Release what the simulation acquired: flush Python's streams and drop the
54
+ * last result and the staged values. The interpreter is NOT finalized and the
55
+ * session namespaces are kept, so later operations keep working. Optional:
56
+ * everything it releases is released by the simulator process ending, and
57
+ * every operation flushes on its own.
58
+ */
59
+ VPY_EXPORT int32_t vpy_cleanup(void) {
60
+ PyGILState_STATE gil;
61
+ int status;
62
+
63
+ flush_c_streams();
64
+ if (!vpy_is_initialized()) {
65
+ /* Nothing was ever started, so there is nothing to release. */
66
+ return VPY_OK;
67
+ }
68
+ if (vpy_enter(&gil) != VPY_OK) {
69
+ return VPY_ERROR;
70
+ }
71
+ clear_result();
72
+ vpy_clear_arguments();
73
+ status = vpy_finish_call(PyObject_CallMethod(vpy_runtime(), "cleanup", NULL));
74
+ PyGILState_Release(gil);
75
+ return status;
76
+ }
77
+
78
+ /*
79
+ * Start a new operation in the session named by the buffer: initialize on
80
+ * first use, select the session and drop the previous result and values.
81
+ */
82
+ VPY_EXPORT int32_t vpy_begin(void) {
83
+ PyGILState_STATE gil;
84
+ PyObject *session;
85
+ int status = VPY_ERROR;
86
+
87
+ if (vpy_initialize() != VPY_OK || vpy_enter(&gil) != VPY_OK) {
88
+ return VPY_ERROR;
89
+ }
90
+ clear_result();
91
+ vpy_clear_arguments();
92
+ session = vpy_buffer_as_str();
93
+ if (session == NULL) {
94
+ vpy_set_error_from_python();
95
+ } else {
96
+ status = vpy_finish_call(PyObject_CallMethod(vpy_runtime(), "select_session", "O", session));
97
+ Py_DECREF(session);
98
+ }
99
+ PyGILState_Release(gil);
100
+ return status;
101
+ }
102
+
103
+ /* Execute the buffer as Python source (is_file = 0) or as a file name (is_file = 1). */
104
+ VPY_EXPORT int32_t vpy_execute(int32_t is_file) {
105
+ PyGILState_STATE gil;
106
+ PyObject *text;
107
+ int status = VPY_ERROR;
108
+
109
+ flush_c_streams();
110
+ if (vpy_enter(&gil) != VPY_OK) {
111
+ return VPY_ERROR;
112
+ }
113
+ text = vpy_buffer_as_str();
114
+ if (text == NULL) {
115
+ vpy_set_error_from_python();
116
+ } else {
117
+ status = vpy_finish_call(PyObject_CallMethod(vpy_runtime(), "execute", "Oi", text, (int)(is_file != 0)));
118
+ Py_DECREF(text);
119
+ }
120
+ PyGILState_Release(gil);
121
+ /* The runtime flushed Python's streams, flush what Python wrote through C */
122
+ flush_c_streams();
123
+ return status;
124
+ }
125
+
126
+ /*
127
+ * Store the conversion result (integer, real, bytes, meta tuple) returned by
128
+ * the runtime. Which fields are meaningful depends on the kind. GIL held.
129
+ */
130
+ static int store_result(PyObject *converted) {
131
+ PyObject *data;
132
+ PyObject *meta;
133
+ Py_ssize_t index;
134
+
135
+ if (!PyTuple_Check(converted) || PyTuple_Size(converted) != 4) {
136
+ vpy_set_error("Internal error: unexpected result conversion format");
137
+ return VPY_ERROR;
138
+ }
139
+ data = PyTuple_GetItem(converted, 2); /* borrowed */
140
+ meta = PyTuple_GetItem(converted, 3); /* borrowed */
141
+ if (!PyBytes_Check(data) || !PyTuple_Check(meta)) {
142
+ vpy_set_error("Internal error: unexpected result conversion format");
143
+ return VPY_ERROR;
144
+ }
145
+
146
+ g_result_integer = (int32_t)PyLong_AsLong(PyTuple_GetItem(converted, 0));
147
+ g_result_real = PyFloat_AsDouble(PyTuple_GetItem(converted, 1));
148
+ for (index = 0; index < PyTuple_Size(meta) && index < NUM_META; index++) {
149
+ g_result_meta[index] = (int32_t)PyLong_AsLong(PyTuple_GetItem(meta, index));
150
+ }
151
+ if (PyErr_Occurred()) {
152
+ vpy_set_error_from_python();
153
+ return VPY_ERROR;
154
+ }
155
+
156
+ Py_INCREF(data);
157
+ g_result = data;
158
+ g_result_data = PyBytes_AsString(data);
159
+ g_result_size = (size_t)PyBytes_Size(data);
160
+ return VPY_OK;
161
+ }
162
+
163
+ /*
164
+ * Evaluate the buffer as a Python expression in the current session and
165
+ * convert the value to the requested VHDL kind and width.
166
+ */
167
+ VPY_EXPORT int32_t vpy_eval(int32_t kind, int32_t width) {
168
+ PyGILState_STATE gil;
169
+ PyObject *text;
170
+ PyObject *converted;
171
+ int status = VPY_ERROR;
172
+
173
+ flush_c_streams();
174
+ if (vpy_enter(&gil) != VPY_OK) {
175
+ return VPY_ERROR;
176
+ }
177
+ clear_result();
178
+ text = vpy_buffer_as_str();
179
+ if (text == NULL) {
180
+ vpy_set_error_from_python();
181
+ } else {
182
+ converted = PyObject_CallMethod(vpy_runtime(), "evaluate", "Oii", text, (int)kind, (int)width);
183
+ Py_DECREF(text);
184
+ if (converted == NULL) {
185
+ vpy_set_error_from_python();
186
+ } else {
187
+ status = store_result(converted);
188
+ Py_DECREF(converted);
189
+ }
190
+ }
191
+ if (status != VPY_OK) {
192
+ clear_result();
193
+ }
194
+ PyGILState_Release(gil);
195
+ /* The runtime flushed Python's streams, flush what Python wrote through C */
196
+ flush_c_streams();
197
+ return status;
198
+ }
199
+
200
+ VPY_EXPORT int32_t vpy_result_integer(void) { return g_result_integer; }
201
+
202
+ VPY_EXPORT double vpy_result_real(void) { return g_result_real; }
203
+
204
+ VPY_EXPORT int32_t vpy_result_meta(int32_t index) {
205
+ if (index < 0 || index >= NUM_META) {
206
+ return 0;
207
+ }
208
+ return g_result_meta[index];
209
+ }
210
+
211
+ /* Copy length bytes of the string/bit-string result starting at offset. */
212
+ VPY_EXPORT void vpy_result_read_string(char *chunk, int32_t offset, int32_t length) {
213
+ if (offset < 0 || length <= 0 || (size_t)offset + (size_t)length > g_result_size) {
214
+ return;
215
+ }
216
+ memcpy(chunk, g_result_data + offset, (size_t)length);
217
+ }
218
+
219
+ /* Copy length integers of an integer_array_t/integer_vector result starting at element offset. */
220
+ VPY_EXPORT void vpy_result_read_integers(int32_t *chunk, int32_t offset, int32_t length) {
221
+ size_t start = (size_t)offset * sizeof(int32_t);
222
+ size_t bytes = (size_t)length * sizeof(int32_t);
223
+
224
+ if (offset < 0 || length <= 0 || start + bytes > g_result_size) {
225
+ return;
226
+ }
227
+ memcpy(chunk, g_result_data + start, bytes);
228
+ }
229
+
230
+ /* Copy length reals of a real_vector result starting at element offset. */
231
+ VPY_EXPORT void vpy_result_read_reals(double *chunk, int32_t offset, int32_t length) {
232
+ size_t start = (size_t)offset * sizeof(double);
233
+ size_t bytes = (size_t)length * sizeof(double);
234
+
235
+ if (offset < 0 || length <= 0 || start + bytes > g_result_size) {
236
+ return;
237
+ }
238
+ memcpy(chunk, g_result_data + start, bytes);
239
+ }