2 Clock domain crossing module
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
15 class MultiRegImpl(Module
):
16 def __init__(self
, i
, o
, odomain
, n
):
19 self
.odomain
= odomain
21 w
, signed
= value_bits_sign(self
.i
)
22 self
.regs
= [Signal((w
, signed
), reset_less
=True)
29 sd
= getattr(self
.sync
, self
.odomain
)
32 self
.comb
+= self
.o
.eq(src
)
34 reg
.attr
.add("no_retiming")
37 class MultiReg(Special
):
38 def __init__(self
, i
, o
, odomain
="sys", n
=2):
39 Special
.__init
__(self
)
42 self
.odomain
= odomain
45 def iter_expressions(self
):
46 yield self
, "i", SPECIAL_INPUT
47 yield self
, "o", SPECIAL_OUTPUT
49 def rename_clock_domain(self
, old
, new
):
50 Special
.rename_clock_domain(self
, old
, new
)
51 if self
.odomain
== old
:
54 def list_clock_domains(self
):
55 r
= Special
.list_clock_domains(self
)
61 return MultiRegImpl(dr
.i
, dr
.o
, dr
.odomain
, dr
.n
)
64 class PulseSynchronizer(Module
):
65 def __init__(self
, idomain
, odomain
):
71 toggle_i
= Signal(reset_less
=True)
72 toggle_o
= Signal() # registered reset_less by MultiReg
73 toggle_o_r
= Signal(reset_less
=True)
75 sync_i
= getattr(self
.sync
, idomain
)
76 sync_o
= getattr(self
.sync
, odomain
)
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
)
84 class BusSynchronizer(Module
):
85 """Clock domain transfer of several bits at once.
87 Ensures that all the bits form a single word that was present
88 synchronously in the input clock domain (unlike direct use of
90 def __init__(self
, width
, idomain
, odomain
, timeout
=128):
91 self
.i
= Signal(width
)
92 self
.o
= Signal(width
, reset_less
=True)
95 self
.specials
+= MultiReg(self
.i
, self
.o
, odomain
)
97 sync_i
= getattr(self
.sync
, idomain
)
98 sync_o
= getattr(self
.sync
, odomain
)
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
)(
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
)
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
))
120 class GrayCounter(Module
):
121 def __init__(self
, width
):
123 self
.q
= Signal(width
)
124 self
.q_next
= Signal(width
)
125 self
.q_binary
= Signal(width
)
126 self
.q_next_binary
= Signal(width
)
132 self
.q_next_binary
.eq(self
.q_binary
+ 1)
134 self
.q_next_binary
.eq(self
.q_binary
)
136 self
.q_next
.eq(self
.q_next_binary ^ self
.q_next_binary
[1:])
139 self
.q_binary
.eq(self
.q_next_binary
),
140 self
.q
.eq(self
.q_next
)
144 class GrayDecoder(Module
):
145 def __init__(self
, width
):
146 self
.i
= Signal(width
)
147 self
.o
= Signal(width
, reset_less
=True)
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
)
158 class ElasticBuffer(Module
):
159 def __init__(self
, width
, depth
, idomain
, odomain
):
160 self
.din
= Signal(width
)
161 self
.dout
= Signal(width
)
166 cd_write
= ClockDomain()
167 cd_read
= ClockDomain()
169 cd_write
.clk
.eq(ClockSignal(idomain
)),
170 cd_read
.clk
.eq(ClockSignal(odomain
)),
171 reset
.eq(ResetSignal(idomain
) |
ResetSignal(odomain
))
174 AsyncResetSynchronizer(cd_write
, reset
),
175 AsyncResetSynchronizer(cd_read
, reset
)
177 self
.clock_domains
+= cd_write
, cd_read
179 wrpointer
= Signal(max=depth
, reset
=depth
//2)
180 rdpointer
= Signal(max=depth
)
182 storage
= Memory(width
, depth
)
183 self
.specials
+= storage
185 wrport
= storage
.get_port(write_capable
=True, clock_domain
="write")
186 rdport
= storage
.get_port(clock_domain
="read")
187 self
.specials
+= wrport
, rdport
189 self
.sync
.write
+= wrpointer
.eq(wrpointer
+ 1)
190 self
.sync
.read
+= rdpointer
.eq(rdpointer
+ 1)
194 wrport
.adr
.eq(wrpointer
),
195 wrport
.dat_w
.eq(self
.din
),
197 rdport
.adr
.eq(rdpointer
),
198 self
.dout
.eq(rdport
.dat_r
)
203 """Compute the lowest common multiple of a and b"""
204 return (a
*b
)//gcd(a
, b
)
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)
215 cd_write
= ClockDomain()
216 cd_read
= ClockDomain()
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
),
224 self
.clock_domains
+= cd_write
, cd_read
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)
233 If(wrpointer
== wrchunks
-1,
236 wrpointer
.eq(wrpointer
+ 1)
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
)
245 If(rdpointer
== rdchunks
-1,
248 rdpointer
.eq(rdpointer
+ 1)
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
)