Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
linuswck opened this issue Nov 15, 2024 · 0 comments
Open

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

linuswck opened this issue Nov 15, 2024 · 0 comments

Comments

@linuswck
Copy link
Contributor

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)
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant