1 """First-in first-out queues."""
4 from ..asserts
import *
5 from .._utils
import log2_int
, deprecated
6 from .coding
import GrayEncoder
, GrayDecoder
7 from .cdc
import FFSynchronizer
, AsyncFFSynchronizer
10 __all__
= ["FIFOInterface", "SyncFIFO", "SyncFIFOBuffered", "AsyncFIFO", "AsyncFIFOBuffered"]
20 Bit width of data entries.
22 Depth of the queue. If zero, the FIFO cannot be read from or written to.
31 Asserted if there is space in the queue, i.e. ``w_en`` can be asserted to write
34 Write strobe. Latches ``w_data`` into the queue. Does nothing if ``w_rdy`` is not asserted.
37 Output data. {r_data_valid}
39 Asserted if there is an entry in the queue, i.e. ``r_en`` can be asserted to read
42 Read strobe. Makes the next entry (if any) available on ``r_data`` at the next cycle.
43 Does nothing if ``r_rdy`` is not asserted.
47 __doc__
= _doc_template
.format(description
="""
48 Data written to the input interface (``w_data``, ``w_rdy``, ``w_en``) is buffered and can be
49 read at the output interface (``r_data``, ``r_rdy``, ``r_en`). The data entry written first
50 to the input also appears first on the output.
53 r_data_valid
="The conditions in which ``r_data`` is valid depends on the type of the queue.",
56 First-word fallthrough. If set, when ``r_rdy`` rises, the first entry is already
57 available, i.e. ``r_data`` is valid. Otherwise, after ``r_rdy`` rises, it is necessary
58 to strobe ``r_en`` for ``r_data`` to become valid.
63 def __init__(self
, *, width
, depth
, fwft
):
64 if not isinstance(width
, int) or width
< 0:
65 raise TypeError("FIFO width must be a non-negative integer, not {!r}"
67 if not isinstance(depth
, int) or depth
< 0:
68 raise TypeError("FIFO depth must be a non-negative integer, not {!r}"
74 self
.w_data
= Signal(width
, reset_less
=True)
75 self
.w_rdy
= Signal() # writable; not full
78 self
.r_data
= Signal(width
, reset_less
=True)
79 self
.r_rdy
= Signal() # readable; not empty
83 def _incr(signal
, modulo
):
84 if modulo
== 2 ** len(signal
):
87 return Mux(signal
== modulo
- 1, 0, signal
+ 1)
90 class SyncFIFO(Elaboratable
, FIFOInterface
):
91 __doc__
= FIFOInterface
._doc
_template
.format(
93 Synchronous first in, first out queue.
95 Read and write interfaces are accessed from the same clock domain. If different clock domains
96 are needed, use :class:`AsyncFIFO`.
100 First-word fallthrough. If set, when the queue is empty and an entry is written into it,
101 that entry becomes available on the output on the same clock cycle. Otherwise, it is
102 necessary to assert ``r_en`` for ``r_data`` to become valid.
105 For FWFT queues, valid if ``r_rdy`` is asserted. For non-FWFT queues, valid on the next
106 cycle after ``r_rdy`` and ``r_en`` have been asserted.
111 Number of unread entries.
115 def __init__(self
, *, width
, depth
, fwft
=True):
116 super().__init
__(width
=width
, depth
=depth
, fwft
=fwft
)
118 self
.level
= Signal(range(depth
+ 1))
120 def elaborate(self
, platform
):
130 self
.w_rdy
.eq(self
.level
!= self
.depth
),
131 self
.r_rdy
.eq(self
.level
!= 0)
134 do_read
= self
.r_rdy
& self
.r_en
135 do_write
= self
.w_rdy
& self
.w_en
137 storage
= Memory(width
=self
.width
, depth
=self
.depth
)
138 w_port
= m
.submodules
.w_port
= storage
.write_port()
139 r_port
= m
.submodules
.r_port
= storage
.read_port(
140 domain
="comb" if self
.fwft
else "sync", transparent
=self
.fwft
)
141 produce
= Signal(range(self
.depth
))
142 consume
= Signal(range(self
.depth
))
145 w_port
.addr
.eq(produce
),
146 w_port
.data
.eq(self
.w_data
),
147 w_port
.en
.eq(self
.w_en
& self
.w_rdy
)
150 m
.d
.sync
+= produce
.eq(_incr(produce
, self
.depth
))
153 r_port
.addr
.eq(consume
),
154 self
.r_data
.eq(r_port
.data
),
157 m
.d
.comb
+= r_port
.en
.eq(self
.r_en
)
159 m
.d
.sync
+= consume
.eq(_incr(consume
, self
.depth
))
161 with m
.If(do_write
& ~do_read
):
162 m
.d
.sync
+= self
.level
.eq(self
.level
+ 1)
163 with m
.If(do_read
& ~do_write
):
164 m
.d
.sync
+= self
.level
.eq(self
.level
- 1)
166 if platform
== "formal":
167 # TODO: move this logic to SymbiYosys
168 with m
.If(Initial()):
170 Assume(produce
< self
.depth
),
171 Assume(consume
< self
.depth
),
173 with m
.If(produce
== consume
):
174 m
.d
.comb
+= Assume((self
.level
== 0) |
(self
.level
== self
.depth
))
175 with m
.If(produce
> consume
):
176 m
.d
.comb
+= Assume(self
.level
== (produce
- consume
))
177 with m
.If(produce
< consume
):
178 m
.d
.comb
+= Assume(self
.level
== (self
.depth
+ produce
- consume
))
181 Assert(produce
< self
.depth
),
182 Assert(consume
< self
.depth
),
184 with m
.If(produce
== consume
):
185 m
.d
.comb
+= Assert((self
.level
== 0) |
(self
.level
== self
.depth
))
186 with m
.If(produce
> consume
):
187 m
.d
.comb
+= Assert(self
.level
== (produce
- consume
))
188 with m
.If(produce
< consume
):
189 m
.d
.comb
+= Assert(self
.level
== (self
.depth
+ produce
- consume
))
194 class SyncFIFOBuffered(Elaboratable
, FIFOInterface
):
195 __doc__
= FIFOInterface
._doc
_template
.format(
197 Buffered synchronous first in, first out queue.
199 This queue's interface is identical to :class:`SyncFIFO` configured as ``fwft=True``, but it
200 does not use asynchronous memory reads, which are incompatible with FPGA block RAMs.
202 In exchange, the latency between an entry being written to an empty queue and that entry
203 becoming available on the output is increased by one cycle compared to :class:`SyncFIFO`.
210 r_data_valid
="Valid if ``r_rdy`` is asserted.",
213 Number of unread entries.
217 def __init__(self
, *, width
, depth
):
218 super().__init
__(width
=width
, depth
=depth
, fwft
=True)
220 self
.level
= Signal(range(depth
+ 1))
222 def elaborate(self
, platform
):
231 # Effectively, this queue treats the output register of the non-FWFT inner queue as
232 # an additional storage element.
233 m
.submodules
.unbuffered
= fifo
= SyncFIFO(width
=self
.width
, depth
=self
.depth
- 1,
237 fifo
.w_data
.eq(self
.w_data
),
238 fifo
.w_en
.eq(self
.w_en
),
239 self
.w_rdy
.eq(fifo
.w_rdy
),
243 self
.r_data
.eq(fifo
.r_data
),
244 fifo
.r_en
.eq(fifo
.r_rdy
& (~self
.r_rdy | self
.r_en
)),
246 with m
.If(fifo
.r_en
):
247 m
.d
.sync
+= self
.r_rdy
.eq(1)
248 with m
.Elif(self
.r_en
):
249 m
.d
.sync
+= self
.r_rdy
.eq(0)
251 m
.d
.comb
+= self
.level
.eq(fifo
.level
+ self
.r_rdy
)
256 class AsyncFIFO(Elaboratable
, FIFOInterface
):
257 __doc__
= FIFOInterface
._doc
_template
.format(
259 Asynchronous first in, first out queue.
261 Read and write interfaces are accessed from different clock domains, which can be set when
262 constructing the FIFO.
264 :class:`AsyncFIFO` can be reset from the write clock domain. When the write domain reset is
265 asserted, the FIFO becomes empty. When the read domain is reset, data remains in the FIFO - the
266 read domain logic should correctly handle this case.
268 :class:`AsyncFIFO` only supports power of 2 depths. Unless ``exact_depth`` is specified,
269 the ``depth`` parameter is rounded up to the next power of 2.
281 r_data_valid
="Valid if ``r_rdy`` is asserted.",
284 Asserted while the FIFO is being reset by the write-domain reset (for at least one
285 read-domain clock cycle).
289 def __init__(self
, *, width
, depth
, r_domain
="read", w_domain
="write", exact_depth
=False):
292 depth_bits
= log2_int(depth
, need_pow2
=exact_depth
)
293 depth
= 1 << depth_bits
294 except ValueError as e
:
295 raise ValueError("AsyncFIFO only supports depths that are powers of 2; requested "
296 "exact depth {} is not"
297 .format(depth
)) from None
300 super().__init
__(width
=width
, depth
=depth
, fwft
=True)
302 self
.r_rst
= Signal()
303 self
._r
_domain
= r_domain
304 self
._w
_domain
= w_domain
305 self
._ctr
_bits
= depth_bits
+ 1
307 def elaborate(self
, platform
):
316 # The design of this queue is the "style #2" from Clifford E. Cummings' paper "Simulation
317 # and Synthesis Techniques for Asynchronous FIFO Design":
318 # http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
320 do_write
= self
.w_rdy
& self
.w_en
321 do_read
= self
.r_rdy
& self
.r_en
323 # TODO: extract this pattern into lib.cdc.GrayCounter
324 produce_w_bin
= Signal(self
._ctr
_bits
)
325 produce_w_nxt
= Signal(self
._ctr
_bits
)
326 m
.d
.comb
+= produce_w_nxt
.eq(produce_w_bin
+ do_write
)
327 m
.d
[self
._w
_domain
] += produce_w_bin
.eq(produce_w_nxt
)
329 # Note: Both read-domain counters must be reset_less (see comments below)
330 consume_r_bin
= Signal(self
._ctr
_bits
, reset_less
=True)
331 consume_r_nxt
= Signal(self
._ctr
_bits
)
332 m
.d
.comb
+= consume_r_nxt
.eq(consume_r_bin
+ do_read
)
333 m
.d
[self
._r
_domain
] += consume_r_bin
.eq(consume_r_nxt
)
335 produce_w_gry
= Signal(self
._ctr
_bits
)
336 produce_r_gry
= Signal(self
._ctr
_bits
)
337 produce_enc
= m
.submodules
.produce_enc
= \
338 GrayEncoder(self
._ctr
_bits
)
339 produce_cdc
= m
.submodules
.produce_cdc
= \
340 FFSynchronizer(produce_w_gry
, produce_r_gry
, o_domain
=self
._r
_domain
)
341 m
.d
.comb
+= produce_enc
.i
.eq(produce_w_nxt
),
342 m
.d
[self
._w
_domain
] += produce_w_gry
.eq(produce_enc
.o
)
344 consume_r_gry
= Signal(self
._ctr
_bits
, reset_less
=True)
345 consume_w_gry
= Signal(self
._ctr
_bits
)
346 consume_enc
= m
.submodules
.consume_enc
= \
347 GrayEncoder(self
._ctr
_bits
)
348 consume_cdc
= m
.submodules
.consume_cdc
= \
349 FFSynchronizer(consume_r_gry
, consume_w_gry
, o_domain
=self
._w
_domain
)
350 m
.d
.comb
+= consume_enc
.i
.eq(consume_r_nxt
)
351 m
.d
[self
._r
_domain
] += consume_r_gry
.eq(consume_enc
.o
)
356 w_full
.eq((produce_w_gry
[-1] != consume_w_gry
[-1]) &
357 (produce_w_gry
[-2] != consume_w_gry
[-2]) &
358 (produce_w_gry
[:-2] == consume_w_gry
[:-2])),
359 r_empty
.eq(consume_r_gry
== produce_r_gry
),
362 storage
= Memory(width
=self
.width
, depth
=self
.depth
)
363 w_port
= m
.submodules
.w_port
= storage
.write_port(domain
=self
._w
_domain
)
364 r_port
= m
.submodules
.r_port
= storage
.read_port (domain
=self
._r
_domain
,
367 w_port
.addr
.eq(produce_w_bin
[:-1]),
368 w_port
.data
.eq(self
.w_data
),
369 w_port
.en
.eq(do_write
),
370 self
.w_rdy
.eq(~w_full
),
373 r_port
.addr
.eq(consume_r_nxt
[:-1]),
374 self
.r_data
.eq(r_port
.data
),
376 self
.r_rdy
.eq(~r_empty
),
379 # Reset handling to maintain FIFO and CDC invariants in the presence of a write-domain
381 # There is a CDC hazard associated with resetting an async FIFO - Gray code counters which
382 # are reset to 0 violate their Gray code invariant. One way to handle this is to ensure
383 # that both sides of the FIFO are asynchronously reset by the same signal. We adopt a
384 # slight variation on this approach - reset control rests entirely with the write domain.
385 # The write domain's reset signal is used to asynchronously reset the read domain's
386 # counters and force the FIFO to be empty when the write domain's reset is asserted.
387 # This requires the two read domain counters to be marked as "reset_less", as they are
388 # reset through another mechanism. See https://github.com/nmigen/nmigen/issues/181 for the
390 w_rst
= ResetSignal(domain
=self
._w
_domain
, allow_reset_less
=True)
393 # Async-set-sync-release synchronizer avoids CDC hazards
394 rst_cdc
= m
.submodules
.rst_cdc
= \
395 AsyncFFSynchronizer(w_rst
, r_rst
, domain
=self
._r
_domain
)
397 # Decode Gray code counter synchronized from write domain to overwrite binary
398 # counter in read domain.
399 rst_dec
= m
.submodules
.rst_dec
= \
400 GrayDecoder(self
._ctr
_bits
)
401 m
.d
.comb
+= rst_dec
.i
.eq(produce_r_gry
)
403 m
.d
.comb
+= r_empty
.eq(1)
404 m
.d
[self
._r
_domain
] += consume_r_gry
.eq(produce_r_gry
)
405 m
.d
[self
._r
_domain
] += consume_r_bin
.eq(rst_dec
.o
)
406 m
.d
[self
._r
_domain
] += self
.r_rst
.eq(1)
408 m
.d
[self
._r
_domain
] += self
.r_rst
.eq(0)
410 if platform
== "formal":
411 with m
.If(Initial()):
412 m
.d
.comb
+= Assume(produce_w_gry
== (produce_w_bin ^ produce_w_bin
[1:]))
413 m
.d
.comb
+= Assume(consume_r_gry
== (consume_r_bin ^ consume_r_bin
[1:]))
418 class AsyncFIFOBuffered(Elaboratable
, FIFOInterface
):
419 __doc__
= FIFOInterface
._doc
_template
.format(
421 Buffered asynchronous first in, first out queue.
423 Read and write interfaces are accessed from different clock domains, which can be set when
424 constructing the FIFO.
426 :class:`AsyncFIFOBuffered` only supports power of 2 plus one depths. Unless ``exact_depth``
427 is specified, the ``depth`` parameter is rounded up to the next power of 2 plus one.
428 (The output buffer acts as an additional queue element.)
430 This queue's interface is identical to :class:`AsyncFIFO`, but it has an additional register
431 on the output, improving timing in case of block RAM that has large clock-to-output delay.
433 In exchange, the latency between an entry being written to an empty queue and that entry
434 becoming available on the output is increased by one cycle compared to :class:`AsyncFIFO`.
446 r_data_valid
="Valid if ``r_rdy`` is asserted.",
449 Asserted while the FIFO is being reset by the write-domain reset (for at least one
450 read-domain clock cycle).
454 def __init__(self
, *, width
, depth
, r_domain
="read", w_domain
="write", exact_depth
=False):
457 depth_bits
= log2_int(max(0, depth
- 1), need_pow2
=exact_depth
)
458 depth
= (1 << depth_bits
) + 1
459 except ValueError as e
:
460 raise ValueError("AsyncFIFOBuffered only supports depths that are one higher "
461 "than powers of 2; requested exact depth {} is not"
462 .format(depth
)) from None
463 super().__init
__(width
=width
, depth
=depth
, fwft
=True)
465 self
.r_rst
= Signal()
466 self
._r
_domain
= r_domain
467 self
._w
_domain
= w_domain
469 def elaborate(self
, platform
):
478 m
.submodules
.unbuffered
= fifo
= AsyncFIFO(width
=self
.width
, depth
=self
.depth
- 1,
479 r_domain
=self
._r
_domain
, w_domain
=self
._w
_domain
)
482 fifo
.w_data
.eq(self
.w_data
),
483 self
.w_rdy
.eq(fifo
.w_rdy
),
484 fifo
.w_en
.eq(self
.w_en
),
487 with m
.If(self
.r_en | ~self
.r_rdy
):
488 m
.d
[self
._r
_domain
] += [
489 self
.r_data
.eq(fifo
.r_data
),
490 self
.r_rdy
.eq(fifo
.r_rdy
),
491 self
.r_rst
.eq(fifo
.r_rst
),