CIRCT 24.0.0git
Loading...
Searching...
No Matches
test_types.py
Go to the documentation of this file.
1import json
2
3import pytest
4
5import esiaccel.types as types
6
7
9 ctxt = types.cpp.Context()
10 manifest = types.cpp.Manifest(
11 ctxt, json.dumps({
12 "apiVersion": 1,
13 "types": [],
14 "modules": []
15 }))
16 assert manifest.api_version == 1
17
18
19@pytest.mark.parametrize("version", [0, 2, -1, 1 << 32 | 1, 1.5, "1", None])
21 ctxt = types.cpp.Context()
22 # Reject the version before parsing any types or design metadata.
23 with pytest.raises(RuntimeError, match="Unsupported ESI ABI version:"):
24 types.cpp.Manifest(ctxt, json.dumps({"apiVersion": version}))
25
26
28 void_type = types.VoidType("void")
29 assert void_type is not None
30 assert isinstance(void_type, types.VoidType)
31
32 bits_type = types.BitsType("bits8", 8)
33 assert bits_type is not None
34 assert isinstance(bits_type, types.BitsType)
35
36 uint_type = types.UIntType("uint32", 32)
37 assert uint_type is not None
38 assert isinstance(uint_type, types.UIntType)
39
40 sint_type = types.SIntType("sint8", 8)
41 assert sint_type is not None
42 assert isinstance(sint_type, types.SIntType)
43 assert sint_type.bit_width == 8
44
45 struct_type = types.StructType(
46 "mystruct",
47 [("field1", types.UIntType("uint8", 8)),
48 ("field2", types.UIntType("uint16", 16))],
49 )
50 assert struct_type is not None
51 assert isinstance(struct_type, types.StructType)
52 field_map = {name: field_type for name, field_type in struct_type.fields}
53 assert isinstance(field_map["field1"], types.UIntType)
54 assert isinstance(field_map["field2"], types.UIntType)
55
56 array_type = types.ArrayType("uint8_array", types.UIntType("uint8", 8), 10)
57 assert array_type is not None
58 assert isinstance(array_type, types.ArrayType)
59 assert hasattr(array_type, "element_type")
60 assert isinstance(array_type.element_type, types.UIntType)
61 assert hasattr(array_type, "size")
62 assert array_type.size == 10
63
64 any_type = types.AnyType("any")
65 assert any_type is not None
66 assert isinstance(any_type, types.AnyType)
67 valid, reason = any_type.is_valid(0)
68 assert not valid
69 assert "any type" in reason
70 assert any_type.bit_width == -1
71 try:
72 any_type.serialize(0)
73 except ValueError as exc:
74 assert "any type" in str(exc)
75 else:
76 assert False, "AnyType.serialize should raise"
77
78 alias_inner = types.UIntType("alias_inner", 16)
79 type_alias = types.TypeAlias("alias_scope", "aliasName", alias_inner)
80 assert type_alias is not None
81 assert isinstance(type_alias, types.TypeAlias)
82 assert type_alias.name == "aliasName"
83 assert isinstance(type_alias.inner_type, types.UIntType)
84 assert type_alias.bit_width == alias_inner.bit_width
85 alias_valid, alias_reason = type_alias.is_valid(42)
86 inner_valid, inner_reason = alias_inner.is_valid(42)
87 assert alias_valid == inner_valid
88 assert alias_reason == inner_reason
89 serialized = type_alias.serialize(42)
90 inner_serialized = alias_inner.serialize(42)
91 assert serialized == inner_serialized
92 assert type_alias.deserialize(serialized) == alias_inner.deserialize(
93 serialized)
94 assert str(type_alias) == "aliasName"
95
96
98 uint8 = types.UIntType("uint8", 8)
99 uint16 = types.UIntType("uint16", 16)
100
101 union_type = types.UnionType("myunion", [("a", uint8), ("b", uint16)])
102 assert union_type is not None
103 assert isinstance(union_type, types.UnionType)
104 assert union_type.bit_width == 16
105
106 field_map = {name: ty for name, ty in union_type.fields}
107 assert isinstance(field_map["a"], types.UIntType)
108 assert isinstance(field_map["b"], types.UIntType)
109
110 # is_valid: single active field
111 valid, reason = union_type.is_valid({"a": 42})
112 assert valid, reason
113 valid, reason = union_type.is_valid({"b": 1000})
114 assert valid, reason
115
116 # is_valid: wrong number of fields
117 valid, reason = union_type.is_valid({"a": 1, "b": 2})
118 assert not valid
119 assert "exactly 1" in reason
120
121 # is_valid: unknown field
122 valid, reason = union_type.is_valid({"c": 1})
123 assert not valid
124 assert "unknown" in reason
125
126 # is_valid: not a dict
127 valid, reason = union_type.is_valid(42)
128 assert not valid
129
130 # serialize / deserialize round-trip through field "a"
131 serialized_a = union_type.serialize({"a": 42})
132 assert serialized_a == bytearray([42, 0])
133 (deserialized, remaining) = union_type.deserialize(serialized_a)
134 assert remaining == bytearray()
135 assert "a" in deserialized
136 assert "b" in deserialized
137 assert deserialized["a"] == 42
138 assert deserialized["b"] == 42
139
140 # serialize / deserialize round-trip through field "b"
141 # Field "b" is 2 bytes (full width), no padding needed.
142 serialized_b = union_type.serialize({"b": 0x1234})
143 assert len(serialized_b) == 2
144 assert serialized_b == bytearray([0x34, 0x12]) # little-endian, no padding
145 (deserialized_b,
146 remaining_b) = union_type.deserialize(serialized_b + bytearray([0xDE, 0xAD]))
147 assert remaining_b == bytearray([0xDE, 0xAD])
148 assert deserialized_b["a"] == 0x34
149 assert deserialized_b["b"] == 0x1234
150
151
153 """Union members share the LSB without changing their internal layout."""
154 uint8 = types.UIntType("uint8", 8)
155 uint32 = types.UIntType("uint32", 32)
156 small_struct = types.StructType("!hw.struct<x: ui8, y: ui8>", [("x", uint8),
157 ("y", uint8)])
158 union_type = types.UnionType("myunion2", [("wide", uint32),
159 ("narrow", small_struct)])
160 assert union_type.bit_width == 32
161
162 serialized = union_type.serialize({"narrow": {"x": 0xAA, "y": 0xBB}})
163 assert serialized == bytearray([0xBB, 0xAA, 0, 0])
164
165 (result, leftover) = union_type.deserialize(serialized)
166 assert leftover == bytearray()
167 assert result["narrow"] == {"x": 0xAA, "y": 0xBB}
168 assert result["wide"] == 0xAABB
169
170
172 union_type = types.UnionType("signed_union",
173 [("small", types.SIntType("si8", 8)),
174 ("wide", types.UIntType("ui16", 16))])
175 assert union_type.serialize({"small": -2}) == bytearray([0xFE, 0])
176 result, remaining = union_type.deserialize(bytearray([0x80, 0xAB]))
177 assert result == {"small": -128, "wide": 0xAB80}
178 assert remaining == bytearray()
179
180
181@pytest.mark.parametrize("use_alias", [False, True])
182@pytest.mark.parametrize("union_width", [8, 12, 16])
183def test_union_subbyte_members(use_alias, union_width):
184 signed = types.SIntType("si5", 5)
185 if use_alias:
186 signed = types.TypeAlias("signed_alias", "Signed", signed)
187 union_type = types.UnionType(
188 "subbyte_union",
189 [("signed", signed), ("unsigned", types.UIntType("ui3", 3)),
190 ("bits", types.BitsType("i5", 5)),
191 ("wide", types.UIntType(f"ui{union_width}", union_width))])
192 union_bytes = (union_width + 7) // 8
193 for value in [-16, -7, -1, 0, 7, 15]:
194 serialized = union_type.serialize({"signed": value})
195 assert serialized == bytearray([value & 0x1F]) + bytearray(union_bytes - 1)
196 decoded, remaining = union_type.deserialize(serialized)
197 assert decoded["signed"] == value
198 assert decoded["unsigned"] == value & 7
199 assert decoded["bits"] == bytearray([value & 0x1F])
200 assert remaining == bytearray()
201
202 raw = bytearray([0xBC]) + bytearray([0x0A] * (union_bytes - 1))
203 decoded, remaining = union_type.deserialize(raw + bytearray([0xDE, 0xAD]))
204 assert decoded["signed"] == -4
205 assert decoded["unsigned"] == 4
206 assert decoded["bits"] == bytearray([0x1C])
207 assert remaining == bytearray([0xDE, 0xAD])
208
209 bits = bytearray([0xF9])
210 assert union_type.serialize({"bits": bits}) == (bytearray([0x19]) +
211 bytearray(union_bytes - 1))
212 assert bits == bytearray([0xF9])
213
214 with pytest.raises(ValueError, match="insufficient data for union"):
215 union_type.deserialize(bytearray(union_bytes - 1))
216
217
219 uint8 = types.UIntType("ui8", 8)
220 union_type = types.UnionType("nested_union",
221 [("small", uint8),
222 ("wide", types.UIntType("ui16", 16))])
223 struct_type = types.StructType("union_struct", [("tag", uint8),
224 ("payload", union_type),
225 ("tail", uint8)])
226 serialized = struct_type.serialize({
227 "tag": 0x12,
228 "payload": {
229 "small": 0xA5
230 },
231 "tail": 0x87
232 })
233 assert serialized == bytearray([0x87, 0xA5, 0, 0x12])
234 result, remaining = struct_type.deserialize(serialized)
235 assert result == {
236 "tag": 0x12,
237 "payload": {
238 "small": 0xA5,
239 "wide": 0xA5
240 },
241 "tail": 0x87
242 }
243 assert remaining == bytearray()
244
245
247 element_type = types.UIntType("ui8", 8)
248 list_type = types._get_esi_type(
249 types.cpp.ListType("!esi.list<ui8>", element_type.cpp_type))
250
251 assert isinstance(list_type, types.ListType)
252 assert isinstance(list_type.element_type, types.UIntType)
253 assert list_type.element_type.id == element_type.id
254 assert list_type.bit_width == -1
255
256 supports_host, reason = list_type.supports_host
257 assert not supports_host
258 assert reason == "list types require an enclosing window encoding"
259
260 valid, invalid_reason = list_type.is_valid([0x12, 0x34])
261 assert valid
262 assert invalid_reason is None
263
264 valid, invalid_reason = list_type.is_valid("not a list")
265 assert not valid
266 assert "must be a list" in invalid_reason
267
268 valid, invalid_reason = list_type.is_valid([0x12, 0x1FF])
269 assert not valid
270 assert invalid_reason == "invalid element 1: out of range: 511"
271
272 with pytest.raises(ValueError, match="cannot be serialized without a window"):
273 list_type.serialize([0x12, 0x34])
274
275 with pytest.raises(ValueError,
276 match="cannot be deserialized without a window"):
277 list_type.deserialize(bytearray([0x12, 0x34]))
278
279
281 uint8 = types.UIntType("ui8", 8)
282 into_type = types.StructType("!hw.struct<header: ui8, data: ui8>",
283 [("header", uint8), ("data", uint8)])
284 header_type = types.StructType("!hw.struct<header: ui8>", [("header", uint8)])
285 data_type = types.StructType("!hw.struct<data: ui8>", [("data", uint8)])
286 lowered_type = types.UnionType(
287 "!hw.union<header: !hw.struct<header: ui8>, data: !hw.struct<data: ui8>>",
288 [("header", header_type), ("data", data_type)])
289 window_type = types.WindowType(
290 '!esi.window<"test_window", !hw.struct<header: ui8, data: ui8>, '
291 '[<"header", [<"header">]>, <"data", [<"data">]>]>',
292 "test_window",
293 into_type,
294 lowered_type,
295 [
296 types.WindowType.Frame("header",
297 [types.WindowType.Field("header", 0, 0)]),
298 types.WindowType.Frame("data",
299 [types.WindowType.Field("data", 0, 0)]),
300 ],
301 )
302
303 supports_host, reason = window_type.supports_host
304 assert not supports_host
305 assert reason is not None
306 assert "not yet supported" in reason
307
308 valid, invalid_reason = window_type.is_valid({"header": 1, "data": 2})
309 assert not valid
310 assert invalid_reason == reason
311
312 with pytest.raises(ValueError, match="not yet supported"):
313 window_type.serialize({"header": 1, "data": 2})
314
315 with pytest.raises(ValueError, match="not yet supported"):
316 window_type.deserialize(bytearray([1, 2]))
test_union_in_struct()
test_union_subbyte_members(use_alias, union_width)
test_union_type()
Definition test_types.py:97
test_list_type_not_supported_for_host()
test_union_signed_variant()
test_manifest_rejects_incompatible_version(version)
Definition test_types.py:20
test_union_padding_with_struct()
test_window_type_not_supported_for_host()
test_manifest_version()
Definition test_types.py:8