hdl.cd: implement local clock domains.
authorwhitequark <cz@m-labs.hk>
Mon, 19 Aug 2019 20:46:46 +0000 (20:46 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 19 Aug 2019 21:44:33 +0000 (21:44 +0000)
Closes #175.

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

index 06f8d790fa89ce99a7e9e2fc5eb379586431f7ee..6813a9d69474f9107b7ebe7f87185ce04ca80d01 100644 (file)
@@ -25,6 +25,9 @@ class ClockDomain:
         If ``True``, the domain uses an asynchronous reset, and registers within this domain
         are initialized to their reset state when reset level changes. Otherwise, registers
         are initialized to reset state at the next clock cycle when reset is asserted.
+    local : bool
+        If ``True``, the domain will propagate only downwards in the design hierarchy. Otherwise,
+        the domain will propagate everywhere.
 
     Attributes
     ----------
@@ -42,7 +45,7 @@ class ClockDomain:
         else:
             return "{}_{}".format(domain_name, signal_name)
 
-    def __init__(self, name=None, reset_less=False, async_reset=False):
+    def __init__(self, name=None, reset_less=False, async_reset=False, local=False):
         if name is None:
             try:
                 name = tracer.get_var_name()
@@ -62,6 +65,8 @@ class ClockDomain:
 
         self.async_reset = async_reset
 
+        self.local = local
+
     def rename(self, new_name):
         self.name = new_name
         self.clk.name = self._name_for(new_name, "clk")
index c21bc82647f40488bc28018864f8fcb05ddc16b2..bd52226cf8001a90ef1956d7d6aff01f210dd658 100644 (file)
@@ -305,12 +305,14 @@ class Fragment:
             subfrag._propagate_domains_up(hierarchy + (hier_name,))
 
             # Second, classify subfragments by domains they define.
-            for domain in subfrag.iter_domains():
-                domain_subfrags[domain].add((subfrag, name, i))
+            for domain_name, domain in subfrag.domains.items():
+                if domain.local:
+                    continue
+                domain_subfrags[domain_name].add((subfrag, name, i))
 
         # For each domain defined by more than one subfragment, rename the domain in each
         # of the subfragments such that they no longer conflict.
-        for domain, subfrags in domain_subfrags.items():
+        for domain_name, subfrags in domain_subfrags.items():
             if len(subfrags) == 1:
                 continue
 
@@ -321,7 +323,7 @@ class Fragment:
                 raise DomainError("Domain '{}' is defined by subfragments {} of fragment '{}'; "
                                   "it is necessary to either rename subfragment domains "
                                   "explicitly, or give names to subfragments"
-                                  .format(domain, ", ".join(names), ".".join(hierarchy)))
+                                  .format(domain_name, ", ".join(names), ".".join(hierarchy)))
 
             if len(names) != len(set(names)):
                 names = sorted("#{}".format(i) for f, n, i in subfrags)
@@ -329,16 +331,18 @@ class Fragment:
                                   "some of which have identical names; it is necessary to either "
                                   "rename subfragment domains explicitly, or give distinct names "
                                   "to subfragments"
-                                  .format(domain, ", ".join(names), ".".join(hierarchy)))
+                                  .format(domain_name, ", ".join(names), ".".join(hierarchy)))
 
             for subfrag, name, i in subfrags:
-                self.subfragments[i] = \
-                    (DomainRenamer({domain: "{}_{}".format(name, domain)})(subfrag), name)
+                domain_name_map = {domain_name: "{}_{}".format(name, domain_name)}
+                self.subfragments[i] = (DomainRenamer(domain_name_map)(subfrag), name)
 
         # Finally, collect the (now unique) subfragment domains, and merge them into our domains.
         for subfrag, name in self.subfragments:
-            for domain in subfrag.iter_domains():
-                self.add_domains(subfrag.domains[domain])
+            for domain_name, domain in subfrag.domains.items():
+                if domain.local:
+                    continue
+                self.add_domains(domain)
 
     def _propagate_domains_down(self):
         # For each domain defined in this fragment, ensure it also exists in all subfragments.
index 23c4637801449ccaa42979baf56fd9850a77fcb3..da04e2c5a504b1a59a102430622e36ba20a57f81 100644 (file)
@@ -8,6 +8,7 @@ class ClockDomainTestCase(FHDLTestCase):
         self.assertEqual(sync.name, "sync")
         self.assertEqual(sync.clk.name, "clk")
         self.assertEqual(sync.rst.name, "rst")
+        self.assertEqual(sync.local, False)
         pix = ClockDomain()
         self.assertEqual(pix.name, "pix")
         self.assertEqual(pix.clk.name, "pix_clk")
@@ -19,6 +20,8 @@ class ClockDomainTestCase(FHDLTestCase):
         with self.assertRaises(ValueError,
                 msg="Clock domain name must be specified explicitly"):
             ClockDomain()
+        cd_reset = ClockDomain(local=True)
+        self.assertEqual(cd_reset.local, True)
 
     def test_with_reset(self):
         pix = ClockDomain()
index c89e8d7a42a596cb0aacf1bb611bde14a1bfc8f5..84bc2e2c458f147cb8ae4ab1fa1d621b947567b7 100644 (file)
@@ -290,6 +290,17 @@ class FragmentDomainsTestCase(FHDLTestCase):
         f1._propagate_domains_up()
         self.assertEqual(f1.domains, {"cd": cd})
 
+    def test_propagate_up_local(self):
+        cd = ClockDomain(local=True)
+
+        f1 = Fragment()
+        f2 = Fragment()
+        f1.add_subfragment(f2)
+        f2.add_domains(cd)
+
+        f1._propagate_domains_up()
+        self.assertEqual(f1.domains, {})
+
     def test_domain_conflict(self):
         cda = ClockDomain("sync")
         cdb = ClockDomain("sync")