6e7b0ad8b5a7b947e6d8817168b07efd96dca262
[litex.git] / litex / gen / genlib / cdc.py
1 """
2 Clock domain crossing module
3 """
4 from fractions import gcd
5
6 from litex.gen.fhdl.structure import *
7 from litex.gen.fhdl.module import Module
8 from litex.gen.fhdl.specials import Special, Memory
9 from litex.gen.fhdl.bitcontainer import value_bits_sign
10 from litex.gen.genlib.misc import WaitTimer
11 from litex.gen.genlib.resetsync import AsyncResetSynchronizer
12
13
14 class MultiRegImpl(Module):
15 def __init__(self, i, o, odomain, n):
16 self.i = i
17 self.o = o
18 self.odomain = odomain
19
20 w, signed = value_bits_sign(self.i)
21 self.regs = [Signal((w, signed)) for i in range(n)]
22
23 ###
24
25 src = self.i
26 for reg in self.regs:
27 sd = getattr(self.sync, self.odomain)
28 sd += reg.eq(src)
29 src = reg
30 self.comb += self.o.eq(src)
31 for reg in self.regs:
32 reg.attr.add("no_retiming")
33
34
35 class MultiReg(Special):
36 def __init__(self, i, o, odomain="sys", n=2):
37 Special.__init__(self)
38 self.i = wrap(i)
39 self.o = wrap(o)
40 self.odomain = odomain
41 self.n = n
42
43 def iter_expressions(self):
44 yield self, "i", SPECIAL_INPUT
45 yield self, "o", SPECIAL_OUTPUT
46
47 def rename_clock_domain(self, old, new):
48 Special.rename_clock_domain(self, old, new)
49 if self.odomain == old:
50 self.odomain = new
51
52 def list_clock_domains(self):
53 r = Special.list_clock_domains(self)
54 r.add(self.odomain)
55 return r
56
57 @staticmethod
58 def lower(dr):
59 return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
60
61
62 class PulseSynchronizer(Module):
63 def __init__(self, idomain, odomain):
64 self.i = Signal()
65 self.o = Signal()
66
67 ###
68
69 toggle_i = Signal()
70 toggle_o = Signal()
71 toggle_o_r = Signal()
72
73 sync_i = getattr(self.sync, idomain)
74 sync_o = getattr(self.sync, odomain)
75
76 sync_i += If(self.i, toggle_i.eq(~toggle_i))
77 self.specials += MultiReg(toggle_i, toggle_o, odomain)
78 sync_o += toggle_o_r.eq(toggle_o)
79 self.comb += self.o.eq(toggle_o ^ toggle_o_r)
80
81
82 class BusSynchronizer(Module):
83 """Clock domain transfer of several bits at once.
84
85 Ensures that all the bits form a single word that was present
86 synchronously in the input clock domain (unlike direct use of
87 ``MultiReg``)."""
88 def __init__(self, width, idomain, odomain, timeout=128):
89 self.i = Signal(width)
90 self.o = Signal(width)
91
92 if width == 1:
93 self.specials += MultiReg(self.i, self.o, odomain)
94 else:
95 sync_i = getattr(self.sync, idomain)
96 sync_o = getattr(self.sync, odomain)
97
98 starter = Signal(reset=1)
99 sync_i += starter.eq(0)
100 self.submodules._ping = PulseSynchronizer(idomain, odomain)
101 self.submodules._pong = PulseSynchronizer(odomain, idomain)
102 self.submodules._timeout = WaitTimer(timeout)
103 self.comb += [
104 self._timeout.wait.eq(~self._ping.i),
105 self._ping.i.eq(starter | self._pong.o | self._timeout.done),
106 self._pong.i.eq(self._ping.i)
107 ]
108
109 ibuffer = Signal(width)
110 obuffer = Signal(width)
111 sync_i += If(self._pong.o, ibuffer.eq(self.i))
112 ibuffer.attr.add("no_retiming")
113 self.specials += MultiReg(ibuffer, obuffer, odomain)
114 sync_o += If(self._ping.o, self.o.eq(obuffer))
115
116
117 class GrayCounter(Module):
118 def __init__(self, width):
119 self.ce = Signal()
120 self.q = Signal(width)
121 self.q_next = Signal(width)
122 self.q_binary = Signal(width)
123 self.q_next_binary = Signal(width)
124
125 ###
126
127 self.comb += [
128 If(self.ce,
129 self.q_next_binary.eq(self.q_binary + 1)
130 ).Else(
131 self.q_next_binary.eq(self.q_binary)
132 ),
133 self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:])
134 ]
135 self.sync += [
136 self.q_binary.eq(self.q_next_binary),
137 self.q.eq(self.q_next)
138 ]
139
140
141 class GrayDecoder(Module):
142 def __init__(self, width):
143 self.i = Signal(width)
144 self.o = Signal(width)
145
146 # # #
147
148 o_comb = Signal(width)
149 self.comb += o_comb[-1].eq(self.i[-1])
150 for i in reversed(range(width-1)):
151 self.comb += o_comb[i].eq(o_comb[i+1] ^ self.i[i])
152 self.sync += self.o.eq(o_comb)
153
154
155 class ElasticBuffer(Module):
156 def __init__(self, width, depth, idomain, odomain):
157 self.din = Signal(width)
158 self.dout = Signal(width)
159
160 # # #
161
162 reset = Signal()
163 cd_write = ClockDomain()
164 cd_read = ClockDomain()
165 self.comb += [
166 cd_write.clk.eq(ClockSignal(idomain)),
167 cd_read.clk.eq(ClockSignal(odomain)),
168 reset.eq(ResetSignal(idomain) | ResetSignal(odomain))
169 ]
170 self.specials += [
171 AsyncResetSynchronizer(cd_write, reset),
172 AsyncResetSynchronizer(cd_read, reset)
173 ]
174 self.clock_domains += cd_write, cd_read
175
176 wrpointer = Signal(max=depth, reset=depth//2)
177 rdpointer = Signal(max=depth)
178
179 storage = Memory(width, depth)
180 self.specials += storage
181
182 wrport = storage.get_port(write_capable=True, clock_domain="write")
183 rdport = storage.get_port(clock_domain="read")
184 self.specials += wrport, rdport
185
186 self.sync.write += wrpointer.eq(wrpointer + 1)
187 self.sync.read += rdpointer.eq(rdpointer + 1)
188
189 self.comb += [
190 wrport.we.eq(1),
191 wrport.adr.eq(wrpointer),
192 wrport.dat_w.eq(self.din),
193
194 rdport.adr.eq(rdpointer),
195 self.dout.eq(rdport.dat_r)
196 ]
197
198
199 def lcm(a, b):
200 """Compute the lowest common multiple of a and b"""
201 return int(a * b / gcd(a, b))
202
203
204 class Gearbox(Module):
205 def __init__(self, iwidth, idomain, owidth, odomain):
206 self.i = Signal(iwidth)
207 self.o = Signal(owidth)
208
209 # # #
210
211 reset = Signal()
212 cd_write = ClockDomain()
213 cd_read = ClockDomain()
214 self.comb += [
215 cd_write.clk.eq(ClockSignal(idomain)),
216 cd_read.clk.eq(ClockSignal(odomain)),
217 reset.eq(ResetSignal(idomain) | ResetSignal(odomain))
218 ]
219 self.specials += [
220 AsyncResetSynchronizer(cd_write, reset),
221 AsyncResetSynchronizer(cd_read, reset)
222 ]
223 self.clock_domains += cd_write, cd_read
224
225 storage = Signal(lcm(iwidth, owidth))
226 wrchunks = len(storage)//iwidth
227 rdchunks = len(storage)//owidth
228 wrpointer = Signal(max=wrchunks, reset=0 if iwidth > owidth else wrchunks-1)
229 rdpointer = Signal(max=rdchunks, reset=rdchunks-1 if iwidth > owidth else 0)
230
231 self.sync.write += \
232 If(wrpointer == wrchunks-1,
233 wrpointer.eq(0)
234 ).Else(
235 wrpointer.eq(wrpointer + 1)
236 )
237 cases = {}
238 for i in range(wrchunks):
239 cases[i] = [storage[iwidth*i:iwidth*(i+1)].eq(self.i)]
240 self.sync.write += Case(wrpointer, cases)
241
242
243 self.sync.read += \
244 If(rdpointer == rdchunks-1,
245 rdpointer.eq(0)
246 ).Else(
247 rdpointer.eq(rdpointer + 1)
248 )
249 cases = {}
250 for i in range(rdchunks):
251 cases[i] = [self.o.eq(storage[owidth*i:owidth*(i+1)])]
252 self.sync.read += Case(rdpointer, cases)