hdl.rec: preserve shapes when constructing a layout.
authorShawn Anastasio <shawnanastasio@yahoo.com>
Fri, 5 Jun 2020 03:19:46 +0000 (22:19 -0500)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 31 Dec 2021 13:39:38 +0000 (13:39 +0000)
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
nmigen/test/test_hdl_rec.py
nmigen/test/test_lib_io.py

index be8934226a1027acbaea076b6ee1c91b5cc583a4..5e5687c8c3ed7da78915cf7ae34863366e2bf7d0 100644 (file)
@@ -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):
index 09af444bfee169ba875bdba006d58c9e67152c9b..4f99800b9f2c455335e8628e017f61119fedc2a5 100644 (file)
@@ -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):
index cb1cd4686d131d21ceadc7742ed41a14c7c9e4fd..156150ee3965f033a8daae79541ab6d1f2645deb 100644 (file)
@@ -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),