hdl.ir: allow adding more than one domain in missing domain callback.
authorwhitequark <whitequark@whitequark.org>
Sat, 3 Aug 2019 18:19:40 +0000 (18:19 +0000)
committerwhitequark <whitequark@whitequark.org>
Sat, 3 Aug 2019 18:19:40 +0000 (18:19 +0000)
This is useful for injecting complex power-on reset logic.

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

index 370b255171bc1d11e86ee32c939a3e545484c1e6..6dd5d08ffa95676ce607eef3076b46cc5ff0f80d 100644 (file)
@@ -363,23 +363,21 @@ class Fragment:
                 if value is None:
                     raise DomainError("Domain '{}' is used but not defined".format(domain_name))
                 if type(value) is ClockDomain:
-                    domain = value
-                    # Only expose ports on clock domains returned directly, i.e. not as a part of
-                    # a fragment driving that domain.
-                    new_domains.append(domain)
+                    self.add_domains(value)
+                    # And expose ports on the newly added clock domain, since it is added directly
+                    # and there was no chance to add any logic driving it.
+                    new_domains.append(value)
                 else:
                     new_fragment = Fragment.get(value, platform=None)
-                    if new_fragment.domains.keys() != {domain_name}:
+                    if domain_name not in new_fragment.domains:
+                        defined = new_fragment.domains.keys()
                         raise DomainError(
-                            "Fragment returned by missing domain callback should define exactly "
-                            "one domain '{}', but defines domain(s) {}."
-                            .format(domain_name,
-                                    ", ".join("'{}'".format(n)
-                                              for n in new_fragment.domains.keys())))
+                            "Fragment returned by missing domain callback does not define "
+                            "requested domain '{}' (defines {})."
+                            .format(domain_name, ", ".join("'{}'".format(n) for n in defined)))
                     new_fragment.flatten = True
                     self.add_subfragment(new_fragment)
-                    domain = new_fragment.domains[domain_name]
-                self.add_domains(domain)
+                    self.add_domains(new_fragment.domains.values())
         return new_domains
 
     def _propagate_domains(self, missing_domain):
index d6c43bec5b96b12519ab9ef6d96e0ad67038607c..c89e8d7a42a596cb0aacf1bb611bde14a1bfc8f5 100644 (file)
@@ -421,6 +421,25 @@ class FragmentDomainsTestCase(FHDLTestCase):
         ])
         self.assertTrue(f2.flatten)
 
+    def test_propagate_create_missing_fragment_many_domains(self):
+        s1 = Signal()
+        f1 = Fragment()
+        f1.add_driver(s1, "sync")
+
+        cd_por  = ClockDomain("por")
+        cd_sync = ClockDomain("sync")
+        f2 = Fragment()
+        f2.add_domains(cd_por, cd_sync)
+
+        new_domains = f1._propagate_domains(missing_domain=lambda name: f2)
+        self.assertEqual(f1.domains.keys(), {"sync", "por"})
+        self.assertEqual(f2.domains.keys(), {"sync", "por"})
+        self.assertEqual(f1.domains["sync"], f2.domains["sync"])
+        self.assertEqual(new_domains, [])
+        self.assertEqual(f1.subfragments, [
+            (f2, None)
+        ])
+
     def test_propagate_create_missing_fragment_wrong(self):
         s1 = Signal()
         f1 = Fragment()
@@ -430,8 +449,8 @@ class FragmentDomainsTestCase(FHDLTestCase):
         f2.add_domains(ClockDomain("foo"))
 
         with self.assertRaises(DomainError,
-                msg="Fragment returned by missing domain callback should define exactly "
-                    "one domain 'sync', but defines domain(s) 'foo'."):
+                msg="Fragment returned by missing domain callback does not define requested "
+                    "domain 'sync' (defines 'foo')."):
             f1._propagate_domains(missing_domain=lambda name: f2)