From 09b9690fa5de2e40bb0b1600bb25d3e0987ac153 Mon Sep 17 00:00:00 2001 From: whitequark Date: Mon, 19 Aug 2019 20:46:46 +0000 Subject: [PATCH] hdl.cd: implement local clock domains. Closes #175. --- nmigen/hdl/cd.py | 7 ++++++- nmigen/hdl/ir.py | 22 +++++++++++++--------- nmigen/test/test_hdl_cd.py | 3 +++ nmigen/test/test_hdl_ir.py | 11 +++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/nmigen/hdl/cd.py b/nmigen/hdl/cd.py index 06f8d79..6813a9d 100644 --- a/nmigen/hdl/cd.py +++ b/nmigen/hdl/cd.py @@ -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") diff --git a/nmigen/hdl/ir.py b/nmigen/hdl/ir.py index c21bc82..bd52226 100644 --- a/nmigen/hdl/ir.py +++ b/nmigen/hdl/ir.py @@ -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. diff --git a/nmigen/test/test_hdl_cd.py b/nmigen/test/test_hdl_cd.py index 23c4637..da04e2c 100644 --- a/nmigen/test/test_hdl_cd.py +++ b/nmigen/test/test_hdl_cd.py @@ -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() diff --git a/nmigen/test/test_hdl_ir.py b/nmigen/test/test_hdl_ir.py index c89e8d7..84bc2e2 100644 --- a/nmigen/test/test_hdl_ir.py +++ b/nmigen/test/test_hdl_ir.py @@ -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") -- 2.30.2