Skip to content

Commit f4190b6

Browse files
committed
Implement RFC 20: Remove non-FWFT FIFOs.
Fixes #875.
1 parent 9d4ffab commit f4190b6

File tree

3 files changed

+21
-83
lines changed

3 files changed

+21
-83
lines changed

amaranth/lib/fifo.py

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -55,31 +55,21 @@ class FIFOInterface:
5555
read at the output interface (``r_data``, ``r_rdy``, ``r_en``). The data entry written first
5656
to the input also appears first on the output.
5757
""",
58-
parameters="""
59-
fwft : bool
60-
First-word fallthrough. If set, when ``r_rdy`` rises, the first entry is already
61-
available, i.e. ``r_data`` is valid. Otherwise, after ``r_rdy`` rises, it is necessary
62-
to strobe ``r_en`` for ``r_data`` to become valid.
63-
""".strip(),
58+
parameters="",
6459
r_data_valid="The conditions in which ``r_data`` is valid depends on the type of the queue.",
6560
attributes="",
6661
w_attributes="",
6762
r_attributes="")
6863

69-
def __init__(self, *, width, depth, fwft=True):
64+
def __init__(self, *, width, depth):
7065
if not isinstance(width, int) or width < 0:
7166
raise TypeError("FIFO width must be a non-negative integer, not {!r}"
7267
.format(width))
7368
if not isinstance(depth, int) or depth < 0:
7469
raise TypeError("FIFO depth must be a non-negative integer, not {!r}"
7570
.format(depth))
76-
if not fwft:
77-
warnings.warn("support for FIFOs with `fwft=False` will be removed without a replacement; "
78-
"consider switching to `fwft=True` or copy the module into your project to continue using it",
79-
DeprecationWarning)
8071
self.width = width
8172
self.depth = depth
82-
self.fwft = fwft
8373

8474
self.w_data = Signal(width, reset_less=True)
8575
self.w_rdy = Signal() # writable; not full
@@ -107,29 +97,17 @@ class SyncFIFO(Elaboratable, FIFOInterface):
10797
Read and write interfaces are accessed from the same clock domain. If different clock domains
10898
are needed, use :class:`AsyncFIFO`.
10999
""".strip(),
110-
parameters="""
111-
fwft : bool
112-
First-word fallthrough. If set, when the queue is empty and an entry is written into it,
113-
that entry becomes available on the output on the same clock cycle. Otherwise, it is
114-
necessary to assert ``r_en`` for ``r_data`` to become valid.
115-
""".strip(),
116-
r_data_valid="For FWFT queues, valid if ``r_rdy`` is asserted. "
117-
"For non-FWFT queues, valid on the next cycle after ``r_rdy`` and ``r_en`` have been asserted.",
100+
parameters="",
101+
r_data_valid="Valid if ``r_rdy`` is asserted.",
118102
attributes="""
119103
level : Signal(range(depth + 1)), out
120104
Number of unread entries. This level is the same between read and write for synchronous FIFOs.
121105
""".strip(),
122106
r_attributes="",
123107
w_attributes="")
124108

125-
def __init__(self, *, width, depth, fwft=True):
126-
if not fwft:
127-
warnings.warn("support for FIFOs with `fwft=False` will be removed without a replacement; "
128-
"consider switching to `fwft=True` or copy the module into your project to continue using it",
129-
DeprecationWarning)
109+
def __init__(self, *, width, depth):
130110
super().__init__(width=width, depth=depth)
131-
# Fix up fwft after initialization to avoid the warning from FIFOInterface.
132-
self.fwft = fwft
133111

134112
self.level = Signal(range(depth + 1))
135113

@@ -154,8 +132,7 @@ def elaborate(self, platform):
154132

155133
storage = Memory(width=self.width, depth=self.depth)
156134
w_port = m.submodules.w_port = storage.write_port()
157-
r_port = m.submodules.r_port = storage.read_port(
158-
domain="comb" if self.fwft else "sync", transparent=self.fwft)
135+
r_port = m.submodules.r_port = storage.read_port(domain="comb")
159136
produce = Signal(range(self.depth))
160137
consume = Signal(range(self.depth))
161138

@@ -171,8 +148,6 @@ def elaborate(self, platform):
171148
r_port.addr.eq(consume),
172149
self.r_data.eq(r_port.data),
173150
]
174-
if not self.fwft:
175-
m.d.comb += r_port.en.eq(self.r_en)
176151
with m.If(do_read):
177152
m.d.sync += consume.eq(_incr(consume, self.depth))
178153

@@ -214,16 +189,13 @@ class SyncFIFOBuffered(Elaboratable, FIFOInterface):
214189
description="""
215190
Buffered synchronous first in, first out queue.
216191
217-
This queue's interface is identical to :class:`SyncFIFO` configured as ``fwft=True``, but it
192+
This queue's interface is identical to :class:`SyncFIFO`, but it
218193
does not use asynchronous memory reads, which are incompatible with FPGA block RAMs.
219194
220195
In exchange, the latency between an entry being written to an empty queue and that entry
221196
becoming available on the output is increased by one cycle compared to :class:`SyncFIFO`.
222197
""".strip(),
223-
parameters="""
224-
fwft : bool
225-
Always set.
226-
""".strip(),
198+
parameters="",
227199
attributes="""
228200
level : Signal(range(depth + 1)), out
229201
Number of unread entries. This level is the same between read and write for synchronous FIFOs.
@@ -369,8 +341,6 @@ class AsyncFIFO(Elaboratable, FIFOInterface):
369341
Read clock domain.
370342
w_domain : str
371343
Write clock domain.
372-
fwft : bool
373-
Always set.
374344
""".strip(),
375345
attributes="",
376346
r_data_valid="Valid if ``r_rdy`` is asserted.",
@@ -548,8 +518,6 @@ class AsyncFIFOBuffered(Elaboratable, FIFOInterface):
548518
Read clock domain.
549519
w_domain : str
550520
Write clock domain.
551-
fwft : bool
552-
Always set.
553521
""".strip(),
554522
attributes="",
555523
r_data_valid="Valid if ``r_rdy`` is asserted.",

docs/stdlib/fifo.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The ``amaranth.lib.fifo`` module provides building blocks for first-in, first-ou
1212

1313
The :class:`FIFOInterface` class can be used directly to substitute a FIFO in tests, or inherited from in a custom FIFO implementation.
1414

15-
.. autoclass:: SyncFIFO(*, width, depth, fwft=True)
15+
.. autoclass:: SyncFIFO(*, width, depth)
1616
.. autoclass:: SyncFIFOBuffered(*, width, depth)
1717
.. autoclass:: AsyncFIFO(*, width, depth, r_domain="read", w_domain="write", exact_depth=False)
1818
.. autoclass:: AsyncFIFOBuffered(*, width, depth, r_domain="read", w_domain="write", exact_depth=False)

tests/test_lib_fifo.py

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ class FIFOTestCase(FHDLTestCase):
1515
def test_depth_wrong(self):
1616
with self.assertRaisesRegex(TypeError,
1717
r"^FIFO width must be a non-negative integer, not -1$"):
18-
FIFOInterface(width=-1, depth=8, fwft=True)
18+
FIFOInterface(width=-1, depth=8)
1919
with self.assertRaisesRegex(TypeError,
2020
r"^FIFO depth must be a non-negative integer, not -1$"):
21-
FIFOInterface(width=8, depth=-1, fwft=True)
21+
FIFOInterface(width=8, depth=-1)
2222

2323
def test_sync_depth(self):
2424
self.assertEqual(SyncFIFO(width=8, depth=0).depth, 0)
@@ -68,8 +68,8 @@ class FIFOModel(Elaboratable, FIFOInterface):
6868
"""
6969
Non-synthesizable first-in first-out queue, implemented naively as a chain of registers.
7070
"""
71-
def __init__(self, *, width, depth, fwft, r_domain, w_domain):
72-
super().__init__(width=width, depth=depth, fwft=fwft)
71+
def __init__(self, *, width, depth, r_domain, w_domain):
72+
super().__init__(width=width, depth=depth)
7373

7474
self.r_domain = r_domain
7575
self.w_domain = w_domain
@@ -90,11 +90,8 @@ def elaborate(self, platform):
9090

9191
m.d.comb += self.r_rdy.eq(self.level > 0)
9292
m.d.comb += r_port.addr.eq((consume + 1) % self.depth)
93-
if self.fwft:
94-
m.d.comb += self.r_data.eq(r_port.data)
93+
m.d.comb += self.r_data.eq(r_port.data)
9594
with m.If(self.r_en & self.r_rdy):
96-
if not self.fwft:
97-
m.d[self.r_domain] += self.r_data.eq(r_port.data)
9895
m.d[self.r_domain] += consume.eq(r_port.addr)
9996

10097
m.d.comb += self.w_rdy.eq(self.level < self.depth)
@@ -136,7 +133,7 @@ def __init__(self, fifo, r_domain, w_domain):
136133
def elaborate(self, platform):
137134
m = Module()
138135
m.submodules.dut = dut = self.fifo
139-
m.submodules.gold = gold = FIFOModel(width=dut.width, depth=dut.depth, fwft=dut.fwft,
136+
m.submodules.gold = gold = FIFOModel(width=dut.width, depth=dut.depth,
140137
r_domain=self.r_domain, w_domain=self.w_domain)
141138

142139
m.d.comb += [
@@ -150,19 +147,7 @@ def elaborate(self, platform):
150147
m.d.comb += Assert(dut.r_level == gold.r_level)
151148
m.d.comb += Assert(dut.w_level == gold.w_level)
152149

153-
if dut.fwft:
154-
m.d.comb += Assert(dut.r_rdy
155-
.implies(dut.r_data == gold.r_data))
156-
else:
157-
# past_dut_r_rdy = Past(dut.r_rdy, domain=self.r_domain)
158-
past_dut_r_rdy = Signal.like(dut.r_rdy, attrs={"amaranth.sample_reg": True})
159-
m.d[self.r_domain] += past_dut_r_rdy.eq(dut.r_rdy)
160-
161-
# past_dut_r_en = Past(dut.r_en, domain=self.r_domain)
162-
past_dut_r_en = Signal.like(dut.r_en, attrs={"amaranth.sample_reg": True})
163-
m.d[self.r_domain] += past_dut_r_en.eq(dut.r_en)
164-
165-
m.d.comb += Assert((past_dut_r_rdy & past_dut_r_en).implies(dut.r_data == gold.r_data))
150+
m.d.comb += Assert(dut.r_rdy.implies(dut.r_data == gold.r_data))
166151

167152
return m
168153

@@ -219,12 +204,7 @@ def elaborate(self, platform):
219204
read_2 = Signal(fifo.width)
220205
with m.State("READ"):
221206
m.d.comb += fifo.r_en.eq(1)
222-
if fifo.fwft:
223-
r_rdy = fifo.r_rdy
224-
else: # r_rdy = Past(fifo.r_rdy, domain=self.r_domain)
225-
r_rdy = Signal.like(fifo.r_rdy, attrs={"amaranth.sample_reg": True})
226-
m.d[self.r_domain] += r_rdy.eq(fifo.r_rdy)
227-
with m.If(r_rdy):
207+
with m.If(fifo.r_rdy):
228208
m.d.sync += [
229209
read_1.eq(read_2),
230210
read_2.eq(fifo.r_data),
@@ -271,21 +251,11 @@ def check_sync_fifo(self, fifo):
271251
bound=fifo.depth * 2 + 1),
272252
mode="hybrid", depth=fifo.depth * 2 + 1)
273253

274-
def test_sync_fwft_pot(self):
275-
self.check_sync_fifo(SyncFIFO(width=8, depth=4, fwft=True))
276-
277-
def test_sync_fwft_npot(self):
278-
self.check_sync_fifo(SyncFIFO(width=8, depth=5, fwft=True))
279-
280-
def test_sync_not_fwft_pot(self):
281-
with warnings.catch_warnings():
282-
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
283-
self.check_sync_fifo(SyncFIFO(width=8, depth=4, fwft=False))
254+
def test_sync_pot(self):
255+
self.check_sync_fifo(SyncFIFO(width=8, depth=4))
284256

285-
def test_sync_not_fwft_npot(self):
286-
with warnings.catch_warnings():
287-
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
288-
self.check_sync_fifo(SyncFIFO(width=8, depth=5, fwft=False))
257+
def test_sync_npot(self):
258+
self.check_sync_fifo(SyncFIFO(width=8, depth=5))
289259

290260
def test_sync_buffered_pot(self):
291261
self.check_sync_fifo(SyncFIFOBuffered(width=8, depth=4))

0 commit comments

Comments
 (0)