-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #229.
- Loading branch information
whitequark
committed
Sep 23, 2019
1 parent
b227352
commit 8deb13c
Showing
7 changed files
with
71 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,101 @@ | ||
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 | ||
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 | ||
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"): | ||
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): | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters