litex: reorganize things, first work working version
[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
4 from litex.gen.fhdl.bitcontainer import value_bits_sign
5 from litex.gen.genlib.misc import WaitTimer
6
7
8 class NoRetiming(Special):
9 def __init__(self, reg):
10 Special.__init__(self)
11 self.reg = reg
12
13 # do nothing
14 @staticmethod
15 def lower(dr):
16 return Module()
17
18
19 class MultiRegImpl(Module):
20 def __init__(self, i, o, odomain, n):
21 self.i = i
22 self.o = o
23 self.odomain = odomain
24
25 w, signed = value_bits_sign(self.i)
26 self.regs = [Signal((w, signed)) for i in range(n)]
27
28 ###
29
30 src = self.i
31 for reg in self.regs:
32 sd = getattr(self.sync, self.odomain)
33 sd += reg.eq(src)
34 src = reg
35 self.comb += self.o.eq(src)
36 self.specials += [NoRetiming(reg) for reg in self.regs]
37
38
39 class MultiReg(Special):
40 def __init__(self, i, o, odomain="sys", n=2):
41 Special.__init__(self)
42 self.i = wrap(i)
43 self.o = wrap(o)
44 self.odomain = odomain
45 self.n = n
46
47 def iter_expressions(self):
48 yield self, "i", SPECIAL_INPUT
49 yield self, "o", SPECIAL_OUTPUT
50
51 def rename_clock_domain(self, old, new):
52 Special.rename_clock_domain(self, old, new)
53 if self.odomain == old:
54 self.odomain = new
55
56 def list_clock_domains(self):
57 r = Special.list_clock_domains(self)
58 r.add(self.odomain)
59 return r
60
61 @staticmethod
62 def lower(dr):
63 return MultiRegImpl(dr.i, dr.o, dr.odomain, dr.n)
64
65
66 class PulseSynchronizer(Module):
67 def __init__(self, idomain, odomain):
68 self.i = Signal()
69 self.o = Signal()
70
71 ###
72
73 toggle_i = Signal()
74 toggle_o = Signal()
75 toggle_o_r = Signal()
76
77 sync_i = getattr(self.sync, idomain)
78 sync_o = getattr(self.sync, odomain)
79
80 sync_i += If(self.i, toggle_i.eq(~toggle_i))
81 self.specials += MultiReg(toggle_i, toggle_o, odomain)
82 sync_o += toggle_o_r.eq(toggle_o)
83 self.comb += self.o.eq(toggle_o ^ toggle_o_r)
84
85
86 class BusSynchronizer(Module):
87 """Clock domain transfer of several bits at once.
88
89 Ensures that all the bits form a single word that was present
90 synchronously in the input clock domain (unlike direct use of
91 ``MultiReg``)."""
92 def __init__(self, width, idomain, odomain, timeout=128):
93 self.i = Signal(width)
94 self.o = Signal(width)
95
96 if width == 1:
97 self.specials += MultiReg(self.i, self.o, odomain)
98 else:
99 sync_i = getattr(self.sync, idomain)
100 sync_o = getattr(self.sync, odomain)
101
102 starter = Signal(reset=1)
103 sync_i += starter.eq(0)
104 self.submodules._ping = PulseSynchronizer(idomain, odomain)
105 self.submodules._pong = PulseSynchronizer(odomain, idomain)
106 self.submodules._timeout = WaitTimer(timeout)
107 self.comb += [
108 self._timeout.wait.eq(~self._ping.i),
109 self._ping.i.eq(starter | self._pong.o | self._timeout.done),
110 self._pong.i.eq(self._ping.i)
111 ]
112
113 ibuffer = Signal(width)
114 obuffer = Signal(width)
115 sync_i += If(self._pong.o, ibuffer.eq(self.i))
116 self.specials += MultiReg(ibuffer, obuffer, odomain)
117 sync_o += If(self._ping.o, self.o.eq(obuffer))
118
119
120 class GrayCounter(Module):
121 def __init__(self, width):
122 self.ce = Signal()
123 self.q = Signal(width)
124 self.q_next = Signal(width)
125 self.q_binary = Signal(width)
126 self.q_next_binary = Signal(width)
127
128 ###
129
130 self.comb += [
131 If(self.ce,
132 self.q_next_binary.eq(self.q_binary + 1)
133 ).Else(
134 self.q_next_binary.eq(self.q_binary)
135 ),
136 self.q_next.eq(self.q_next_binary ^ self.q_next_binary[1:])
137 ]
138 self.sync += [
139 self.q_binary.eq(self.q_next_binary),
140 self.q.eq(self.q_next)
141 ]