Skip to content

Commit e8cf90e

Browse files
Copilotmballance
andcommitted
Fix duplicate SV struct typedefs when multiple APIs share same struct type
Co-authored-by: mballance <1340805+mballance@users.noreply.github.com>
1 parent 56c45e3 commit e8cf90e

2 files changed

Lines changed: 52 additions & 5 deletions

File tree

src/hdl_if/impl/call/gen_sv_class.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def __init__(self, out, ind="", uvm=False, deprecated=False):
3737
self._deprecated = deprecated
3838
self._have_imp = False
3939
self._struct_types = set() # Track struct types used in API
40+
self._emitted_struct_types = set() # Track struct types already emitted (across multiple gen() calls)
4041
pass
4142

4243
def _collect_methods(self, api: ApiDef):
@@ -93,16 +94,20 @@ def gen(self, api : ApiDef):
9394
# Collect struct types used in the API
9495
self._collect_struct_types(api)
9596

96-
# Generate struct typedefs
97-
if self._struct_types:
98-
for struct_type in sorted(self._struct_types, key=lambda t: t.__name__):
97+
# Generate struct typedefs only for types not already emitted
98+
new_struct_types = self._struct_types - self._emitted_struct_types
99+
if new_struct_types:
100+
for struct_type in sorted(new_struct_types, key=lambda t: t.__name__):
99101
self.gen_struct_typedef(struct_type)
100102
self.println()
101103

102-
# Generate conversion functions for each struct
103-
for struct_type in sorted(self._struct_types, key=lambda t: t.__name__):
104+
# Generate conversion functions for each new struct
105+
for struct_type in sorted(new_struct_types, key=lambda t: t.__name__):
104106
self.gen_struct_conversion_functions(struct_type)
105107
self.println()
108+
109+
self._emitted_struct_types.update(new_struct_types)
110+
self._struct_types = set()
106111

107112
# Existing class generation (unchanged)
108113
# self.gen_class_interface_exp(api)

tests/unit/test_api_gen_sv_filtering.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,3 +450,45 @@ def __init__(self):
450450
assert len(filtered) == 3
451451
names = [a.name for a in filtered]
452452
assert names == ["First", "Second", "Third"]
453+
454+
def test_no_duplicate_struct_typedefs_across_apis(self):
455+
"""Test that struct typedefs are not duplicated when multiple APIs share the same struct type."""
456+
457+
class Point(ct.Structure):
458+
_fields_ = [
459+
("x", ct.c_int32),
460+
("y", ct.c_int32),
461+
]
462+
463+
@api
464+
class FirstAPI(object):
465+
@imp
466+
async def func1(self, pt: Point):
467+
pass
468+
469+
@api
470+
class SecondAPI(object):
471+
@exp
472+
def func2(self, pt: Point):
473+
pass
474+
475+
all_apis = ApiDefRgy.inst().getApis()
476+
477+
out = io.StringIO()
478+
gen = GenSVClass(out, ind="", uvm=False, deprecated=False)
479+
for a in all_apis:
480+
gen.gen(a)
481+
sv_content = out.getvalue()
482+
483+
# Count occurrences of the struct typedef — must appear exactly once
484+
typedef_count = sv_content.count("typedef struct")
485+
assert typedef_count == 1, (
486+
f"Expected 1 struct typedef for Point_t, but found {typedef_count}. "
487+
f"Duplicate typedefs cause SV compilation errors."
488+
)
489+
# Conversion function definitions must also appear exactly once
490+
# (the name may also appear in calls within API method bodies)
491+
assert sv_content.count("function Point_t pyhdl_if_py_to_struct_Point") == 1, \
492+
"pyhdl_if_py_to_struct_Point conversion function definition duplicated"
493+
assert sv_content.count("function pyhdl_if::PyObject pyhdl_if_struct_to_py_Point") == 1, \
494+
"pyhdl_if_struct_to_py_Point conversion function definition duplicated"

0 commit comments

Comments
 (0)