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