From 2b7dc37ffe9b6717530134ac136400f5a4e8efb6 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 25 May 2019 21:57:07 +0000 Subject: [PATCH] hdl.rec: allow providing fields during construction. 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 | 21 +++++++++++--- nmigen/test/test_hdl_rec.py | 27 ++++++++++++++---- nmigen/test/test_lib_io.py | 56 ++++++++++++++++++------------------- 3 files changed, 67 insertions(+), 37 deletions(-) diff --git a/nmigen/hdl/rec.py b/nmigen/hdl/rec.py index 63a977c..7abd6ff 100644 --- a/nmigen/hdl/rec.py +++ b/nmigen/hdl/rec.py @@ -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] diff --git a/nmigen/test/test_hdl_rec.py b/nmigen/test/test_hdl_rec.py index 81aa89d..ba1600c 100644 --- a/nmigen/test/test_hdl_rec.py +++ b/nmigen/test/test_hdl_rec.py @@ -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): diff --git a/nmigen/test/test_lib_io.py b/nmigen/test/test_lib_io.py index 57371ef..f2cfdef 100644 --- a/nmigen/test/test_lib_io.py +++ b/nmigen/test/test_lib_io.py @@ -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), }) -- 2.30.2