Skip to content

Commit

Permalink
lib.fifo: fix reset handling of asynchronous FIFOs.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean-François Nguyen committed Feb 9, 2021
1 parent f7c2b94 commit 0dcf329
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 8 deletions.
24 changes: 16 additions & 8 deletions nmigen/lib/fifo.py
Expand Up @@ -355,7 +355,7 @@ def elaborate(self, platform):
consume_enc = m.submodules.consume_enc = \
GrayEncoder(self._ctr_bits)
consume_cdc = m.submodules.consume_cdc = \
FFSynchronizer(consume_r_gry, consume_w_gry, o_domain=self._w_domain)
FFSynchronizer(consume_r_gry, consume_w_gry, o_domain=self._w_domain, reset_less=False)
m.d.comb += consume_enc.i.eq(consume_r_nxt)
m.d[self._r_domain] += consume_r_gry.eq(consume_enc.o)

Expand Down Expand Up @@ -414,9 +414,10 @@ def elaborate(self, platform):
w_rst = ResetSignal(domain=self._w_domain, allow_reset_less=True)
r_rst = Signal()

# Async-set-sync-release synchronizer avoids CDC hazards
# Async-set-sync-release synchronizer avoids CDC hazards. In the read domain, 3 clock cycles
# are needed to propagate the reset value of produce_w_gry to consume_r_bin.
rst_cdc = m.submodules.rst_cdc = \
AsyncFFSynchronizer(w_rst, r_rst, o_domain=self._r_domain)
AsyncFFSynchronizer(w_rst, r_rst, o_domain=self._r_domain, stages=3)

# Decode Gray code counter synchronized from write domain to overwrite binary
# counter in read domain.
Expand All @@ -425,7 +426,7 @@ def elaborate(self, platform):
m.d.comb += rst_dec.i.eq(produce_r_gry)
with m.If(r_rst):
m.d.comb += r_empty.eq(1)
m.d[self._r_domain] += consume_r_gry.eq(produce_r_gry)
m.d[self._r_domain] += consume_r_gry.eq(produce_r_gry.reset)
m.d[self._r_domain] += consume_r_bin.eq(rst_dec.o)
m.d[self._r_domain] += self.r_rst.eq(1)
with m.Else():
Expand All @@ -435,6 +436,9 @@ def elaborate(self, platform):
with m.If(Initial()):
m.d.comb += Assume(produce_w_gry == (produce_w_bin ^ produce_w_bin[1:]))
m.d.comb += Assume(consume_r_gry == (consume_r_bin ^ consume_r_bin[1:]))
for i in range(rst_cdc._stages):
with m.If(Past(Initial(), i)):
m.d.comb += Assume(r_rst.implies(w_rst))

return m

Expand Down Expand Up @@ -509,21 +513,25 @@ def elaborate(self, platform):
]

r_consume_buffered = Signal()
m.d.comb += r_consume_buffered.eq((self.r_rdy - self.r_en) & self.r_rdy)
m.d.comb += r_consume_buffered.eq((self.r_rdy - self.r_en) & self.r_rdy & ~fifo.r_rst)
m.d[self._r_domain] += self.r_level.eq(fifo.r_level + r_consume_buffered)

w_consume_buffered = Signal()
m.submodules.consume_buffered_cdc = FFSynchronizer(r_consume_buffered, w_consume_buffered, o_domain=self._w_domain, stages=4)
m.submodules.consume_buffered_cdc = FFSynchronizer(r_consume_buffered, w_consume_buffered,
o_domain=self._w_domain, stages=4, reset_less=False)
m.d.comb += self.w_level.eq(fifo.w_level + w_consume_buffered)

with m.If(self.r_en | ~self.r_rdy):
with m.If(fifo.r_rst):
m.d[self._r_domain] += self.r_rdy.eq(0)
with m.Elif(self.r_en | ~self.r_rdy):
m.d[self._r_domain] += [
self.r_data.eq(fifo.r_data),
self.r_rdy.eq(fifo.r_rdy),
self.r_rst.eq(fifo.r_rst),
]
m.d.comb += [
fifo.r_en.eq(1)
]

m.d[self._r_domain] += self.r_rst.eq(fifo.r_rst)

return m
76 changes: 76 additions & 0 deletions tests/test_lib_fifo.py
Expand Up @@ -357,3 +357,79 @@ def test_async_buffered_fifo_level_full(self):
def test_async_buffered_fifo_level_empty(self):
fifo = AsyncFIFOBuffered(width=32, depth=9, r_domain="read", w_domain="write")
self.check_async_fifo_level(fifo, fill_in=0, expected_level=0, read=True)

def check_async_fifo_reset(self, fifo, fill_in, r_period, w_period):
m = Module()
m.submodules.fifo = fifo
write_rst = Signal()
m.d.comb += ResetSignal("write").eq(write_rst)
read_por_done = Signal()

def write_process():
while not (yield read_por_done):
yield

yield fifo.w_en.eq(1)
for _ in range(fill_in):
yield
yield fifo.w_en.eq(0)
yield

while not (yield fifo.r_rdy):
yield

yield write_rst.eq(1)
yield
yield write_rst.eq(0)
yield
yield Passive()
while True:
self.assertEqual((yield fifo.w_level), 0)
yield

def read_process():
while not (yield fifo.r_rst):
yield
while (yield fifo.r_rst):
yield
yield read_por_done.eq(1)

while not (yield fifo.r_rst):
yield
while (yield fifo.r_rst):
yield
for _ in range(10):
self.assertEqual((yield fifo.r_level), 0)
yield

simulator = Simulator(m)
simulator.add_clock(w_period, domain="write")
simulator.add_clock(r_period, domain="read")
simulator.add_sync_process(write_process, domain="write")
simulator.add_sync_process(read_process, domain="read")
with simulator.write_vcd("test.vcd"):
simulator.run()

def test_async_fifo_reset_r10w10(self):
fifo = AsyncFIFO(width=1, depth=8, r_domain="read", w_domain="write")
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=10e-9)

def test_async_fifo_reset_r10w27(self):
fifo = AsyncFIFO(width=1, depth=8, r_domain="read", w_domain="write")
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=27e-9)

def test_async_fifo_reset_r27w10(self):
fifo = AsyncFIFO(width=1, depth=8, r_domain="read", w_domain="write")
self.check_async_fifo_reset(fifo, fill_in=4, r_period=27e-9, w_period=10e-9)

def test_async_buffered_fifo_reset_r10w10(self):
fifo = AsyncFIFOBuffered(width=1, depth=8, r_domain="read", w_domain="write")
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=10e-9)

def test_async_buffered_fifo_reset_r10w27(self):
fifo = AsyncFIFOBuffered(width=1, depth=8, r_domain="read", w_domain="write")
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=27e-9)

def test_async_buffered_fifo_reset_r27w10(self):
fifo = AsyncFIFOBuffered(width=1, depth=8, r_domain="read", w_domain="write")
self.check_async_fifo_reset(fifo, fill_in=4, r_period=27e-9, w_period=10e-9)

0 comments on commit 0dcf329

Please sign in to comment.