update comments
[ieee754fpu.git] / src / add / queue.py
index bf40dc27fd181e8a80ff15aa89318e089a8d5194..0038953d6ea8b3d33fd21273219f4922a725101b 100644 (file)
@@ -23,7 +23,7 @@
 # TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 # MODIFICATIONS.
 
-from nmigen import Module, Signal, Memory, Mux
+from nmigen import Module, Signal, Memory, Mux, Elaboratable
 from nmigen.tools import bits_for
 from nmigen.cli import main
 from nmigen.lib.fifo import FIFOInterface
@@ -31,17 +31,23 @@ from nmigen.lib.fifo import FIFOInterface
 # translated from https://github.com/freechipsproject/chisel3/blob/a4a29e29c3f1eed18f851dcf10bdc845571dfcb6/src/main/scala/chisel3/util/Decoupled.scala#L185   # noqa
 
 
-class Queue(FIFOInterface):
+class Queue(FIFOInterface, Elaboratable):
     def __init__(self, width, depth, fwft=True, pipe=False):
         """ Queue (FIFO) with pipe mode and first-write fall-through capability
 
-            * width: width of Queue data in/out
-            * depth: queue depth.  NOTE: may be set to 0 (this is ok)
-            * fwft : first-write, fall-through mode (Chisel Queue "flow" mode)
-            * pipe : pipe mode.  NOTE: this mode can cause unanticipated
-                     problems.  when read is enabled, so is writeable.
-                     therefore if read is enabled, the data ABSOLUTELY MUST
-                     be read.
+            * :width: width of Queue data in/out
+            * :depth: queue depth.  NOTE: may be set to 0 (this is ok)
+            * :fwft : first-write, fall-through mode (Chisel Queue "flow" mode)
+            * :pipe : pipe mode.  NOTE: this mode can cause unanticipated
+                      problems.  when read is enabled, so is writeable.
+                      therefore if read is enabled, the data ABSOLUTELY MUST
+                      be read.
+
+            fwft mode = True basically means that the data may be transferred
+            combinatorially from input to output.
+
+            Attributes:
+            * level: available free space (number of unread entries)
 
             din  = enq_data, writable  = enq_ready, we = enq_valid
             dout = deq_data, re = deq_ready, readable = deq_valid
@@ -49,7 +55,7 @@ class Queue(FIFOInterface):
         FIFOInterface.__init__(self, width, depth, fwft)
         self.pipe = pipe
         self.depth = depth
-        self.count = Signal(bits_for(depth))
+        self.level = Signal(bits_for(depth))
 
     def elaborate(self, platform):
         m = Module()
@@ -59,6 +65,15 @@ class Queue(FIFOInterface):
         m.submodules.ram_read = ram_read = ram.read_port(synchronous=False)
         m.submodules.ram_write = ram_write = ram.write_port()
 
+        # convenience names
+        p_ready_o = self.writable
+        p_valid_i = self.we
+        enq_data = self.din
+
+        n_valid_o = self.readable
+        n_ready_i = self.re
+        deq_data = self.dout
+
         # intermediaries
         ptr_width = bits_for(self.depth - 1) if self.depth > 1 else 0
         enq_ptr = Signal(ptr_width) # cyclic pointer to "insert" point (wrport)
@@ -81,19 +96,19 @@ class Queue(FIFOInterface):
                      deq_max.eq(deq_ptr == self.depth - 1),
                      empty.eq(ptr_match & ~maybe_full),
                      full.eq(ptr_match & maybe_full),
-                     do_enq.eq(self.writable & self.we), # write conditions ok
-                     do_deq.eq(self.re & self.readable), # read conditions ok
+                     do_enq.eq(p_ready_o & p_valid_i), # write conditions ok
+                     do_deq.eq(n_ready_i & n_valid_o), # read conditions ok
 
                      # set readable and writable (NOTE: see pipe mode below)
-                     self.readable.eq(~empty), # cannot read if empty!
-                     self.writable.eq(~full),  # cannot write if full!
+                     n_valid_o.eq(~empty), # cannot read if empty!
+                     p_ready_o.eq(~full),  # cannot write if full!
 
                      # set up memory and connect to input and output
                      ram_write.addr.eq(enq_ptr),
-                     ram_write.data.eq(self.din),
+                     ram_write.data.eq(enq_data),
                      ram_write.en.eq(do_enq),
                      ram_read.addr.eq(deq_ptr),
-                     self.dout.eq(ram_read.data) # NOTE: overridden in fwft mode
+                     deq_data.eq(ram_read.data) # NOTE: overridden in fwft mode
                     ]
 
         # under write conditions, SRAM write-pointer moves on next clock
@@ -115,25 +130,26 @@ class Queue(FIFOInterface):
         # this done combinatorially to give the exact same characteristics
         # as Memory "write-through"... without relying on a changing API
         if self.fwft:
-            with m.If(self.we):
-                m.d.comb += self.readable.eq(1)
+            with m.If(p_valid_i):
+                m.d.comb += n_valid_o.eq(1)
             with m.If(empty):
-                m.d.comb += self.dout.eq(self.din)
+                m.d.comb += deq_data.eq(enq_data)
                 m.d.comb += do_deq.eq(0)
-                with m.If(self.re):
+                with m.If(n_ready_i):
                     m.d.comb += do_enq.eq(0)
 
-        # pipe mode: read-enabled requires writability.
+        # pipe mode: if next stage says it's ready (readable), we
+        #            *must* declare the input ready (writeable).
         if self.pipe:
-            with m.If(self.re):
-                m.d.comb += self.writable.eq(1)
+            with m.If(n_ready_i):
+                m.d.comb += p_ready_o.eq(1)
 
-        if self.depth == 1 << len(self.count):  # is depth a power of 2
-            m.d.comb += self.count.eq(
-                Mux(self.maybe_full & self.ptr_match, self.depth, 0)
-                | self.ptr_diff)
+        # set the count (available free space), optimise on power-of-two
+        if self.depth == 1 << ptr_width:  # is depth a power of 2
+            m.d.comb += self.level.eq(
+                Mux(maybe_full & ptr_match, self.depth, 0) | ptr_diff)
         else:
-            m.d.comb += self.count.eq(Mux(ptr_match,
+            m.d.comb += self.level.eq(Mux(ptr_match,
                                           Mux(maybe_full, self.depth, 0),
                                           Mux(deq_ptr > enq_ptr,
                                               self.depth + ptr_diff,
@@ -150,7 +166,7 @@ if __name__ == "__main__":
 
     def queue_ports(queue, name_prefix):
         retval = []
-        for name in ["count",
+        for name in ["level",
                      "dout",
                      "readable",
                      "writable"]:
@@ -166,6 +182,7 @@ if __name__ == "__main__":
             m.d.comb += port.eq(signal)
             retval.append(signal)
         return retval
+
     m.submodules.reg_stage = reg_stage
     ports += queue_ports(reg_stage, "reg_stage_")
     m.submodules.break_ready_chain_stage = break_ready_chain_stage