Skip to content

Commit 4cb2dde

Browse files
wanda-phiwhitequark
authored andcommitted
lib.data: add .format() implementation.
1 parent 67f5b61 commit 4cb2dde

File tree

2 files changed

+119
-4
lines changed

2 files changed

+119
-4
lines changed

amaranth/lib/data.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from amaranth._utils import final
88
from amaranth.hdl import *
9-
from amaranth.hdl._repr import *
9+
from amaranth.hdl._repr import Repr, FormatInt, FormatEnum
1010
from amaranth import hdl
1111

1212

@@ -248,6 +248,22 @@ def from_bits(self, raw):
248248
"""
249249
return Const(self, raw)
250250

251+
def format(self, value, format_spec):
252+
if format_spec != "":
253+
raise ValueError(f"Format specifier {format_spec!r} is not supported for layouts")
254+
value = Value.cast(value)
255+
fields = {}
256+
for key, field in self:
257+
shape = Shape.cast(field.shape)
258+
field_value = value[field.offset:field.offset+shape.width]
259+
if isinstance(field.shape, ShapeCastable):
260+
fields[str(key)] = field.shape.format(field.shape(field_value), "")
261+
else:
262+
if shape.signed:
263+
field_value = field_value.as_signed()
264+
fields[str(key)] = Format("{}", field_value)
265+
return Format.Struct(value, fields)
266+
251267
def _value_repr(self, value):
252268
yield Repr(FormatInt(), value)
253269
for key, field in self:
@@ -539,6 +555,22 @@ def size(self):
539555
def __repr__(self):
540556
return f"ArrayLayout({self._elem_shape!r}, {self.length})"
541557

558+
def format(self, value, format_spec):
559+
if format_spec != "":
560+
raise ValueError(f"Format specifier {format_spec!r} is not supported for layouts")
561+
value = Value.cast(value)
562+
fields = []
563+
shape = Shape.cast(self._elem_shape)
564+
for index in range(self._length):
565+
field_value = value[shape.width * index:shape.width * (index + 1)]
566+
if isinstance(self._elem_shape, ShapeCastable):
567+
fields.append(self._elem_shape.format(self._elem_shape(field_value), ""))
568+
else:
569+
if shape.signed:
570+
field_value = field_value.as_signed()
571+
fields.append(Format("{}", field_value))
572+
return Format.Array(value, fields)
573+
542574

543575
class FlexibleLayout(Layout):
544576
"""Description of a flexible layout.
@@ -1163,6 +1195,9 @@ def const(cls, init):
11631195
def from_bits(cls, bits):
11641196
return cls.as_shape().from_bits(bits)
11651197

1198+
def format(cls, value, format_spec):
1199+
return cls.__layout.format(value, format_spec)
1200+
11661201
def _value_repr(cls, value):
11671202
return cls.__layout._value_repr(value)
11681203

tests/test_lib_data.py

+83-3
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def test_immutable(self):
7575
data.Field(1, 0).offset = 1
7676

7777

78-
class StructLayoutTestCase(TestCase):
78+
class StructLayoutTestCase(FHDLTestCase):
7979
def test_construct(self):
8080
sl = data.StructLayout({
8181
"a": unsigned(1),
@@ -126,8 +126,21 @@ def test_member_value_wrong(self):
126126
r"^Struct layout member shape must be a shape-castable object, not 1\.0$"):
127127
data.StructLayout({"a": 1.0})
128128

129+
def test_format(self):
130+
sl = data.StructLayout({
131+
"a": unsigned(1),
132+
"b": signed(2),
133+
})
134+
sig = Signal(sl)
135+
self.assertRepr(sl.format(sig, ""), """
136+
(format-struct (sig sig)
137+
('a' (format '{}' (slice (sig sig) 0:1)))
138+
('b' (format '{}' (s (slice (sig sig) 1:3))))
139+
)
140+
""")
141+
129142

130-
class UnionLayoutTestCase(TestCase):
143+
class UnionLayoutTestCase(FHDLTestCase):
131144
def test_construct(self):
132145
ul = data.UnionLayout({
133146
"a": unsigned(1),
@@ -184,8 +197,21 @@ def test_const_two_members_wrong(self):
184197
r"\(specified: a, b\)$"):
185198
data.UnionLayout({"a": 1, "b": 2}).const(dict(a=1, b=2))
186199

200+
def test_format(self):
201+
ul = data.UnionLayout({
202+
"a": unsigned(1),
203+
"b": 2
204+
})
205+
sig = Signal(ul)
206+
self.assertRepr(ul.format(sig, ""), """
207+
(format-struct (sig sig)
208+
('a' (format '{}' (slice (sig sig) 0:1)))
209+
('b' (format '{}' (slice (sig sig) 0:2)))
210+
)
211+
""")
187212

188-
class ArrayLayoutTestCase(TestCase):
213+
214+
class ArrayLayoutTestCase(FHDLTestCase):
189215
def test_construct(self):
190216
al = data.ArrayLayout(unsigned(2), 3)
191217
self.assertEqual(al.elem_shape, unsigned(2))
@@ -243,6 +269,48 @@ def test_key_wrong_type(self):
243269
r"^Cannot index array layout with 'a'$"):
244270
al["a"]
245271

272+
def test_format(self):
273+
al = data.ArrayLayout(unsigned(2), 3)
274+
sig = Signal(al)
275+
self.assertRepr(al.format(sig, ""), """
276+
(format-array (sig sig)
277+
(format '{}' (slice (sig sig) 0:2))
278+
(format '{}' (slice (sig sig) 2:4))
279+
(format '{}' (slice (sig sig) 4:6))
280+
)
281+
""")
282+
283+
def test_format_signed(self):
284+
al = data.ArrayLayout(signed(2), 3)
285+
sig = Signal(al)
286+
self.assertRepr(al.format(sig, ""), """
287+
(format-array (sig sig)
288+
(format '{}' (s (slice (sig sig) 0:2)))
289+
(format '{}' (s (slice (sig sig) 2:4)))
290+
(format '{}' (s (slice (sig sig) 4:6)))
291+
)
292+
""")
293+
294+
def test_format_nested(self):
295+
al = data.ArrayLayout(data.ArrayLayout(unsigned(2), 2), 3)
296+
sig = Signal(al)
297+
self.assertRepr(al.format(sig, ""), """
298+
(format-array (sig sig)
299+
(format-array (slice (sig sig) 0:4)
300+
(format '{}' (slice (slice (sig sig) 0:4) 0:2))
301+
(format '{}' (slice (slice (sig sig) 0:4) 2:4))
302+
)
303+
(format-array (slice (sig sig) 4:8)
304+
(format '{}' (slice (slice (sig sig) 4:8) 0:2))
305+
(format '{}' (slice (slice (sig sig) 4:8) 2:4))
306+
)
307+
(format-array (slice (sig sig) 8:12)
308+
(format '{}' (slice (slice (sig sig) 8:12) 0:2))
309+
(format '{}' (slice (slice (sig sig) 8:12) 2:4))
310+
)
311+
)
312+
""")
313+
246314

247315
class FlexibleLayoutTestCase(TestCase):
248316
def test_construct(self):
@@ -1012,6 +1080,18 @@ class S(data.Struct):
10121080
self.assertRepr(v.b.q.as_value(), "(slice (slice (sig v) 1:9) 4:8)")
10131081
self.assertRepr(v.b.q.r, "(s (slice (slice (slice (sig v) 1:9) 4:8) 0:2))")
10141082
self.assertRepr(v.b.q.s, "(s (slice (slice (slice (sig v) 1:9) 4:8) 2:4))")
1083+
self.assertRepr(S.format(v, ""), """
1084+
(format-struct (sig v)
1085+
('a' (format '{}' (slice (sig v) 0:1)))
1086+
('b' (format-struct (slice (sig v) 1:9)
1087+
('p' (format '{}' (slice (slice (sig v) 1:9) 0:4)))
1088+
('q' (format-struct (slice (slice (sig v) 1:9) 4:8)
1089+
('r' (format '{}' (s (slice (slice (slice (sig v) 1:9) 4:8) 0:2))))
1090+
('s' (format '{}' (s (slice (slice (slice (sig v) 1:9) 4:8) 2:4))))
1091+
))
1092+
))
1093+
)
1094+
""")
10151095

10161096
def test_construct_init(self):
10171097
class S(data.Struct):

0 commit comments

Comments
 (0)