From ee4c768413ce142d12400ad5c6697f614539f6e9 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 11 Oct 2019 13:07:42 +0000 Subject: [PATCH] hdl.ast: deprecate Signal.{range,enum}. Although constructor methods can improve clarity, there are many contexts in which it is useful to use range() as a shape: notably Layout, but also Const and AnyConst/AnyValue. Instead of duplicating these constructor methods everywhere (which is not even easily possible for Layout), use casting to Shape, introduced in 6aabdc0a. Fixes #225. --- examples/basic/fsm.py | 2 +- examples/basic/por.py | 2 +- examples/basic/uart.py | 8 +++---- nmigen/hdl/ast.py | 41 +++++++++++++++++++++--------------- nmigen/hdl/mem.py | 4 ++-- nmigen/lib/coding.py | 12 +++++------ nmigen/lib/fifo.py | 8 +++---- nmigen/test/test_hdl_ast.py | 40 +++++++++++++++++------------------ nmigen/test/test_hdl_dsl.py | 2 +- nmigen/test/test_lib_fifo.py | 6 +++--- 10 files changed, 66 insertions(+), 59 deletions(-) diff --git a/examples/basic/fsm.py b/examples/basic/fsm.py index 86b00db..712718b 100644 --- a/examples/basic/fsm.py +++ b/examples/basic/fsm.py @@ -15,7 +15,7 @@ class UARTReceiver(Elaboratable): def elaborate(self, platform): m = Module() - ctr = Signal.range(self.divisor) + ctr = Signal(range(self.divisor)) stb = Signal() with m.If(ctr == 0): m.d.sync += ctr.eq(self.divisor - 1) diff --git a/examples/basic/por.py b/examples/basic/por.py index b503e4d..8bdc020 100644 --- a/examples/basic/por.py +++ b/examples/basic/por.py @@ -7,7 +7,7 @@ cd_por = ClockDomain(reset_less=True) cd_sync = ClockDomain() m.domains += cd_por, cd_sync -delay = Signal.range(256, reset=255) +delay = Signal(range(256), reset=255) with m.If(delay != 0): m.d.por += delay.eq(delay - 1) m.d.comb += [ diff --git a/examples/basic/uart.py b/examples/basic/uart.py index d7a9a9c..1279ae0 100644 --- a/examples/basic/uart.py +++ b/examples/basic/uart.py @@ -31,9 +31,9 @@ class UART(Elaboratable): def elaborate(self, platform): m = Module() - tx_phase = Signal.range(self.divisor) + tx_phase = Signal(range(self.divisor)) tx_shreg = Signal(1 + self.data_bits + 1, reset=-1) - tx_count = Signal.range(len(tx_shreg) + 1) + tx_count = Signal(range(len(tx_shreg) + 1)) m.d.comb += self.tx_o.eq(tx_shreg[0]) with m.If(tx_count == 0): @@ -54,9 +54,9 @@ class UART(Elaboratable): tx_phase.eq(self.divisor - 1), ] - rx_phase = Signal.range(self.divisor) + rx_phase = Signal(range(self.divisor)) rx_shreg = Signal(1 + self.data_bits + 1, reset=-1) - rx_count = Signal.range(len(rx_shreg) + 1) + rx_count = Signal(range(len(rx_shreg) + 1)) m.d.comb += self.rx_data.eq(rx_shreg[1:-1]) with m.If(rx_count == 0): diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index db4c24e..5aba156 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -35,7 +35,20 @@ class DUID: class Shape(typing.NamedTuple): """Bit width and signedness of a value. - Attributes + A ``Shape`` can be constructed using: + * explicit bit width and signedness; + * aliases :func:`signed` and :func:`unsigned`; + * casting from a variety of objects. + + A ``Shape`` can be cast from: + * an integer, where the integer specifies the bit width; + * a range, where the result is wide enough to represent any element of the range, and is + signed if any element of the range is signed; + * an :class:`Enum` with all integer members or :class:`IntEnum`, where the result is wide + enough to represent any member of the enumeration, and is signed if any member of + the enumeration is signed. + + Parameters ---------- width : int The number of bits in the representation, including the sign bit (if any). @@ -79,10 +92,12 @@ Shape.__init__ = _Shape___init__ def unsigned(width): + """Shorthand for ``Shape(width, signed=False)``.""" return Shape(width, signed=False) def signed(width): + """Shorthand for ``Shape(width, signed=True)``.""" return Shape(width, signed=True) @@ -806,7 +821,7 @@ class Signal(Value, DUID): # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension if min is not None or max is not None: warnings.warn("instead of `Signal(min={min}, max={max})`, " - "use `Signal.range({min}, {max})`" + "use `Signal(range({min}, {max}))`" .format(min=min or 0, max=max or 2), DeprecationWarning, stacklevel=2 + src_loc_at) @@ -846,6 +861,9 @@ class Signal(Value, DUID): self.reset_less = bool(reset_less) self.attrs = OrderedDict(() if attrs is None else attrs) + + if decoder is None and isinstance(shape, type) and issubclass(shape, Enum): + decoder = shape if isinstance(decoder, type) and issubclass(decoder, Enum): def enum_decoder(value): try: @@ -857,27 +875,16 @@ class Signal(Value, DUID): self.decoder = decoder @classmethod + @deprecated("instead of `Signal.range(...)`, use `Signal(range(...))`") def range(cls, *args, src_loc_at=0, **kwargs): - """Create Signal that can represent a given range. - - The parameters to ``Signal.range`` are the same as for the built-in ``range`` function. - That is, for any given ``range(*args)``, ``Signal.range(*args)`` can represent any - ``x for x in range(*args)``. - """ - return cls(Shape.cast(range(*args)), src_loc_at=1 + src_loc_at, **kwargs) + return cls(range(*args), src_loc_at=2 + src_loc_at, **kwargs) @classmethod + @deprecated("instead of `Signal.enum(...)`, use `Signal(...)`") def enum(cls, enum_type, *, src_loc_at=0, **kwargs): - """Create Signal that can represent a given enumeration. - - Parameters - ---------- - enum : type (inheriting from :class:`enum.Enum`) - Enumeration to base this Signal on. - """ if not issubclass(enum_type, Enum): raise TypeError("Type {!r} is not an enumeration") - return cls(Shape.cast(enum_type), src_loc_at=1 + src_loc_at, decoder=enum_type, **kwargs) + return cls(enum_type, src_loc_at=2 + src_loc_at, **kwargs) @classmethod def like(cls, other, *, name=None, name_suffix=None, src_loc_at=0, **kwargs): diff --git a/nmigen/hdl/mem.py b/nmigen/hdl/mem.py index 1101dab..1205fa1 100644 --- a/nmigen/hdl/mem.py +++ b/nmigen/hdl/mem.py @@ -73,7 +73,7 @@ class ReadPort(Elaboratable): self.domain = domain self.transparent = transparent - self.addr = Signal.range(memory.depth, + self.addr = Signal(range(memory.depth), name="{}_r_addr".format(memory.name), src_loc_at=2) self.data = Signal(memory.width, name="{}_r_data".format(memory.name), src_loc_at=2) @@ -149,7 +149,7 @@ class WritePort(Elaboratable): self.domain = domain self.granularity = granularity - self.addr = Signal.range(memory.depth, + self.addr = Signal(range(memory.depth), name="{}_w_addr".format(memory.name), src_loc_at=2) self.data = Signal(memory.width, name="{}_w_data".format(memory.name), src_loc_at=2) diff --git a/nmigen/lib/coding.py b/nmigen/lib/coding.py index 300a718..5abfbd7 100644 --- a/nmigen/lib/coding.py +++ b/nmigen/lib/coding.py @@ -25,7 +25,7 @@ class Encoder(Elaboratable): ---------- i : Signal(width), in One-hot input. - o : Signal.range(width), out + o : Signal(range(width)), out Encoded binary. n : Signal, out Invalid: either none or multiple input bits are asserted. @@ -34,7 +34,7 @@ class Encoder(Elaboratable): self.width = width self.i = Signal(width) - self.o = Signal.range(width) + self.o = Signal(range(width)) self.n = Signal() def elaborate(self, platform): @@ -64,7 +64,7 @@ class PriorityEncoder(Elaboratable): ---------- i : Signal(width), in Input requests. - o : Signal.range(width), out + o : Signal(range(width)), out Encoded binary. n : Signal, out Invalid: no input bits are asserted. @@ -73,7 +73,7 @@ class PriorityEncoder(Elaboratable): self.width = width self.i = Signal(width) - self.o = Signal.range(width) + self.o = Signal(range(width)) self.n = Signal() def elaborate(self, platform): @@ -98,7 +98,7 @@ class Decoder(Elaboratable): Attributes ---------- - i : Signal.range(width), in + i : Signal(range(width)), in Input binary. o : Signal(width), out Decoded one-hot. @@ -108,7 +108,7 @@ class Decoder(Elaboratable): def __init__(self, width): self.width = width - self.i = Signal.range(width) + self.i = Signal(range(width)) self.n = Signal() self.o = Signal(width) diff --git a/nmigen/lib/fifo.py b/nmigen/lib/fifo.py index da468b0..2b3d3a6 100644 --- a/nmigen/lib/fifo.py +++ b/nmigen/lib/fifo.py @@ -187,7 +187,7 @@ class SyncFIFO(Elaboratable, FIFOInterface): def __init__(self, *, width, depth, fwft=True): super().__init__(width=width, depth=depth, fwft=fwft) - self.level = Signal.range(depth + 1) + self.level = Signal(range(depth + 1)) def elaborate(self, platform): m = Module() @@ -210,8 +210,8 @@ class SyncFIFO(Elaboratable, FIFOInterface): w_port = m.submodules.w_port = storage.write_port() r_port = m.submodules.r_port = storage.read_port( domain="comb" if self.fwft else "sync", transparent=self.fwft) - produce = Signal.range(self.depth) - consume = Signal.range(self.depth) + produce = Signal(range(self.depth)) + consume = Signal(range(self.depth)) m.d.comb += [ w_port.addr.eq(produce), @@ -289,7 +289,7 @@ class SyncFIFOBuffered(Elaboratable, FIFOInterface): def __init__(self, *, width, depth): super().__init__(width=width, depth=depth, fwft=True) - self.level = Signal.range(depth + 1) + self.level = Signal(range(depth + 1)) def elaborate(self, platform): m = Module() diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index 9022ec0..0346478 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -445,7 +445,7 @@ class OperatorTestCase(FHDLTestCase): """) def test_matches_enum(self): - s = Signal.enum(SignedEnum) + s = Signal(SignedEnum) self.assertRepr(s.matches(SignedEnum.FOO), """ (== (sig s) (const 1'sd-1)) """) @@ -520,7 +520,7 @@ class SliceTestCase(FHDLTestCase): class BitSelectTestCase(FHDLTestCase): def setUp(self): self.c = Const(0, 8) - self.s = Signal.range(self.c.width) + self.s = Signal(range(self.c.width)) def test_shape(self): s1 = self.c.bit_select(self.s, 2) @@ -545,7 +545,7 @@ class BitSelectTestCase(FHDLTestCase): class WordSelectTestCase(FHDLTestCase): def setUp(self): self.c = Const(0, 8) - self.s = Signal.range(self.c.width) + self.s = Signal(range(self.c.width)) def test_shape(self): s1 = self.c.word_select(self.s, 2) @@ -617,8 +617,8 @@ class ArrayTestCase(FHDLTestCase): def test_becomes_immutable(self): a = Array([1,2,3]) - s1 = Signal.range(len(a)) - s2 = Signal.range(len(a)) + s1 = Signal(range(len(a))) + s2 = Signal(range(len(a))) v1 = a[s1] v2 = a[s2] with self.assertRaisesRegex(ValueError, @@ -634,7 +634,7 @@ class ArrayTestCase(FHDLTestCase): def test_repr(self): a = Array([1,2,3]) self.assertEqual(repr(a), "(array mutable [1, 2, 3])") - s = Signal.range(len(a)) + s = Signal(range(len(a))) v = a[s] self.assertEqual(repr(a), "(array [1, 2, 3])") @@ -642,8 +642,8 @@ class ArrayTestCase(FHDLTestCase): class ArrayProxyTestCase(FHDLTestCase): def test_index_shape(self): m = Array(Array(x * y for y in range(1, 4)) for x in range(1, 4)) - a = Signal.range(3) - b = Signal.range(3) + a = Signal(range(3)) + b = Signal(range(3)) v = m[a][b] self.assertEqual(v.shape(), (4, False)) @@ -651,14 +651,14 @@ class ArrayProxyTestCase(FHDLTestCase): from collections import namedtuple pair = namedtuple("pair", ("p", "n")) a = Array(pair(i, -i) for i in range(10)) - s = Signal.range(len(a)) + s = Signal(range(len(a))) v = a[s] self.assertEqual(v.p.shape(), (4, False)) self.assertEqual(v.n.shape(), (6, True)) def test_repr(self): a = Array([1, 2, 3]) - s = Signal.range(3) + s = Signal(range(3)) v = a[s] self.assertEqual(repr(v), "(proxy (array [1, 2, 3]) (sig s))") @@ -676,17 +676,17 @@ class SignalTestCase(FHDLTestCase): self.assertEqual(s4.shape(), (2, True)) s5 = Signal(0) self.assertEqual(s5.shape(), (0, False)) - s6 = Signal.range(16) + s6 = Signal(range(16)) self.assertEqual(s6.shape(), (4, False)) - s7 = Signal.range(4, 16) + s7 = Signal(range(4, 16)) self.assertEqual(s7.shape(), (4, False)) - s8 = Signal.range(-4, 16) + s8 = Signal(range(-4, 16)) self.assertEqual(s8.shape(), (5, True)) - s9 = Signal.range(-20, 16) + s9 = Signal(range(-20, 16)) self.assertEqual(s9.shape(), (6, True)) - s10 = Signal.range(0) + s10 = Signal(range(0)) self.assertEqual(s10.shape(), (0, False)) - s11 = Signal.range(1) + s11 = Signal(range(1)) self.assertEqual(s11.shape(), (1, False)) # deprecated with warnings.catch_warnings(): @@ -709,7 +709,7 @@ class SignalTestCase(FHDLTestCase): def test_min_max_deprecated(self): with self.assertWarns(DeprecationWarning, - msg="instead of `Signal(min=0, max=10)`, use `Signal.range(0, 10)`"): + msg="instead of `Signal(min=0, max=10)`, use `Signal(range(0, 10))`"): Signal(max=10) with warnings.catch_warnings(): warnings.filterwarnings(action="ignore", category=DeprecationWarning) @@ -755,7 +755,7 @@ class SignalTestCase(FHDLTestCase): def test_like(self): s1 = Signal.like(Signal(4)) self.assertEqual(s1.shape(), (4, False)) - s2 = Signal.like(Signal.range(-15, 1)) + s2 = Signal.like(Signal(range(-15, 1))) self.assertEqual(s2.shape(), (5, True)) s3 = Signal.like(Signal(4, reset=0b111, reset_less=True)) self.assertEqual(s3.reset, 0b111) @@ -780,9 +780,9 @@ class SignalTestCase(FHDLTestCase): self.assertEqual(s.decoder(3), "3") def test_enum(self): - s1 = Signal.enum(UnsignedEnum) + s1 = Signal(UnsignedEnum) self.assertEqual(s1.shape(), (2, False)) - s2 = Signal.enum(SignedEnum) + s2 = Signal(SignedEnum) self.assertEqual(s2.shape(), (2, True)) self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1") diff --git a/nmigen/test/test_hdl_dsl.py b/nmigen/test/test_hdl_dsl.py index e45a753..48993dd 100644 --- a/nmigen/test/test_hdl_dsl.py +++ b/nmigen/test/test_hdl_dsl.py @@ -368,7 +368,7 @@ class DSLTestCase(FHDLTestCase): RED = 1 BLUE = 2 m = Module() - se = Signal.enum(Color) + se = Signal(Color) with m.Switch(se): with m.Case(Color.RED): m.d.comb += self.c1.eq(1) diff --git a/nmigen/test/test_lib_fifo.py b/nmigen/test/test_lib_fifo.py index 1b3a919..2398f8f 100644 --- a/nmigen/test/test_lib_fifo.py +++ b/nmigen/test/test_lib_fifo.py @@ -67,7 +67,7 @@ class FIFOModel(Elaboratable, FIFOInterface): self.r_domain = r_domain self.w_domain = w_domain - self.level = Signal.range(self.depth + 1) + self.level = Signal(range(self.depth + 1)) def elaborate(self, platform): m = Module() @@ -76,8 +76,8 @@ class FIFOModel(Elaboratable, FIFOInterface): w_port = m.submodules.w_port = storage.write_port(domain=self.w_domain) r_port = m.submodules.r_port = storage.read_port (domain="comb") - produce = Signal.range(self.depth) - consume = Signal.range(self.depth) + produce = Signal(range(self.depth)) + consume = Signal(range(self.depth)) m.d.comb += self.r_rdy.eq(self.level > 0) m.d.comb += r_port.addr.eq((consume + 1) % self.depth) -- 2.30.2