build.{dsl,res}: allow platform-dependent attributes using callables.

Fixes #132.
This commit is contained in:
whitequark 2019-07-08 11:15:04 +00:00
parent 0ab0a74ec1
commit 7b4fbf8e01
4 changed files with 24 additions and 8 deletions

View 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")

View 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))

View 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)

View 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)