lib.fifo: handle depth=0, elaborating to a dummy FIFO with no logic.
authorwhitequark <whitequark@whitequark.org>
Mon, 23 Sep 2019 12:27:59 +0000 (12:27 +0000)
committerwhitequark <whitequark@whitequark.org>
Mon, 23 Sep 2019 12:27:59 +0000 (12:27 +0000)
nmigen/lib/fifo.py
nmigen/test/test_lib_fifo.py

index 60f8a3b98b35d46ff5efe3ed22cc12b92e527869..4d50f34d0bbf5368815c132762f344816e4d0d09 100644 (file)
@@ -19,7 +19,7 @@ class FIFOInterface:
     width : int
         Bit width of data entries.
     depth : int
-        Depth of the queue.
+        Depth of the queue. If zero, the FIFO cannot be read from or written to.
     {parameters}
 
     Attributes
@@ -64,19 +64,19 @@ class FIFOInterface:
         if not isinstance(width, int) or width < 0:
             raise TypeError("FIFO width must be a non-negative integer, not '{!r}'"
                             .format(width))
-        if not isinstance(depth, int) or depth <= 0:
-            raise TypeError("FIFO depth must be a positive integer, not '{!r}'"
+        if not isinstance(depth, int) or depth < 0:
+            raise TypeError("FIFO depth must be a non-negative integer, not '{!r}'"
                             .format(depth))
         self.width = width
         self.depth = depth
         self.fwft  = fwft
 
         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.r_data = Signal(width, reset_less=True)
-        self.r_rdy  = Signal() # not empty
+        self.r_rdy  = Signal() # readable; not empty
         self.r_en   = Signal()
 
     # 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):
         m = Module()
+        if self.depth == 0:
+            m.d.comb += [
+                self.w_rdy.eq(0),
+                self.r_rdy.eq(0),
+            ]
+            return m
+
         m.d.comb += [
             self.w_rdy.eq(self.level != self.depth),
             self.r_rdy.eq(self.level != 0)
@@ -286,6 +293,12 @@ class SyncFIFOBuffered(Elaboratable, FIFOInterface):
 
     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
 
         # Effectively, this queue treats the output register of the non-FWFT inner queue as
         # an additional storage element.
@@ -338,25 +351,35 @@ class AsyncFIFO(Elaboratable, FIFOInterface):
     w_attributes="")
 
     def __init__(self, *, width, depth, r_domain="read", w_domain="write", exact_depth=False):
-        try:
-            depth_bits = log2_int(depth, need_pow2=exact_depth)
-        except ValueError as e:
-            raise ValueError("AsyncFIFO only supports depths that are powers of 2; requested "
-                             "exact depth {} is not"
-                             .format(depth)) from None
-        super().__init__(width=width, depth=1 << depth_bits, fwft=True)
+        if depth != 0:
+            try:
+                depth_bits = log2_int(depth, need_pow2=exact_depth)
+                depth = 1 << depth_bits
+            except ValueError as e:
+                raise ValueError("AsyncFIFO only supports depths that are powers of 2; requested "
+                                 "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._w_domain = w_domain
         self._ctr_bits = depth_bits + 1
 
     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
         # and Synthesis Techniques for Asynchronous FIFO Design":
         # http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
 
-        m = Module()
-
         do_write = self.w_rdy & self.w_en
         do_read  = self.r_rdy & self.r_en
 
@@ -456,19 +479,28 @@ class AsyncFIFOBuffered(Elaboratable, FIFOInterface):
     w_attributes="")
 
     def __init__(self, *, width, depth, r_domain="read", w_domain="write", exact_depth=False):
-        try:
-            depth_bits = log2_int(max(0, depth - 1), need_pow2=exact_depth)
-        except ValueError as e:
-            raise ValueError("AsyncFIFOBuffered only supports depths that are one higher "
-                             "than powers of 2; requested exact depth {} is not"
-                             .format(depth)) from None
-        super().__init__(width=width, depth=(1 << depth_bits) + 1, fwft=True)
+        if depth != 0:
+            try:
+                depth_bits = log2_int(max(0, depth - 1), need_pow2=exact_depth)
+                depth = (1 << depth_bits) + 1
+            except ValueError as e:
+                raise ValueError("AsyncFIFOBuffered only supports depths that are one higher "
+                                 "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._w_domain = w_domain
 
     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
+
         m.submodules.unbuffered = fifo = AsyncFIFO(width=self.width, depth=self.depth - 1,
             r_domain=self._r_domain, w_domain=self._w_domain)
 
index d1d465f97f5c9f02340cd04c456bacb387d0c7b0..d1167526ca961907bf9245cab30ae3e22d36e132 100644 (file)
@@ -11,10 +11,21 @@ class FIFOTestCase(FHDLTestCase):
                 msg="FIFO width must be a non-negative integer, not '-1'"):
             FIFOInterface(width=-1, depth=8, fwft=True)
         with self.assertRaises(TypeError,
-                msg="FIFO depth must be a positive integer, not '0'"):
-            FIFOInterface(width=8, depth=0, fwft=True)
+                msg="FIFO depth must be a non-negative integer, not '-1'"):
+            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):
+        self.assertEqual(AsyncFIFO(width=8, depth=0 ).depth, 0)
         self.assertEqual(AsyncFIFO(width=8, depth=1 ).depth, 1)
         self.assertEqual(AsyncFIFO(width=8, depth=2 ).depth, 2)
         self.assertEqual(AsyncFIFO(width=8, depth=3 ).depth, 4)
@@ -30,6 +41,7 @@ class FIFOTestCase(FHDLTestCase):
             AsyncFIFO(width=8, depth=15, exact_depth=True)
 
     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=2 ).depth, 2)
         self.assertEqual(AsyncFIFOBuffered(width=8, depth=3 ).depth, 3)