move DynamicPipe to nmutil
[ieee754fpu.git] / src / nmutil / dynamicpipe.py
1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
3
4 from abc import ABCMeta
5
6 from nmutil.singlepipe import SimpleHandshake
7 import threading
8
9 # with many thanks to jsbueno on stackexchange for this one
10 # https://stackoverflow.com/questions/57273070/
11 # list post:
12 # http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2019-July/002259.html
13
14 class Meta(ABCMeta):
15 registry = {}
16 recursing = threading.local()
17 recursing.check = False
18 mlock = threading.Lock()
19
20 def __call__(cls, *args, **kw):
21 mcls = cls.__class__
22 if mcls.recursing.check:
23 return super().__call__(*args, **kw)
24 spec = args[0]
25 base = spec.pipekls # pick up the dynamic class from PipelineSpec, HERE
26
27 if (cls, base) not in mcls.registry:
28 print ("__call__", args, kw, cls, base,
29 base.__bases__, cls.__bases__)
30 mcls.registry[cls, base] = type(
31 cls.__name__,
32 (cls, base) + cls.__bases__[1:],
33 {}
34 )
35 real_cls = mcls.registry[cls, base]
36
37 with mcls.mlock:
38 mcls.recursing.check = True
39 instance = real_cls.__class__.__call__(real_cls, *args, **kw)
40 mcls.recursing.check = False
41 return instance
42
43
44 # Inherit from this class instead of SimpleHandshake (or other ControlBase
45 # derivative), and the metaclass will instead *replace* DynamicPipe -
46 # *at runtime* - with the class that is specified *as a parameter*
47 # in PipelineSpec.
48 #
49 # as explained in the list posting and in the stackexchange post, this is
50 # needed to avoid a MASSIVE suite of duplicated multiple-inheritance classes
51 # that "Mix in" SimpleHandshake (or other).
52 #
53 # unfortunately, composition does not work in this instance
54 # (make an *instance* of SimpleHandshake or other class and pass it in)
55 # due to the multiple level inheritance, and in several places
56 # the inheriting class needs to do some setup that the deriving class
57 # needs in order to function correctly.
58
59 class DynamicPipe(metaclass=Meta):
60 def __init__(self, *args):
61 print ("DynamicPipe init", super(), args)
62 super().__init__(self, *args)
63
64
65 # bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
66 # for the dynamically-derived class. luckily, SimpleHandshake only needs
67 # "self" as the 1st argument (it is its own "Stage"). anything else
68 # could hypothetically be passed through the pspec.
69 class SimpleHandshakeRedir(SimpleHandshake):
70 def __init__(self, mod, *args):
71 print ("redir", mod, args)
72 stage = self
73 if args and args[0].stage:
74 stage = args[0].stage
75 SimpleHandshake.__init__(self, stage)
76