lib.cdc: update PulseSynchronizer to follow conventions.

Fixes #370.
This commit is contained in:
whitequark 2020-06-28 05:17:33 +00:00
parent 2606ee33ad
commit 25ce260207
2 changed files with 27 additions and 26 deletions

View file

@ -220,41 +220,41 @@ class PulseSynchronizer(Elaboratable):
"""A one-clock pulse on the input produces a one-clock pulse on the output. """A one-clock pulse on the input produces a one-clock pulse on the output.
If the output clock is faster than the input clock, then the input may be safely asserted at If the output clock is faster than the input clock, then the input may be safely asserted at
100% duty cycle. Otherwise, if the clock ratio is n : 1, the input may be asserted at most once 100% duty cycle. Otherwise, if the clock ratio is `n`:1, the input may be asserted at most once
in every n input clocks, else pulses may be dropped. in every `n` input clocks, else pulses may be dropped. Other than this there is no constraint
Other than this there is no constraint on the ratio of input and output clock frequency. on the ratio of input and output clock frequency.
Parameters Parameters
---------- ----------
i_domain : str i_domain : str
Name of input clock domain. Name of input clock domain.
o-domain : str o_domain : str
Name of output clock domain. Name of output clock domain.
sync_stages : int stages : int, >=2
Number of synchronisation flops between the two clock domains. 2 is the default, and Number of synchronization stages between input and output. The lowest safe number is 2,
minimum safe value. High-frequency designs may choose to increase this. with higher numbers reducing MTBF further, at the cost of increased deassertion latency.
""" """
def __init__(self, i_domain, o_domain, sync_stages=2): def __init__(self, i_domain, o_domain, *, stages=2):
if not isinstance(sync_stages, int) or sync_stages < 1: _check_stages(stages)
raise TypeError("sync_stages must be a positive integer, not '{!r}'".format(sync_stages))
self.i = Signal() self.i = Signal()
self.o = Signal() self.o = Signal()
self.i_domain = i_domain
self.o_domain = o_domain self._i_domain = i_domain
self.sync_stages = sync_stages self._o_domain = o_domain
self._stages = stages
def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
itoggle = Signal() i_toggle = Signal()
otoggle = Signal() o_toggle = Signal()
r_toggle = Signal()
ff_sync = m.submodules.ff_sync = \ ff_sync = m.submodules.ff_sync = \
FFSynchronizer(itoggle, otoggle, o_domain=self.o_domain, stages=self.sync_stages) FFSynchronizer(i_toggle, o_toggle, o_domain=self._o_domain, stages=self._stages)
otoggle_prev = Signal()
m.d[self.i_domain] += itoggle.eq(itoggle ^ self.i) m.d[self._i_domain] += i_toggle.eq(i_toggle ^ self.i)
m.d[self.o_domain] += otoggle_prev.eq(otoggle) m.d[self._o_domain] += r_toggle.eq(o_toggle)
m.d.comb += self.o.eq(otoggle ^ otoggle_prev) m.d.comb += self.o.eq(o_toggle ^ r_toggle)
return m return m

View file

@ -195,12 +195,13 @@ class ResetSynchronizerTestCase(FHDLTestCase):
# TODO: test with distinct clocks # TODO: test with distinct clocks
class PulseSynchronizerTestCase(FHDLTestCase): class PulseSynchronizerTestCase(FHDLTestCase):
def test_paramcheck(self): def test_stages_wrong(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError,
ps = PulseSynchronizer("w", "r", sync_stages=0) msg="Synchronization stage count must be a positive integer, not 0"):
with self.assertRaises(TypeError): PulseSynchronizer("w", "r", stages=0)
ps = PulseSynchronizer("w", "r", sync_stages="abc") with self.assertRaises(ValueError,
ps = PulseSynchronizer("w", "r", sync_stages = 1) msg="Synchronization stage count may not safely be less than 2"):
PulseSynchronizer("w", "r", stages=1)
def test_smoke(self): def test_smoke(self):
m = Module() m = Module()