build.{dsl,res}: allow platform-dependent attributes using callables.
authorwhitequark <whitequark@whitequark.org>
Mon, 8 Jul 2019 11:15:04 +0000 (11:15 +0000)
committerwhitequark <whitequark@whitequark.org>
Mon, 8 Jul 2019 11:15:04 +0000 (11:15 +0000)
Fixes #132.

nmigen/back/rtlil.py
nmigen/build/dsl.py
nmigen/build/res.py
nmigen/test/test_build_dsl.py

index 52ddb5c4da4e06942294a047a8a4a33c78f0950c..6e3c87fb846cd8c30e78c6f681ecf1065bfe70c9 100644 (file)
@@ -127,7 +127,7 @@ class _ModuleBuilder(_Namer, _BufferedBuilder, _AttrBuilder):
                 self._append("    parameter \\{} {}'{:b}\n",
                              param, len(value), value.value)
             else:
-                assert False
+                assert False, "Bad parameter {!r}".format(value)
         for port, wire in ports.items():
             self._append("    connect {} {}\n", port, wire)
         self._append("  end\n")
index 0c936bb7a88c8a293a09b2a054d910afdfd6699c..a3e4a580bc789deb996f57fa78aff511193a6c51 100644 (file)
@@ -91,10 +91,10 @@ def DiffPairsN(*args, **kwargs):
 
 class Attrs(OrderedDict):
     def __init__(self, **attrs):
-        for attr_key, attr_value in attrs.items():
-            if not (attr_value is None or isinstance(attr_value, str)):
-                raise TypeError("Attribute value must be None or str, not {!r}"
-                                .format(attr_value))
+        for key, value in attrs.items():
+            if not (value is None or isinstance(value, str) or hasattr(value, "__call__")):
+                raise TypeError("Value of attribute {} must be None, str, or callable, not {!r}"
+                                .format(key, value))
 
         super().__init__(**attrs)
 
@@ -103,6 +103,8 @@ class Attrs(OrderedDict):
         for key, value in self.items():
             if value is None:
                 items.append("!" + key)
+            elif hasattr(value, "__call__"):
+                items.append(key + "=" + repr(value))
             else:
                 items.append(key + "=" + value)
         return "(attrs {})".format(" ".join(items))
index 9977dd54abc0bb5a1beb93a26f2dcce26afa8399..c1c0dce1d221cbfda8ced501c102154526ab5ac1 100644 (file)
@@ -103,13 +103,21 @@ class ResourceManager:
             return dir, xdr
 
         def resolve(resource, dir, xdr, name, attrs):
+            for attr_key, attr_value in attrs.items():
+                if hasattr(attr_value, "__call__"):
+                    attr_value = attr_value(self)
+                    assert attr_value is None or isinstance(attr_value, str)
+                if attr_value is None:
+                    del attrs[attr_key]
+                else:
+                    attrs[attr_key] = attr_value
+
             if isinstance(resource.ios[0], Subsignal):
                 fields = OrderedDict()
                 for sub in resource.ios:
-                    sub_attrs = {k: v for k, v in {**attrs, **sub.attrs}.items() if v is not None}
                     fields[sub.name] = resolve(sub, dir[sub.name], xdr[sub.name],
                                                name="{}__{}".format(name, sub.name),
-                                               attrs=sub_attrs)
+                                               attrs={**attrs, **sub.attrs})
                 return Record([
                     (f_name, f.layout) for (f_name, f) in fields.items()
                 ], fields=fields, name=name)
index e7839c02ccc18351e06fbe91d680ae5dd8ff3f12..c8f95ab1e4623637859c25709bd1685b528eb521 100644 (file)
@@ -118,9 +118,15 @@ class AttrsTestCase(FHDLTestCase):
         self.assertEqual(a["FOO"], None)
         self.assertEqual(repr(a), "(attrs !FOO)")
 
+    def test_callable(self):
+        fn = lambda self: "FOO"
+        a = Attrs(FOO=fn)
+        self.assertEqual(a["FOO"], fn)
+        self.assertEqual(repr(a), "(attrs FOO={!r})".format(fn))
+
     def test_wrong_value(self):
         with self.assertRaises(TypeError,
-                msg="Attribute value must be None or str, not 1"):
+                msg="Value of attribute FOO must be None, str, or callable, not 1"):
             a = Attrs(FOO=1)