add bug #148 record
[ieee754fpu.git] / src / nmutil / iocontrol.py
index 8254795ea133798630d87480b73a012f7fca0171..efe0c38cb75c6018c7a35217128cbe4666a91331 100644 (file)
@@ -1,82 +1,23 @@
 """ IO Control API
 
     Associated development bugs:
+    * http://bugs.libre-riscv.org/show_bug.cgi?id=148
     * http://bugs.libre-riscv.org/show_bug.cgi?id=64
     * http://bugs.libre-riscv.org/show_bug.cgi?id=57
 
-    Stage API:
-    ---------
-
-    stage requires compliance with a strict API that may be
-    implemented in several means, including as a static class.
-
-    Stages do not HOLD data, and they definitely do not contain
-    signalling (ready/valid).  They do however specify the FORMAT
-    of the incoming and outgoing data, and they provide a means to
-    PROCESS that data (from incoming format to outgoing format).
-
-    Stage Blocks really must be combinatorial blocks.  It would be ok
-    to have input come in from sync'd sources (clock-driven) however by
-    doing so they would no longer be deterministic, and chaining such
-    blocks with such side-effects together could result in unexpected,
-    unpredictable, unreproduceable behaviour.
-    So generally to be avoided, then unless you know what you are doing.
-
-    the methods of a stage instance must be as follows:
-
-    * ispec() - Input data format specification.  Takes a bit of explaining.
-                The requirements are: something that eventually derives from
-                nmigen Value must be returned *OR* an iterator or iterable
-                or sequence (list, tuple etc.) or generator must *yield*
-                thing(s) that (eventually) derive from the nmigen Value class.
-
-                Complex to state, very simple in practice:
-                see test_buf_pipe.py for over 25 worked examples.
-
-    * ospec() - Output data format specification.
-                format requirements identical to ispec.
-
-    * process(m, i) - Optional function for processing ispec-formatted data.
-                returns a combinatorial block of a result that
-                may be assigned to the output, by way of the "nmoperator.eq"
-                function.  Note that what is returned here can be
-                extremely flexible.  Even a dictionary can be returned
-                as long as it has fields that match precisely with the
-                Record into which its values is intended to be assigned.
-                Again: see example unit tests for details.
-
-    * setup(m, i) - Optional function for setting up submodules.
-                may be used for more complex stages, to link
-                the input (i) to submodules.  must take responsibility
-                for adding those submodules to the module (m).
-                the submodules must be combinatorial blocks and
-                must have their inputs and output linked combinatorially.
-
-    Both StageCls (for use with non-static classes) and Stage (for use
-    by static classes) are abstract classes from which, for convenience
-    and as a courtesy to other developers, anything conforming to the
-    Stage API may *choose* to derive.  See Liskov Substitution Principle:
-    https://en.wikipedia.org/wiki/Liskov_substitution_principle
-
-    StageChain:
-    ----------
-
-    A useful combinatorial wrapper around stages that chains them together
-    and then presents a Stage-API-conformant interface.  By presenting
-    the same API as the stages it wraps, it can clearly be used recursively.
-
-    ControlBase:
-    -----------
-
-    The base class for pipelines.  Contains previous and next ready/valid/data.
-    Also has an extremely useful "connect" function that can be used to
-    connect a chain of pipelines and present the exact same prev/next
-    ready/valid/data API.
-
-    Note: pipelines basically do not become pipelines as such until
-    handed to a derivative of ControlBase.  ControlBase itself is *not*
-    strictly considered a pipeline class.  Wishbone and AXI4 (master or
-    slave) could be derived from ControlBase, for example.
+    Important: see Stage API (stageapi.py) in combination with below
+
+    Main classes: PrevControl and NextControl.
+
+    These classes manage the data and the synchronisation state
+    to the previous and next stage, respectively.  ready/valid
+    signals are used by the Pipeline classes to tell if data
+    may be safely passed from stage to stage.
+
+    The connection from one stage to the next is carried out with
+    NextControl.connect_to_next.  It is *not* necessary to have
+    a PrevControl.connect_to_prev because it is functionally
+    directly equivalent to prev->next->connect_to_next.
 """
 
 from nmigen import Signal, Cat, Const, Module, Value, Elaboratable
@@ -171,11 +112,12 @@ class PrevControl(Elaboratable):
         * data_i : an input - MUST be added by the USER of this class
     """
 
-    def __init__(self, i_width=1, stage_ctl=False, maskwid=0):
+    def __init__(self, i_width=1, stage_ctl=False, maskwid=0, offs=0):
         self.stage_ctl = stage_ctl
         self.maskwid = maskwid
         if maskwid:
             self.mask_i = Signal(maskwid)                # prev   >>in  self
+            self.stop_i = Signal(maskwid)                # prev   >>in  self
         self.valid_i = Signal(i_width, name="p_valid_i") # prev   >>in  self
         self._ready_o = Signal(name="p_ready_o")         # prev   <<out self
         self.data_i = None # XXX MUST BE ADDED BY USER
@@ -191,7 +133,8 @@ class PrevControl(Elaboratable):
             return self.s_ready_o # set dynamically by stage
         return self._ready_o      # return this when not under dynamic control
 
-    def _connect_in(self, prev, direct=False, fn=None, do_data=True):
+    def _connect_in(self, prev, direct=False, fn=None,
+                    do_data=True, do_stop=True):
         """ internal helper function to connect stage to an input source.
             do not use to connect stage-to-stage!
         """
@@ -200,6 +143,8 @@ class PrevControl(Elaboratable):
                prev.ready_o.eq(self.ready_o)]
         if self.maskwid:
             res.append(self.mask_i.eq(prev.mask_i))
+            if do_stop:
+                res.append(self.stop_i.eq(prev.stop_i))
         if do_data is False:
             return res
         data_i = fn(prev.data_i) if fn is not None else prev.data_i
@@ -241,6 +186,7 @@ class PrevControl(Elaboratable):
         yield self.ready_o
         if self.maskwid:
             yield self.mask_i
+            yield self.stop_i
         if hasattr(self.data_i, "ports"):
             yield from self.data_i.ports()
         elif isinstance(self.data_i, Sequence):
@@ -263,6 +209,7 @@ class NextControl(Elaboratable):
         self.maskwid = maskwid
         if maskwid:
             self.mask_o = Signal(maskwid)       # self out>>  next
+            self.stop_o = Signal(maskwid)       # self out>>  next
         self.valid_o = Signal(name="n_valid_o") # self out>>  next
         self.ready_i = Signal(name="n_ready_i") # self <<in   next
         self.data_o = None # XXX MUST BE ADDED BY USER
@@ -276,7 +223,7 @@ class NextControl(Elaboratable):
             return self.ready_i & self.d_valid
         return self.ready_i
 
-    def connect_to_next(self, nxt, do_data=True):
+    def connect_to_next(self, nxt, do_data=True, do_stop=True):
         """ helper function to connect to the next stage data/valid/ready.
             data/valid is passed *TO* nxt, and ready comes *IN* from nxt.
             use this when connecting stage-to-stage
@@ -288,11 +235,15 @@ class NextControl(Elaboratable):
                self.ready_i.eq(nxt.ready_o)]
         if self.maskwid:
             res.append(nxt.mask_i.eq(self.mask_o))
+            if do_stop:
+                res.append(nxt.stop_i.eq(self.stop_o))
         if do_data:
             res.append(nmoperator.eq(nxt.data_i, self.data_o))
+        print ("connect to next", self, self.maskwid, nxt.data_i, do_data, do_stop)
         return res
 
-    def _connect_out(self, nxt, direct=False, fn=None, do_data=True):
+    def _connect_out(self, nxt, direct=False, fn=None,
+                     do_data=True, do_stop=True):
         """ internal helper function to connect stage to an output source.
             do not use to connect stage-to-stage!
         """
@@ -301,6 +252,8 @@ class NextControl(Elaboratable):
                self.ready_i.eq(ready_i)]
         if self.maskwid:
             res.append(nxt.mask_o.eq(self.mask_o))
+            if do_stop:
+                res.append(nxt.stop_o.eq(self.stop_o))
         if not do_data:
             return res
         data_o = fn(nxt.data_o) if fn is not None else nxt.data_o
@@ -316,6 +269,7 @@ class NextControl(Elaboratable):
         yield self.valid_o
         if self.maskwid:
             yield self.mask_o
+            yield self.stop_o
         if hasattr(self.data_o, "ports"):
             yield from self.data_o.ports()
         elif isinstance(self.data_o, Sequence):