lib.cdc: update PulseSynchronizer to follow conventions.
[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 domain : str
113 Name of clock domain to reset.
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, *, domain="sync", stages=2, async_edge="pos", max_input_delay=None):
129 _check_stages(stages)
130
131 self.i = i
132 self.o = o
133
134 self._domain = domain
135 self._stages = stages
136
137 if async_edge not in ("pos", "neg"):
138 raise ValueError("AsyncFFSynchronizer async edge must be one of 'pos' or 'neg', "
139 "not {!r}"
140 .format(async_edge))
141 self._edge = async_edge
142
143 self._max_input_delay = max_input_delay
144
145 def elaborate(self, platform):
146 if hasattr(platform, "get_async_ff_sync"):
147 return platform.get_async_ff_sync(self)
148
149 if self._max_input_delay is not None:
150 raise NotImplementedError("Platform '{}' does not support constraining input delay "
151 "for AsyncFFSynchronizer"
152 .format(type(platform).__name__))
153
154 m = Module()
155 m.domains += ClockDomain("async_ff", async_reset=True, local=True)
156 flops = [Signal(1, name="stage{}".format(index), reset=1)
157 for index in range(self._stages)]
158 for i, o in zip((0, *flops), flops):
159 m.d.async_ff += o.eq(i)
160
161 if self._edge == "pos":
162 m.d.comb += ResetSignal("async_ff").eq(self.i)
163 else:
164 m.d.comb += ResetSignal("async_ff").eq(~self.i)
165
166 m.d.comb += [
167 ClockSignal("async_ff").eq(ClockSignal(self._domain)),
168 self.o.eq(flops[-1])
169 ]
170
171 return m
172
173
174 class ResetSynchronizer(Elaboratable):
175 """Synchronize deassertion of a clock domain reset.
176
177 The reset of the clock domain driven by the :class:`ResetSynchronizer` is asserted
178 asynchronously and deasserted synchronously, eliminating metastability during deassertion.
179
180 The driven clock domain could use a reset that is asserted either synchronously or
181 asynchronously; a reset is always deasserted synchronously. A domain with an asynchronously
182 asserted reset is useful if the clock of the domain may be gated, yet the domain still
183 needs to be reset promptly; otherwise, synchronously asserted reset (the default) should
184 be used.
185
186 Parameters
187 ----------
188 arst : Signal(1), in
189 Asynchronous reset signal, to be synchronized.
190 domain : str
191 Name of clock domain to reset.
192 stages : int, >=2
193 Number of synchronization stages between input and output. The lowest safe number is 2,
194 with higher numbers reducing MTBF further, at the cost of increased deassertion latency.
195 max_input_delay : None or float
196 Maximum delay from the input signal's clock to the first synchronization stage, in seconds.
197 If specified and the platform does not support it, elaboration will fail.
198
199 Platform override
200 -----------------
201 Define the ``get_reset_sync`` platform method to override the implementation of
202 :class:`ResetSynchronizer`, e.g. to instantiate library cells directly.
203 """
204 def __init__(self, arst, *, domain="sync", stages=2, max_input_delay=None):
205 _check_stages(stages)
206
207 self.arst = arst
208
209 self._domain = domain
210 self._stages = stages
211
212 self._max_input_delay = max_input_delay
213
214 def elaborate(self, platform):
215 return AsyncFFSynchronizer(self.arst, ResetSignal(self._domain), domain=self._domain,
216 stages=self._stages, max_input_delay=self._max_input_delay)
217
218
219 class PulseSynchronizer(Elaboratable):
220 """A one-clock pulse on the input produces a one-clock pulse on the output.
221
222 If the output clock is faster than the input clock, then the input may be safely asserted at
223 100% duty cycle. Otherwise, if the clock ratio is `n`:1, the input may be asserted at most once
224 in every `n` input clocks, else pulses may be dropped. Other than this there is no constraint
225 on the ratio of input and output clock frequency.
226
227 Parameters
228 ----------
229 i_domain : str
230 Name of input clock domain.
231 o_domain : str
232 Name of output clock domain.
233 stages : int, >=2
234 Number of synchronization stages between input and output. The lowest safe number is 2,
235 with higher numbers reducing MTBF further, at the cost of increased deassertion latency.
236 """
237 def __init__(self, i_domain, o_domain, *, stages=2):
238 _check_stages(stages)
239
240 self.i = Signal()
241 self.o = Signal()
242
243 self._i_domain = i_domain
244 self._o_domain = o_domain
245 self._stages = stages
246
247 def elaborate(self, platform):
248 m = Module()
249
250 i_toggle = Signal()
251 o_toggle = Signal()
252 r_toggle = Signal()
253 ff_sync = m.submodules.ff_sync = \
254 FFSynchronizer(i_toggle, o_toggle, o_domain=self._o_domain, stages=self._stages)
255
256 m.d[self._i_domain] += i_toggle.eq(i_toggle ^ self.i)
257 m.d[self._o_domain] += r_toggle.eq(o_toggle)
258 m.d.comb += self.o.eq(o_toggle ^ r_toggle)
259
260 return m