2 Clock domain crossing module
4 from fractions
import gcd
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
14 class MultiRegImpl(Module
):
15 def __init__(self
, i
, o
, odomain
, n
):
18 self
.odomain
= odomain
20 w
, signed
= value_bits_sign(self
.i
)
21 self
.regs
= [Signal((w
, signed
)) for i
in range(n
)]
27 sd
= getattr(self
.sync
, self
.odomain
)
30 self
.comb
+= self
.o
.eq(src
)
32 reg
.attr
.add("no_retiming")
35 class MultiReg(Special
):
36 def __init__(self
, i
, o
, odomain
="sys", n
=2):
37 Special
.__init
__(self
)
40 self
.odomain
= odomain
43 def iter_expressions(self
):
44 yield self
, "i", SPECIAL_INPUT
45 yield self
, "o", SPECIAL_OUTPUT
47 def rename_clock_domain(self
, old
, new
):
48 Special
.rename_clock_domain(self
, old
, new
)
49 if self
.odomain
== old
:
52 def list_clock_domains(self
):
53 r
= Special
.list_clock_domains(self
)
59 return MultiRegImpl(dr
.i
, dr
.o
, dr
.odomain
, dr
.n
)
62 class PulseSynchronizer(Module
):
63 def __init__(self
, idomain
, odomain
):
73 sync_i
= getattr(self
.sync
, idomain
)
74 sync_o
= getattr(self
.sync
, odomain
)
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
)
82 class BusSynchronizer(Module
):
83 """Clock domain transfer of several bits at once.
85 Ensures that all the bits form a single word that was present
86 synchronously in the input clock domain (unlike direct use of
88 def __init__(self
, width
, idomain
, odomain
, timeout
=128):
89 self
.i
= Signal(width
)
90 self
.o
= Signal(width
)
93 self
.specials
+= MultiReg(self
.i
, self
.o
, odomain
)
95 sync_i
= getattr(self
.sync
, idomain
)
96 sync_o
= getattr(self
.sync
, odomain
)
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
)
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
)
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
))
117 class GrayCounter(Module
):
118 def __init__(self
, width
):
120 self
.q
= Signal(width
)
121 self
.q_next
= Signal(width
)
122 self
.q_binary
= Signal(width
)
123 self
.q_next_binary
= Signal(width
)
129 self
.q_next_binary
.eq(self
.q_binary
+ 1)
131 self
.q_next_binary
.eq(self
.q_binary
)
133 self
.q_next
.eq(self
.q_next_binary ^ self
.q_next_binary
[1:])
136 self
.q_binary
.eq(self
.q_next_binary
),
137 self
.q
.eq(self
.q_next
)
141 class GrayDecoder(Module
):
142 def __init__(self
, width
):
143 self
.i
= Signal(width
)
144 self
.o
= Signal(width
)
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
)
155 class ElasticBuffer(Module
):
156 def __init__(self
, width
, depth
, idomain
, odomain
):
157 self
.din
= Signal(width
)
158 self
.dout
= Signal(width
)
163 cd_write
= ClockDomain()
164 cd_read
= ClockDomain()
166 cd_write
.clk
.eq(ClockSignal(idomain
)),
167 cd_read
.clk
.eq(ClockSignal(odomain
)),
168 reset
.eq(ResetSignal(idomain
) |
ResetSignal(odomain
))
171 AsyncResetSynchronizer(cd_write
, reset
),
172 AsyncResetSynchronizer(cd_read
, reset
)
174 self
.clock_domains
+= cd_write
, cd_read
176 wrpointer
= Signal(max=depth
, reset
=depth
//2)
177 rdpointer
= Signal(max=depth
)
179 storage
= Memory(width
, depth
)
180 self
.specials
+= storage
182 wrport
= storage
.get_port(write_capable
=True, clock_domain
="write")
183 rdport
= storage
.get_port(clock_domain
="read")
184 self
.specials
+= wrport
, rdport
186 self
.sync
.write
+= wrpointer
.eq(wrpointer
+ 1)
187 self
.sync
.read
+= rdpointer
.eq(rdpointer
+ 1)
191 wrport
.adr
.eq(wrpointer
),
192 wrport
.dat_w
.eq(self
.din
),
194 rdport
.adr
.eq(rdpointer
),
195 self
.dout
.eq(rdport
.dat_r
)
200 """Compute the lowest common multiple of a and b"""
201 return int(a
* b
/ gcd(a
, b
))
204 class Gearbox(Module
):
205 def __init__(self
, iwidth
, idomain
, owidth
, odomain
):
206 self
.i
= Signal(iwidth
)
207 self
.o
= Signal(owidth
)
212 cd_write
= ClockDomain()
213 cd_read
= ClockDomain()
215 cd_write
.clk
.eq(ClockSignal(idomain
)),
216 cd_read
.clk
.eq(ClockSignal(odomain
)),
217 reset
.eq(ResetSignal(idomain
) |
ResetSignal(odomain
))
220 AsyncResetSynchronizer(cd_write
, reset
),
221 AsyncResetSynchronizer(cd_read
, reset
)
223 self
.clock_domains
+= cd_write
, cd_read
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)
232 If(wrpointer
== wrchunks
-1,
235 wrpointer
.eq(wrpointer
+ 1)
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
)
244 If(rdpointer
== rdchunks
-1,
247 rdpointer
.eq(rdpointer
+ 1)
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
)