lib.fifo.AsyncFFSynchronizer: check input and output signal width
[nmigen.git] / nmigen / lib / cdc.py
1 from .._utils import deprecated
2 from .. import *
3
4
5 __all__ = ["FFSynchronizer", "AsyncFFSynchronizer", "ResetSynchronizer", "PulseSynchronizer"]
6
7
8 def _check_stages(stages):
9 if not isinstance(stages, int) or stages < 1:
10 raise TypeError("Synchronization stage count must be a positive integer, not {!r}"
11 .format(stages))
12 if stages < 2:
13 raise ValueError("Synchronization stage count may not safely be less than 2")
14
15
16 class FFSynchronizer(Elaboratable):
17 """Resynchronise a signal to a different clock domain.
18
19 Consists of a chain of flip-flops. Eliminates metastabilities at the output, but provides
20 no other guarantee as to the safe domain-crossing of a signal.
21
22 Parameters
23 ----------
24 i : Signal(n), in
25 Signal to be resynchronised.
26 o : Signal(n), out
27 Signal connected to synchroniser output.
28 o_domain : str
29 Name of output clock domain.
30 reset : int
31 Reset value of the flip-flops. On FPGAs, even if ``reset_less`` is True,
32 the :class:`FFSynchronizer` is still set to this value during initialization.
33 reset_less : bool
34 If ``True`` (the default), this :class:`FFSynchronizer` is unaffected by ``o_domain``
35 reset. See "Note on Reset" below.
36 stages : int
37 Number of synchronization stages between input and output. The lowest safe number is 2,
38 with higher numbers reducing MTBF further, at the cost of increased latency.
39 max_input_delay : None or float
40 Maximum delay from the input signal's clock to the first synchronization stage, in seconds.
41 If specified and the platform does not support it, elaboration will fail.
42
43 Platform override
44 -----------------
45 Define the ``get_ff_sync`` platform method to override the implementation of
46 :class:`FFSynchronizer`, e.g. to instantiate library cells directly.
47
48 Note on Reset
49 -------------
50 :class:`FFSynchronizer` is non-resettable by default. Usually this is the safest option;
51 on FPGAs the :class:`FFSynchronizer` will still be initialized to its ``reset`` value when
52 the FPGA loads its configuration.
53
54 However, in designs where the value of the :class:`FFSynchronizer` must be valid immediately
55 after reset, consider setting ``reset_less`` to False if any of the following is true:
56
57 - You are targeting an ASIC, or an FPGA that does not allow arbitrary initial flip-flop states;
58 - Your design features warm (non-power-on) resets of ``o_domain``, so the one-time
59 initialization at power on is insufficient;
60 - Your design features a sequenced reset, and the :class:`FFSynchronizer` must maintain
61 its reset value until ``o_domain`` reset specifically is deasserted.
62
63 :class:`FFSynchronizer` is reset by the ``o_domain`` reset only.
64 """
65 def __init__(self, i, o, *, o_domain="sync", reset=0, reset_less=True, stages=2,
66 max_input_delay=None):
67 _check_stages(stages)
68
69 self.i = i
70 self.o = o
71
72 self._reset = reset
73 self._reset_less = reset_less
74 self._o_domain = o_domain
75 self._stages = stages
76
77 self._max_input_delay = max_input_delay
78
79 def elaborate(self, platform):
80 if hasattr(platform, "get_ff_sync"):
81 return platform.get_ff_sync(self)
82
83 if self._max_input_delay is not None:
84 raise NotImplementedError("Platform '{}' does not support constraining input delay "
85 "for FFSynchronizer"
86 .format(type(platform).__name__))
87
88 m = Module()
89 flops = [Signal(self.i.shape(), name="stage{}".format(index),
90 reset=self._reset, reset_less=self._reset_less)
91 for index in range(self._stages)]
92 for i, o in zip((self.i, *flops), flops):
93 m.d[self._o_domain] += o.eq(i)
94 m.d.comb += self.o.eq(flops[-1])
95 return m
96
97
98 class AsyncFFSynchronizer(Elaboratable):
99 """Synchronize deassertion of an asynchronous signal.
100
101 The signal driven by the :class:`AsyncFFSynchronizer` is asserted asynchronously and deasserted
102 synchronously, eliminating metastability during deassertion.
103
104 This synchronizer is primarily useful for resets and reset-like signals.
105
106 Parameters
107 ----------
108 i : Signal(1), in
109 Asynchronous input signal, to be synchronized.
110 o : Signal(1), out
111 Synchronously released output signal.
112 o_domain : str
113 Name of clock domain to synchronize to.
114 stages : int, >=2
115 Number of synchronization stages between input and output. The lowest safe number is 2,
116 with higher numbers reducing MTBF further, at the cost of increased deassertion latency.
117 async_edge : str
118 The edge of the input signal which causes the output to be set. Must be one of "pos" or "neg".
119 max_input_delay : None or float
120 Maximum delay from the input signal's clock to the first synchronization stage, in seconds.
121 If specified and the platform does not support it, elaboration will fail.
122
123 Platform override
124 -----------------
125 Define the ``get_async_ff_sync`` platform method to override the implementation of
126 :class:`AsyncFFSynchronizer`, e.g. to instantiate library cells directly.
127 """
128 def __init__(self, i, o, *, o_domain="sync", stages=2, async_edge="pos", max_input_delay=None):
129 _check_stages(stages)
130
131 if len(i) != 1:
132 raise ValueError("AsyncFFSynchronizer input width must be 1, not {}"
133 .format(len(i)))
134 if len(o) != 1:
135 raise ValueError("AsyncFFSynchronizer output width must be 1, not {}"
136 .format(len(o)))
137
138 if async_edge not in ("pos", "neg"):
139 raise ValueError("AsyncFFSynchronizer async edge must be one of 'pos' or 'neg', "
140 "not {!r}"
141 .format(async_edge))
142
143 self.i = i
144 self.o = o
145
146 self._o_domain = o_domain
147 self._stages = stages
148
149 self._edge = async_edge
150
151 self._max_input_delay = max_input_delay
152
153 def elaborate(self, platform):
154 if hasattr(platform, "get_async_ff_sync"):
155 return platform.get_async_ff_sync(self)
156
157 if self._max_input_delay is not None:
158 raise NotImplementedError("Platform '{}' does not support constraining input delay "
159 "for AsyncFFSynchronizer"
160 .format(type(platform).__name__))
161
162 m = Module()
163 m.domains += ClockDomain("async_ff", async_reset=True, local=True)
164 flops = [Signal(1, name="stage{}".format(index), reset=1)
165 for index in range(self._stages)]
166 for i, o in zip((0, *flops), flops):
167 m.d.async_ff += o.eq(i)
168
169 if self._edge == "pos":
170 m.d.comb += ResetSignal("async_ff").eq(self.i)
171 else:
172 m.d.comb += ResetSignal("async_ff").eq(~self.i)
173
174 m.d.comb += [
175 ClockSignal("async_ff").eq(ClockSignal(self._o_domain)),
176 self.o.eq(flops[-1])
177 ]
178
179 return m
180
181
182 class ResetSynchronizer(Elaboratable):
183 """Synchronize deassertion of a clock domain reset.
184
185 The reset of the clock domain driven by the :class:`ResetSynchronizer` is asserted
186 asynchronously and deasserted synchronously, eliminating metastability during deassertion.
187
188 The driven clock domain could use a reset that is asserted either synchronously or
189 asynchronously; a reset is always deasserted synchronously. A domain with an asynchronously
190 asserted reset is useful if the clock of the domain may be gated, yet the domain still
191 needs to be reset promptly; otherwise, synchronously asserted reset (the default) should
192 be used.
193
194 Parameters
195 ----------
196 arst : Signal(1), in
197 Asynchronous reset signal, to be synchronized.
198 domain : str
199 Name of clock domain to reset.
200 stages : int, >=2
201 Number of synchronization stages between input and output. The lowest safe number is 2,
202 with higher numbers reducing MTBF further, at the cost of increased deassertion latency.
203 max_input_delay : None or float
204 Maximum delay from the input signal's clock to the first synchronization stage, in seconds.
205 If specified and the platform does not support it, elaboration will fail.
206
207 Platform override
208 -----------------
209 Define the ``get_reset_sync`` platform method to override the implementation of
210 :class:`ResetSynchronizer`, e.g. to instantiate library cells directly.
211 """
212 def __init__(self, arst, *, domain="sync", stages=2, max_input_delay=None):
213 _check_stages(stages)
214
215 self.arst = arst
216
217 self._domain = domain
218 self._stages = stages
219
220 self._max_input_delay = max_input_delay
221
222 def elaborate(self, platform):
223 return AsyncFFSynchronizer(self.arst, ResetSignal(self._domain), o_domain=self._domain,
224 stages=self._stages, max_input_delay=self._max_input_delay)
225
226
227 class PulseSynchronizer(Elaboratable):
228 """A one-clock pulse on the input produces a one-clock pulse on the output.
229
230 If the output clock is faster than the input clock, then the input may be safely asserted at
231 100% duty cycle. Otherwise, if the clock ratio is `n`:1, the input may be asserted at most once
232 in every `n` input clocks, else pulses may be dropped. Other than this there is no constraint
233 on the ratio of input and output clock frequency.
234
235 Parameters
236 ----------
237 i_domain : str
238 Name of input clock domain.
239 o_domain : str
240 Name of output clock domain.
241 stages : int, >=2
242 Number of synchronization stages between input and output. The lowest safe number is 2,
243 with higher numbers reducing MTBF further, at the cost of increased deassertion latency.
244 """
245 def __init__(self, i_domain, o_domain, *, stages=2):
246 _check_stages(stages)
247
248 self.i = Signal()
249 self.o = Signal()
250
251 self._i_domain = i_domain
252 self._o_domain = o_domain
253 self._stages = stages
254
255 def elaborate(self, platform):
256 m = Module()
257
258 i_toggle = Signal()
259 o_toggle = Signal()
260 r_toggle = Signal()
261 ff_sync = m.submodules.ff_sync = \
262 FFSynchronizer(i_toggle, o_toggle, o_domain=self._o_domain, stages=self._stages)
263
264 m.d[self._i_domain] += i_toggle.eq(i_toggle ^ self.i)
265 m.d[self._o_domain] += r_toggle.eq(o_toggle)
266 m.d.comb += self.o.eq(o_toggle ^ r_toggle)
267
268 return m