resolve awful meta-class hacking (with thanks to jsbueno on stackexchange)
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 30 Jul 2019 23:14:12 +0000 (00:14 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 30 Jul 2019 23:14:12 +0000 (00:14 +0100)
src/ieee754/fpadd/addstages.py
src/ieee754/pipeline.py
src/nmutil/singlepipe.py

index 2bc23df0dabf89f8a4e194d5e573a88d5d740d0e..f875ef920247243e9bfd3265055c66cd87584e1c 100644 (file)
@@ -5,8 +5,8 @@
 from nmigen import Module
 from nmigen.cli import main, verilog
 
-from nmutil.singlepipe import (StageChain, SimpleHandshake,
-                        PassThroughStage)
+from nmutil.singlepipe import StageChain
+from ieee754.pipeline import DynamicPipe
 
 from ieee754.fpcommon.fpbase import FPState
 from ieee754.fpcommon.denorm import FPSCData
@@ -15,13 +15,12 @@ from ieee754.fpadd.align import FPAddAlignSingleMod
 from ieee754.fpadd.add0 import FPAddStage0Mod
 from ieee754.fpadd.add1 import FPAddStage1Mod
 
-
-class FPAddAlignSingleAdd(FPState, SimpleHandshake):
+class FPAddAlignSingleAdd(DynamicPipe):
 
     def __init__(self, pspec):
-        FPState.__init__(self, "align")
+        #FPState.__init__(self, "align")
         self.pspec = pspec
-        SimpleHandshake.__init__(self, self) # pipeline is its own stage
+        super().__init__(pspec)
         self.a1o = self.ospec()
 
     def ispec(self):
index b1e3b0ba111b08eca8e0d5b17a172b0a8350041c..2e57245920126fdd75c97b2c6fe4792f25e8ca43 100644 (file)
@@ -1,7 +1,11 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 # See Notices.txt for copyright information
 
+from abc import ABCMeta
+from nmigen import Elaboratable
+
 from nmutil.singlepipe import SimpleHandshake
+import threading
 
 
 class PipelineSpec:
@@ -27,18 +31,55 @@ class PipelineSpec:
         self.id_wid = id_width
         self.op_wid = op_wid
         self.opkls = opkls
-        self.pipekls = pipekls or SimpleHandshake
+        self.pipekls = pipekls or SimpleHandshakeRedir
         self.core_config = None
         self.fpformat = None
         self.n_comb_stages = None
 
+# with many thanks to jsbueno on stackexchange for this one
+# https://stackoverflow.com/questions/57273070/
+
+class Meta(ABCMeta):
+    registry = {}
+    recursing = threading.local()
+    recursing.check = False
+    mlock = threading.Lock()
+
+    def __call__(cls, *args, **kw):
+        mcls = cls.__class__
+        if mcls.recursing.check:
+            return super().__call__(*args, **kw)
+        spec = args[0]
+        base = spec.pipekls
+
+        if (cls, base) not in mcls.registry:
+            print ("__call__", args, kw, cls, base, base.__bases__, cls.__bases__)
+            mcls.registry[cls, base] = type(
+                cls.__name__,
+                (cls, base) + cls.__bases__[1:],
+                {}
+            )
+        real_cls = mcls.registry[cls, base]
+
+        with mcls.mlock:
+            mcls.recursing.check = True
+            instance = real_cls.__class__.__call__(real_cls, *args, **kw)
+            mcls.recursing.check = False
+        return instance
+
+
+class DynamicPipe(metaclass=Meta):
+    def __init__(self, *args):
+        print ("DynamicPipe init", super(), args)
+        super().__init__(self, *args)
+
 
-def DynamicPipeCreate(pspec, *args, **kwargs):
-    superclass = pspec.pipekls
-    class DynamicPipe(superclass):
-        def __init__(self, *args, **kwargs):
-            print(superclass)
-            superclass.__init__(self, *args, **kwargs)
-        pass
-    return DynamicPipe(*args, **kwargs)
+# bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
+# for the dynamically-derived class.  luckily, SimpleHandshake only needs
+# "self" as the 1st argument (it is its own "Stage").  anything else
+# could hypothetically be passed through the pspec.
+class SimpleHandshakeRedir(SimpleHandshake):
+    def __init__(self, pspec, *args):
+        print ("redir", pspec, args)
+        SimpleHandshake.__init__(self, self)
 
index b9214bd590446e1e56ed4c5947a4b24abc2a03f8..8a5b06dcaa41cb043d175f65beb34716a0f3fbea 100644 (file)
@@ -138,7 +138,7 @@ import inspect
 from nmutil.iocontrol import (PrevControl, NextControl, Object, RecordObject)
 from nmutil.stageapi import (_spec, StageCls, Stage, StageChain, StageHelper)
 from nmutil import nmoperator
-                      
+
 
 class RecordBasedStage(Stage):
     """ convenience class which provides a Records-based layout.
@@ -190,6 +190,7 @@ class ControlBase(StageHelper, Elaboratable):
             * add data_o member to NextControl (n)
             Calling ControlBase._new_data is a good way to do that.
         """
+        print ("ControlBase", self, stage, in_multi, stage_ctl)
         StageHelper.__init__(self, stage)
 
         # set up input and output IO ACK (prev/next ready/valid)