Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.

Async FIFO Output is Broken when FIFO Depth is Set to 2 #296

@linuswck

Description

@linuswck

Description

I found this when I was fixing cdc issue for fast servo port for linien. I used an async fifo for cdc purpose.

Here is where I made the change in fast-servo.
https://git.m-labs.hk/M-Labs/nix-servo/commit/87059eef2b7316dbdde7867c134bd843a66f8d85

self.cdc_fifo.sink.stb & self.cdc_fifo.source.ack are always high during operation.
https://git.m-labs.hk/M-Labs/nix-servo/src/commit/77643909efdad21f518d9192231188325f7296b2/fast-servo/linien-gateware/cores/dac.py#L52-L55

I connected the fast servo dac output to an oscilloscope and I observed that

  • Async FIFO Depth = 2: DAC Modulating waveform was periodically broken.
  • Async FIFO Depth = 4: DAC Modulating waveform looked good.

Related AsyncFIFO Migen Module

migen/migen/genlib/fifo.py

Lines 177 to 234 in c19ae9f

class AsyncFIFO(Module, _FIFOInterface):
"""Asynchronous FIFO (first in, first out)
Read and write interfaces are accessed from different clock domains,
named `read` and `write`. Use `ClockDomainsRenamer` to rename to
other names.
{interface}
"""
__doc__ = __doc__.format(interface=_FIFOInterface.__doc__)
def __init__(self, width, depth):
_FIFOInterface.__init__(self, width, depth)
###
depth_bits = log2_int(depth, True)
produce = ClockDomainsRenamer("write")(GrayCounter(depth_bits+1))
consume = ClockDomainsRenamer("read")(GrayCounter(depth_bits+1))
self.submodules += produce, consume
self.comb += [
produce.ce.eq(self.writable & self.we),
consume.ce.eq(self.readable & self.re)
]
produce_rdomain = Signal(depth_bits+1)
produce.q.attr.add("no_retiming")
self.specials += MultiReg(produce.q, produce_rdomain, "read")
consume_wdomain = Signal(depth_bits+1)
consume.q.attr.add("no_retiming")
self.specials += MultiReg(consume.q, consume_wdomain, "write")
if depth_bits == 1:
self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1])
| (produce.q[-2] == consume_wdomain[-2]))
else:
self.comb += [
self.writable.eq((produce.q[-1] == consume_wdomain[-1])
| (produce.q[-2] == consume_wdomain[-2])
| (produce.q[:-2] != consume_wdomain[:-2]))
]
self.comb += self.readable.eq(consume.q != produce_rdomain)
storage = Memory(self.width, depth)
self.specials += storage
wrport = storage.get_port(write_capable=True, clock_domain="write")
self.specials += wrport
self.comb += [
wrport.adr.eq(produce.q_binary[:-1]),
wrport.dat_w.eq(self.din),
wrport.we.eq(produce.ce)
]
rdport = storage.get_port(clock_domain="read")
self.specials += rdport
self.comb += [
rdport.adr.eq(consume.q_next_binary[:-1]),
self.dout.eq(rdport.dat_r)
]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions