parent
b227352258
commit
8deb13cea3
|
@ -1,11 +1,11 @@
|
|||
from nmigen import *
|
||||
from nmigen.lib.cdc import MultiReg
|
||||
from nmigen.lib.cdc import FFSynchronizer
|
||||
from nmigen.cli import main
|
||||
|
||||
|
||||
i, o = Signal(name="i"), Signal(name="o")
|
||||
m = Module()
|
||||
m.submodules += MultiReg(i, o)
|
||||
m.submodules += FFSynchronizer(i, o)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(m, ports=[i, o])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import warnings
|
||||
|
||||
from ...tools import deprecated
|
||||
from ...lib.cdc import MultiReg as NativeMultiReg
|
||||
from ...lib.cdc import FFSynchronizer as NativeFFSynchronizer
|
||||
from ...hdl.ast import *
|
||||
from ..fhdl.module import CompatModule
|
||||
from ..fhdl.structure import If
|
||||
|
@ -10,14 +10,20 @@ from ..fhdl.structure import If
|
|||
__all__ = ["MultiReg", "GrayCounter", "GrayDecoder"]
|
||||
|
||||
|
||||
class MultiReg(NativeMultiReg):
|
||||
class MultiReg(NativeFFSynchronizer):
|
||||
def __init__(self, i, o, odomain="sync", n=2, reset=0):
|
||||
old_opts = []
|
||||
new_opts = []
|
||||
if odomain != "sync":
|
||||
warnings.warn("instead of `MultiReg(..., odomain={!r})`, "
|
||||
"use `MultiReg(..., o_domain={!r})`"
|
||||
.format(odomain, odomain),
|
||||
DeprecationWarning, stacklevel=2)
|
||||
super().__init__(i, o, o_domain=odomain, n=n, reset=reset)
|
||||
old_opts.append(", odomain={!r}".format(odomain))
|
||||
new_opts.append(", o_domain={!r}".format(odomain))
|
||||
if n != 2:
|
||||
old_opts.append(", n={!r}".format(n))
|
||||
new_opts.append(", stages={!r}".format(n))
|
||||
warnings.warn("instead of `MultiReg(...{})`, use `FFSynchronizer(...{})`"
|
||||
.format("".join(old_opts), "".join(new_opts)),
|
||||
DeprecationWarning, stacklevel=2)
|
||||
super().__init__(i, o, o_domain=odomain, stages=n, reset=reset)
|
||||
self.odomain = odomain
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
from ..tools import deprecated
|
||||
from .. import *
|
||||
|
||||
|
||||
__all__ = ["MultiReg", "ResetSynchronizer"]
|
||||
__all__ = ["FFSynchronizer", "ResetSynchronizer"]
|
||||
# TODO(nmigen-0.2): remove this
|
||||
__all__ += ["MultiReg"]
|
||||
|
||||
|
||||
class MultiReg(Elaboratable):
|
||||
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
|
||||
|
@ -12,70 +15,75 @@ class MultiReg(Elaboratable):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
i : Signal(), in
|
||||
Signal to be resynchronised
|
||||
o : Signal(), out
|
||||
Signal connected to synchroniser output
|
||||
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.
|
||||
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 MultiReg is
|
||||
still set to this value during initialization.
|
||||
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 MultiReg is unaffected by ``o_domain`` reset.
|
||||
If True (the default), this :class:`FFSynchronizer` 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,
|
||||
Define the ``get_ff_sync`` platform method to override the implementation of :class:`FFSynchronizer`,
|
||||
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.
|
||||
: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 MultiReg must be valid immediately after reset,
|
||||
consider setting ``reset_less`` to False if any of the following is true:
|
||||
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 MultiReg must maintain its reset value until
|
||||
``o_domain`` reset specifically is deasserted.
|
||||
- Your design features a sequenced reset, and the :class:`FFSynchronizer` must maintain
|
||||
its reset value until ``o_domain`` reset specifically is deasserted.
|
||||
|
||||
MultiReg is reset by the ``o_domain`` reset only.
|
||||
:class:`FFSynchronizer` is reset by the ``o_domain`` reset only.
|
||||
"""
|
||||
def __init__(self, i, o, *, o_domain="sync", n=2, reset=0, reset_less=True):
|
||||
def __init__(self, i, o, *, o_domain="sync", stages=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)]
|
||||
self._stages = [Signal(self.i.shape(), name="stage{}".format(index),
|
||||
reset=reset, reset_less=reset_less)
|
||||
for index in range(stages)]
|
||||
|
||||
def elaborate(self, platform):
|
||||
if hasattr(platform, "get_multi_reg"):
|
||||
return platform.get_multi_reg(self)
|
||||
if hasattr(platform, "get_ff_sync"):
|
||||
return platform.get_ff_sync(self)
|
||||
|
||||
m = Module()
|
||||
for i, o in zip((self.i, *self._regs), self._regs):
|
||||
for i, o in zip((self.i, *self._stages), self._stages):
|
||||
m.d[self._o_domain] += o.eq(i)
|
||||
m.d.comb += self.o.eq(self._regs[-1])
|
||||
m.d.comb += self.o.eq(self._stages[-1])
|
||||
return m
|
||||
|
||||
|
||||
# TODO(nmigen-0.2): remove this
|
||||
MultiReg = deprecated("instead of `MultiReg`, use `FFSynchronizer`")(FFSynchronizer)
|
||||
|
||||
|
||||
class ResetSynchronizer(Elaboratable):
|
||||
def __init__(self, arst, *, domain="sync", n=2):
|
||||
def __init__(self, arst, *, domain="sync", stages=2):
|
||||
self.arst = arst
|
||||
|
||||
self._domain = domain
|
||||
self._regs = [Signal(1, name="arst{}".format(i), reset=1)
|
||||
for i in range(n)]
|
||||
self._stages = [Signal(1, name="stage{}".format(i), reset=1)
|
||||
for i in range(stages)]
|
||||
|
||||
def elaborate(self, platform):
|
||||
if hasattr(platform, "get_reset_sync"):
|
||||
|
@ -83,11 +91,11 @@ class ResetSynchronizer(Elaboratable):
|
|||
|
||||
m = Module()
|
||||
m.domains += ClockDomain("reset_sync", async_reset=True, local=True)
|
||||
for i, o in zip((0, *self._regs), self._regs):
|
||||
for i, o in zip((0, *self._stages), self._stages):
|
||||
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])
|
||||
ResetSignal(self._domain).eq(self._stages[-1])
|
||||
]
|
||||
return m
|
||||
|
|
|
@ -4,7 +4,7 @@ from .. import *
|
|||
from ..asserts import *
|
||||
from ..tools import log2_int, deprecated
|
||||
from .coding import GrayEncoder
|
||||
from .cdc import MultiReg
|
||||
from .cdc import FFSynchronizer
|
||||
|
||||
|
||||
__all__ = ["FIFOInterface", "SyncFIFO", "SyncFIFOBuffered", "AsyncFIFO", "AsyncFIFOBuffered"]
|
||||
|
@ -399,7 +399,7 @@ class AsyncFIFO(Elaboratable, FIFOInterface):
|
|||
produce_enc = m.submodules.produce_enc = \
|
||||
GrayEncoder(self._ctr_bits)
|
||||
produce_cdc = m.submodules.produce_cdc = \
|
||||
MultiReg(produce_w_gry, produce_r_gry, o_domain=self._r_domain)
|
||||
FFSynchronizer(produce_w_gry, produce_r_gry, o_domain=self._r_domain)
|
||||
m.d.comb += produce_enc.i.eq(produce_w_nxt),
|
||||
m.d[self._w_domain] += produce_w_gry.eq(produce_enc.o)
|
||||
|
||||
|
@ -408,7 +408,7 @@ class AsyncFIFO(Elaboratable, FIFOInterface):
|
|||
consume_enc = m.submodules.consume_enc = \
|
||||
GrayEncoder(self._ctr_bits)
|
||||
consume_cdc = m.submodules.consume_cdc = \
|
||||
MultiReg(consume_r_gry, consume_w_gry, o_domain=self._w_domain)
|
||||
FFSynchronizer(consume_r_gry, consume_w_gry, o_domain=self._w_domain)
|
||||
m.d.comb += consume_enc.i.eq(consume_r_nxt)
|
||||
m.d[self._r_domain] += consume_r_gry.eq(consume_enc.o)
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ from ..back.pysim import *
|
|||
from ..lib.cdc import *
|
||||
|
||||
|
||||
class MultiRegTestCase(FHDLTestCase):
|
||||
class FFSynchronizerTestCase(FHDLTestCase):
|
||||
def test_basic(self):
|
||||
i = Signal()
|
||||
o = Signal()
|
||||
frag = MultiReg(i, o)
|
||||
frag = FFSynchronizer(i, o)
|
||||
with Simulator(frag) as sim:
|
||||
sim.add_clock(1e-6)
|
||||
def process():
|
||||
|
@ -26,7 +26,7 @@ class MultiRegTestCase(FHDLTestCase):
|
|||
def test_reset_value(self):
|
||||
i = Signal(reset=1)
|
||||
o = Signal()
|
||||
frag = MultiReg(i, o, reset=1)
|
||||
frag = FFSynchronizer(i, o, reset=1)
|
||||
with Simulator(frag) as sim:
|
||||
sim.add_clock(1e-6)
|
||||
def process():
|
||||
|
|
8
nmigen/vendor/xilinx_7series.py
vendored
8
nmigen/vendor/xilinx_7series.py
vendored
|
@ -361,10 +361,10 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
|
|||
)
|
||||
return m
|
||||
|
||||
def get_multi_reg(self, multireg):
|
||||
def get_ff_sync(self, ff_sync):
|
||||
m = Module()
|
||||
for i, o in zip((multireg.i, *multireg._regs), multireg._regs):
|
||||
for i, o in zip((ff_sync.i, *ff_sync._stages), ff_sync._stages):
|
||||
o.attrs["ASYNC_REG"] = "TRUE"
|
||||
m.d[multireg._o_domain] += o.eq(i)
|
||||
m.d.comb += multireg.o.eq(multireg._regs[-1])
|
||||
m.d[ff_sync._o_domain] += o.eq(i)
|
||||
m.d.comb += ff_sync.o.eq(ff_sync._stages[-1])
|
||||
return m
|
||||
|
|
8
nmigen/vendor/xilinx_spartan_3_6.py
vendored
8
nmigen/vendor/xilinx_spartan_3_6.py
vendored
|
@ -411,12 +411,12 @@ class XilinxSpartan3Or6Platform(TemplatedPlatform):
|
|||
)
|
||||
return m
|
||||
|
||||
def get_multi_reg(self, multireg):
|
||||
def get_ff_sync(self, ff_sync):
|
||||
m = Module()
|
||||
for i, o in zip((multireg.i, *multireg._regs), multireg._regs):
|
||||
for i, o in zip((ff_sync.i, *ff_sync._stages), ff_sync._stages):
|
||||
o.attrs["ASYNC_REG"] = "TRUE"
|
||||
m.d[multireg._o_domain] += o.eq(i)
|
||||
m.d.comb += multireg.o.eq(multireg._regs[-1])
|
||||
m.d[ff_sync._o_domain] += o.eq(i)
|
||||
m.d.comb += ff_sync.o.eq(multireg._stages[-1])
|
||||
return m
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue