add in_multi and stage_ctl args to FIFOControl
[ieee754fpu.git] / src / add / singlepipe.py
index 311fee97c7095c87a35c8acb71882c41f0c0a1d8..96fb86f3e91a46f0f8f52d57458d3f2e9cbb3b2c 100644 (file)
 
 from nmigen import Signal, Cat, Const, Mux, Module, Value
 from nmigen.cli import verilog, rtlil
-from nmigen.lib.fifo import SyncFIFO
+from nmigen.lib.fifo import SyncFIFO, SyncFIFOBuffered
 from nmigen.hdl.ast import ArrayProxy
 from nmigen.hdl.rec import Record, Layout
 
 from abc import ABCMeta, abstractmethod
 from collections.abc import Sequence
+from queue import Queue
 
 
 class RecordObject(Record):
@@ -628,6 +629,11 @@ class ControlBase:
 
         return eqs
 
+    def _postprocess(self, i):
+        if hasattr(self.stage, "postprocess"):
+            return self.stage.postprocess(i)
+        return i
+
     def set_input(self, i):
         """ helper function to set the input data
         """
@@ -734,14 +740,16 @@ class BufferedHandshake(ControlBase):
 
         # data pass-through conditions
         with self.m.If(npnn):
+            o_data = self._postprocess(result)
             self.m.d.sync += [self.n.o_valid.eq(p_i_valid), # valid if p_valid
-                              eq(self.n.o_data, result),    # update output
+                              eq(self.n.o_data, o_data),    # update output
                              ]
         # buffer flush conditions (NOTE: can override data passthru conditions)
         with self.m.If(nir_por_n): # not stalled
             # Flush the [already processed] buffer to the output port.
+            o_data = self._postprocess(r_data)
             self.m.d.sync += [self.n.o_valid.eq(1),  # reg empty
-                              eq(self.n.o_data, r_data), # flush buffer
+                              eq(self.n.o_data, o_data), # flush buffer
                              ]
         # output ready conditions
         self.m.d.sync += self.p._o_ready.eq(nir_novn | por_pivn)
@@ -811,12 +819,14 @@ class SimpleHandshake(ControlBase):
 
         # previous valid and ready
         with m.If(p_i_valid_p_o_ready):
+            o_data = self._postprocess(result)
             m.d.sync += [r_busy.eq(1),      # output valid
-                         eq(self.n.o_data, result), # update output
+                         eq(self.n.o_data, o_data), # update output
                         ]
         # previous invalid or not ready, however next is accepting
         with m.Elif(n_i_ready):
-            m.d.sync += [eq(self.n.o_data, result)]
+            o_data = self._postprocess(result)
+            m.d.sync += [eq(self.n.o_data, o_data)]
             # TODO: could still send data here (if there was any)
             #m.d.sync += self.n.o_valid.eq(0) # ...so set output invalid
             m.d.sync += r_busy.eq(0) # ...so set output invalid
@@ -917,7 +927,8 @@ class UnbufferedPipeline(ControlBase):
 
         with m.If(pv):
             m.d.sync += eq(r_data, self.stage.process(self.p.i_data))
-        m.d.comb += eq(self.n.o_data, r_data)
+        o_data = self._postprocess(r_data)
+        m.d.comb += eq(self.n.o_data, o_data)
 
         return self.m
 
@@ -960,22 +971,22 @@ class UnbufferedPipeline2(ControlBase):
 
         -------   -    - -
         0 0 0 0   0    0 1   process(i_data)
-        0 0 0 1   1    1 0   reg
+        0 0 0 1   1    1 0   reg (odata, unchanged)
         0 0 1 0   0    0 1   process(i_data)
         0 0 1 1   0    0 1   process(i_data)
         -------   -    - -
         0 1 0 0   0    0 1   process(i_data)
-        0 1 0 1   1    1 0   reg
+        0 1 0 1   1    1 0   reg (odata, unchanged)
         0 1 1 0   0    0 1   process(i_data)
         0 1 1 1   0    0 1   process(i_data)
         -------   -    - -
         1 0 0 0   0    1 1   process(i_data)
-        1 0 0 1   1    1 0   reg
+        1 0 0 1   1    1 0   reg (odata, unchanged)
         1 0 1 0   0    1 1   process(i_data)
         1 0 1 1   0    1 1   process(i_data)
         -------   -    - -
         1 1 0 0   0    1 1   process(i_data)
-        1 1 0 1   1    1 0   reg
+        1 1 0 1   1    1 0   reg (odata, unchanged)
         1 1 1 0   0    1 1   process(i_data)
         1 1 1 1   0    1 1   process(i_data)
         -------   -    - -
@@ -997,8 +1008,10 @@ class UnbufferedPipeline2(ControlBase):
         m.d.comb += self.p._o_ready.eq(~buf_full)
         m.d.sync += buf_full.eq(~self.n.i_ready_test & self.n.o_valid)
 
-        odata = Mux(buf_full, buf, self.stage.process(self.p.i_data))
-        m.d.comb += eq(self.n.o_data, odata)
+        o_data = Mux(buf_full, buf, self.stage.process(self.p.i_data))
+        if hasattr(self.stage, "postprocess"):
+            o_data = self.stage.postprocess(o_data)
+        m.d.comb += eq(self.n.o_data, o_data)
         m.d.sync += eq(buf, self.n.o_data)
 
         return self.m
@@ -1025,23 +1038,23 @@ class PassThroughHandshake(ControlBase):
         V R R V                       V R
 
         -------   -    -    -   -     - -
-        0 0 0 0   0    1    1   0     1 1   reg
-        0 0 0 1   0    1    0   0     1 0   reg
-        0 0 1 0   0    1    1   0     1 1   reg
-        0 0 1 1   0    1    1   0     1 1   reg
+        0 0 0 0   0    1    1   0     1 1   odata (unchanged)
+        0 0 0 1   0    1    0   0     1 0   odata (unchanged)
+        0 0 1 0   0    1    1   0     1 1   odata (unchanged)
+        0 0 1 1   0    1    1   0     1 1   odata (unchanged)
         -------   -    -    -   -     - -
-        0 1 0 0   0    0    1   0     0 1   reg
-        0 1 0 1   0    0    0   0     0 0   reg
-        0 1 1 0   0    0    1   0     0 1   reg
-        0 1 1 1   0    0    1   0     0 1   reg
+        0 1 0 0   0    0    1   0     0 1   odata (unchanged)
+        0 1 0 1   0    0    0   0     0 0   odata (unchanged)
+        0 1 1 0   0    0    1   0     0 1   odata (unchanged)
+        0 1 1 1   0    0    1   0     0 1   odata (unchanged)
         -------   -    -    -   -     - -
         1 0 0 0   0    1    1   1     1 1   process(in)
-        1 0 0 1   0    1    0   0     1 0   reg
+        1 0 0 1   0    1    0   0     1 0   odata (unchanged)
         1 0 1 0   0    1    1   1     1 1   process(in)
         1 0 1 1   0    1    1   1     1 1   process(in)
         -------   -    -    -   -     - -
         1 1 0 0   1    1    1   1     1 1   process(in)
-        1 1 0 1   1    1    0   0     1 0   reg
+        1 1 0 1   1    1    0   0     1 0   odata (unchanged)
         1 1 1 0   1    1    1   1     1 1   process(in)
         1 1 1 1   1    1    1   1     1 1   process(in)
         -------   -    -    -   -     - -
@@ -1051,6 +1064,8 @@ class PassThroughHandshake(ControlBase):
     def elaborate(self, platform):
         self.m = m = ControlBase._elaborate(self, platform)
 
+        r_data = self.stage.ospec() # output type
+
         # temporaries
         p_i_valid = Signal(reset_less=True)
         pvr = Signal(reset_less=True)
@@ -1060,8 +1075,11 @@ class PassThroughHandshake(ControlBase):
         m.d.comb += self.p.o_ready.eq(~self.n.o_valid |  self.n.i_ready_test)
         m.d.sync += self.n.o_valid.eq(p_i_valid       | ~self.p.o_ready)
 
-        odata = Mux(pvr, self.stage.process(self.p.i_data), self.n.o_data)
-        m.d.sync += eq(self.n.o_data, odata)
+        odata = Mux(pvr, self.stage.process(self.p.i_data), r_data)
+        m.d.sync += eq(r_data, odata)
+        if hasattr(self.stage, "postprocess"):
+            r_data = self.stage.postprocess(r_data)
+        m.d.comb += eq(self.n.o_data, r_data)
 
         return m
 
@@ -1082,11 +1100,17 @@ class FIFOControl(ControlBase):
         i_data -> fifo.din -> FIFO -> fifo.dout -> o_data
     """
 
-    def __init__(self, depth, stage):
-        """ * iospecfn: specification for incoming and outgoing data
-            * depth   : number of entries in the FIFO
+    def __init__(self, depth, stage, in_multi=None, stage_ctl=False,
+                                     fwft=True, buffered=False):
+        """ FIFO Control
+
+            * depth: number of entries in the FIFO
+            * stage: data processing block
+            * fwft : first word fall-thru mode (non-fwft introduces delay)
+            * buffered: use buffered FIFO (introduces extra cycle delay)
 
             NOTE 1: FPGAs may have trouble with the defaults for SyncFIFO
+                    (fwft=True, buffered=False)
 
             NOTE 2: i_data *must* have a shape function.  it can therefore
                     be a Signal, or a Record, or a RecordObject.
@@ -1101,15 +1125,23 @@ class FIFOControl(ControlBase):
             function
         """
 
+        assert not (fwft and buffered), "buffered cannot do fwft"
+        if buffered:
+            depth += 1
+        self.fwft = fwft
+        self.buffered = buffered
         self.fdepth = depth
-        ControlBase.__init__(self, stage=stage)
+        ControlBase.__init__(self, stage, in_multi, stage_ctl)
 
     def elaborate(self, platform):
         self.m = m = ControlBase._elaborate(self, platform)
 
         # make a FIFO with a signal of equal width to the o_data.
         (fwidth, _) = self.n.o_data.shape()
-        fifo = SyncFIFO(fwidth, self.fdepth)
+        if self.buffered:
+            fifo = SyncFIFOBuffered(fwidth, self.fdepth)
+        else:
+            fifo = Queue(fwidth, self.fdepth, fwft=self.fwft)
         m.submodules.fifo = fifo
 
         # store result of processing in combinatorial temporary
@@ -1124,26 +1156,24 @@ class FIFOControl(ControlBase):
                      eq(fifo.din, flatten(result)),
                    ]
 
-        # next: make the FIFO "look" like a NextControl...
-        fn = NextControl()
-        fn.o_valid, fn.i_ready, fn.o_data = fifo.readable, fifo.re, fifo.dout
-        m.d.comb += fn._connect_out(self.n, fn=flatten) # ...so we can do this!
-
-        # err... that should be all!
-        return m
-
-        # XXX
-        # XXX UNUSED CODE!
-        # XXX
-
-        # prev: make the FIFO "look" like a PrevControl...
-        fp = PrevControl()
-        fp.i_valid, fp._o_ready, fp.i_data = fifo.we, fifo.writable, fifo.din
-        m.d.comb += fp._connect_in(self.p, True, fn=flatten)
-
         # connect next rdy/valid/data - do flatten on o_data
-        m.d.comb += [self.n.o_valid.eq(fifo.readable),
+        connections = [self.n.o_valid.eq(fifo.readable),
                      fifo.re.eq(self.n.i_ready_test),
-                     flatten(self.n.o_data).eq(fifo.dout),
                    ]
+        if self.fwft or self.buffered:
+            m.d.comb += connections
+        else:
+            m.d.sync += connections # unbuffered fwft mode needs sync
+        o_data = flatten(self.n.o_data).eq(fifo.dout)
+        if hasattr(self.stage, "postprocess"):
+            o_data = self.stage.postprocess(o_data)
+        m.d.comb += o_data
 
+        return m
+
+"""
+class BufferedHandshake(FIFOControl):
+    def __init__(self, stage, in_multi=None, stage_ctl=False):
+        FIFOControl.__init__(self, 2, stage, in_multi, stage_ctl,
+                                   fwft=True, buffered=False)
+"""