Skip to content

Commit

Permalink
csr.reg: migrate to lib.wiring interfaces.
Browse files Browse the repository at this point in the history
  • Loading branch information
jfng committed Sep 12, 2023
1 parent 0764373 commit 041a0c6
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 106 deletions.
184 changes: 127 additions & 57 deletions amaranth_soc/csr/reg.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Mapping, Sequence
import enum
from amaranth import *
from amaranth.lib import enum, wiring
from amaranth.lib.wiring import In, Out

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


class FieldPort:
class FieldPort(wiring.Interface):
class Access(enum.Enum):
"""Field access mode."""
R = "r"
Expand All @@ -22,64 +23,135 @@ def readable(self):
def writable(self):
return self == self.W or self == self.RW

class Signature(wiring.Signature):
"""CSR register field port signature.
Parameters
----------
shape : :ref:`shape-castable <lang-shapecasting>`
Shape of the field.
access : :class:`FieldPort.Access`
Field access mode.
Interface attributes
--------------------
r_data : Signal(shape)
Read data. Must always be valid, and is sampled when ``r_stb`` is asserted.
r_stb : Signal()
Read strobe. Fields with read side effects should perform them when this strobe is
asserted.
w_data : Signal(shape)
Write data. Valid only when ``w_stb`` is asserted.
w_stb : Signal()
Write strobe. Fields should update their value or perform the write side effect when
this strobe is asserted.
Raises
------
See :meth:`FieldPort.Signature.check_parameters`.
"""
def __init__(self, shape, access):
self.check_parameters(shape, access)

self._shape = Shape.cast(shape)
self._access = FieldPort.Access(access)

members = {
"r_data": Out(self.shape),
"r_stb": In(1),
"w_data": In(self.shape),
"w_stb": In(1),
}
super().__init__(members)

@property
def shape(self):
return self._shape

@property
def access(self):
return self._access

@classmethod
def check_parameters(cls, shape, access):
"""Validate signature parameters.
Raises
------
:exc:`TypeError`
If ``shape`` is not a shape-castable object.
:exc:`ValueError`
If ``access`` is not a member of :class:`FieldPort.Access`.
"""
try:
Shape.cast(shape)
except TypeError as e:
raise TypeError(f"Field shape must be a shape-castable object, not {shape!r}") from e
# TODO(py3.9): Remove this. Python 3.8 and below use cls.__name__ in the error message
# instead of cls.__qualname__.
# FieldPort.Access(access)
try:
FieldPort.Access(access)
except ValueError as e:
raise ValueError(f"{access!r} is not a valid FieldPort.Access") from e

def create(self, *, path=()):
"""Create a compatible interface.
See :meth:`wiring.Signature.create` for details.
Returns
-------
A :class:`FieldPort` object using this signature.
"""
return FieldPort(self, path=path)

def __eq__(self, other):
"""Compare signatures.
Two signatures are equal if they have the same shape and field access mode.
"""
return (isinstance(other, FieldPort.Signature) and
Shape.cast(self.shape) == Shape.cast(other.shape) and
self.access == other.access)

def __repr__(self):
return f"csr.FieldPort.Signature({self.members!r})"

"""CSR register field port.
An interface between a CSR register and one of its fields.
Parameters
----------
shape : :ref:`shape-castable <lang-shapecasting>`
Shape of the field.
access : :class:`FieldPort.Access`
Field access mode.
Attributes
----------
r_data : Signal(shape)
Read data. Must always be valid, and is sampled when ``r_stb`` is asserted.
r_stb : Signal()
Read strobe. Fields with read side effects should perform them when this strobe is
asserted.
w_data : Signal(shape)
Write data. Valid only when ``w_stb`` is asserted.
w_stb : Signal()
Write strobe. Fields should update their value or perform the write side effect when
this strobe is asserted.
signature : :class:`FieldPort.Signature`
Field port signature.
path : iter(:class:`str`)
Path to the field port. Optional. See :class:`wiring.Interface`.
Raises
------
:exc:`TypeError`
If ``shape`` is not a shape-castable object.
:exc:`ValueError`
:exc:`TypeError`
If ``access`` is not a member of :class:`FieldPort.Access`.
"""
def __init__(self, shape, access):
try:
shape = Shape.cast(shape)
except TypeError as e:
raise TypeError("Field shape must be a shape-castable object, not {!r}"
.format(shape)) from e
if not isinstance(access, FieldPort.Access) and access not in ("r", "w", "rw"):
raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
.format(access))
self._shape = shape
self._access = FieldPort.Access(access)

self.r_data = Signal(shape)
self.r_stb = Signal()
self.w_data = Signal(shape)
self.w_stb = Signal()
def __init__(self, signature, *, path=()):
if not isinstance(signature, FieldPort.Signature):
raise TypeError(f"This interface requires a csr.FieldPort.Signature, not "
f"{signature!r}")
super().__init__(signature, path=path)

@property
def shape(self):
return self._shape
return self.signature.shape

@property
def access(self):
return self._access
return self.signature.access

def __repr__(self):
return "FieldPort({}, {})".format(self.shape, self.access)
return f"csr.FieldPort({self.signature!r})"


class Field(Elaboratable):
Expand Down Expand Up @@ -109,7 +181,7 @@ class Field(Elaboratable):
attributes="")

def __init__(self, shape, access):
self.port = FieldPort(shape, access)
self.port = FieldPort.Signature(shape, access).create(path=("port",))

@property
def shape(self):
Expand Down Expand Up @@ -296,7 +368,7 @@ class Register(Elaboratable):
Raises
------
:exc:`ValueError`
:exc:`TypeError`
If ``access`` is not a member of :class:`Element.Access`.
:exc:`TypeError`
If ``fields`` is not ``None`` or a :class:`FieldMap` or a :class:`FieldArray`.
Expand All @@ -305,39 +377,37 @@ class Register(Elaboratable):
:exc:`ValueError`
If ``access`` is not writable and at least one field is writable.
"""
def __init__(self, access, fields=None):
def __init__(self, access="rw", fields=None):
if not isinstance(access, Element.Access) and access not in ("r", "w", "rw"):
raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
.format(access))
raise TypeError(f"Access mode must be one of \"r\", \"w\", or \"rw\", not {access!r}")
access = Element.Access(access)

if hasattr(self, "__annotations__"):
annotation_fields = {}
annot_fields = {}
for key, value in self.__annotations__.items():
if isinstance(value, (Field, FieldMap, FieldArray)):
annotation_fields[key] = value
annot_fields[key] = value

if fields is None:
fields = FieldMap(annotation_fields)
elif annotation_fields:
raise ValueError("Field collection {} cannot be provided in addition to field annotations: {}"
.format(fields, ", ".join(annotation_fields.keys())))
fields = FieldMap(annot_fields)
elif annot_fields:
raise ValueError(f"Field collection {fields} cannot be provided in addition to "
f"field annotations: {', '.join(annot_fields)}")

if not isinstance(fields, (FieldMap, FieldArray)):
raise TypeError("Field collection must be a FieldMap or a FieldArray, not {!r}"
.format(fields))
raise TypeError(f"Field collection must be a FieldMap or a FieldArray, not {fields!r}")

width = 0
for field_name, field in fields.flatten():
width += Shape.cast(field.shape).width
if field.access.readable() and not access.readable():
raise ValueError("Field {} is readable, but register access mode is '{}'"
.format("__".join(field_name), access))
raise ValueError(f"Field {'__'.join(field_name)} is readable, but register access "
f"mode is {access!r}")
if field.access.writable() and not access.writable():
raise ValueError("Field {} is writable, but register access mode is '{}'"
.format("__".join(field_name), access))
raise ValueError(f"Field {'__'.join(field_name)} is writable, but register access "
f"mode is {access!r}")

self.element = Element(width, access)
self.element = Element.Signature(width, access).create(path=("element",))
self._fields = fields

@property
Expand Down
8 changes: 0 additions & 8 deletions tests/test_csr_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
class RTestCase(unittest.TestCase):
def test_simple(self):
f = field.R(unsigned(4))
self.assertEqual(repr(f.port), "FieldPort(unsigned(4), Access.R)")
self.assertEqual(f.r_data.shape(), unsigned(4))

def test_sim(self):
Expand All @@ -30,7 +29,6 @@ def process():
class WTestCase(unittest.TestCase):
def test_simple(self):
f = field.W(unsigned(4))
self.assertEqual(repr(f.port), "FieldPort(unsigned(4), Access.W)")
self.assertEqual(f.w_data.shape(), unsigned(4))

def test_sim(self):
Expand All @@ -51,10 +49,8 @@ class RWTestCase(unittest.TestCase):
def test_simple(self):
f4 = field.RW(unsigned(4), reset=0x5)
f8 = field.RW(signed(8))
self.assertEqual(repr(f4.port), "FieldPort(unsigned(4), Access.RW)")
self.assertEqual(f4.data.shape(), unsigned(4))
self.assertEqual(f4.reset, 0x5)
self.assertEqual(repr(f8.port), "FieldPort(signed(8), Access.RW)")
self.assertEqual(f8.data.shape(), signed(8))
self.assertEqual(f8.reset, 0)

Expand Down Expand Up @@ -82,11 +78,9 @@ class RW1CTestCase(unittest.TestCase):
def test_simple(self):
f4 = field.RW1C(unsigned(4), reset=0x5)
f8 = field.RW1C(signed(8))
self.assertEqual(repr(f4.port), "FieldPort(unsigned(4), Access.RW)")
self.assertEqual(f4.data.shape(), unsigned(4))
self.assertEqual(f4.set .shape(), unsigned(4))
self.assertEqual(f4.reset, 0x5)
self.assertEqual(repr(f8.port), "FieldPort(signed(8), Access.RW)")
self.assertEqual(f8.data.shape(), signed(8))
self.assertEqual(f8.set .shape(), signed(8))
self.assertEqual(f8.reset, 0)
Expand Down Expand Up @@ -122,11 +116,9 @@ class RW1STestCase(unittest.TestCase):
def test_simple(self):
f4 = field.RW1S(unsigned(4), reset=0x5)
f8 = field.RW1S(signed(8))
self.assertEqual(repr(f4.port), "FieldPort(unsigned(4), Access.RW)")
self.assertEqual(f4.data .shape(), unsigned(4))
self.assertEqual(f4.clear.shape(), unsigned(4))
self.assertEqual(f4.reset, 0x5)
self.assertEqual(repr(f8.port), "FieldPort(signed(8), Access.RW)")
self.assertEqual(f8.data .shape(), signed(8))
self.assertEqual(f8.clear.shape(), signed(8))
self.assertEqual(f8.reset, 0)
Expand Down
Loading

0 comments on commit 041a0c6

Please sign in to comment.