amaranth/nmigen/lib/cdc.py
whitequark 2d2ab6e09d lib.cdc: make domain properties private.
It is not correct to access domain properties from user code, because
it will not match the reality if DomainRenamer has been applied to
the module.
2019-09-12 13:54:48 +00:00

94 lines
3.3 KiB
Python

from .. import *
__all__ = ["MultiReg", "ResetSynchronizer"]
class MultiReg(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(), in
Signal to be resynchronised
o : Signal(), out
Signal connected to synchroniser output
o_domain : str
Name of output clock domain
n : int
Number of flops between input and output.
reset : int
Reset value of the flip-flops. On FPGAs, even if ``reset_less`` is True, the MultiReg is
still set to this value during initialization.
reset_less : bool
If True (the default), this MultiReg is unaffected by ``o_domain`` reset.
See "Note on Reset" below.
Platform override
-----------------
Define the ``get_multi_reg`` platform method to override the implementation of MultiReg,
e.g. to instantiate library cells directly.
Note on Reset
-------------
MultiReg is non-resettable by default. Usually this is the safest option; on FPGAs
the MultiReg will still be initialized to its ``reset`` value when the FPGA loads its
configuration.
However, in designs where the value of the MultiReg 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 MultiReg must maintain its reset value until
``o_domain`` reset specifically is deasserted.
MultiReg is reset by the ``o_domain`` reset only.
"""
def __init__(self, i, o, *, o_domain="sync", n=2, reset=0, reset_less=True):
self.i = i
self.o = o
self._o_domain = o_domain
self._regs = [Signal(self.i.shape(), name="cdc{}".format(i), reset=reset,
reset_less=reset_less)
for i in range(n)]
def elaborate(self, platform):
if hasattr(platform, "get_multi_reg"):
return platform.get_multi_reg(self)
m = Module()
for i, o in zip((self.i, *self._regs), self._regs):
m.d[self._o_domain] += o.eq(i)
m.d.comb += self.o.eq(self._regs[-1])
return m
class ResetSynchronizer(Elaboratable):
def __init__(self, arst, *, domain="sync", n=2):
self.arst = arst
self._domain = domain
self._regs = [Signal(1, name="arst{}".format(i), reset=1)
for i in range(n)]
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)
for i, o in zip((0, *self._regs), self._regs):
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(self._regs[-1])
]
return m