CIRCT 23.0.0git
Loading...
Searching...
No Matches
test_codegen.py
Go to the documentation of this file.
1"""Tests for UnionType support in codegen (CppTypePlanner + CppTypeEmitter)."""
2
3import tempfile
4from pathlib import Path
5
6import esiaccel.types as types
7from esiaccel.codegen import CppTypePlanner, CppTypeEmitter
8
9
10def _generate_header(type_table, system_name="test_ns"):
11 """Helper: run the planner + emitter on a type table and return the header."""
12 planner = CppTypePlanner(type_table)
13 emitter = CppTypeEmitter(planner)
14 with tempfile.TemporaryDirectory() as tmpdir:
15 emitter.write_header(Path(tmpdir), system_name)
16 return (Path(tmpdir) / "types.h").read_text()
17
18
20 """A simple union with two scalar fields produces a C++ union."""
21 uint8 = types.UIntType("ui8", 8)
22 uint16 = types.UIntType("ui16", 16)
23 union_t = types.UnionType("!hw.union<a: ui8, b: ui16>", [("a", uint8),
24 ("b", uint16)])
25
26 hdr = _generate_header([union_t])
27 assert "union " in hdr
28 assert '_ESI_ID = "!hw.union<a: ui8, b: ui16>"' in hdr
29 # Field "a" (8 bits) is narrower than the 16-bit union → wrapper struct
30 # with _pad before the data field.
31 assert "struct _union_a_ui8_b_ui16_a" in hdr
32 assert "uint8_t _pad[1]" in hdr
33 assert "uint8_t a;" in hdr
34 # In the wrapper struct, _pad appears before the field.
35 pad_pos = hdr.index("uint8_t _pad[1]")
36 field_a_pos = hdr.index("uint8_t a;")
37 assert pad_pos < field_a_pos
38 # Field "b" (16 bits) is full width → no wrapper, appears directly in union.
39 assert "uint16_t b;" in hdr
40 # Wrapper struct for "a" is emitted before the union keyword.
41 wrapper_pos = hdr.index("struct _union_a_ui8_b_ui16_a")
42 union_pos = hdr.index("union ")
43 assert wrapper_pos < union_pos
44 # Inside the union, "a" comes before "b" (declaration order preserved).
45 union_body = hdr[union_pos:]
46 assert union_body.index("a;") < union_body.index("b;")
47 # The union member for "a" uses the wrapper struct type, not a raw int.
48 assert "_union_a_ui8_b_ui16_a a;" in union_body
49
50
52 """A union containing a struct field emits the struct before the union."""
53 uint8 = types.UIntType("ui8", 8)
54 uint16 = types.UIntType("ui16", 16)
55 inner = types.StructType("!hw.struct<x: ui8, y: ui8>", [("x", uint8),
56 ("y", uint8)])
57 union_t = types.UnionType("!hw.union<header: ui16, data: !s>",
58 [("header", uint16), ("data", inner)])
59
60 hdr = _generate_header([union_t])
61 # The inner struct must appear before the wrapper structs and union.
62 struct_pos = hdr.index("struct _struct")
63 union_pos = hdr.index("union ")
64 assert struct_pos < union_pos
65 assert "data;" in hdr
66 # "header" is 16 bits in a 16-bit union (both fields are 16 bits),
67 # so no padding wrapper is needed for either field.
68 assert "_pad" not in hdr
69
70
72 """Unions are properly ordered with respect to struct dependencies."""
73 uint8 = types.UIntType("ui8", 8)
74 s1 = types.StructType("!hw.struct<p: ui8>", [("p", uint8)])
75 s2 = types.StructType("!hw.struct<q: ui8>", [("q", uint8)])
76 union_t = types.UnionType("!hw.union<a: !s1, b: !s2>", [("a", s1), ("b", s2)])
77
78 hdr = _generate_header([union_t])
79 union_pos = hdr.index("union ")
80 # Both structs should appear before the union.
81 for keyword in ["struct _struct_p_ui8", "struct _struct_q_ui8"]:
82 assert keyword in hdr
83 assert hdr.index(keyword) < union_pos
84
85
87 """A struct with a union field emits the union before the struct."""
88 uint8 = types.UIntType("ui8", 8)
89 uint16 = types.UIntType("ui16", 16)
90 union_t = types.UnionType("!hw.union<a: ui8, b: ui16>", [("a", uint8),
91 ("b", uint16)])
92 outer = types.StructType("!hw.struct<tag: ui8, data: !u>",
93 [("tag", uint8), ("data", union_t)])
94
95 hdr = _generate_header([outer])
96 assert "union " in hdr
97 # Wrapper struct for padded field "a" and the union both precede the
98 # outer struct that references the union.
99 wrapper_pos = hdr.index("struct _union")
100 union_pos = hdr.index("union ")
101 struct_pos = hdr.index("struct _struct")
102 assert wrapper_pos < union_pos < struct_pos
103
104
106 """The planner auto-generates deterministic names for unions."""
107 uint8 = types.UIntType("ui8", 8)
108 union_t = types.UnionType("!hw.union<x: ui8>", [("x", uint8)])
109
110 planner = CppTypePlanner([union_t])
111 assert union_t in planner.type_id_map
112 name = planner.type_id_map[union_t]
113 assert name.startswith("_union")
114
115
117 """A TypeAlias wrapping a union emits the union then a using alias."""
118 uint8 = types.UIntType("ui8", 8)
119 uint16 = types.UIntType("ui16", 16)
120 union_t = types.UnionType("!hw.union<a: ui8, b: ui16>", [("a", uint8),
121 ("b", uint16)])
122 alias = types.TypeAlias("!hw.typealias<MyUnion>", "MyUnion", union_t)
123
124 hdr = _generate_header([alias])
125 assert "union " in hdr
126 assert "using MyUnion" in hdr
127
128
130 """Integrals of the same width don't need padding wrappers."""
131 uint16 = types.UIntType("ui16", 16)
132 sint16 = types.SIntType("si16", 16)
133 union_t = types.UnionType("!hw.union<a: ui16, b: si16>", [("a", uint16),
134 ("b", sint16)])
135
136 hdr = _generate_header([union_t])
137 assert "union " in hdr
138 assert "_pad" not in hdr
139 # Both fields appear directly in the union as raw types.
140 union_body = hdr[hdr.index("union "):]
141 assert "uint16_t a;" in union_body
142 assert "int16_t b;" in union_body
143
144
146 """Union fields are emitted in declaration order, not reversed."""
147 uint8 = types.UIntType("ui8", 8)
148 sint16 = types.SIntType("si16", 16)
149 uint32 = types.UIntType("ui32", 32)
150 union_t = types.UnionType("!hw.union<z: ui8, m: si16, a: ui32>",
151 [("z", uint8), ("m", sint16), ("a", uint32)])
152
153 hdr = _generate_header([union_t])
154 # Fields z (8 bit) and m (16 bit) need padding wrappers; a (32 bit) doesn't.
155 assert "_pad[3]" in hdr # z wrapper: 4 - 1 = 3 bytes padding
156 assert "_pad[2]" in hdr # m wrapper: 4 - 2 = 2 bytes padding
157 # In each wrapper struct, _pad appears before the data field.
158 z_pad = hdr.index("_pad[3]")
159 z_field = hdr.index("uint8_t z;")
160 assert z_pad < z_field
161 m_pad = hdr.index("_pad[2]")
162 m_field = hdr.index("int16_t m;")
163 assert m_pad < m_field
164 # Inside the union body, field order is preserved (z, m, a).
165 union_body = hdr[hdr.index("union "):]
166 z_pos = union_body.index(" z;")
167 m_pos = union_body.index(" m;")
168 a_pos = union_body.index(" a;")
169 assert z_pos < m_pos < a_pos
170 # Field a (full width) has no wrapper.
171 assert "uint32_t a;" in union_body
172 # Wrapped fields use wrapper struct types as union members.
173 assert "_union_z_ui8_m_si16_a_ui32_z z;" in union_body
174 assert "_union_z_ui8_m_si16_a_ui32_m m;" in union_body
175
176
178 """Bulk-encoded list windows emit a SegmentedMessageData helper."""
179 uint16 = types.UIntType("ui16", 16)
180 uint32 = types.UIntType("ui32", 32)
181 coord_struct_id = "!hw.struct<x: ui32, y: ui32>"
182 coord_alias_id = (
183 f"!hw.typealias<@esi_runtime_codegen::@Coord, {coord_struct_id}>")
184 coord_list_id = f"!esi.list<{coord_alias_id}>"
185 arg_struct_id = (
186 f"!hw.struct<x_translation: ui32, y_translation: ui32, coords: "
187 f"{coord_list_id}>")
188 header_struct_id = (
189 "!hw.struct<x_translation: ui32, y_translation: ui32, coords_count: "
190 "ui16>")
191 data_struct_id = f"!hw.struct<coords: !hw.array<1x{coord_alias_id}>>"
192 lowered_id = f"!hw.union<header: {header_struct_id}, data: {data_struct_id}>"
193 serial_args_id = (f'!esi.window<"serial_coord_args", {arg_struct_id}, '
194 '[<"header", [<"x_translation">, <"y_translation">, '
195 '<"coords" countWidth 16>]>, <"data", [<"coords", 1>]>]>')
196
197 coord_inner = types.StructType(coord_struct_id, [("x", uint32),
198 ("y", uint32)])
199 coord = types.TypeAlias(coord_alias_id, "Coord", coord_inner)
200 coord_list = types.ListType(coord_list_id, coord)
201 arg_struct = types.StructType(arg_struct_id, [("x_translation", uint32),
202 ("y_translation", uint32),
203 ("coords", coord_list)])
204 header_struct = types.StructType(header_struct_id, [("x_translation", uint32),
205 ("y_translation", uint32),
206 ("coords_count", uint16)])
207 data_struct = types.StructType(
208 data_struct_id,
209 [("coords", types.ArrayType(f"!hw.array<1x{coord_alias_id}>", coord, 1))],
210 )
211 lowered = types.UnionType(lowered_id, [("header", header_struct),
212 ("data", data_struct)])
213 serial_args = types.WindowType(
214 serial_args_id, "serial_coord_args", arg_struct, lowered, [
215 types.WindowType.Frame(
216 "header",
217 [
218 types.WindowType.Field("x_translation", 0, 0),
219 types.WindowType.Field("y_translation", 0, 0),
220 types.WindowType.Field("coords", 0, 16),
221 ],
222 ),
223 types.WindowType.Frame(
224 "data",
225 [types.WindowType.Field("coords", 1, 0)],
226 ),
227 ])
228
229 hdr = _generate_header([coord, serial_args])
230 assert "Unsupported type" not in hdr
231 assert "struct serial_coord_args : public esi::SegmentedMessageData" in hdr
232 assert "using value_type = Coord;" in hdr
233 assert "using count_type = uint16_t;" in hdr
234 assert "count_type coords_count;" in hdr
235 assert "uint8_t _pad[2];" in hdr
236 assert "Coord coords;" in hdr
237 assert hdr.index("struct data_frame {") < hdr.index(
238 "private:\n struct header_frame {")
239 assert "std::vector<data_frame> data_frames;" in hdr
240 assert "esi::Segment segment(size_t idx) const override" in hdr
241 assert "footer.coords_count = 0;" in hdr
242 assert "const std::vector<value_type> &coords" in hdr
243 assert "void construct(uint32_t x_translation, uint32_t y_translation, std::vector<data_frame> frames)" in hdr
244 assert "construct(x_translation, y_translation, std::move(frames));" in hdr
245 assert "auto &frame = frames.emplace_back();" in hdr
246 assert "for (const auto &element : coords) {" in hdr
247 assert "frame.coords = element;" in hdr
248 assert '!esi.window<\\"serial_coord_args\\"' in hdr
249 assert 'throw std::out_of_range("serial_coord_args: invalid segment index")' in hdr
250
251
253 """Headers pad out to the data frame width for count-only windows."""
254 uint16 = types.UIntType("ui16", 16)
255 uint32 = types.UIntType("ui32", 32)
256 element_id = "!hw.struct<x: ui32, y: ui32>"
257 list_id = f"!esi.list<{element_id}>"
258 arg_struct_id = f"!hw.struct<coords: {list_id}>"
259 header_struct_id = "!hw.struct<coords_count: ui16>"
260 data_struct_id = f"!hw.struct<coords: !hw.array<1x{element_id}>>"
261 lowered_id = f"!hw.union<header: {header_struct_id}, data: {data_struct_id}>"
262 window_id = (f'!esi.window<"coords_only", {arg_struct_id}, '
263 '[<"header", [<"coords" countWidth 16>]>, '
264 '<"data", [<"coords", 1>]>]>')
265
266 element = types.StructType(element_id, [("x", uint32), ("y", uint32)])
267 coord_list = types.ListType(list_id, element)
268 arg_struct = types.StructType(arg_struct_id, [("coords", coord_list)])
269 header_struct = types.StructType(header_struct_id, [("coords_count", uint16)])
270 data_struct = types.StructType(
271 data_struct_id,
272 [("coords", types.ArrayType(f"!hw.array<1x{element_id}>", element, 1))],
273 )
274 lowered = types.UnionType(lowered_id, [("header", header_struct),
275 ("data", data_struct)])
276 window = types.WindowType(window_id, "coords_only", arg_struct, lowered, [
277 types.WindowType.Frame("header",
278 [types.WindowType.Field("coords", 0, 16)]),
279 types.WindowType.Frame("data", [types.WindowType.Field("coords", 1, 0)]),
280 ])
281
282 hdr = _generate_header([window])
283 assert "struct coords_only : public esi::SegmentedMessageData" in hdr
284 assert "struct header_frame {\n uint8_t _pad[6];\n count_type coords_count;\n };" in hdr
285 assert "header_frame footer{};" in hdr
286 assert "void construct(std::vector<data_frame> frames)" in hdr
287
288
290 """Window helpers copy array header fields and array-valued elements."""
291 uint8 = types.UIntType("ui8", 8)
292 uint16 = types.UIntType("ui16", 16)
293 header_array_id = "!hw.array<2xui16>"
294 value_array_id = "!hw.array<4xui8>"
295 list_id = f"!esi.list<{value_array_id}>"
296 arg_struct_id = (
297 f"!hw.struct<header_words: {header_array_id}, payloads: {list_id}>")
298 header_struct_id = (
299 f"!hw.struct<header_words: {header_array_id}, payloads_count: ui16>")
300 data_struct_id = f"!hw.struct<payloads: !hw.array<1x{value_array_id}>>"
301 lowered_id = f"!hw.union<header: {header_struct_id}, data: {data_struct_id}>"
302 window_id = (f'!esi.window<"array_payloads", {arg_struct_id}, '
303 '[<"header", [<"header_words">, <"payloads" countWidth 16>]>, '
304 '<"data", [<"payloads", 1>]>]>')
305
306 header_array = types.ArrayType(header_array_id, uint16, 2)
307 value_array = types.ArrayType(value_array_id, uint8, 4)
308 payload_list = types.ListType(list_id, value_array)
309 arg_struct = types.StructType(arg_struct_id, [("header_words", header_array),
310 ("payloads", payload_list)])
311 header_struct = types.StructType(header_struct_id,
312 [("header_words", header_array),
313 ("payloads_count", uint16)])
314 data_struct = types.StructType(
315 data_struct_id,
316 [("payloads",
317 types.ArrayType(f"!hw.array<1x{value_array_id}>", value_array, 1))],
318 )
319 lowered = types.UnionType(lowered_id, [("header", header_struct),
320 ("data", data_struct)])
321 window = types.WindowType(window_id, "array_payloads", arg_struct, lowered, [
322 types.WindowType.Frame(
323 "header",
324 [
325 types.WindowType.Field("header_words", 0, 0),
326 types.WindowType.Field("payloads", 0, 16),
327 ],
328 ),
329 types.WindowType.Frame(
330 "data",
331 [types.WindowType.Field("payloads", 1, 0)],
332 ),
333 ])
334
335 hdr = _generate_header([window])
336 assert "#include <cstring>" in hdr
337 assert "struct array_payloads : public esi::SegmentedMessageData" in hdr
338 assert "using value_type = uint8_t[4];" in hdr
339 assert "using count_type = uint16_t;" in hdr
340 assert "uint16_t header_words[2];" in hdr
341 assert "uint8_t payloads[4];" in hdr
342 assert "array_payloads(const uint16_t (&header_words)[2], const std::vector<value_type> &payloads)" in hdr
343 assert "void construct(const uint16_t (&header_words)[2], std::vector<data_frame> frames)" in hdr
344 assert "std::memcpy(&header.header_words, &header_words, sizeof(header.header_words));" in hdr
345 assert "std::memcpy(&frame.payloads, &element, sizeof(frame.payloads));" in hdr
346 assert 'throw std::out_of_range("array_payloads: invalid segment index")' in hdr
test_windowed_list_arrays_in_header_and_value_type()
test_union_field_order_preserved()
test_union_with_struct_field()
_generate_header(type_table, system_name="test_ns")
test_windowed_list_bulk_message_wrapper()
test_union_ordering_among_structs()
test_union_planner_naming()
test_union_same_width_integrals()
test_windowed_list_header_padding_matches_frame_width()