lib.fifo: handle depth=0, elaborating to a dummy FIFO with no logic.
This commit is contained in:
parent
bc53bbf564
commit
fd625619f1
|
@ -19,7 +19,7 @@ class FIFOInterface:
|
||||||
width : int
|
width : int
|
||||||
Bit width of data entries.
|
Bit width of data entries.
|
||||||
depth : int
|
depth : int
|
||||||
Depth of the queue.
|
Depth of the queue. If zero, the FIFO cannot be read from or written to.
|
||||||
{parameters}
|
{parameters}
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
|
@ -64,19 +64,19 @@ class FIFOInterface:
|
||||||
if not isinstance(width, int) or width < 0:
|
if not isinstance(width, int) or width < 0:
|
||||||
raise TypeError("FIFO width must be a non-negative integer, not '{!r}'"
|
raise TypeError("FIFO width must be a non-negative integer, not '{!r}'"
|
||||||
.format(width))
|
.format(width))
|
||||||
if not isinstance(depth, int) or depth <= 0:
|
if not isinstance(depth, int) or depth < 0:
|
||||||
raise TypeError("FIFO depth must be a positive integer, not '{!r}'"
|
raise TypeError("FIFO depth must be a non-negative integer, not '{!r}'"
|
||||||
.format(depth))
|
.format(depth))
|
||||||
self.width = width
|
self.width = width
|
||||||
self.depth = depth
|
self.depth = depth
|
||||||
self.fwft = fwft
|
self.fwft = fwft
|
||||||
|
|
||||||
self.w_data = Signal(width, reset_less=True)
|
self.w_data = Signal(width, reset_less=True)
|
||||||
self.w_rdy = Signal() # not full
|
self.w_rdy = Signal() # writable; not full
|
||||||
self.w_en = Signal()
|
self.w_en = Signal()
|
||||||
|
|
||||||
self.r_data = Signal(width, reset_less=True)
|
self.r_data = Signal(width, reset_less=True)
|
||||||
self.r_rdy = Signal() # not empty
|
self.r_rdy = Signal() # readable; not empty
|
||||||
self.r_en = Signal()
|
self.r_en = Signal()
|
||||||
|
|
||||||
# TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
|
# TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
|
||||||
|
@ -191,6 +191,13 @@ class SyncFIFO(Elaboratable, FIFOInterface):
|
||||||
|
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
m = Module()
|
m = Module()
|
||||||
|
if self.depth == 0:
|
||||||
|
m.d.comb += [
|
||||||
|
self.w_rdy.eq(0),
|
||||||
|
self.r_rdy.eq(0),
|
||||||
|
]
|
||||||
|
return m
|
||||||
|
|
||||||
m.d.comb += [
|
m.d.comb += [
|
||||||
self.w_rdy.eq(self.level != self.depth),
|
self.w_rdy.eq(self.level != self.depth),
|
||||||
self.r_rdy.eq(self.level != 0)
|
self.r_rdy.eq(self.level != 0)
|
||||||
|
@ -286,6 +293,12 @@ class SyncFIFOBuffered(Elaboratable, FIFOInterface):
|
||||||
|
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
m = Module()
|
m = Module()
|
||||||
|
if self.depth == 0:
|
||||||
|
m.d.comb += [
|
||||||
|
self.w_rdy.eq(0),
|
||||||
|
self.r_rdy.eq(0),
|
||||||
|
]
|
||||||
|
return m
|
||||||
|
|
||||||
# Effectively, this queue treats the output register of the non-FWFT inner queue as
|
# Effectively, this queue treats the output register of the non-FWFT inner queue as
|
||||||
# an additional storage element.
|
# an additional storage element.
|
||||||
|
@ -338,25 +351,35 @@ class AsyncFIFO(Elaboratable, FIFOInterface):
|
||||||
w_attributes="")
|
w_attributes="")
|
||||||
|
|
||||||
def __init__(self, *, width, depth, r_domain="read", w_domain="write", exact_depth=False):
|
def __init__(self, *, width, depth, r_domain="read", w_domain="write", exact_depth=False):
|
||||||
try:
|
if depth != 0:
|
||||||
depth_bits = log2_int(depth, need_pow2=exact_depth)
|
try:
|
||||||
except ValueError as e:
|
depth_bits = log2_int(depth, need_pow2=exact_depth)
|
||||||
raise ValueError("AsyncFIFO only supports depths that are powers of 2; requested "
|
depth = 1 << depth_bits
|
||||||
"exact depth {} is not"
|
except ValueError as e:
|
||||||
.format(depth)) from None
|
raise ValueError("AsyncFIFO only supports depths that are powers of 2; requested "
|
||||||
super().__init__(width=width, depth=1 << depth_bits, fwft=True)
|
"exact depth {} is not"
|
||||||
|
.format(depth)) from None
|
||||||
|
else:
|
||||||
|
depth_bits = 0
|
||||||
|
super().__init__(width=width, depth=depth, fwft=True)
|
||||||
|
|
||||||
self._r_domain = r_domain
|
self._r_domain = r_domain
|
||||||
self._w_domain = w_domain
|
self._w_domain = w_domain
|
||||||
self._ctr_bits = depth_bits + 1
|
self._ctr_bits = depth_bits + 1
|
||||||
|
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
if self.depth == 0:
|
||||||
|
m.d.comb += [
|
||||||
|
self.w_rdy.eq(0),
|
||||||
|
self.r_rdy.eq(0),
|
||||||
|
]
|
||||||
|
return m
|
||||||
|
|
||||||
# The design of this queue is the "style #2" from Clifford E. Cummings' paper "Simulation
|
# The design of this queue is the "style #2" from Clifford E. Cummings' paper "Simulation
|
||||||
# and Synthesis Techniques for Asynchronous FIFO Design":
|
# and Synthesis Techniques for Asynchronous FIFO Design":
|
||||||
# http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
|
# http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
|
||||||
|
|
||||||
m = Module()
|
|
||||||
|
|
||||||
do_write = self.w_rdy & self.w_en
|
do_write = self.w_rdy & self.w_en
|
||||||
do_read = self.r_rdy & self.r_en
|
do_read = self.r_rdy & self.r_en
|
||||||
|
|
||||||
|
@ -456,19 +479,28 @@ class AsyncFIFOBuffered(Elaboratable, FIFOInterface):
|
||||||
w_attributes="")
|
w_attributes="")
|
||||||
|
|
||||||
def __init__(self, *, width, depth, r_domain="read", w_domain="write", exact_depth=False):
|
def __init__(self, *, width, depth, r_domain="read", w_domain="write", exact_depth=False):
|
||||||
try:
|
if depth != 0:
|
||||||
depth_bits = log2_int(max(0, depth - 1), need_pow2=exact_depth)
|
try:
|
||||||
except ValueError as e:
|
depth_bits = log2_int(max(0, depth - 1), need_pow2=exact_depth)
|
||||||
raise ValueError("AsyncFIFOBuffered only supports depths that are one higher "
|
depth = (1 << depth_bits) + 1
|
||||||
"than powers of 2; requested exact depth {} is not"
|
except ValueError as e:
|
||||||
.format(depth)) from None
|
raise ValueError("AsyncFIFOBuffered only supports depths that are one higher "
|
||||||
super().__init__(width=width, depth=(1 << depth_bits) + 1, fwft=True)
|
"than powers of 2; requested exact depth {} is not"
|
||||||
|
.format(depth)) from None
|
||||||
|
super().__init__(width=width, depth=depth, fwft=True)
|
||||||
|
|
||||||
self._r_domain = r_domain
|
self._r_domain = r_domain
|
||||||
self._w_domain = w_domain
|
self._w_domain = w_domain
|
||||||
|
|
||||||
def elaborate(self, platform):
|
def elaborate(self, platform):
|
||||||
m = Module()
|
m = Module()
|
||||||
|
if self.depth == 0:
|
||||||
|
m.d.comb += [
|
||||||
|
self.w_rdy.eq(0),
|
||||||
|
self.r_rdy.eq(0),
|
||||||
|
]
|
||||||
|
return m
|
||||||
|
|
||||||
m.submodules.unbuffered = fifo = AsyncFIFO(width=self.width, depth=self.depth - 1,
|
m.submodules.unbuffered = fifo = AsyncFIFO(width=self.width, depth=self.depth - 1,
|
||||||
r_domain=self._r_domain, w_domain=self._w_domain)
|
r_domain=self._r_domain, w_domain=self._w_domain)
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,21 @@ class FIFOTestCase(FHDLTestCase):
|
||||||
msg="FIFO width must be a non-negative integer, not '-1'"):
|
msg="FIFO width must be a non-negative integer, not '-1'"):
|
||||||
FIFOInterface(width=-1, depth=8, fwft=True)
|
FIFOInterface(width=-1, depth=8, fwft=True)
|
||||||
with self.assertRaises(TypeError,
|
with self.assertRaises(TypeError,
|
||||||
msg="FIFO depth must be a positive integer, not '0'"):
|
msg="FIFO depth must be a non-negative integer, not '-1'"):
|
||||||
FIFOInterface(width=8, depth=0, fwft=True)
|
FIFOInterface(width=8, depth=-1, fwft=True)
|
||||||
|
|
||||||
|
def test_sync_depth(self):
|
||||||
|
self.assertEqual(SyncFIFO(width=8, depth=0).depth, 0)
|
||||||
|
self.assertEqual(SyncFIFO(width=8, depth=1).depth, 1)
|
||||||
|
self.assertEqual(SyncFIFO(width=8, depth=2).depth, 2)
|
||||||
|
|
||||||
|
def test_sync_buffered_depth(self):
|
||||||
|
self.assertEqual(SyncFIFOBuffered(width=8, depth=0).depth, 0)
|
||||||
|
self.assertEqual(SyncFIFOBuffered(width=8, depth=1).depth, 1)
|
||||||
|
self.assertEqual(SyncFIFOBuffered(width=8, depth=2).depth, 2)
|
||||||
|
|
||||||
def test_async_depth(self):
|
def test_async_depth(self):
|
||||||
|
self.assertEqual(AsyncFIFO(width=8, depth=0 ).depth, 0)
|
||||||
self.assertEqual(AsyncFIFO(width=8, depth=1 ).depth, 1)
|
self.assertEqual(AsyncFIFO(width=8, depth=1 ).depth, 1)
|
||||||
self.assertEqual(AsyncFIFO(width=8, depth=2 ).depth, 2)
|
self.assertEqual(AsyncFIFO(width=8, depth=2 ).depth, 2)
|
||||||
self.assertEqual(AsyncFIFO(width=8, depth=3 ).depth, 4)
|
self.assertEqual(AsyncFIFO(width=8, depth=3 ).depth, 4)
|
||||||
|
@ -30,6 +41,7 @@ class FIFOTestCase(FHDLTestCase):
|
||||||
AsyncFIFO(width=8, depth=15, exact_depth=True)
|
AsyncFIFO(width=8, depth=15, exact_depth=True)
|
||||||
|
|
||||||
def test_async_buffered_depth(self):
|
def test_async_buffered_depth(self):
|
||||||
|
self.assertEqual(AsyncFIFOBuffered(width=8, depth=0 ).depth, 0)
|
||||||
self.assertEqual(AsyncFIFOBuffered(width=8, depth=1 ).depth, 2)
|
self.assertEqual(AsyncFIFOBuffered(width=8, depth=1 ).depth, 2)
|
||||||
self.assertEqual(AsyncFIFOBuffered(width=8, depth=2 ).depth, 2)
|
self.assertEqual(AsyncFIFOBuffered(width=8, depth=2 ).depth, 2)
|
||||||
self.assertEqual(AsyncFIFOBuffered(width=8, depth=3 ).depth, 3)
|
self.assertEqual(AsyncFIFOBuffered(width=8, depth=3 ).depth, 3)
|
||||||
|
|
Loading…
Reference in a new issue