diff --git a/amaranth/hdl/_ast.py b/amaranth/hdl/_ast.py index 9fbf2b709..5541dd74d 100644 --- a/amaranth/hdl/_ast.py +++ b/amaranth/hdl/_ast.py @@ -1972,9 +1972,11 @@ def __init__(self, shape=None, *, name=None, init=None, reset=None, reset_less=F if decoder is not None: # The value representation is specified explicitly. Since we do not expose `hdl._repr`, - # this is the only way to add a custom filter to the signal right now. The setter sets - # `self._value_repr` as well as the compatibility `self.decoder`. - pass + # this is the only way to add a custom filter to the signal right now. + if isinstance(decoder, type) and issubclass(decoder, Enum): + self._value_repr = (_repr.Repr(_repr.FormatEnum(decoder), self),) + else: + self._value_repr = (_repr.Repr(_repr.FormatCustom(decoder), self),) else: # If it's an enum, expose it via `self.decoder` for compatibility, whether it's a Python # enum or an Amaranth enum. This also sets the value representation, even for custom @@ -1995,20 +1997,16 @@ def __init__(self, shape=None, *, name=None, init=None, reset=None, reset_less=F self._value_repr = (_repr.Repr(_repr.FormatInt(), self),) # Compute the value representation that will be used by Amaranth. - if decoder is None: - self._value_repr = (_repr.Repr(_repr.FormatInt(), self),) - self._decoder = None - elif not (isinstance(decoder, type) and issubclass(decoder, Enum)): - self._value_repr = (_repr.Repr(_repr.FormatCustom(decoder), self),) - self._decoder = decoder - else: # Violence. In the name of backwards compatibility! - self._value_repr = (_repr.Repr(_repr.FormatEnum(decoder), self),) + if isinstance(decoder, type) and issubclass(decoder, Enum): + # Violence. In the name of backwards compatibility! def enum_decoder(value): try: return "{0.name:}/{0.value:}".format(decoder(value)) except ValueError: return str(value) self._decoder = enum_decoder + else: + self._decoder = decoder @property def width(self): diff --git a/amaranth/hdl/_repr.py b/amaranth/hdl/_repr.py index 398d10b6a..6c70db162 100644 --- a/amaranth/hdl/_repr.py +++ b/amaranth/hdl/_repr.py @@ -14,6 +14,9 @@ class FormatInt(Format): def format(self, value): return f"{value:d}" + def __repr__(self): + return f"FormatInt()" + class FormatEnum(Format): def __init__(self, enum): @@ -25,6 +28,9 @@ def format(self, value): except ValueError: return f"?/{value:d}" + def __repr__(self): + return f"FormatEnum({self.enum.__name__})" + class FormatCustom(Format): def __init__(self, formatter): @@ -33,6 +39,9 @@ def __init__(self, formatter): def format(self, value): return self.formatter(value) + def __repr__(self): + return f"FormatCustom({self.formatter})" + class Repr: def __init__(self, format, value, *, path=()): @@ -44,3 +53,6 @@ def __init__(self, format, value, *, path=()): self.format = format self.value = value self.path = path + + def __repr__(self): + return f"Repr({self.format!r}, {self.value!r}, {self.path!r})" \ No newline at end of file diff --git a/tests/test_hdl_ast.py b/tests/test_hdl_ast.py index d47785533..d233b23f6 100644 --- a/tests/test_hdl_ast.py +++ b/tests/test_hdl_ast.py @@ -1321,6 +1321,7 @@ class Color(Enum): s = Signal(decoder=Color) self.assertEqual(s.decoder(1), "RED/1") self.assertEqual(s.decoder(3), "3") + self.assertRepr(s._value_repr, "(Repr(FormatEnum(Color), (sig s), ()),)") def test_enum(self): s1 = Signal(UnsignedEnum) @@ -1328,6 +1329,7 @@ def test_enum(self): s2 = Signal(SignedEnum) self.assertEqual(s2.shape(), signed(2)) self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1") + self.assertRepr(s2._value_repr, "(Repr(FormatEnum(SignedEnum), (sig s2), ()),)") def test_const_wrong(self): s1 = Signal() @@ -1335,6 +1337,10 @@ def test_const_wrong(self): r"^Value \(sig s1\) cannot be converted to an Amaranth constant$"): Const.cast(s1) + def test_value_repr(self): + s = Signal() + self.assertRepr(s._value_repr, "(Repr(FormatInt(), (sig s), ()),)") + class ClockSignalTestCase(FHDLTestCase): def test_domain(self): diff --git a/tests/test_lib_data.py b/tests/test_lib_data.py index 19c46db55..0426053f1 100644 --- a/tests/test_lib_data.py +++ b/tests/test_lib_data.py @@ -458,6 +458,11 @@ def test_construct_signal(self): self.assertIsInstance(cv, Signal) self.assertEqual(cv.shape(), unsigned(3)) self.assertEqual(cv.name, "v") + self.assertRepr(cv._value_repr, """ + (Repr(FormatInt(), (sig v), ()), + Repr(FormatInt(), (slice (sig v) 0:1), ('a',)), + Repr(FormatInt(), (slice (sig v) 1:3), ('b',))) + """) def test_construct_signal_init(self): v1 = Signal(data.StructLayout({"a": unsigned(1), "b": unsigned(2)}), diff --git a/tests/test_lib_enum.py b/tests/test_lib_enum.py index 6d7799bb2..9d8d0cf98 100644 --- a/tests/test_lib_enum.py +++ b/tests/test_lib_enum.py @@ -137,6 +137,7 @@ class EnumA(IntEnum, shape=signed(4)): B = -3 a = Signal(EnumA) self.assertRepr(a, "(sig a)") + self.assertRepr(a._value_repr, "(Repr(FormatEnum(EnumA), (sig a), ()),)") def test_enum_view(self): class EnumA(Enum, shape=signed(4)):