From: whitequark Date: Mon, 8 Jul 2019 11:15:04 +0000 (+0000) Subject: build.{dsl,res}: allow platform-dependent attributes using callables. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=2b5f3878d4699e330267f8f806996df649ae53c6;p=nmigen.git build.{dsl,res}: allow platform-dependent attributes using callables. Fixes #132. --- diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index 52ddb5c..6e3c87f 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -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") diff --git a/nmigen/build/dsl.py b/nmigen/build/dsl.py index 0c936bb..a3e4a58 100644 --- a/nmigen/build/dsl.py +++ b/nmigen/build/dsl.py @@ -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)) diff --git a/nmigen/build/res.py b/nmigen/build/res.py index 9977dd5..c1c0dce 100644 --- a/nmigen/build/res.py +++ b/nmigen/build/res.py @@ -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) diff --git a/nmigen/test/test_build_dsl.py b/nmigen/test/test_build_dsl.py index e7839c0..c8f95ab 100644 --- a/nmigen/test/test_build_dsl.py +++ b/nmigen/test/test_build_dsl.py @@ -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)