2 Clock domain crossing module
5 from litex
.gen
.fhdl
.structure
import *
6 from litex
.gen
.fhdl
.module
import Module
7 from litex
.gen
.fhdl
.specials
import Special
, Memory
8 from litex
.gen
.fhdl
.bitcontainer
import value_bits_sign
9 from litex
.gen
.genlib
.misc
import WaitTimer
10 from litex
.gen
.genlib
.resetsync
import AsyncResetSynchronizer
13 class MultiRegImpl(Module
):
14 def __init__(self
, i
, o
, odomain
, n
):
17 self
.odomain
= odomain
19 w
, signed
= value_bits_sign(self
.i
)
20 self
.regs
= [Signal((w
, signed
)) for i
in range(n
)]
26 sd
= getattr(self
.sync
, self
.odomain
)
29 self
.comb
+= self
.o
.eq(src
)
31 reg
.attr
.add("no_retiming")
34 class MultiReg(Special
):
35 def __init__(self
, i
, o
, odomain
="sys", n
=2):
36 Special
.__init
__(self
)
39 self
.odomain
= odomain
42 def iter_expressions(self
):
43 yield self
, "i", SPECIAL_INPUT
44 yield self
, "o", SPECIAL_OUTPUT
46 def rename_clock_domain(self
, old
, new
):
47 Special
.rename_clock_domain(self
, old
, new
)
48 if self
.odomain
== old
:
51 def list_clock_domains(self
):
52 r
= Special
.list_clock_domains(self
)
58 return MultiRegImpl(dr
.i
, dr
.o
, dr
.odomain
, dr
.n
)
61 class PulseSynchronizer(Module
):
62 def __init__(self
, idomain
, odomain
):
72 sync_i
= getattr(self
.sync
, idomain
)
73 sync_o
= getattr(self
.sync
, odomain
)
75 sync_i
+= If(self
.i
, toggle_i
.eq(~toggle_i
))
76 self
.specials
+= MultiReg(toggle_i
, toggle_o
, odomain
)
77 sync_o
+= toggle_o_r
.eq(toggle_o
)
78 self
.comb
+= self
.o
.eq(toggle_o ^ toggle_o_r
)
81 class BusSynchronizer(Module
):
82 """Clock domain transfer of several bits at once.
84 Ensures that all the bits form a single word that was present
85 synchronously in the input clock domain (unlike direct use of
87 def __init__(self
, width
, idomain
, odomain
, timeout
=128):
88 self
.i
= Signal(width
)
89 self
.o
= Signal(width
)
92 self
.specials
+= MultiReg(self
.i
, self
.o
, odomain
)
94 sync_i
= getattr(self
.sync
, idomain
)
95 sync_o
= getattr(self
.sync
, odomain
)
97 starter
= Signal(reset
=1)
98 sync_i
+= starter
.eq(0)
99 self
.submodules
._ping
= PulseSynchronizer(idomain
, odomain
)
100 self
.submodules
._pong
= PulseSynchronizer(odomain
, idomain
)
101 self
.submodules
._timeout
= WaitTimer(timeout
)
103 self
._timeout
.wait
.eq(~self
._ping
.i
),
104 self
._ping
.i
.eq(starter | self
._pong
.o | self
._timeout
.done
),
105 self
._pong
.i
.eq(self
._ping
.i
)
108 ibuffer
= Signal(width
)
109 obuffer
= Signal(width
)
110 sync_i
+= If(self
._pong
.o
, ibuffer
.eq(self
.i
))
111 ibuffer
.attr
.add("no_retiming")
112 self
.specials
+= MultiReg(ibuffer
, obuffer
, odomain
)
113 sync_o
+= If(self
._ping
.o
, self
.o
.eq(obuffer
))
116 class GrayCounter(Module
):
117 def __init__(self
, width
):
119 self
.q
= Signal(width
)
120 self
.q_next
= Signal(width
)
121 self
.q_binary
= Signal(width
)
122 self
.q_next_binary
= Signal(width
)
128 self
.q_next_binary
.eq(self
.q_binary
+ 1)
130 self
.q_next_binary
.eq(self
.q_binary
)
132 self
.q_next
.eq(self
.q_next_binary ^ self
.q_next_binary
[1:])
135 self
.q_binary
.eq(self
.q_next_binary
),
136 self
.q
.eq(self
.q_next
)
140 class GrayDecoder(Module
):
141 def __init__(self
, width
):
142 self
.i
= Signal(width
)
143 self
.o
= Signal(width
)
147 o_comb
= Signal(width
)
148 self
.comb
+= o_comb
[-1].eq(self
.i
[-1])
149 for i
in reversed(range(width
-1)):
150 self
.comb
+= o_comb
[i
].eq(o_comb
[i
+1] ^ self
.i
[i
])
151 self
.sync
+= self
.o
.eq(o_comb
)
154 class ElasticBuffer(Module
):
155 def __init__(self
, width
, depth
, idomain
, odomain
):
156 self
.din
= Signal(width
)
157 self
.dout
= Signal(width
)
162 cd_write
= ClockDomain()
163 cd_read
= ClockDomain()
165 cd_write
.clk
.eq(ClockSignal(idomain
)),
166 cd_read
.clk
.eq(ClockSignal(odomain
)),
167 reset
.eq(ResetSignal(idomain
) |
ResetSignal(odomain
))
170 AsyncResetSynchronizer(cd_write
, reset
),
171 AsyncResetSynchronizer(cd_read
, reset
)
173 self
.clock_domains
+= cd_write
, cd_read
175 wrpointer
= Signal(max=depth
, reset
=depth
//2)
176 rdpointer
= Signal(max=depth
)
178 storage
= Memory(width
, depth
)
179 self
.specials
+= storage
181 wrport
= storage
.get_port(write_capable
=True, clock_domain
="write")
182 rdport
= storage
.get_port(clock_domain
="read")
183 self
.specials
+= wrport
, rdport
185 self
.sync
.write
+= wrpointer
.eq(wrpointer
+ 1)
186 self
.sync
.read
+= rdpointer
.eq(rdpointer
+ 1)
190 wrport
.adr
.eq(wrpointer
),
191 wrport
.dat_w
.eq(self
.din
),
193 rdport
.adr
.eq(rdpointer
),
194 self
.dout
.eq(rdport
.dat_r
)