lib.cdc: add optional reset to MultiReg, and document its use cases.
authorLuke Wren <wren6991@gmail.com>
Tue, 19 Mar 2019 03:36:55 +0000 (03:36 +0000)
committerwhitequark <whitequark@whitequark.org>
Thu, 28 Mar 2019 05:21:48 +0000 (05:21 +0000)
nmigen/lib/cdc.py

index c7624bc7b644d9fcf04d48cdb804539ad164b70b..3018b37da3aa72904a7d5caa088bb7625dd589aa 100644 (file)
@@ -5,13 +5,57 @@ __all__ = ["MultiReg", "ResetSynchronizer"]
 
 
 class MultiReg:
-    def __init__(self, i, o, odomain="sync", n=2, reset=0):
+    """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
+    -----------------
+    Define the ``get_multi_reg`` platform metehod 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 ``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):
         self.i = i
         self.o = o
         self.odomain = odomain
 
         self._regs = [Signal(self.i.shape(), name="cdc{}".format(i),
-                             reset=reset, reset_less=True, attrs={"no_retiming": True})
+                             reset=reset, reset_less=reset_less, attrs={"no_retiming": True})
                       for i in range(n)]
 
     def elaborate(self, platform):