hdl.cd: implement local clock domains.

Closes #175.
This commit is contained in:
whitequark 2019-08-19 20:46:46 +00:00
parent 9bdadbff09
commit 003ba3b45f
4 changed files with 33 additions and 10 deletions

View file

@ -25,6 +25,9 @@ class ClockDomain:
If ``True``, the domain uses an asynchronous reset, and registers within this domain 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 their reset state when reset level changes. Otherwise, registers
are initialized to reset state at the next clock cycle when reset is asserted. 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 Attributes
---------- ----------
@ -42,7 +45,7 @@ class ClockDomain:
else: else:
return "{}_{}".format(domain_name, signal_name) 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: if name is None:
try: try:
name = tracer.get_var_name() name = tracer.get_var_name()
@ -62,6 +65,8 @@ class ClockDomain:
self.async_reset = async_reset self.async_reset = async_reset
self.local = local
def rename(self, new_name): def rename(self, new_name):
self.name = new_name self.name = new_name
self.clk.name = self._name_for(new_name, "clk") self.clk.name = self._name_for(new_name, "clk")

View file

@ -305,12 +305,14 @@ class Fragment:
subfrag._propagate_domains_up(hierarchy + (hier_name,)) subfrag._propagate_domains_up(hierarchy + (hier_name,))
# Second, classify subfragments by domains they define. # Second, classify subfragments by domains they define.
for domain in subfrag.iter_domains(): for domain_name, domain in subfrag.domains.items():
domain_subfrags[domain].add((subfrag, name, i)) 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 # For each domain defined by more than one subfragment, rename the domain in each
# of the subfragments such that they no longer conflict. # 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: if len(subfrags) == 1:
continue continue
@ -321,7 +323,7 @@ class Fragment:
raise DomainError("Domain '{}' is defined by subfragments {} of fragment '{}'; " raise DomainError("Domain '{}' is defined by subfragments {} of fragment '{}'; "
"it is necessary to either rename subfragment domains " "it is necessary to either rename subfragment domains "
"explicitly, or give names to subfragments" "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)): if len(names) != len(set(names)):
names = sorted("#{}".format(i) for f, n, i in subfrags) 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 " "some of which have identical names; it is necessary to either "
"rename subfragment domains explicitly, or give distinct names " "rename subfragment domains explicitly, or give distinct names "
"to subfragments" "to subfragments"
.format(domain, ", ".join(names), ".".join(hierarchy))) .format(domain_name, ", ".join(names), ".".join(hierarchy)))
for subfrag, name, i in subfrags: for subfrag, name, i in subfrags:
self.subfragments[i] = \ domain_name_map = {domain_name: "{}_{}".format(name, domain_name)}
(DomainRenamer({domain: "{}_{}".format(name, domain)})(subfrag), name) self.subfragments[i] = (DomainRenamer(domain_name_map)(subfrag), name)
# Finally, collect the (now unique) subfragment domains, and merge them into our domains. # Finally, collect the (now unique) subfragment domains, and merge them into our domains.
for subfrag, name in self.subfragments: for subfrag, name in self.subfragments:
for domain in subfrag.iter_domains(): for domain_name, domain in subfrag.domains.items():
self.add_domains(subfrag.domains[domain]) if domain.local:
continue
self.add_domains(domain)
def _propagate_domains_down(self): def _propagate_domains_down(self):
# For each domain defined in this fragment, ensure it also exists in all subfragments. # For each domain defined in this fragment, ensure it also exists in all subfragments.

View file

@ -8,6 +8,7 @@ class ClockDomainTestCase(FHDLTestCase):
self.assertEqual(sync.name, "sync") self.assertEqual(sync.name, "sync")
self.assertEqual(sync.clk.name, "clk") self.assertEqual(sync.clk.name, "clk")
self.assertEqual(sync.rst.name, "rst") self.assertEqual(sync.rst.name, "rst")
self.assertEqual(sync.local, False)
pix = ClockDomain() pix = ClockDomain()
self.assertEqual(pix.name, "pix") self.assertEqual(pix.name, "pix")
self.assertEqual(pix.clk.name, "pix_clk") self.assertEqual(pix.clk.name, "pix_clk")
@ -19,6 +20,8 @@ class ClockDomainTestCase(FHDLTestCase):
with self.assertRaises(ValueError, with self.assertRaises(ValueError,
msg="Clock domain name must be specified explicitly"): msg="Clock domain name must be specified explicitly"):
ClockDomain() ClockDomain()
cd_reset = ClockDomain(local=True)
self.assertEqual(cd_reset.local, True)
def test_with_reset(self): def test_with_reset(self):
pix = ClockDomain() pix = ClockDomain()

View file

@ -290,6 +290,17 @@ class FragmentDomainsTestCase(FHDLTestCase):
f1._propagate_domains_up() f1._propagate_domains_up()
self.assertEqual(f1.domains, {"cd": cd}) 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): def test_domain_conflict(self):
cda = ClockDomain("sync") cda = ClockDomain("sync")
cdb = ClockDomain("sync") cdb = ClockDomain("sync")