Skip to content

Commit 4027317

Browse files
author
whitequark
committed
lib.fifo: register GrayEncoder output before CDC.
Without this register, static hazards in the encoder could cause multiple encoder output bits to toggle, which would be incorrectly sampled by the 2FF synchronizer. Reported by @Wren6991.
1 parent e93bf4b commit 4027317

File tree

1 file changed

+25
-14
lines changed

1 file changed

+25
-14
lines changed

nmigen/lib/fifo.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -297,29 +297,37 @@ def elaborate(self, platform):
297297

298298
m = Module()
299299

300+
do_write = self.writable & self.we
301+
do_read = self.readable & self.re
302+
303+
# TODO: extract this pattern into lib.cdc.GrayCounter
300304
produce_w_bin = Signal(self._ctr_bits)
305+
produce_w_nxt = Signal(self._ctr_bits)
306+
m.d.comb += produce_w_nxt.eq(produce_w_bin + do_write)
307+
m.d.write += produce_w_bin.eq(produce_w_nxt)
308+
309+
consume_r_bin = Signal(self._ctr_bits)
310+
consume_r_nxt = Signal(self._ctr_bits)
311+
m.d.comb += consume_r_nxt.eq(consume_r_bin + do_read)
312+
m.d.read += consume_r_bin.eq(consume_r_nxt)
313+
301314
produce_w_gry = Signal(self._ctr_bits)
302315
produce_r_gry = Signal(self._ctr_bits)
303316
produce_enc = m.submodules.produce_enc = \
304317
GrayEncoder(self._ctr_bits)
305318
produce_cdc = m.submodules.produce_cdc = \
306319
MultiReg(produce_w_gry, produce_r_gry, odomain="read")
307-
m.d.comb += [
308-
produce_enc.i.eq(produce_w_bin),
309-
produce_w_gry.eq(produce_enc.o),
310-
]
320+
m.d.comb += produce_enc.i.eq(produce_w_nxt),
321+
m.d.write += produce_w_gry.eq(produce_enc.o)
311322

312-
consume_r_bin = Signal(self._ctr_bits)
313323
consume_r_gry = Signal(self._ctr_bits)
314324
consume_w_gry = Signal(self._ctr_bits)
315325
consume_enc = m.submodules.consume_enc = \
316326
GrayEncoder(self._ctr_bits)
317327
consume_cdc = m.submodules.consume_cdc = \
318328
MultiReg(consume_r_gry, consume_w_gry, odomain="write")
319-
m.d.comb += [
320-
consume_enc.i.eq(consume_r_bin),
321-
consume_r_gry.eq(consume_enc.o),
322-
]
329+
m.d.comb += consume_enc.i.eq(consume_r_nxt)
330+
m.d.read += consume_r_gry.eq(consume_enc.o)
323331

324332
m.d.comb += [
325333
self.writable.eq(
@@ -329,11 +337,6 @@ def elaborate(self, platform):
329337
self.readable.eq(consume_r_gry != produce_r_gry)
330338
]
331339

332-
do_write = self.writable & self.we
333-
do_read = self.readable & self.re
334-
m.d.write += produce_w_bin.eq(produce_w_bin + do_write)
335-
m.d.read += consume_r_bin.eq(consume_r_bin + do_read)
336-
337340
storage = Memory(self.width, self.depth)
338341
wrport = m.submodules.wrport = storage.write_port(domain="write")
339342
rdport = m.submodules.rdport = storage.read_port (domain="read")
@@ -347,6 +350,14 @@ def elaborate(self, platform):
347350
self.dout.eq(rdport.data),
348351
]
349352

353+
if platform == "formal":
354+
# TODO: move this logic elsewhere
355+
initstate = Signal()
356+
m.submodules += Instance("$initstate", o_Y=initstate)
357+
with m.If(initstate):
358+
m.d.comb += Assume(produce_w_gry == (produce_w_bin ^ produce_w_bin[1:]))
359+
m.d.comb += Assume(consume_r_gry == (consume_r_bin ^ consume_r_bin[1:]))
360+
350361
return m
351362

352363

0 commit comments

Comments
 (0)