merge most of misoc 54e1ef82 and migen e93d0601 changes
[litex.git] / litex / gen / genlib / cdc.py
1 """
2 Clock domain crossing module
3 """
4
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
11
12
13 class MultiRegImpl(Module):
14 def __init__(self, i, o, odomain, n):
15 self.i = i
16 self.o = o
17 self.odomain = odomain
18
19 w, signed = value_bits_sign(self.i)
20 self.regs = [Signal((w, signed)) for i in range(n)]
21
22 ###
23
24 src = self.i
25 for reg in self.regs:
26 sd = getattr(self.sync, self.odomain)
27 sd += reg.eq(src)
28 src = reg
29 self.comb += self.o.eq(src)
30 for reg in self.regs:
31 reg.attr.add("no_retiming")
32
33
34 class MultiReg(Special):
35 def __init__(self, i, o, odomain="sys", n=2):
36 Special.__init__(self)
37 self.i = wrap(i)
38 self.o = wrap(o)
39 self.odomain = odomain
40 self.n = n
41
42 def iter_expressions(self):
43 yield self, "i", SPECIAL_INPUT
44 yield self, "o", SPECIAL_OUTPUT
45
46 def rename_clock_domain(self, old, new):
47 Special.rename_clock_domain(self, old, new)
48 if self.odomain == old:
49 self.odomain = new
50
51 def list_clock_domains(self):
52 r = Special.list_clock_domains(self)
53 r.add(self.odomain)
54 return r
55
56 @staticmethod
57 def lower(dr):
58 return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
59
60
61 class PulseSynchronizer(Module):
62 def __init__(self, idomain, odomain):
63 self.i = Signal()
64 self.o = Signal()
65
66 ###
67
68 toggle_i = Signal()
69 toggle_o = Signal()
70 toggle_o_r = Signal()
71
72 sync_i = getattr(self.sync, idomain)
73 sync_o = getattr(self.sync, odomain)
74
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)
79
80
81 class BusSynchronizer(Module):
82 """Clock domain transfer of several bits at once.
83
84 Ensures that all the bits form a single word that was present
85 synchronously in the input clock domain (unlike direct use of
86 ``MultiReg``)."""
87 def __init__(self, width, idomain, odomain, timeout=128):
88 self.i = Signal(width)
89 self.o = Signal(width)
90
91 if width == 1:
92 self.specials += MultiReg(self.i, self.o, odomain)
93 else:
94 sync_i = getattr(self.sync, idomain)
95 sync_o = getattr(self.sync, odomain)
96
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)
102 self.comb += [
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)
106 ]
107
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))
114
115
116 class GrayCounter(Module):
117 def __init__(self, width):
118 self.ce = Signal()
119 self.q = Signal(width)
120 self.q_next = Signal(width)
121 self.q_binary = Signal(width)
122 self.q_next_binary = Signal(width)
123
124 ###
125
126 self.comb += [
127 If(self.ce,
128 self.q_next_binary.eq(self.q_binary + 1)
129 ).Else(
130 self.q_next_binary.eq(self.q_binary)
131 ),
132 self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:])
133 ]
134 self.sync += [
135 self.q_binary.eq(self.q_next_binary),
136 self.q.eq(self.q_next)
137 ]
138
139
140 class GrayDecoder(Module):
141 def __init__(self, width):
142 self.i = Signal(width)
143 self.o = Signal(width)
144
145 # # #
146
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)
152
153
154 class ElasticBuffer(Module):
155 def __init__(self, width, depth, idomain, odomain):
156 self.din = Signal(width)
157 self.dout = Signal(width)
158
159 # # #
160
161 reset = Signal()
162 cd_write = ClockDomain()
163 cd_read = ClockDomain()
164 self.comb += [
165 cd_write.clk.eq(ClockSignal(idomain)),
166 cd_read.clk.eq(ClockSignal(odomain)),
167 reset.eq(ResetSignal(idomain) | ResetSignal(odomain))
168 ]
169 self.specials += [
170 AsyncResetSynchronizer(cd_write, reset),
171 AsyncResetSynchronizer(cd_read, reset)
172 ]
173 self.clock_domains += cd_write, cd_read
174
175 wrpointer = Signal(max=depth, reset=depth//2)
176 rdpointer = Signal(max=depth)
177
178 storage = Memory(width, depth)
179 self.specials += storage
180
181 wrport = storage.get_port(write_capable=True, clock_domain="write")
182 rdport = storage.get_port(clock_domain="read")
183 self.specials += wrport, rdport
184
185 self.sync.write += wrpointer.eq(wrpointer + 1)
186 self.sync.read += rdpointer.eq(rdpointer + 1)
187
188 self.comb += [
189 wrport.we.eq(1),
190 wrport.adr.eq(wrpointer),
191 wrport.dat_w.eq(self.din),
192
193 rdport.adr.eq(rdpointer),
194 self.dout.eq(rdport.dat_r)
195 ]