hdl.ir: resolve hierarchy conflicts before creating missing domains.
authorwhitequark <whitequark@whitequark.org>
Sat, 18 Jan 2020 10:30:36 +0000 (10:30 +0000)
committerwhitequark <whitequark@whitequark.org>
Sat, 18 Jan 2020 10:30:36 +0000 (10:30 +0000)
Otherwise, code such as:

    m.submodules.a = (something with cd_sync)
    m.submodules.b = (something with cd_sync)
    m.d.b_sync += x.eq(y)

causes an assertion failure.

Fixes #304 (again).

nmigen/hdl/ir.py
nmigen/test/test_hdl_ir.py

index a43e222999e138b04a6850891e0532da8ba5dd30..26dec85a4c13d86d646e5696829c47136ebdf9b4 100644 (file)
@@ -203,6 +203,15 @@ class Fragment:
         driver_subfrags = SignalDict()
         memory_subfrags = OrderedDict()
         def add_subfrag(registry, entity, entry):
+            # Because of missing domain insertion, at the point when this code runs, we have
+            # a mixture of bound and unbound {Clock,Reset}Signals. Map the bound ones to
+            # the actual signals (because the signal itself can be driven as well); but leave
+            # the unbound ones as it is, because there's no concrete signal for it yet anyway.
+            if isinstance(entity, ClockSignal) and entity.domain in self.domains:
+                entity = self.domains[entity.domain].clk
+            elif isinstance(entity, ResetSignal) and entity.domain in self.domains:
+                entity = self.domains[entity.domain].rst
+
             if entity not in registry:
                 registry[entity] = set()
             registry[entity].add(entry)
@@ -387,12 +396,15 @@ class Fragment:
                         "requested domain '{}' (defines {})."
                         .format(domain_name, ", ".join("'{}'".format(n) for n in defined)))
                 self.add_subfragment(new_fragment, "cd_{}".format(domain_name))
+                self.add_domains(new_fragment.domains.values())
         return new_domains
 
     def _propagate_domains(self, missing_domain):
-        new_domains = self.create_missing_domains(missing_domain)
         self._propagate_domains_up()
         self._propagate_domains_down()
+        self._resolve_hierarchy_conflicts()
+        new_domains = self.create_missing_domains(missing_domain)
+        self._propagate_domains_down()
         return new_domains
 
     def _prepare_use_def_graph(self, parent, level, uses, defs, ios, top):
@@ -543,7 +555,6 @@ class Fragment:
         fragment = SampleLowerer()(self)
         new_domains = fragment._propagate_domains(missing_domain)
         fragment = DomainLowerer()(fragment)
-        fragment._resolve_hierarchy_conflicts()
         if ports is None:
             fragment._propagate_ports(ports=(), all_undef_as_ports=True)
         else:
index 4685523b93ed675b96b7dbefa97daeffdf242200..42395ccbf4222117a376f9a2af672d313a9e0512 100644 (file)
@@ -408,6 +408,22 @@ class FragmentDomainsTestCase(FHDLTestCase):
             None: SignalSet((ResetSignal("b_sync"),))
         }))
 
+    def test_domain_conflict_rename_drivers(self):
+        cda = ClockDomain("sync")
+        cdb = ClockDomain("sync")
+        s = Signal()
+
+        fa = Fragment()
+        fa.add_domains(cda)
+        fb = Fragment()
+        fb.add_domains(cdb)
+        f = Fragment()
+        f.add_subfragment(fa, "a")
+        f.add_subfragment(fb, "b")
+        f.add_driver(s, "b_sync")
+
+        f._propagate_domains(lambda name: ClockDomain(name))
+
     def test_propagate_down(self):
         cd = ClockDomain()