ElasticBuffer: infer reset
[litex.git] / litex / gen / genlib / cdc.py
1 from litex.gen.fhdl.structure import *
2 from litex.gen.fhdl.module import Module
3 from litex.gen.fhdl.specials import Special, Memory
4 from litex.gen.fhdl.bitcontainer import value_bits_sign
5 from litex.gen.genlib.misc import WaitTimer
6 from litex.gen.genlib.resetsync import AsyncResetSynchronizer
7
8
9 class NoRetiming(Special):
10 def __init__(self, reg):
11 Special.__init__(self)
12 self.reg = reg
13
14 # do nothing
15 @staticmethod
16 def lower(dr):
17 return Module()
18
19
20 class MultiRegImpl(Module):
21 def __init__(self, i, o, odomain, n):
22 self.i = i
23 self.o = o
24 self.odomain = odomain
25
26 w, signed = value_bits_sign(self.i)
27 self.regs = [Signal((w, signed)) for i in range(n)]
28
29 ###
30
31 src = self.i
32 for reg in self.regs:
33 sd = getattr(self.sync, self.odomain)
34 sd += reg.eq(src)
35 src = reg
36 self.comb += self.o.eq(src)
37 self.specials += [NoRetiming(reg) for reg in self.regs]
38
39
40 class MultiReg(Special):
41 def __init__(self, i, o, odomain="sys", n=2):
42 Special.__init__(self)
43 self.i = wrap(i)
44 self.o = wrap(o)
45 self.odomain = odomain
46 self.n = n
47
48 def iter_expressions(self):
49 yield self, "i", SPECIAL_INPUT
50 yield self, "o", SPECIAL_OUTPUT
51
52 def rename_clock_domain(self, old, new):
53 Special.rename_clock_domain(self, old, new)
54 if self.odomain == old:
55 self.odomain = new
56
57 def list_clock_domains(self):
58 r = Special.list_clock_domains(self)
59 r.add(self.odomain)
60 return r
61
62 @staticmethod
63 def lower(dr):
64 return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
65
66
67 class PulseSynchronizer(Module):
68 def __init__(self, idomain, odomain):
69 self.i = Signal()
70 self.o = Signal()
71
72 ###
73
74 toggle_i = Signal()
75 toggle_o = Signal()
76 toggle_o_r = Signal()
77
78 sync_i = getattr(self.sync, idomain)
79 sync_o = getattr(self.sync, odomain)
80
81 sync_i += If(self.i, toggle_i.eq(~toggle_i))
82 self.specials += MultiReg(toggle_i, toggle_o, odomain)
83 sync_o += toggle_o_r.eq(toggle_o)
84 self.comb += self.o.eq(toggle_o ^ toggle_o_r)
85
86
87 class BusSynchronizer(Module):
88 """Clock domain transfer of several bits at once.
89
90 Ensures that all the bits form a single word that was present
91 synchronously in the input clock domain (unlike direct use of
92 ``MultiReg``)."""
93 def __init__(self, width, idomain, odomain, timeout=128):
94 self.i = Signal(width)
95 self.o = Signal(width)
96
97 if width == 1:
98 self.specials += MultiReg(self.i, self.o, odomain)
99 else:
100 sync_i = getattr(self.sync, idomain)
101 sync_o = getattr(self.sync, odomain)
102
103 starter = Signal(reset=1)
104 sync_i += starter.eq(0)
105 self.submodules._ping = PulseSynchronizer(idomain, odomain)
106 self.submodules._pong = PulseSynchronizer(odomain, idomain)
107 self.submodules._timeout = WaitTimer(timeout)
108 self.comb += [
109 self._timeout.wait.eq(~self._ping.i),
110 self._ping.i.eq(starter | self._pong.o | self._timeout.done),
111 self._pong.i.eq(self._ping.i)
112 ]
113
114 ibuffer = Signal(width)
115 obuffer = Signal(width)
116 sync_i += If(self._pong.o, ibuffer.eq(self.i))
117 self.specials += MultiReg(ibuffer, obuffer, odomain)
118 sync_o += If(self._ping.o, self.o.eq(obuffer))
119
120
121 class GrayCounter(Module):
122 def __init__(self, width):
123 self.ce = Signal()
124 self.q = Signal(width)
125 self.q_next = Signal(width)
126 self.q_binary = Signal(width)
127 self.q_next_binary = Signal(width)
128
129 ###
130
131 self.comb += [
132 If(self.ce,
133 self.q_next_binary.eq(self.q_binary + 1)
134 ).Else(
135 self.q_next_binary.eq(self.q_binary)
136 ),
137 self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:])
138 ]
139 self.sync += [
140 self.q_binary.eq(self.q_next_binary),
141 self.q.eq(self.q_next)
142 ]
143
144
145 class ElasticBuffer(Module):
146 def __init__(self, width, depth, idomain, odomain):
147 self.din = Signal(width)
148 self.dout = Signal(width)
149
150 # # #
151
152 reset = Signal()
153 cd_write = ClockDomain()
154 cd_read = ClockDomain()
155 self.comb += [
156 cd_write.clk.eq(ClockSignal(idomain)),
157 cd_read.clk.eq(ClockSignal(odomain)),
158 reset.eq(ResetSignal(idomain) | ResetSignal(odomain))
159 ]
160 self.specials += [
161 AsyncResetSynchronizer(cd_write, reset),
162 AsyncResetSynchronizer(cd_read, reset)
163 ]
164 self.clock_domains += cd_write, cd_read
165
166 wrpointer = Signal(max=depth, reset=depth//2)
167 rdpointer = Signal(max=depth)
168
169 storage = Memory(width, depth)
170 self.specials += storage
171
172 wrport = storage.get_port(write_capable=True, clock_domain="write")
173 rdport = storage.get_port(clock_domain="read")
174 self.specials += wrport, rdport
175
176 self.sync.write += wrpointer.eq(wrpointer + 1)
177 self.sync.read += rdpointer.eq(rdpointer + 1)
178
179 self.comb += [
180 wrport.we.eq(1),
181 wrport.adr.eq(wrpointer),
182 wrport.dat_w.eq(self.din),
183
184 rdport.adr.eq(rdpointer),
185 self.dout.eq(rdport.dat_r)
186 ]