2018-12-15 07:23:42 -07:00
|
|
|
from .. import *
|
2018-12-12 03:12:35 -07:00
|
|
|
|
|
|
|
|
2019-01-26 11:07:59 -07:00
|
|
|
__all__ = ["MultiReg", "ResetSynchronizer"]
|
2018-12-12 03:12:35 -07:00
|
|
|
|
|
|
|
|
2019-04-21 02:52:57 -06:00
|
|
|
class MultiReg(Elaboratable):
|
2019-03-18 21:36:55 -06:00
|
|
|
"""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
|
|
|
|
odomain : 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 ``odomain`` reset.
|
|
|
|
See "Note on Reset" below.
|
|
|
|
|
|
|
|
Platform override
|
|
|
|
-----------------
|
2019-06-09 04:24:01 -06:00
|
|
|
Define the ``get_multi_reg`` platform method to override the implementation of MultiReg,
|
2019-03-18 21:36:55 -06:00
|
|
|
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 ``odomain``, 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
|
|
|
|
``odomain`` reset specifically is deasserted.
|
|
|
|
|
|
|
|
MultiReg is reset by the ``odomain`` reset only.
|
|
|
|
"""
|
|
|
|
def __init__(self, i, o, odomain="sync", n=2, reset=0, reset_less=True):
|
2018-12-12 03:12:35 -07:00
|
|
|
self.i = i
|
|
|
|
self.o = o
|
|
|
|
self.odomain = odomain
|
|
|
|
|
2019-06-28 01:22:54 -06:00
|
|
|
self._regs = [Signal(self.i.shape(), name="cdc{}".format(i), reset=reset,
|
|
|
|
reset_less=reset_less)
|
2018-12-12 03:52:32 -07:00
|
|
|
for i in range(n)]
|
2018-12-12 03:12:35 -07:00
|
|
|
|
2019-01-25 19:31:12 -07:00
|
|
|
def elaborate(self, platform):
|
2018-12-12 05:38:24 -07:00
|
|
|
if hasattr(platform, "get_multi_reg"):
|
|
|
|
return platform.get_multi_reg(self)
|
|
|
|
|
|
|
|
m = Module()
|
2018-12-12 03:52:32 -07:00
|
|
|
for i, o in zip((self.i, *self._regs), self._regs):
|
2018-12-12 05:38:24 -07:00
|
|
|
m.d[self.odomain] += o.eq(i)
|
|
|
|
m.d.comb += self.o.eq(self._regs[-1])
|
2019-01-25 19:31:12 -07:00
|
|
|
return m
|
2019-01-26 11:07:59 -07:00
|
|
|
|
|
|
|
|
2019-04-21 02:52:57 -06:00
|
|
|
class ResetSynchronizer(Elaboratable):
|
2019-01-26 11:07:59 -07:00
|
|
|
def __init__(self, arst, domain="sync", n=2):
|
|
|
|
self.arst = arst
|
|
|
|
self.domain = domain
|
|
|
|
|
2019-06-28 01:22:54 -06:00
|
|
|
self._regs = [Signal(1, name="arst{}".format(i), reset=1)
|
2019-01-26 11:07:59 -07:00
|
|
|
for i in range(n)]
|
|
|
|
|
|
|
|
def elaborate(self, platform):
|
|
|
|
if hasattr(platform, "get_reset_sync"):
|
|
|
|
return platform.get_reset_sync(self)
|
|
|
|
|
|
|
|
m = Module()
|
lib.cdc: avoid interior clock domains in ResetSynchronizer.
Such clock domains will "leak" into the enclosing scope, which is
generally undesirable. Also, this is instructive for a platform
overriding the behavior, since it provides guidance on how to
correctly instantiate platform-specific flops.
I've considered also doing this for MultiReg(), but it is very
challenging in presence of non-reset-less CDC FFs, since Yosys'
$dffsr primitive has separate set and clear inputs, and reshuffling
the reset value for those results in quite a bit of additional logic.
(That said, it might have to be done anyway, precisely because
letting Yosys generate this additional logic might prove too much
for the toolchain to cope with, and again, platform-independent
code should provide guidance to platform-specific code.)
2019-06-28 01:34:10 -06:00
|
|
|
for i, o in zip((Const(0, 1), *self._regs), self._regs):
|
|
|
|
m.submodules += Instance("$adff",
|
|
|
|
p_CLK_POLARITY=1,
|
|
|
|
p_ARST_POLARITY=1,
|
|
|
|
p_ARST_VALUE=Const(1, 1),
|
|
|
|
p_WIDTH=1,
|
|
|
|
i_CLK=ClockSignal(self.domain),
|
|
|
|
i_ARST=self.arst,
|
|
|
|
i_D=i,
|
|
|
|
o_Q=o
|
|
|
|
)
|
|
|
|
m.d.comb += ResetSignal(self.domain).eq(self._regs[-1])
|
2019-01-26 11:07:59 -07:00
|
|
|
return m
|