fhdl.ir: record port direction explicitly.
authorwhitequark <whitequark@whitequark.org>
Thu, 13 Dec 2018 13:12:31 +0000 (13:12 +0000)
committerwhitequark <whitequark@whitequark.org>
Thu, 13 Dec 2018 13:12:31 +0000 (13:12 +0000)
No point in recalculating this in the backend when writing RTLIL or
Verilog port directions.

nmigen/back/rtlil.py
nmigen/fhdl/ast.py
nmigen/fhdl/ir.py
nmigen/test/test_fhdl_ir.py

index fdb7fb9f2ce14a3adbc0ae11a3eecf32a14871b3..d1c7c7a95043f533702ee95e5bbe3700e5af7fae 100644 (file)
@@ -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.
index c5f03e11246b264d85798f14bc16bd7ef476b039..c74a69bf2be6bc15cba708ffa9c17ae5ef8584e7 100644 (file)
@@ -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=()):
index a3d1d1d11ce495b79a9f0d2c4b96f50020281515..383ef74164acfb663eae0540fb16b69645068d15 100644 (file)
@@ -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
index 35fdbf2c3f16c57480e572ceab8acdd9e9d6fcf0..decfa3c08819492d56ba9c49054f19991b497d18 100644 (file)
@@ -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):