8f4e14237ca03596a69efc3e15550f3d785fc796
[litex.git] / migen / genlib / fifo.py
1 from migen.fhdl.structure import *
2 from migen.fhdl.module import Module
3 from migen.fhdl.specials import Memory
4 from migen.fhdl.bitcontainer import log2_int
5 from migen.fhdl.decorators import ClockDomainsRenamer
6 from migen.genlib.cdc import NoRetiming, MultiReg, GrayCounter
7
8
9 def _inc(signal, modulo):
10 if modulo == 2**len(signal):
11 return signal.eq(signal + 1)
12 else:
13 return If(signal == (modulo - 1),
14 signal.eq(0)
15 ).Else(
16 signal.eq(signal + 1)
17 )
18
19
20 class _FIFOInterface:
21 """
22 Data written to the input interface (`din`, `we`, `writable`) is
23 buffered and can be read at the output interface (`dout`, `re`,
24 `readable`). The data entry written first to the input
25 also appears first on the output.
26
27 Parameters
28 ----------
29 width : int
30 Bit width for the data.
31 depth : int
32 Depth of the FIFO.
33
34 Attributes
35 ----------
36 din : in, width
37 Input data
38 writable : out
39 There is space in the FIFO and `we` can be asserted to load new data.
40 we : in
41 Write enable signal to latch `din` into the FIFO. Does nothing if
42 `writable` is not asserted.
43 dout : out, width
44 Output data. Only valid if `readable` is asserted.
45 readable : out
46 Output data `dout` valid, FIFO not empty.
47 re : in
48 Acknowledge `dout`. If asserted, the next entry will be
49 available on the next cycle (if `readable` is high then).
50 """
51 def __init__(self, width, depth):
52 self.we = Signal()
53 self.writable = Signal() # not full
54 self.re = Signal()
55 self.readable = Signal() # not empty
56
57 self.din = Signal(width)
58 self.dout = Signal(width)
59 self.width = width
60
61
62 class SyncFIFO(Module, _FIFOInterface):
63 """Synchronous FIFO (first in, first out)
64
65 Read and write interfaces are accessed from the same clock domain.
66 If different clock domains are needed, use :class:`AsyncFIFO`.
67
68 {interface}
69 level : out
70 Number of unread entries.
71 replace : in
72 Replaces the last entry written into the FIFO with `din`. Does nothing
73 if that entry has already been read (i.e. the FIFO is empty).
74 Assert in conjunction with `we`.
75 """
76 __doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
77
78 def __init__(self, width, depth, fwft=True):
79 _FIFOInterface.__init__(self, width, depth)
80
81 self.level = Signal(max=depth+1)
82 self.replace = Signal()
83
84 ###
85
86 produce = Signal(max=depth)
87 consume = Signal(max=depth)
88 storage = Memory(self.width, depth)
89 self.specials += storage
90
91 wrport = storage.get_port(write_capable=True)
92 self.specials += wrport
93 self.comb += [
94 If(self.replace,
95 wrport.adr.eq(produce-1)
96 ).Else(
97 wrport.adr.eq(produce)
98 ),
99 wrport.dat_w.eq(self.din),
100 wrport.we.eq(self.we & (self.writable | self.replace))
101 ]
102 self.sync += If(self.we & self.writable & ~self.replace,
103 _inc(produce, depth))
104
105 do_read = Signal()
106 self.comb += do_read.eq(self.readable & self.re)
107
108 rdport = storage.get_port(async_read=fwft, has_re=not fwft)
109 self.specials += rdport
110 self.comb += [
111 rdport.adr.eq(consume),
112 self.dout.eq(rdport.dat_r)
113 ]
114 if not fwft:
115 self.comb += rdport.re.eq(do_read)
116 self.sync += If(do_read, _inc(consume, depth))
117
118 self.sync += \
119 If(self.we & self.writable & ~self.replace,
120 If(~do_read, self.level.eq(self.level + 1))
121 ).Elif(do_read,
122 self.level.eq(self.level - 1)
123 )
124 self.comb += [
125 self.writable.eq(self.level != depth),
126 self.readable.eq(self.level != 0)
127 ]
128
129
130 class SyncFIFOBuffered(Module, _FIFOInterface):
131 def __init__(self, width, depth):
132 _FIFOInterface.__init__(self, width, depth)
133 self.submodules.fifo = fifo = SyncFIFO(width, depth, False)
134
135 self.writable = fifo.writable
136 self.din = fifo.din
137 self.we = fifo.we
138 self.dout = fifo.dout
139 self.level = Signal(max=depth+2)
140
141 ###
142
143 self.comb += fifo.re.eq(fifo.readable & (~self.readable | self.re))
144 self.sync += \
145 If(fifo.re,
146 self.readable.eq(1),
147 ).Elif(self.re,
148 self.readable.eq(0),
149 )
150 self.comb += self.level.eq(fifo.level + self.readable)
151
152
153 class AsyncFIFO(Module, _FIFOInterface):
154 """Asynchronous FIFO (first in, first out)
155
156 Read and write interfaces are accessed from different clock domains,
157 named `read` and `write`. Use `ClockDomainsRenamer` to rename to
158 other names.
159
160 {interface}
161 """
162 __doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
163
164 def __init__(self, width, depth):
165 _FIFOInterface.__init__(self, width, depth)
166
167 ###
168
169 depth_bits = log2_int(depth, True)
170
171 produce = ClockDomainsRenamer("write")(GrayCounter(depth_bits+1))
172 consume = ClockDomainsRenamer("read")(GrayCounter(depth_bits+1))
173 self.submodules += produce, consume
174 self.comb += [
175 produce.ce.eq(self.writable & self.we),
176 consume.ce.eq(self.readable & self.re)
177 ]
178
179 produce_rdomain = Signal(depth_bits+1)
180 self.specials += [
181 NoRetiming(produce.q),
182 MultiReg(produce.q, produce_rdomain, "read")
183 ]
184 consume_wdomain = Signal(depth_bits+1)
185 self.specials += [
186 NoRetiming(consume.q),
187 MultiReg(consume.q, consume_wdomain, "write")
188 ]
189 if depth_bits == 1:
190 self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1])
191 | (produce.q[-2] == consume_wdomain[-2]))
192 else:
193 self.comb += [
194 self.writable.eq((produce.q[-1] == consume_wdomain[-1])
195 | (produce.q[-2] == consume_wdomain[-2])
196 | (produce.q[:-2] != consume_wdomain[:-2]))
197 ]
198 self.comb += self.readable.eq(consume.q != produce_rdomain)
199
200 storage = Memory(self.width, depth)
201 self.specials += storage
202 wrport = storage.get_port(write_capable=True, clock_domain="write")
203 self.specials += wrport
204 self.comb += [
205 wrport.adr.eq(produce.q_binary[:-1]),
206 wrport.dat_w.eq(self.din),
207 wrport.we.eq(produce.ce)
208 ]
209 rdport = storage.get_port(clock_domain="read")
210 self.specials += rdport
211 self.comb += [
212 rdport.adr.eq(consume.q_next_binary[:-1]),
213 self.dout.eq(rdport.dat_r)
214 ]