From d99f04a380235c0094858bff5466dab577429452 Mon Sep 17 00:00:00 2001 From: Shawn Anastasio Date: Thu, 4 Jun 2020 22:19:46 -0500 Subject: [PATCH] hdl.rec: preserve shapes when constructing a layout. Preserve the original user-provided shape, while still checking its validity. This allows Enum decoders to work when specifying record fields with Enums. Fixes #393. --- nmigen/hdl/rec.py | 5 +-- nmigen/test/test_hdl_rec.py | 31 +++++++++++------- nmigen/test/test_lib_io.py | 63 +++++++++++++++++++++---------------- 3 files changed, 59 insertions(+), 40 deletions(-) diff --git a/nmigen/hdl/rec.py b/nmigen/hdl/rec.py index be89342..5e5687c 100644 --- a/nmigen/hdl/rec.py +++ b/nmigen/hdl/rec.py @@ -47,7 +47,8 @@ class Layout: .format(field)) if not isinstance(shape, Layout): try: - shape = Shape.cast(shape, src_loc_at=1 + src_loc_at) + # Check provided shape by calling Shape.cast and checking for exception + Shape.cast(shape, src_loc_at=1 + src_loc_at) except Exception as error: raise TypeError("Field {!r} has invalid shape: should be castable to Shape " "or a list of fields of a nested record" @@ -134,7 +135,7 @@ class Record(UserValue): if isinstance(field_shape, Layout): assert isinstance(field, Record) and field_shape == field.layout else: - assert isinstance(field, Signal) and field_shape == field.shape() + assert isinstance(field, Signal) and Shape.cast(field_shape) == field.shape() self.fields[field_name] = field else: if isinstance(field_shape, Layout): diff --git a/nmigen/test/test_hdl_rec.py b/nmigen/test/test_hdl_rec.py index 09af444..4f99800 100644 --- a/nmigen/test/test_hdl_rec.py +++ b/nmigen/test/test_hdl_rec.py @@ -12,6 +12,11 @@ class UnsignedEnum(Enum): class LayoutTestCase(FHDLTestCase): + def assertFieldEqual(self, field, expected): + (shape, dir) = field + shape = Shape.cast(shape) + self.assertEqual((shape, dir), expected) + def test_fields(self): layout = Layout.cast([ ("cyc", 1), @@ -24,28 +29,28 @@ class LayoutTestCase(FHDLTestCase): ]) ]) - self.assertEqual(layout["cyc"], ((1, False), DIR_NONE)) - self.assertEqual(layout["data"], ((32, True), DIR_NONE)) - self.assertEqual(layout["stb"], ((1, False), DIR_FANOUT)) - self.assertEqual(layout["ack"], ((1, False), DIR_FANIN)) + self.assertFieldEqual(layout["cyc"], ((1, False), DIR_NONE)) + self.assertFieldEqual(layout["data"], ((32, True), DIR_NONE)) + self.assertFieldEqual(layout["stb"], ((1, False), DIR_FANOUT)) + self.assertFieldEqual(layout["ack"], ((1, False), DIR_FANIN)) sublayout = layout["info"][0] self.assertEqual(layout["info"][1], DIR_NONE) - self.assertEqual(sublayout["a"], ((1, False), DIR_NONE)) - self.assertEqual(sublayout["b"], ((1, False), DIR_NONE)) + self.assertFieldEqual(sublayout["a"], ((1, False), DIR_NONE)) + self.assertFieldEqual(sublayout["b"], ((1, False), DIR_NONE)) def test_enum_field(self): layout = Layout.cast([ ("enum", UnsignedEnum), ("enum_dir", UnsignedEnum, DIR_FANOUT), ]) - self.assertEqual(layout["enum"], ((2, False), DIR_NONE)) - self.assertEqual(layout["enum_dir"], ((2, False), DIR_FANOUT)) + self.assertFieldEqual(layout["enum"], ((2, False), DIR_NONE)) + self.assertFieldEqual(layout["enum_dir"], ((2, False), DIR_FANOUT)) def test_range_field(self): layout = Layout.cast([ ("range", range(0, 7)), ]) - self.assertEqual(layout["range"], ((3, False), DIR_NONE)) + self.assertFieldEqual(layout["range"], ((3, False), DIR_NONE)) def test_slice_tuple(self): layout = Layout.cast([ @@ -60,9 +65,9 @@ class LayoutTestCase(FHDLTestCase): self.assertEqual(layout["a", "c"], expect) def test_repr(self): - self.assertEqual(repr(Layout([("a", 1), ("b", signed(2))])), + self.assertEqual(repr(Layout([("a", unsigned(1)), ("b", signed(2))])), "Layout([('a', unsigned(1)), ('b', signed(2))])") - self.assertEqual(repr(Layout([("a", 1), ("b", [("c", signed(3))])])), + self.assertEqual(repr(Layout([("a", unsigned(1)), ("b", [("c", signed(3))])])), "Layout([('a', unsigned(1)), " "('b', Layout([('c', signed(3))]))])") @@ -201,6 +206,10 @@ class RecordTestCase(FHDLTestCase): self.assertIs(r2.a, r1.a) self.assertIs(r2.c, r1.c) + def test_enum_decoder(self): + r1 = Record([("a", UnsignedEnum)]) + self.assertEqual(r1.a.decoder(UnsignedEnum.FOO), "FOO/1") + class ConnectTestCase(FHDLTestCase): def setUp_flat(self): diff --git a/nmigen/test/test_lib_io.py b/nmigen/test/test_lib_io.py index cb1cd46..156150e 100644 --- a/nmigen/test/test_lib_io.py +++ b/nmigen/test/test_lib_io.py @@ -5,95 +5,104 @@ from ..back.pysim import * from ..lib.io import * -class PinLayoutCombTestCase(FHDLTestCase): +class PinLayoutTestCase(FHDLTestCase): + def assertLayoutEqual(self, layout, expected): + casted_layout = {} + for name, (shape, dir) in layout.items(): + casted_layout[name] = (Shape.cast(shape), dir) + + self.assertEqual(casted_layout, expected) + + +class PinLayoutCombTestCase(PinLayoutTestCase): def test_pin_layout_i(self): layout_1 = pin_layout(1, dir="i") - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "i": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="i") - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "i": ((2, False), DIR_NONE), }) def test_pin_layout_o(self): layout_1 = pin_layout(1, dir="o") - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "o": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="o") - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "o": ((2, False), DIR_NONE), }) def test_pin_layout_oe(self): layout_1 = pin_layout(1, dir="oe") - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "o": ((1, False), DIR_NONE), "oe": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="oe") - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "o": ((2, False), DIR_NONE), "oe": ((1, False), DIR_NONE), }) def test_pin_layout_io(self): layout_1 = pin_layout(1, dir="io") - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "i": ((1, False), DIR_NONE), "o": ((1, False), DIR_NONE), "oe": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="io") - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "i": ((2, False), DIR_NONE), "o": ((2, False), DIR_NONE), "oe": ((1, False), DIR_NONE), }) -class PinLayoutSDRTestCase(FHDLTestCase): +class PinLayoutSDRTestCase(PinLayoutTestCase): def test_pin_layout_i(self): layout_1 = pin_layout(1, dir="i", xdr=1) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "i_clk": ((1, False), DIR_NONE), "i": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="i", xdr=1) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "i_clk": ((1, False), DIR_NONE), "i": ((2, False), DIR_NONE), }) def test_pin_layout_o(self): layout_1 = pin_layout(1, dir="o", xdr=1) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "o_clk": ((1, False), DIR_NONE), "o": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="o", xdr=1) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "o_clk": ((1, False), DIR_NONE), "o": ((2, False), DIR_NONE), }) def test_pin_layout_oe(self): layout_1 = pin_layout(1, dir="oe", xdr=1) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "o_clk": ((1, False), DIR_NONE), "o": ((1, False), DIR_NONE), "oe": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="oe", xdr=1) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "o_clk": ((1, False), DIR_NONE), "o": ((2, False), DIR_NONE), "oe": ((1, False), DIR_NONE), @@ -101,7 +110,7 @@ class PinLayoutSDRTestCase(FHDLTestCase): def test_pin_layout_io(self): layout_1 = pin_layout(1, dir="io", xdr=1) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "i_clk": ((1, False), DIR_NONE), "i": ((1, False), DIR_NONE), "o_clk": ((1, False), DIR_NONE), @@ -110,7 +119,7 @@ class PinLayoutSDRTestCase(FHDLTestCase): }) layout_2 = pin_layout(2, dir="io", xdr=1) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "i_clk": ((1, False), DIR_NONE), "i": ((2, False), DIR_NONE), "o_clk": ((1, False), DIR_NONE), @@ -119,17 +128,17 @@ class PinLayoutSDRTestCase(FHDLTestCase): }) -class PinLayoutDDRTestCase(FHDLTestCase): +class PinLayoutDDRTestCase(PinLayoutTestCase): def test_pin_layout_i(self): layout_1 = pin_layout(1, dir="i", xdr=2) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "i_clk": ((1, False), DIR_NONE), "i0": ((1, False), DIR_NONE), "i1": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="i", xdr=2) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "i_clk": ((1, False), DIR_NONE), "i0": ((2, False), DIR_NONE), "i1": ((2, False), DIR_NONE), @@ -137,14 +146,14 @@ class PinLayoutDDRTestCase(FHDLTestCase): def test_pin_layout_o(self): layout_1 = pin_layout(1, dir="o", xdr=2) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "o_clk": ((1, False), DIR_NONE), "o0": ((1, False), DIR_NONE), "o1": ((1, False), DIR_NONE), }) layout_2 = pin_layout(2, dir="o", xdr=2) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "o_clk": ((1, False), DIR_NONE), "o0": ((2, False), DIR_NONE), "o1": ((2, False), DIR_NONE), @@ -152,7 +161,7 @@ class PinLayoutDDRTestCase(FHDLTestCase): def test_pin_layout_oe(self): layout_1 = pin_layout(1, dir="oe", xdr=2) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "o_clk": ((1, False), DIR_NONE), "o0": ((1, False), DIR_NONE), "o1": ((1, False), DIR_NONE), @@ -160,7 +169,7 @@ class PinLayoutDDRTestCase(FHDLTestCase): }) layout_2 = pin_layout(2, dir="oe", xdr=2) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "o_clk": ((1, False), DIR_NONE), "o0": ((2, False), DIR_NONE), "o1": ((2, False), DIR_NONE), @@ -169,7 +178,7 @@ class PinLayoutDDRTestCase(FHDLTestCase): def test_pin_layout_io(self): layout_1 = pin_layout(1, dir="io", xdr=2) - self.assertEqual(layout_1.fields, { + self.assertLayoutEqual(layout_1.fields, { "i_clk": ((1, False), DIR_NONE), "i0": ((1, False), DIR_NONE), "i1": ((1, False), DIR_NONE), @@ -180,7 +189,7 @@ class PinLayoutDDRTestCase(FHDLTestCase): }) layout_2 = pin_layout(2, dir="io", xdr=2) - self.assertEqual(layout_2.fields, { + self.assertLayoutEqual(layout_2.fields, { "i_clk": ((1, False), DIR_NONE), "i0": ((2, False), DIR_NONE), "i1": ((2, False), DIR_NONE), -- 2.30.2