update comments
[ieee754fpu.git] / src / add / stageapi.py
index 259b9de92a9ce20a81f792321ba959cc6f99bbb3..9651bf79fe778f1b26aa510d8a0f5d6c0800778d 100644 (file)
     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.
+    Stage Blocks really should be combinatorial blocks (Moore FSMs).
+    It would be ok to have input come in from sync'd sources
+    (clock-driven, Mealy FSMs) 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.
+    https://en.wikipedia.org/wiki/Moore_machine
+    https://en.wikipedia.org/wiki/Mealy_machine
 
     the methods of a stage instance must be as follows:
 
@@ -80,6 +84,13 @@ import nmoperator
 
 
 def _spec(fn, name=None):
+    """ useful function that determines if "fn" has an argument "name".
+        if so, fn(name) is called otherwise fn() is called.
+
+        means that ispec and ospec can be declared with *or without*
+        a name argument.  normally it would be necessary to have
+        "ispec(name=None)" to achieve the same effect.
+    """
     if name is None:
         return fn()
     varnames = dict(inspect.getmembers(fn.__code__))['co_varnames']
@@ -155,13 +166,20 @@ class StageHelper(Stage):
         return _spec(self._ispecfn, name)
 
     def set_specs(self, p, n):
-        self._ispecfn = p.stage.ispec
-        self._ospecfn = n.stage.ospec
+        """ sets up the ispecfn and ospecfn for getting input and output data
+        """
+        if hasattr(p, "stage"):
+            p = p.stage
+        if hasattr(n, "stage"):
+            n = n.stage
+        self._ispecfn = p.ispec
+        self._ospecfn = n.ospec
 
     def new_specs(self, name):
         """ allocates new ispec and ospec pair
         """
-        return self.ispec("%s_i" % name), self.ospec("%s_o" % name)
+        return (_spec(self.ispec, "%s_i" % name),
+                _spec(self.ospec, "%s_o" % name))
 
     def process(self, i):
         if self.stage and hasattr(self.stage, "process"):
@@ -179,7 +197,7 @@ class StageHelper(Stage):
         return i
 
 
-class StageChain(StageCls):
+class StageChain(StageHelper):
     """ pass in a list of stages, and they will automatically be
         chained together via their input and output specs into a
         combinatorial chain, to create one giant combinatorial block.
@@ -220,17 +238,9 @@ class StageChain(StageCls):
     def __init__(self, chain, specallocate=False):
         assert len(chain) > 0, "stage chain must be non-zero length"
         self.chain = chain
+        StageHelper.__init__(self, None)
         self.setup = self._sa_setup if specallocate else self._na_setup
-
-    def ispec(self):
-        """ returns the ispec of the first of the chain
-        """
-        return _spec(self.chain[0].ispec, "chainin")
-
-    def ospec(self):
-        """ returns the ospec of the last of the chain
-        """
-        return _spec(self.chain[-1].ospec, "chainout")
+        self.set_specs(self.chain[0], self.chain[-1])
 
     def _sa_setup(self, m, i):
         for (idx, c) in enumerate(self.chain):