Skip to content

Commit 0dcf329

Browse files
author
Jean-François Nguyen
committed
lib.fifo: fix reset handling of asynchronous FIFOs.
1 parent f7c2b94 commit 0dcf329

File tree

2 files changed

+92
-8
lines changed

2 files changed

+92
-8
lines changed

nmigen/lib/fifo.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ def elaborate(self, platform):
355355
consume_enc = m.submodules.consume_enc = \
356356
GrayEncoder(self._ctr_bits)
357357
consume_cdc = m.submodules.consume_cdc = \
358-
FFSynchronizer(consume_r_gry, consume_w_gry, o_domain=self._w_domain)
358+
FFSynchronizer(consume_r_gry, consume_w_gry, o_domain=self._w_domain, reset_less=False)
359359
m.d.comb += consume_enc.i.eq(consume_r_nxt)
360360
m.d[self._r_domain] += consume_r_gry.eq(consume_enc.o)
361361

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

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

421422
# Decode Gray code counter synchronized from write domain to overwrite binary
422423
# counter in read domain.
@@ -425,7 +426,7 @@ def elaborate(self, platform):
425426
m.d.comb += rst_dec.i.eq(produce_r_gry)
426427
with m.If(r_rst):
427428
m.d.comb += r_empty.eq(1)
428-
m.d[self._r_domain] += consume_r_gry.eq(produce_r_gry)
429+
m.d[self._r_domain] += consume_r_gry.eq(produce_r_gry.reset)
429430
m.d[self._r_domain] += consume_r_bin.eq(rst_dec.o)
430431
m.d[self._r_domain] += self.r_rst.eq(1)
431432
with m.Else():
@@ -435,6 +436,9 @@ def elaborate(self, platform):
435436
with m.If(Initial()):
436437
m.d.comb += Assume(produce_w_gry == (produce_w_bin ^ produce_w_bin[1:]))
437438
m.d.comb += Assume(consume_r_gry == (consume_r_bin ^ consume_r_bin[1:]))
439+
for i in range(rst_cdc._stages):
440+
with m.If(Past(Initial(), i)):
441+
m.d.comb += Assume(r_rst.implies(w_rst))
438442

439443
return m
440444

@@ -509,21 +513,25 @@ def elaborate(self, platform):
509513
]
510514

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

515519
w_consume_buffered = Signal()
516-
m.submodules.consume_buffered_cdc = FFSynchronizer(r_consume_buffered, w_consume_buffered, o_domain=self._w_domain, stages=4)
520+
m.submodules.consume_buffered_cdc = FFSynchronizer(r_consume_buffered, w_consume_buffered,
521+
o_domain=self._w_domain, stages=4, reset_less=False)
517522
m.d.comb += self.w_level.eq(fifo.w_level + w_consume_buffered)
518523

519-
with m.If(self.r_en | ~self.r_rdy):
524+
with m.If(fifo.r_rst):
525+
m.d[self._r_domain] += self.r_rdy.eq(0)
526+
with m.Elif(self.r_en | ~self.r_rdy):
520527
m.d[self._r_domain] += [
521528
self.r_data.eq(fifo.r_data),
522529
self.r_rdy.eq(fifo.r_rdy),
523-
self.r_rst.eq(fifo.r_rst),
524530
]
525531
m.d.comb += [
526532
fifo.r_en.eq(1)
527533
]
528534

535+
m.d[self._r_domain] += self.r_rst.eq(fifo.r_rst)
536+
529537
return m

tests/test_lib_fifo.py

+76
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,79 @@ def test_async_buffered_fifo_level_full(self):
357357
def test_async_buffered_fifo_level_empty(self):
358358
fifo = AsyncFIFOBuffered(width=32, depth=9, r_domain="read", w_domain="write")
359359
self.check_async_fifo_level(fifo, fill_in=0, expected_level=0, read=True)
360+
361+
def check_async_fifo_reset(self, fifo, fill_in, r_period, w_period):
362+
m = Module()
363+
m.submodules.fifo = fifo
364+
write_rst = Signal()
365+
m.d.comb += ResetSignal("write").eq(write_rst)
366+
read_por_done = Signal()
367+
368+
def write_process():
369+
while not (yield read_por_done):
370+
yield
371+
372+
yield fifo.w_en.eq(1)
373+
for _ in range(fill_in):
374+
yield
375+
yield fifo.w_en.eq(0)
376+
yield
377+
378+
while not (yield fifo.r_rdy):
379+
yield
380+
381+
yield write_rst.eq(1)
382+
yield
383+
yield write_rst.eq(0)
384+
yield
385+
yield Passive()
386+
while True:
387+
self.assertEqual((yield fifo.w_level), 0)
388+
yield
389+
390+
def read_process():
391+
while not (yield fifo.r_rst):
392+
yield
393+
while (yield fifo.r_rst):
394+
yield
395+
yield read_por_done.eq(1)
396+
397+
while not (yield fifo.r_rst):
398+
yield
399+
while (yield fifo.r_rst):
400+
yield
401+
for _ in range(10):
402+
self.assertEqual((yield fifo.r_level), 0)
403+
yield
404+
405+
simulator = Simulator(m)
406+
simulator.add_clock(w_period, domain="write")
407+
simulator.add_clock(r_period, domain="read")
408+
simulator.add_sync_process(write_process, domain="write")
409+
simulator.add_sync_process(read_process, domain="read")
410+
with simulator.write_vcd("test.vcd"):
411+
simulator.run()
412+
413+
def test_async_fifo_reset_r10w10(self):
414+
fifo = AsyncFIFO(width=1, depth=8, r_domain="read", w_domain="write")
415+
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=10e-9)
416+
417+
def test_async_fifo_reset_r10w27(self):
418+
fifo = AsyncFIFO(width=1, depth=8, r_domain="read", w_domain="write")
419+
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=27e-9)
420+
421+
def test_async_fifo_reset_r27w10(self):
422+
fifo = AsyncFIFO(width=1, depth=8, r_domain="read", w_domain="write")
423+
self.check_async_fifo_reset(fifo, fill_in=4, r_period=27e-9, w_period=10e-9)
424+
425+
def test_async_buffered_fifo_reset_r10w10(self):
426+
fifo = AsyncFIFOBuffered(width=1, depth=8, r_domain="read", w_domain="write")
427+
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=10e-9)
428+
429+
def test_async_buffered_fifo_reset_r10w27(self):
430+
fifo = AsyncFIFOBuffered(width=1, depth=8, r_domain="read", w_domain="write")
431+
self.check_async_fifo_reset(fifo, fill_in=4, r_period=10e-9, w_period=27e-9)
432+
433+
def test_async_buffered_fifo_reset_r27w10(self):
434+
fifo = AsyncFIFOBuffered(width=1, depth=8, r_domain="read", w_domain="write")
435+
self.check_async_fifo_reset(fifo, fill_in=4, r_period=27e-9, w_period=10e-9)

0 commit comments

Comments
 (0)