clarify whats being obtained from _connect_out function
[ieee754fpu.git] / src / nmutil / singlepipe.py
index ba4acfbe3d4195f63c467a9b679753fdb9bc0bd7..e109900dc876faccf3040a745f45a5182f696321 100644 (file)
@@ -4,7 +4,16 @@
     * http://bugs.libre-riscv.org/show_bug.cgi?id=64
     * http://bugs.libre-riscv.org/show_bug.cgi?id=57
 
-    Important: see Stage API (stageapi.py) in combination with below
+    Important: see Stage API (stageapi.py) and IO Control API
+    (iocontrol.py) in combination with below.  This module
+    "combines" the Stage API with the IO Control API to create
+    the Pipeline API.
+
+    The one critically important key difference between StageAPI and
+    PipelineAPI:
+
+        * StageAPI: combinatorial (NO REGISTERS / LATCHES PERMITTED)
+        * PipelineAPI: synchronous registers / latches get added here
 
     RecordBasedStage:
     ----------------
@@ -461,7 +470,15 @@ class MaskNoDelayCancellable(ControlBase):
 class MaskCancellable(ControlBase):
     """ Mask-activated Cancellable pipeline
 
-        Argument: stage.  see Stage API above
+        Arguments:
+
+        * stage.  see Stage API above
+        * maskwid - sets up cancellation capability (mask and stop).
+        * in_multi
+        * stage_ctl
+        * dynamic - allows switching from sync to combinatorial (passthrough)
+                    USE WITH CARE.  will need the entire pipe to be quiescent
+                    before switching, otherwise data WILL be destroyed.
 
         stage-1   p.valid_i >>in   stage   n.valid_o out>>   stage+1
         stage-1   p.ready_o <<out  stage   n.ready_i <<in    stage+1
@@ -469,58 +486,82 @@ class MaskCancellable(ControlBase):
                               |             |
                               +--process->--^
     """
-    def __init__(self, stage, maskwid, in_multi=None, stage_ctl=False):
+    def __init__(self, stage, maskwid, in_multi=None, stage_ctl=False,
+                       dynamic=False):
         ControlBase.__init__(self, stage, in_multi, stage_ctl, maskwid)
+        self.dynamic = dynamic
+        if dynamic:
+            self.latchmode = Signal()
+        else:
+            self.latchmode = Const(1)
 
     def elaborate(self, platform):
         self.m = m = ControlBase.elaborate(self, platform)
 
-        r_busy = Signal()
-        result = _spec(self.stage.ospec, "r_tmp")
-
-        # establish if the data should be passed on.  cancellation is
-        # a global signal.
-        p_valid_i = Signal(reset_less=True)
-        #print ("self.p.data_i", self.p.data_i)
-        maskedout = Signal(len(self.p.mask_i), reset_less=True)
-        m.d.comb += maskedout.eq(self.p.mask_i & ~self.p.stop_i)
-
-        # establish some combinatorial temporaries
-        n_ready_i = Signal(reset_less=True, name="n_i_rdy_data")
-        p_valid_i_p_ready_o = Signal(reset_less=True)
-        m.d.comb += [p_valid_i.eq(self.p.valid_i_test & maskedout.bool()),
-                     n_ready_i.eq(self.n.ready_i_test),
-                     p_valid_i_p_ready_o.eq(p_valid_i & self.p.ready_o),
-        ]
-
-        # store result of processing in combinatorial temporary
-        m.d.comb += nmoperator.eq(result, self.data_r)
-
-        # if idmask nonzero, mask gets passed on (and register set).
-        # register is left as-is if idmask is zero, but out-mask is set to zero
-        # note however: only the *uncancelled* mask bits get passed on
-        m.d.sync += self.n.mask_o.eq(Mux(p_valid_i, maskedout, 0))
-
-        # always pass on stop (as combinatorial: single signal)
-        m.d.comb += self.n.stop_o.eq(self.p.stop_i)
-
-        # previous valid and ready
-        with m.If(p_valid_i_p_ready_o):
-            data_o = self._postprocess(result) # XXX TBD, does nothing right now
-            m.d.sync += [r_busy.eq(1),      # output valid
-                         nmoperator.eq(self.n.data_o, data_o), # update output
-                        ]
-        # previous invalid or not ready, however next is accepting
-        with m.Elif(n_ready_i):
-            data_o = self._postprocess(result) # XXX TBD, does nothing right now
-            m.d.sync += [nmoperator.eq(self.n.data_o, data_o)]
-            # TODO: could still send data here (if there was any)
-            #m.d.sync += self.n.valid_o.eq(0) # ...so set output invalid
-            m.d.sync += r_busy.eq(0) # ...so set output invalid
-
-        m.d.comb += self.n.valid_o.eq(r_busy)
-        # if next is ready, so is previous
-        m.d.comb += self.p._ready_o.eq(n_ready_i)
+        mask_r = Signal(len(self.p.mask_i), reset_less=True)
+        data_r = _spec(self.stage.ospec, "data_r")
+        m.d.comb += nmoperator.eq(data_r, self._postprocess(self.data_r))
+
+        with m.If(self.latchmode):
+            r_busy = Signal()
+            r_latch = _spec(self.stage.ospec, "r_latch")
+
+            # establish if the data should be passed on.  cancellation is
+            # a global signal.
+            p_valid_i = Signal(reset_less=True)
+            #print ("self.p.data_i", self.p.data_i)
+            maskedout = Signal(len(self.p.mask_i), reset_less=True)
+            m.d.comb += maskedout.eq(self.p.mask_i & ~self.p.stop_i)
+
+            # establish some combinatorial temporaries
+            n_ready_i = Signal(reset_less=True, name="n_i_rdy_data")
+            p_valid_i_p_ready_o = Signal(reset_less=True)
+            m.d.comb += [p_valid_i.eq(self.p.valid_i_test & maskedout.bool()),
+                         n_ready_i.eq(self.n.ready_i_test),
+                         p_valid_i_p_ready_o.eq(p_valid_i & self.p.ready_o),
+            ]
+
+            # if idmask nonzero, mask gets passed on (and register set).
+            # register is left as-is if idmask is zero, but out-mask is set to
+            # zero
+            # note however: only the *uncancelled* mask bits get passed on
+            m.d.sync += mask_r.eq(Mux(p_valid_i, maskedout, 0))
+            m.d.comb += self.n.mask_o.eq(mask_r)
+
+            # always pass on stop (as combinatorial: single signal)
+            m.d.comb += self.n.stop_o.eq(self.p.stop_i)
+
+            stor = Signal(reset_less=True)
+            m.d.comb += stor.eq(p_valid_i_p_ready_o | n_ready_i)
+            with m.If(stor):
+                # store result of processing in combinatorial temporary
+                m.d.sync += nmoperator.eq(r_latch, data_r)
+
+            # previous valid and ready
+            with m.If(p_valid_i_p_ready_o):
+                m.d.sync += r_busy.eq(1)      # output valid
+            # previous invalid or not ready, however next is accepting
+            with m.Elif(n_ready_i):
+                m.d.sync += r_busy.eq(0) # ...so set output invalid
+
+            # output set combinatorially from latch
+            m.d.comb += nmoperator.eq(self.n.data_o, r_latch)
+
+            m.d.comb += self.n.valid_o.eq(r_busy)
+            # if next is ready, so is previous
+            m.d.comb += self.p._ready_o.eq(n_ready_i)
+
+        with m.Else():
+            # pass everything straight through.  p connected to n: data,
+            # valid, mask, everything.  this is "effectively" just a
+            # StageChain: MaskCancellable is doing "nothing" except
+            # combinatorially passing everything through
+            # (except now it's *dynamically selectable* whether to do that)
+            m.d.comb += self.n.valid_o.eq(self.p.valid_i_test)
+            m.d.comb += self.p._ready_o.eq(self.n.ready_i_test)
+            m.d.comb += self.n.stop_o.eq(self.p.stop_i)
+            m.d.comb += self.n.mask_o.eq(self.p.mask_i)
+            m.d.comb += nmoperator.eq(self.n.data_o, data_r)
 
         return self.m
 
@@ -910,14 +951,14 @@ class FIFOControl(ControlBase):
         m.submodules.fn = fn = NextControl()
         fn.valid_o, fn.ready_i, fn.data_o  = fifo.readable, fifo.re, fifo.dout
         connections = fn._connect_out(self.n, fn=nmoperator.cat)
+        valid_eq, ready_eq, data_o = connections
 
         # ok ok so we can't just do the ready/valid eqs straight:
         # first 2 from connections are the ready/valid, 3rd is data.
         if self.fwft:
-            m.d.comb += connections[:2] # combinatorial on next ready/valid
+            m.d.comb += [valid_eq, ready_eq] # combinatorial on next ready/valid
         else:
-            m.d.sync += connections[:2]  # non-fwft mode needs sync
-        data_o = connections[2] # get the data
+            m.d.sync += [valid_eq, ready_eq] # non-fwft mode needs sync
         data_o = self._postprocess(data_o) # XXX TBD, does nothing right now
         m.d.comb += data_o