lib.cdc: MultiReg→FFSynchronizer.
authorwhitequark <cz@m-labs.hk>
Mon, 23 Sep 2019 14:17:44 +0000 (14:17 +0000)
committerwhitequark <cz@m-labs.hk>
Mon, 23 Sep 2019 14:18:45 +0000 (14:18 +0000)
Fixes #229.

examples/basic/cdc.py
nmigen/compat/genlib/cdc.py
nmigen/lib/cdc.py
nmigen/lib/fifo.py
nmigen/test/test_lib_cdc.py
nmigen/vendor/xilinx_7series.py
nmigen/vendor/xilinx_spartan_3_6.py

index ab691aee0d5b7d83ee0a3cda724f702b2abdb05a..658d075e1ef8cfce51f1cec62f1f75f0868b552f 100644 (file)
@@ -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])
index b65fb4ba6c739708315d046df144f8230c6e7c4b..5264169e610acc3bff1e0ebf45ad8892bf55e0eb 100644 (file)
@@ -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
 
 
index 3ed10777439bb866367437d9c2030cd02a32852d..62d39391e0c48353bad09bf40f60aabe603a3725 100644 (file)
@@ -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
index 4d50f34d0bbf5368815c132762f344816e4d0d09..3775bf84146d933a41454ef1719192d32f5fabe8 100644 (file)
@@ -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)
 
index 9aea5d4279f11f9eeee58195869475c8bf312dc6..0d4894b466f3f89be8b84673b6fd92e1d43d6239 100644 (file)
@@ -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():
index 6bf7ee0ccdc48193e32a3196af62d8589a639845..2e7706c8cdc233eb310eb8fe06e346df85aa3dd8 100644 (file)
@@ -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
index 05dff90b4a53647315e158bd7de5dc7c0ede3133..0a5df0ead379c3fded49e990ae59a842fb65b301 100644 (file)
@@ -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