hdl.rec: allow providing fields during construction.
authorwhitequark <cz@m-labs.hk>
Sat, 25 May 2019 21:57:07 +0000 (21:57 +0000)
committerwhitequark <cz@m-labs.hk>
Sat, 25 May 2019 22:06:56 +0000 (22:06 +0000)
This allows creating records populated with e.g. signals with custom
names, or sub-records that are instances of Record subclasses.

nmigen/hdl/rec.py
nmigen/test/test_hdl_rec.py
nmigen/test/test_lib_io.py

index 63a977ccf705a260d3ae8182ef32c4eba5da15a4..7abd6ff85cbe9f43464044a841ed63e0c245f3d6 100644 (file)
@@ -52,6 +52,8 @@ class Layout:
             if name in self.fields:
                 raise NameError("Field {!r} has a name that is already present in the layout"
                                 .format(field))
+            if isinstance(shape, int):
+                shape = (shape, False)
             self.fields[name] = (shape, direction)
 
     def __getitem__(self, name):
@@ -61,10 +63,13 @@ class Layout:
         for name, (shape, dir) in self.fields.items():
             yield (name, shape, dir)
 
+    def __eq__(self, other):
+        return self.fields == other.fields
+
 
 # Unlike most Values, Record *can* be subclassed.
 class Record(Value):
-    def __init__(self, layout, name=None):
+    def __init__(self, layout, name=None, *, fields=None):
         if name is None:
             name = tracer.get_var_name(default=None)
 
@@ -79,10 +84,18 @@ class Record(Value):
         self.layout = Layout.wrap(layout)
         self.fields = OrderedDict()
         for field_name, field_shape, field_dir in self.layout:
-            if isinstance(field_shape, Layout):
-                self.fields[field_name] = Record(field_shape, name=concat(name, field_name))
+            if fields is not None and field_name in fields:
+                field = fields[field_name]
+                if isinstance(field_shape, Layout):
+                    assert isinstance(field, Record) and field_shape == field.layout
+                else:
+                    assert isinstance(field, Signal) and field_shape == field.shape()
+                self.fields[field_name] = field
             else:
-                self.fields[field_name] = Signal(field_shape, name=concat(name, field_name))
+                if isinstance(field_shape, Layout):
+                    self.fields[field_name] = Record(field_shape, name=concat(name, field_name))
+                else:
+                    self.fields[field_name] = Signal(field_shape, name=concat(name, field_name))
 
     def __getattr__(self, name):
         return self[name]
index 81aa89d2fd7c43fd049e3c5ad85715d3e6403330..ba1600c5443cfd393096701636409f10c2795dec 100644 (file)
@@ -16,14 +16,14 @@ class LayoutTestCase(FHDLTestCase):
             ])
         ])
 
-        self.assertEqual(layout["cyc"], (1, DIR_NONE))
+        self.assertEqual(layout["cyc"], ((1, False), DIR_NONE))
         self.assertEqual(layout["data"], ((32, True), DIR_NONE))
-        self.assertEqual(layout["stb"], (1, DIR_FANOUT))
-        self.assertEqual(layout["ack"], (1, DIR_FANIN))
+        self.assertEqual(layout["stb"], ((1, False), DIR_FANOUT))
+        self.assertEqual(layout["ack"], ((1, False), DIR_FANIN))
         sublayout = layout["info"][0]
         self.assertEqual(layout["info"][1], DIR_NONE)
-        self.assertEqual(sublayout["a"], (1, DIR_NONE))
-        self.assertEqual(sublayout["b"], (1, DIR_NONE))
+        self.assertEqual(sublayout["a"], ((1, False), DIR_NONE))
+        self.assertEqual(sublayout["b"], ((1, False), DIR_NONE))
 
     def test_wrong_field(self):
         with self.assertRaises(TypeError,
@@ -106,6 +106,23 @@ class RecordTestCase(FHDLTestCase):
                 msg="Unnamed record does not have a field 'en'. Did you mean one of: stb, ack?"):
             r.en
 
+    def test_construct_with_fields(self):
+        ns = Signal(1)
+        nr = Record([
+            ("burst", 1)
+        ])
+        r = Record([
+            ("stb", 1),
+            ("info", [
+                ("burst", 1)
+            ])
+        ], fields={
+            "stb":  ns,
+            "info": nr
+        })
+        self.assertIs(r.stb, ns)
+        self.assertIs(r.info, nr)
+
 
 class ConnectTestCase(FHDLTestCase):
     def setUp_flat(self):
index 57371ef222370c7141ba9a8da23a8ee55c9c676d..f2cfdeff29999394fdb34abd1831f64ce59ab352 100644 (file)
@@ -8,38 +8,38 @@ class PinLayoutSDRTestCase(FHDLTestCase):
     def test_pin_layout_i(self):
         layout_1 = pin_layout(1, dir="i")
         self.assertEqual(layout_1.fields, {
-            "i": (1, DIR_NONE),
+            "i": ((1, False), DIR_NONE),
         })
 
         layout_2 = pin_layout(2, dir="i")
         self.assertEqual(layout_2.fields, {
-            "i": (2, DIR_NONE),
+            "i": ((2, False), DIR_NONE),
         })
 
     def test_pin_layout_o(self):
         layout_1 = pin_layout(1, dir="o")
         self.assertEqual(layout_1.fields, {
-            "o": (1, DIR_NONE),
+            "o": ((1, False), DIR_NONE),
         })
 
         layout_2 = pin_layout(2, dir="o")
         self.assertEqual(layout_2.fields, {
-            "o": (2, DIR_NONE),
+            "o": ((2, False), DIR_NONE),
         })
 
     def test_pin_layout_io(self):
         layout_1 = pin_layout(1, dir="io")
         self.assertEqual(layout_1.fields, {
-            "i":  (1, DIR_NONE),
-            "o":  (1, DIR_NONE),
-            "oe": (1, DIR_NONE),
+            "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, {
-            "i":  (2, DIR_NONE),
-            "o":  (2, DIR_NONE),
-            "oe": (1, DIR_NONE),
+            "i":  ((2, False), DIR_NONE),
+            "o":  ((2, False), DIR_NONE),
+            "oe": ((1, False), DIR_NONE),
         })
 
 
@@ -47,46 +47,46 @@ class PinLayoutDDRTestCase(FHDLTestCase):
     def test_pin_layout_i(self):
         layout_1 = pin_layout(1, dir="i", xdr=2)
         self.assertEqual(layout_1.fields, {
-            "i0": (1, DIR_NONE),
-            "i1": (1, 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, {
-            "i0": (2, DIR_NONE),
-            "i1": (2, DIR_NONE),
+            "i0": ((2, False), DIR_NONE),
+            "i1": ((2, False), DIR_NONE),
         })
 
     def test_pin_layout_o(self):
         layout_1 = pin_layout(1, dir="o", xdr=2)
         self.assertEqual(layout_1.fields, {
-            "o0": (1, DIR_NONE),
-            "o1": (1, 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, {
-            "o0": (2, DIR_NONE),
-            "o1": (2, DIR_NONE),
+            "o0": ((2, False), DIR_NONE),
+            "o1": ((2, False), DIR_NONE),
         })
 
     def test_pin_layout_io(self):
         layout_1 = pin_layout(1, dir="io", xdr=2)
         self.assertEqual(layout_1.fields, {
-            "i0": (1, DIR_NONE),
-            "i1": (1, DIR_NONE),
-            "o0": (1, DIR_NONE),
-            "o1": (1, DIR_NONE),
-            "oe": (1, DIR_NONE),
+            "i0": ((1, False), DIR_NONE),
+            "i1": ((1, False), DIR_NONE),
+            "o0": ((1, False), DIR_NONE),
+            "o1": ((1, False), DIR_NONE),
+            "oe": ((1, False), DIR_NONE),
         })
 
         layout_2 = pin_layout(2, dir="io", xdr=2)
         self.assertEqual(layout_2.fields, {
-            "i0": (2, DIR_NONE),
-            "i1": (2, DIR_NONE),
-            "o0": (2, DIR_NONE),
-            "o1": (2, DIR_NONE),
-            "oe": (1, DIR_NONE),
+            "i0": ((2, False), DIR_NONE),
+            "i1": ((2, False), DIR_NONE),
+            "o0": ((2, False), DIR_NONE),
+            "o1": ((2, False), DIR_NONE),
+            "oe": ((1, False), DIR_NONE),
         })