Skip to content

Commit 3a562fd

Browse files
committed
csr.reg: migrate to lib.wiring interfaces.
1 parent 1e07895 commit 3a562fd

File tree

3 files changed

+210
-106
lines changed

3 files changed

+210
-106
lines changed

amaranth_soc/csr/reg.py

Lines changed: 127 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from collections.abc import Mapping, Sequence
2-
import enum
32
from amaranth import *
3+
from amaranth.lib import enum, wiring
4+
from amaranth.lib.wiring import In, Out
45

56
from ..memory import MemoryMap
67
from .bus import Element, Multiplexer
@@ -9,7 +10,7 @@
910
__all__ = ["FieldPort", "Field", "FieldMap", "FieldArray", "Register", "RegisterMap", "Bridge"]
1011

1112

12-
class FieldPort:
13+
class FieldPort(wiring.Interface):
1314
class Access(enum.Enum):
1415
"""Field access mode."""
1516
R = "r"
@@ -22,64 +23,135 @@ def readable(self):
2223
def writable(self):
2324
return self == self.W or self == self.RW
2425

26+
class Signature(wiring.Signature):
27+
"""CSR register field port signature.
28+
29+
Parameters
30+
----------
31+
shape : :ref:`shape-castable <lang-shapecasting>`
32+
Shape of the field.
33+
access : :class:`FieldPort.Access`
34+
Field access mode.
35+
36+
Interface attributes
37+
--------------------
38+
r_data : Signal(shape)
39+
Read data. Must always be valid, and is sampled when ``r_stb`` is asserted.
40+
r_stb : Signal()
41+
Read strobe. Fields with read side effects should perform them when this strobe is
42+
asserted.
43+
w_data : Signal(shape)
44+
Write data. Valid only when ``w_stb`` is asserted.
45+
w_stb : Signal()
46+
Write strobe. Fields should update their value or perform the write side effect when
47+
this strobe is asserted.
48+
49+
Raises
50+
------
51+
See :meth:`FieldPort.Signature.check_parameters`.
52+
"""
53+
def __init__(self, shape, access):
54+
self.check_parameters(shape, access)
55+
56+
self._shape = Shape.cast(shape)
57+
self._access = FieldPort.Access(access)
58+
59+
members = {
60+
"r_data": Out(self.shape),
61+
"r_stb": In(1),
62+
"w_data": In(self.shape),
63+
"w_stb": In(1),
64+
}
65+
super().__init__(members)
66+
67+
@property
68+
def shape(self):
69+
return self._shape
70+
71+
@property
72+
def access(self):
73+
return self._access
74+
75+
@classmethod
76+
def check_parameters(cls, shape, access):
77+
"""Validate signature parameters.
78+
79+
Raises
80+
------
81+
:exc:`TypeError`
82+
If ``shape`` is not a shape-castable object.
83+
:exc:`ValueError`
84+
If ``access`` is not a member of :class:`FieldPort.Access`.
85+
"""
86+
try:
87+
Shape.cast(shape)
88+
except TypeError as e:
89+
raise TypeError(f"Field shape must be a shape-castable object, not {shape!r}") from e
90+
# TODO(py3.9): Remove this. Python 3.8 and below use cls.__name__ in the error message
91+
# instead of cls.__qualname__.
92+
# FieldPort.Access(access)
93+
try:
94+
FieldPort.Access(access)
95+
except ValueError as e:
96+
raise ValueError(f"{access!r} is not a valid FieldPort.Access") from e
97+
98+
def create(self, *, path=()):
99+
"""Create a compatible interface.
100+
101+
See :meth:`wiring.Signature.create` for details.
102+
103+
Returns
104+
-------
105+
A :class:`FieldPort` object using this signature.
106+
"""
107+
return FieldPort(self, path=path)
108+
109+
def __eq__(self, other):
110+
"""Compare signatures.
111+
112+
Two signatures are equal if they have the same shape and field access mode.
113+
"""
114+
return (isinstance(other, FieldPort.Signature) and
115+
Shape.cast(self.shape) == Shape.cast(other.shape) and
116+
self.access == other.access)
117+
118+
def __repr__(self):
119+
return f"csr.FieldPort.Signature({self.members!r})"
120+
25121
"""CSR register field port.
26122
27123
An interface between a CSR register and one of its fields.
28124
29125
Parameters
30126
----------
31-
shape : :ref:`shape-castable <lang-shapecasting>`
32-
Shape of the field.
33-
access : :class:`FieldPort.Access`
34-
Field access mode.
35-
36-
Attributes
37-
----------
38-
r_data : Signal(shape)
39-
Read data. Must always be valid, and is sampled when ``r_stb`` is asserted.
40-
r_stb : Signal()
41-
Read strobe. Fields with read side effects should perform them when this strobe is
42-
asserted.
43-
w_data : Signal(shape)
44-
Write data. Valid only when ``w_stb`` is asserted.
45-
w_stb : Signal()
46-
Write strobe. Fields should update their value or perform the write side effect when
47-
this strobe is asserted.
127+
signature : :class:`FieldPort.Signature`
128+
Field port signature.
129+
path : iter(:class:`str`)
130+
Path to the field port. Optional. See :class:`wiring.Interface`.
48131
49132
Raises
50133
------
51134
:exc:`TypeError`
52135
If ``shape`` is not a shape-castable object.
53-
:exc:`ValueError`
136+
:exc:`TypeError`
54137
If ``access`` is not a member of :class:`FieldPort.Access`.
55138
"""
56-
def __init__(self, shape, access):
57-
try:
58-
shape = Shape.cast(shape)
59-
except TypeError as e:
60-
raise TypeError("Field shape must be a shape-castable object, not {!r}"
61-
.format(shape)) from e
62-
if not isinstance(access, FieldPort.Access) and access not in ("r", "w", "rw"):
63-
raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
64-
.format(access))
65-
self._shape = shape
66-
self._access = FieldPort.Access(access)
67-
68-
self.r_data = Signal(shape)
69-
self.r_stb = Signal()
70-
self.w_data = Signal(shape)
71-
self.w_stb = Signal()
139+
def __init__(self, signature, *, path=()):
140+
if not isinstance(signature, FieldPort.Signature):
141+
raise TypeError(f"This interface requires a csr.FieldPort.Signature, not "
142+
f"{signature!r}")
143+
super().__init__(signature, path=path)
72144

73145
@property
74146
def shape(self):
75-
return self._shape
147+
return self.signature.shape
76148

77149
@property
78150
def access(self):
79-
return self._access
151+
return self.signature.access
80152

81153
def __repr__(self):
82-
return "FieldPort({}, {})".format(self.shape, self.access)
154+
return f"csr.FieldPort({self.signature!r})"
83155

84156

85157
class Field(Elaboratable):
@@ -109,7 +181,7 @@ class Field(Elaboratable):
109181
attributes="")
110182

111183
def __init__(self, shape, access):
112-
self.port = FieldPort(shape, access)
184+
self.port = FieldPort.Signature(shape, access).create(path=("port",))
113185

114186
@property
115187
def shape(self):
@@ -296,7 +368,7 @@ class Register(Elaboratable):
296368
297369
Raises
298370
------
299-
:exc:`ValueError`
371+
:exc:`TypeError`
300372
If ``access`` is not a member of :class:`Element.Access`.
301373
:exc:`TypeError`
302374
If ``fields`` is not ``None`` or a :class:`FieldMap` or a :class:`FieldArray`.
@@ -305,39 +377,37 @@ class Register(Elaboratable):
305377
:exc:`ValueError`
306378
If ``access`` is not writable and at least one field is writable.
307379
"""
308-
def __init__(self, access, fields=None):
380+
def __init__(self, access="rw", fields=None):
309381
if not isinstance(access, Element.Access) and access not in ("r", "w", "rw"):
310-
raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
311-
.format(access))
382+
raise TypeError(f"Access mode must be one of \"r\", \"w\", or \"rw\", not {access!r}")
312383
access = Element.Access(access)
313384

314385
if hasattr(self, "__annotations__"):
315-
annotation_fields = {}
386+
annot_fields = {}
316387
for key, value in self.__annotations__.items():
317388
if isinstance(value, (Field, FieldMap, FieldArray)):
318-
annotation_fields[key] = value
389+
annot_fields[key] = value
319390

320391
if fields is None:
321-
fields = FieldMap(annotation_fields)
322-
elif annotation_fields:
323-
raise ValueError("Field collection {} cannot be provided in addition to field annotations: {}"
324-
.format(fields, ", ".join(annotation_fields.keys())))
392+
fields = FieldMap(annot_fields)
393+
elif annot_fields:
394+
raise ValueError(f"Field collection {fields} cannot be provided in addition to "
395+
f"field annotations: {', '.join(annot_fields)}")
325396

326397
if not isinstance(fields, (FieldMap, FieldArray)):
327-
raise TypeError("Field collection must be a FieldMap or a FieldArray, not {!r}"
328-
.format(fields))
398+
raise TypeError(f"Field collection must be a FieldMap or a FieldArray, not {fields!r}")
329399

330400
width = 0
331401
for field_name, field in fields.flatten():
332402
width += Shape.cast(field.shape).width
333403
if field.access.readable() and not access.readable():
334-
raise ValueError("Field {} is readable, but register access mode is '{}'"
335-
.format("__".join(field_name), access))
404+
raise ValueError(f"Field {'__'.join(field_name)} is readable, but register access "
405+
f"mode is {access!r}")
336406
if field.access.writable() and not access.writable():
337-
raise ValueError("Field {} is writable, but register access mode is '{}'"
338-
.format("__".join(field_name), access))
407+
raise ValueError(f"Field {'__'.join(field_name)} is writable, but register access "
408+
f"mode is {access!r}")
339409

340-
self.element = Element(width, access)
410+
self.element = Element.Signature(width, access).create(path=("element",))
341411
self._fields = fields
342412

343413
@property

tests/test_csr_field.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
class RTestCase(unittest.TestCase):
1111
def test_simple(self):
1212
f = field.R(unsigned(4))
13-
self.assertEqual(repr(f.port), "FieldPort(unsigned(4), Access.R)")
1413
self.assertEqual(f.r_data.shape(), unsigned(4))
1514

1615
def test_sim(self):
@@ -30,7 +29,6 @@ def process():
3029
class WTestCase(unittest.TestCase):
3130
def test_simple(self):
3231
f = field.W(unsigned(4))
33-
self.assertEqual(repr(f.port), "FieldPort(unsigned(4), Access.W)")
3432
self.assertEqual(f.w_data.shape(), unsigned(4))
3533

3634
def test_sim(self):
@@ -51,10 +49,8 @@ class RWTestCase(unittest.TestCase):
5149
def test_simple(self):
5250
f4 = field.RW(unsigned(4), reset=0x5)
5351
f8 = field.RW(signed(8))
54-
self.assertEqual(repr(f4.port), "FieldPort(unsigned(4), Access.RW)")
5552
self.assertEqual(f4.data.shape(), unsigned(4))
5653
self.assertEqual(f4.reset, 0x5)
57-
self.assertEqual(repr(f8.port), "FieldPort(signed(8), Access.RW)")
5854
self.assertEqual(f8.data.shape(), signed(8))
5955
self.assertEqual(f8.reset, 0)
6056

@@ -82,11 +78,9 @@ class RW1CTestCase(unittest.TestCase):
8278
def test_simple(self):
8379
f4 = field.RW1C(unsigned(4), reset=0x5)
8480
f8 = field.RW1C(signed(8))
85-
self.assertEqual(repr(f4.port), "FieldPort(unsigned(4), Access.RW)")
8681
self.assertEqual(f4.data.shape(), unsigned(4))
8782
self.assertEqual(f4.set .shape(), unsigned(4))
8883
self.assertEqual(f4.reset, 0x5)
89-
self.assertEqual(repr(f8.port), "FieldPort(signed(8), Access.RW)")
9084
self.assertEqual(f8.data.shape(), signed(8))
9185
self.assertEqual(f8.set .shape(), signed(8))
9286
self.assertEqual(f8.reset, 0)
@@ -122,11 +116,9 @@ class RW1STestCase(unittest.TestCase):
122116
def test_simple(self):
123117
f4 = field.RW1S(unsigned(4), reset=0x5)
124118
f8 = field.RW1S(signed(8))
125-
self.assertEqual(repr(f4.port), "FieldPort(unsigned(4), Access.RW)")
126119
self.assertEqual(f4.data .shape(), unsigned(4))
127120
self.assertEqual(f4.clear.shape(), unsigned(4))
128121
self.assertEqual(f4.reset, 0x5)
129-
self.assertEqual(repr(f8.port), "FieldPort(signed(8), Access.RW)")
130122
self.assertEqual(f8.data .shape(), signed(8))
131123
self.assertEqual(f8.clear.shape(), signed(8))
132124
self.assertEqual(f8.reset, 0)

0 commit comments

Comments
 (0)