from ..tools import deprecated from .. import * __all__ = ["FFSynchronizer", "ResetSynchronizer"] # TODO(nmigen-0.2): remove this __all__ += ["MultiReg"] def _check_stages(stages): if not isinstance(stages, int) or stages < 1: raise TypeError("Synchronization stage count must be a positive integer, not '{!r}'" .format(stages)) if stages < 2: raise ValueError("Synchronization stage count may not safely be less than 2") class FFSynchronizer(Elaboratable): """Resynchronise a signal to a different clock domain. Consists of a chain of flip-flops. Eliminates metastabilities at the output, but provides no other guarantee as to the safe domain-crossing of a signal. Parameters ---------- i : Signal(n), in Signal to be resynchronised. o : Signal(n), out Signal connected to synchroniser output. o_domain : str Name of output clock domain. stages : int Number of synchronization stages between input and output. The lowest safe number is 2, with higher numbers reducing MTBF further, at the cost of increased latency. reset : int Reset value of the flip-flops. On FPGAs, even if ``reset_less`` is True, the :class:`FFSynchronizer` is still set to this value during initialization. reset_less : bool If True (the default), this :class:`FFSynchronizer` is unaffected by ``o_domain`` reset. See "Note on Reset" below. Platform override ----------------- Define the ``get_ff_sync`` platform method to override the implementation of :class:`FFSynchronizer`, e.g. to instantiate library cells directly. Note on Reset ------------- :class:`FFSynchronizer` is non-resettable by default. Usually this is the safest option; on FPGAs the :class:`FFSynchronizer` will still be initialized to its ``reset`` value when the FPGA loads its configuration. However, in designs where the value of the :class:`FFSynchronizer` must be valid immediately after reset, consider setting ``reset_less`` to False if any of the following is true: - You are targeting an ASIC, or an FPGA that does not allow arbitrary initial flip-flop states; - Your design features warm (non-power-on) resets of ``o_domain``, so the one-time initialization at power on is insufficient; - Your design features a sequenced reset, and the :class:`FFSynchronizer` must maintain its reset value until ``o_domain`` reset specifically is deasserted. :class:`FFSynchronizer` is reset by the ``o_domain`` reset only. """ def __init__(self, i, o, *, o_domain="sync", reset=0, reset_less=True, stages=2): _check_stages(stages) self.i = i self.o = o self._reset = reset self._reset_less = reset_less self._o_domain = o_domain self._stages = stages def elaborate(self, platform): if hasattr(platform, "get_ff_sync"): return platform.get_ff_sync(self) m = Module() flops = [Signal(self.i.shape(), name="stage{}".format(index), reset=self._reset, reset_less=self._reset_less) for index in range(self._stages)] for i, o in zip((self.i, *flops), flops): m.d[self._o_domain] += o.eq(i) m.d.comb += self.o.eq(flops[-1]) return m # TODO(nmigen-0.2): remove this MultiReg = deprecated("instead of `MultiReg`, use `FFSynchronizer`")(FFSynchronizer) class ResetSynchronizer(Elaboratable): """Synchronize deassertion of a clock domain reset. The reset of the clock domain driven by the :class:`ResetSynchronizer` is asserted asynchronously and deasserted synchronously, eliminating metastability during deassertion. The driven clock domain could use a reset that is asserted either synchronously or asynchronously; a reset is always deasserted synchronously. A domain with an asynchronously asserted reset is useful if the clock of the domain may be gated, yet the domain still needs to be reset promptly; otherwise, synchronously asserted reset (the default) should be used. Parameters ---------- arst : Signal(1), out Asynchronous reset signal, to be synchronized. domain : str Name of clock domain to reset. stages : int, >=2 Number of synchronization stages between input and output. The lowest safe number is 2, with higher numbers reducing MTBF further, at the cost of increased deassertion latency. Platform override ----------------- Define the ``get_reset_sync`` platform method to override the implementation of :class:`ResetSynchronizer`, e.g. to instantiate library cells directly. """ def __init__(self, arst, *, domain="sync", stages=2): _check_stages(stages) self.arst = arst self._domain = domain self._stages = stages def elaborate(self, platform): if hasattr(platform, "get_reset_sync"): return platform.get_reset_sync(self) m = Module() m.domains += ClockDomain("reset_sync", async_reset=True, local=True) flops = [Signal(1, name="stage{}".format(index), reset=1) for index in range(self._stages)] for i, o in zip((0, *flops), flops): m.d.reset_sync += o.eq(i) m.d.comb += [ ClockSignal("reset_sync").eq(ClockSignal(self._domain)), ResetSignal("reset_sync").eq(self.arst), ResetSignal(self._domain).eq(flops[-1]) ] return m