From 68276a0d3ffb69b5aefe2bd088a905d004fed121 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 13 Dec 2018 13:12:31 +0000 Subject: [PATCH] fhdl.ir: record port direction explicitly. No point in recalculating this in the backend when writing RTLIL or Verilog port directions. --- nmigen/back/rtlil.py | 21 ++++++---- nmigen/fhdl/ast.py | 21 +++++++++- nmigen/fhdl/ir.py | 13 +++--- nmigen/test/test_fhdl_ir.py | 82 +++++++++++++++++++------------------ 4 files changed, 83 insertions(+), 54 deletions(-) diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index fdb7fb9..d1c7c7a 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -231,11 +231,15 @@ class _ValueTransformer(xfrm.ValueTransformer): def add_driven(self, signal, sync): self.driven[signal] = sync - def add_port(self, signal, kind=None): - if signal in self.driven: - self.ports[signal] = (len(self.ports), "output") - else: - self.ports[signal] = (len(self.ports), "input") + def add_port(self, signal, kind): + assert kind in ("i", "o", "io") + if kind == "i": + kind = "input" + elif kind == "o": + kind = "output" + elif kind == "io": + kind = "inout" + self.ports[signal] = (len(self.ports), kind) @contextmanager def lhs(self): @@ -412,10 +416,11 @@ def convert_fragment(builder, fragment, name, top): for domain, signal in fragment.iter_drivers(): xformer.add_driven(signal, sync=domain is not None) - # Register all signals used as ports in the current fragment. The wires are lazily - # generated, so registering ports eagerly ensures they get correct direction qualifiers. + # Transform all signals used as ports in the current fragment eagerly and outside of + # any hierarchy, to make sure they get sensible (non-prefixed) names. for signal in fragment.ports: - xformer.add_port(signal) + xformer.add_port(signal, fragment.ports[signal]) + xformer(signal) # Transform all clocks clocks and resets eagerly and outside of any hierarchy, to make # sure they get sensible (non-prefixed) names. This does not affect semantics. diff --git a/nmigen/fhdl/ast.py b/nmigen/fhdl/ast.py index c5f03e1..c74a69b 100644 --- a/nmigen/fhdl/ast.py +++ b/nmigen/fhdl/ast.py @@ -516,7 +516,7 @@ class Signal(Value, DUID): if name is None: try: - name = tracer.get_var_name() + name = tracer.get_var_name(depth=2 + src_loc_at) except tracer.NameNotFound: name = "$signal" self.name = name @@ -557,7 +557,8 @@ class Signal(Value, DUID): other : Value Object to base this Signal on. """ - kw = dict(shape=cls.wrap(other).shape(), name=tracer.get_var_name()) + kw = dict(shape=cls.wrap(other).shape(), + name=tracer.get_var_name(depth=2 + src_loc_at)) if isinstance(other, cls): kw.update(reset=other.reset, reset_less=other.reset_less, attrs=other.attrs) kw.update(kwargs) @@ -749,9 +750,25 @@ class ValueDict(MutableMapping): def __iter__(self): return map(lambda x: None if x is None else x.value, sorted(self._inner)) + def __eq__(self, other): + if not isinstance(other, ValueDict): + return False + if len(self) != len(other): + return False + for ak, bk in zip(self, other): + if ValueKey(ak) != ValueKey(bk): + return False + if self[ak] != other[bk]: + return False + return True + def __len__(self): return len(self._inner) + def __repr__(self): + pairs = ["({!r}, {!r})".format(k, v) for k, v in self.items()] + return "ValueDict([{}])".format(", ".join(pairs)) + class ValueSet(MutableSet): def __init__(self, elements=()): diff --git a/nmigen/fhdl/ir.py b/nmigen/fhdl/ir.py index a3d1d1d..383ef74 100644 --- a/nmigen/fhdl/ir.py +++ b/nmigen/fhdl/ir.py @@ -14,17 +14,19 @@ class DomainError(Exception): class Fragment: def __init__(self): - self.ports = ValueSet() + self.ports = ValueDict() self.drivers = OrderedDict() self.statements = [] self.domains = OrderedDict() self.subfragments = [] - def add_ports(self, *ports): - self.ports.update(flatten(ports)) + def add_ports(self, *ports, kind): + assert kind in ("i", "o", "io") + for port in flatten(ports): + self.ports[port] = kind def iter_ports(self): - yield from self.ports + yield from self.ports.keys() def drive(self, signal, domain=None): if domain not in self.drivers: @@ -161,6 +163,7 @@ class Fragment: outs |= ports & sub_outs # We've computed the precise set of input and output ports. - self.add_ports(ins, outs) + self.add_ports(ins, kind="i") + self.add_ports(outs, kind="o") return ins, outs diff --git a/nmigen/test/test_fhdl_ir.py b/nmigen/test/test_fhdl_ir.py index 35fdbf2..decfa3c 100644 --- a/nmigen/test/test_fhdl_ir.py +++ b/nmigen/test/test_fhdl_ir.py @@ -16,10 +16,8 @@ class FragmentPortsTestCase(FHDLTestCase): def test_empty(self): f = Fragment() - ins, outs = f._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet()) - self.assertEqual(outs, ValueSet()) - self.assertEqual(f.ports, ValueSet()) + f._propagate_ports(ports=()) + self.assertEqual(f.ports, ValueDict([])) def test_self_contained(self): f = Fragment() @@ -28,10 +26,8 @@ class FragmentPortsTestCase(FHDLTestCase): self.s1.eq(self.c1) ) - ins, outs = f._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet()) - self.assertEqual(outs, ValueSet()) - self.assertEqual(f.ports, ValueSet()) + f._propagate_ports(ports=()) + self.assertEqual(f.ports, ValueDict([])) def test_infer_input(self): f = Fragment() @@ -39,10 +35,10 @@ class FragmentPortsTestCase(FHDLTestCase): self.c1.eq(self.s1) ) - ins, outs = f._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet((self.s1,))) - self.assertEqual(outs, ValueSet()) - self.assertEqual(f.ports, ValueSet((self.s1,))) + f._propagate_ports(ports=()) + self.assertEqual(f.ports, ValueDict([ + (self.s1, "i") + ])) def test_request_output(self): f = Fragment() @@ -50,10 +46,11 @@ class FragmentPortsTestCase(FHDLTestCase): self.c1.eq(self.s1) ) - ins, outs = f._propagate_ports(ports=(self.c1,)) - self.assertEqual(ins, ValueSet((self.s1,))) - self.assertEqual(outs, ValueSet((self.c1,))) - self.assertEqual(f.ports, ValueSet((self.s1, self.c1))) + f._propagate_ports(ports=(self.c1,)) + self.assertEqual(f.ports, ValueDict([ + (self.s1, "i"), + (self.c1, "o") + ])) def test_input_in_subfragment(self): f1 = Fragment() @@ -65,11 +62,11 @@ class FragmentPortsTestCase(FHDLTestCase): self.s1.eq(0) ) f1.add_subfragment(f2) - ins, outs = f1._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet()) - self.assertEqual(outs, ValueSet()) - self.assertEqual(f1.ports, ValueSet()) - self.assertEqual(f2.ports, ValueSet((self.s1,))) + f1._propagate_ports(ports=()) + self.assertEqual(f1.ports, ValueDict()) + self.assertEqual(f2.ports, ValueDict([ + (self.s1, "o"), + ])) def test_input_only_in_subfragment(self): f1 = Fragment() @@ -78,11 +75,13 @@ class FragmentPortsTestCase(FHDLTestCase): self.c1.eq(self.s1) ) f1.add_subfragment(f2) - ins, outs = f1._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet((self.s1,))) - self.assertEqual(outs, ValueSet()) - self.assertEqual(f1.ports, ValueSet((self.s1,))) - self.assertEqual(f2.ports, ValueSet((self.s1,))) + f1._propagate_ports(ports=()) + self.assertEqual(f1.ports, ValueDict([ + (self.s1, "i"), + ])) + self.assertEqual(f2.ports, ValueDict([ + (self.s1, "i"), + ])) def test_output_from_subfragment(self): f1 = Fragment() @@ -95,11 +94,13 @@ class FragmentPortsTestCase(FHDLTestCase): ) f1.add_subfragment(f2) - ins, outs = f1._propagate_ports(ports=(self.c2,)) - self.assertEqual(ins, ValueSet()) - self.assertEqual(outs, ValueSet((self.c2,))) - self.assertEqual(f1.ports, ValueSet((self.c2,))) - self.assertEqual(f2.ports, ValueSet((self.c2,))) + f1._propagate_ports(ports=(self.c2,)) + self.assertEqual(f1.ports, ValueDict([ + (self.c2, "o"), + ])) + self.assertEqual(f2.ports, ValueDict([ + (self.c2, "o"), + ])) def test_input_cd(self): sync = ClockDomain() @@ -110,10 +111,12 @@ class FragmentPortsTestCase(FHDLTestCase): f.add_domains(sync) f.drive(self.c1, "sync") - ins, outs = f._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet((self.s1, sync.clk, sync.rst))) - self.assertEqual(outs, ValueSet(())) - self.assertEqual(f.ports, ValueSet((self.s1, sync.clk, sync.rst))) + f._propagate_ports(ports=()) + self.assertEqual(f.ports, ValueDict([ + (self.s1, "i"), + (sync.clk, "i"), + (sync.rst, "i"), + ])) def test_input_cd_reset_less(self): sync = ClockDomain(reset_less=True) @@ -124,10 +127,11 @@ class FragmentPortsTestCase(FHDLTestCase): f.add_domains(sync) f.drive(self.c1, "sync") - ins, outs = f._propagate_ports(ports=()) - self.assertEqual(ins, ValueSet((self.s1, sync.clk))) - self.assertEqual(outs, ValueSet(())) - self.assertEqual(f.ports, ValueSet((self.s1, sync.clk))) + f._propagate_ports(ports=()) + self.assertEqual(f.ports, ValueDict([ + (self.s1, "i"), + (sync.clk, "i"), + ])) class FragmentDomainsTestCase(FHDLTestCase): -- 2.30.2