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