Skip to content

Commit 16b91ba

Browse files
authored
Merge pull request #48 from fvutils/mballance/struct
Add support for passing structs
2 parents d001a45 + d7176f9 commit 16b91ba

17 files changed

Lines changed: 1480 additions & 4 deletions

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ hardware description languages, with a current focus on SystemVerilog.
66
- Call SystemVerilog functions and tasks from Python, and invoke Python methods from SystemVerilog
77
- Use the Python C API, and a SystemVerilog convenience API, to call Python
88
- **Run async pytest tests from SystemVerilog testbenches**
9+
- **Pass structured data between Python and SystemVerilog using ctypes.Structure**
910

1011
## Key Features
1112

@@ -24,6 +25,26 @@ endmodule
2425

2526
See [doc/pytest_runner.md](doc/pytest_runner.md) for complete documentation and examples.
2627

28+
### Struct Type Support
29+
Pass complex structured data between Python and SystemVerilog using `ctypes.Structure`:
30+
31+
```python
32+
import ctypes as ct
33+
import hdl_if as hif
34+
35+
class Point(ct.Structure):
36+
_fields_ = [("x", ct.c_int32), ("y", ct.c_int32)]
37+
38+
@hif.api
39+
class GeometryAPI(object):
40+
@hif.imp
41+
async def draw_point(self, p: Point):
42+
"""Called from SystemVerilog"""
43+
pass
44+
```
45+
46+
PyHDL-IF automatically generates SystemVerilog struct typedefs and conversion functions. See [doc/struct.md](doc/struct.md) for complete documentation.
47+
2748
## Installing PyHDL-IF
2849
Installing PyHDL-IF in your own Python virtual environment is easy:
2950

doc/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ variety of abstraction levels.
1818
overview
1919
sim_integ
2020
pytest_runner
21+
structs
2122
uvm
2223
cmdref
2324
py_api

doc/source/overview.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Call-Interface API
2727
The *Call Interface* API enables users to create pairs of communicating
2828
objects, where one object lives in Python, the other lives in HDL.
2929

30+
The Call Interface supports passing structured data between Python and
31+
SystemVerilog using ``ctypes.Structure`` types. This enables efficient
32+
interchange of complex data structures. See :doc:`structs` for details.
33+
3034
TLM API
3135
*******
3236
The TLM interface implements a FIFO-based interface between Python and

doc/source/quickstart.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,31 @@ Then, run tests from your SystemVerilog testbench:
5252
5353
For complete details, see :doc:`pytest_runner`.
5454

55+
Using Structured Data (Structs)
56+
================================
57+
58+
PyHDL-IF supports passing structured data between Python and SystemVerilog using
59+
``ctypes.Structure`` types:
60+
61+
.. code-block:: python
62+
63+
import ctypes as ct
64+
import hdl_if as hif
65+
66+
class Point(ct.Structure):
67+
_fields_ = [
68+
("x", ct.c_int32),
69+
("y", ct.c_int32),
70+
]
71+
72+
@hif.api
73+
class GeometryAPI(object):
74+
@hif.imp
75+
async def draw_point(self, p: Point):
76+
"""Called from SystemVerilog"""
77+
pass
78+
79+
PyHDL-IF automatically generates SystemVerilog struct typedefs and conversion
80+
functions. For complete details and a working example, see :doc:`structs`.
81+
82+

doc/source/structs.rst

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
######################
2+
Struct Type Support
3+
######################
4+
5+
PyHDL-IF supports using Python ``ctypes.Structure`` types as function parameters
6+
and return types in API methods. This enables efficient passing of structured data
7+
between Python and SystemVerilog.
8+
9+
Overview
10+
========
11+
12+
When you define an API method that uses a struct type, PyHDL-IF automatically:
13+
14+
1. Generates a SystemVerilog ``typedef struct`` definition
15+
2. Creates bidirectional conversion functions between Python and SystemVerilog
16+
3. Handles the struct in method calls transparently
17+
18+
Defining Struct Types
19+
======================
20+
21+
Struct types are defined as Python classes inheriting from ``ctypes.Structure``:
22+
23+
.. code-block:: python
24+
25+
import ctypes as ct
26+
import hdl_if as hif
27+
28+
class Point(ct.Structure):
29+
_fields_ = [
30+
("x", ct.c_int32),
31+
("y", ct.c_int32),
32+
]
33+
34+
class Color(ct.Structure):
35+
_fields_ = [
36+
("r", ct.c_uint8),
37+
("g", ct.c_uint8),
38+
("b", ct.c_uint8),
39+
("a", ct.c_uint8),
40+
]
41+
42+
Using Structs in APIs
43+
=====================
44+
45+
Once defined, structs can be used as parameters and return types in API methods:
46+
47+
.. code-block:: python
48+
49+
@hif.api
50+
class GraphicsAPI(object):
51+
52+
@hif.imp
53+
async def set_point(self, p: Point):
54+
"""Called from SV to set a point"""
55+
pass
56+
57+
@hif.imp
58+
async def get_point(self) -> Point:
59+
"""Called from SV to get a point"""
60+
pass
61+
62+
@hif.imp
63+
async def set_color(self, c: Color):
64+
"""Called from SV to set a color"""
65+
pass
66+
67+
Supported Field Types
68+
=====================
69+
70+
PyHDL-IF supports all standard ctypes in struct fields:
71+
72+
.. list-table:: Type Mapping
73+
:header-rows: 1
74+
:widths: 30 30 40
75+
76+
* - Python Type
77+
- SystemVerilog Type
78+
- Description
79+
* - ``ctypes.c_int8``
80+
- ``byte``
81+
- Signed 8-bit integer
82+
* - ``ctypes.c_uint8``
83+
- ``byte unsigned``
84+
- Unsigned 8-bit integer
85+
* - ``ctypes.c_int16``
86+
- ``shortint``
87+
- Signed 16-bit integer
88+
* - ``ctypes.c_uint16``
89+
- ``shortint unsigned``
90+
- Unsigned 16-bit integer
91+
* - ``ctypes.c_int32``
92+
- ``int``
93+
- Signed 32-bit integer
94+
* - ``ctypes.c_uint32``
95+
- ``int unsigned``
96+
- Unsigned 32-bit integer
97+
* - ``ctypes.c_int64``
98+
- ``longint``
99+
- Signed 64-bit integer
100+
* - ``ctypes.c_uint64``
101+
- ``longint unsigned``
102+
- Unsigned 64-bit integer
103+
* - ``ctypes.c_float``
104+
- ``shortreal``
105+
- 32-bit floating point
106+
* - ``ctypes.c_double``
107+
- ``real``
108+
- 64-bit floating point
109+
* - ``ctypes.c_bool``
110+
- ``bit``
111+
- Boolean
112+
* - ``str``
113+
- ``string``
114+
- String type
115+
116+
Generated SystemVerilog Code
117+
=============================
118+
119+
For the ``Point`` example above, PyHDL-IF generates:
120+
121+
Typedef
122+
-------
123+
124+
.. code-block:: systemverilog
125+
126+
typedef struct packed {
127+
int x;
128+
int y;
129+
} Point_t;
130+
131+
Conversion Functions
132+
--------------------
133+
134+
**Python to SystemVerilog:**
135+
136+
.. code-block:: systemverilog
137+
138+
function Point_t pyhdl_if_py_to_struct_Point(pyhdl_if::PyObject py_obj);
139+
Point_t result;
140+
pyhdl_if::PyObject __field_x, __field_y;
141+
__field_x = pyhdl_if::PyObject_GetAttrString(py_obj, "x");
142+
result.x = int'(pyhdl_if::PyLong_AsLong(__field_x));
143+
pyhdl_if::Py_DecRef(__field_x);
144+
__field_y = pyhdl_if::PyObject_GetAttrString(py_obj, "y");
145+
result.y = int'(pyhdl_if::PyLong_AsLong(__field_y));
146+
pyhdl_if::Py_DecRef(__field_y);
147+
return result;
148+
endfunction
149+
150+
**SystemVerilog to Python:**
151+
152+
.. code-block:: systemverilog
153+
154+
function pyhdl_if::PyObject pyhdl_if_struct_to_py_Point(Point_t sv_struct);
155+
pyhdl_if::PyObject __module, __class, __args, result;
156+
__module = pyhdl_if::PyImport_ImportModule("your_module");
157+
__class = pyhdl_if::PyObject_GetAttrString(__module, "Point");
158+
__args = pyhdl_if::PyTuple_New(2);
159+
void'(pyhdl_if::PyTuple_SetItem(__args, 0,
160+
pyhdl_if::PyLong_FromLong(longint'(sv_struct.x))));
161+
void'(pyhdl_if::PyTuple_SetItem(__args, 1,
162+
pyhdl_if::PyLong_FromLong(longint'(sv_struct.y))));
163+
result = pyhdl_if::PyObject_Call(__class, __args, null);
164+
pyhdl_if::Py_DecRef(__args);
165+
pyhdl_if::Py_DecRef(__class);
166+
pyhdl_if::Py_DecRef(__module);
167+
return result;
168+
endfunction
169+
170+
Using Structs from SystemVerilog
171+
=================================
172+
173+
In your SystemVerilog testbench, implement the interface methods using the
174+
generated struct types:
175+
176+
.. code-block:: systemverilog
177+
178+
class GraphicsAPI_Impl implements graphics_api_pkg::GraphicsAPI_imp_if;
179+
graphics_api_pkg::Point_t m_point;
180+
181+
virtual task set_point(input graphics_api_pkg::Point_t p);
182+
$display("Received point: x=%0d, y=%0d", p.x, p.y);
183+
m_point = p;
184+
endtask
185+
186+
virtual task get_point(output graphics_api_pkg::Point_t retval);
187+
$display("Returning point: x=%0d, y=%0d", m_point.x, m_point.y);
188+
retval = m_point;
189+
endtask
190+
endclass
191+
192+
Packed vs Unpacked Structs
193+
===========================
194+
195+
PyHDL-IF automatically determines whether to use ``packed`` or unpacked structs:
196+
197+
- **Packed structs**: Used when all fields are synthesizable types (integers, bits)
198+
- **Unpacked structs**: Used when any field is a floating-point type (``real``, ``shortreal``)
199+
200+
This is necessary because SystemVerilog does not allow real types in packed structs.
201+
202+
Complete Example
203+
================
204+
205+
A complete working example is available at ``examples/call/dpi/struct_passing/``.
206+
The example demonstrates:
207+
208+
- Defining multiple struct types (Point, Color, Rectangle)
209+
- Using structs as both parameters and return values
210+
- Bidirectional struct passing between Python and SystemVerilog
211+
- A simple graphics API implementation
212+
213+
Run the example:
214+
215+
.. code-block:: bash
216+
217+
cd examples/call/dpi/struct_passing
218+
dfm run -Dsim=vlt sim-run
219+
220+
Limitations
221+
===========
222+
223+
1. **Nested Structs**: Structs containing other structs as fields are not currently tested
224+
2. **Arrays in Structs**: Array fields (using ctypes array types) are not currently supported
225+
3. **Module Name**: The SV-to-Python conversion function needs to import the Python module
226+
containing the struct definition. Ensure the module is in the Python path.
227+
228+
See Also
229+
========
230+
231+
- :doc:`quickstart` - Getting started guide
232+
- :doc:`py_api` - Python API reference
233+
- :doc:`sv_api` - SystemVerilog API reference
234+
- Full struct documentation: `doc/struct.md <https://github.com/fvutils/pyhdl-if/blob/main/doc/struct.md>`_

examples/Examples.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,13 @@ You can see the detailed commands used to run the example by running:
6565
% dfm --log-level=INFO run -Dsim=<sim> sim-run
6666
```
6767

68+
## Available Examples
69+
70+
### call/dpi/call_sv_bfm
71+
Demonstrates calling SystemVerilog tasks and functions from Python using a Wishbone initiator BFM.
72+
73+
### call/dpi/struct_passing
74+
Demonstrates passing structured data (ctypes.Structure) between Python and SystemVerilog. Shows how to define and use custom struct types for complex data interchange.
75+
76+
6877

0 commit comments

Comments
 (0)