cleanup
[ieee754fpu.git] / src / ieee754 / pipeline.py
1 # SPDX-License-Identifier: LGPL-2.1-or-later
2 # See Notices.txt for copyright information
3
4 from abc import ABCMeta
5 from nmigen import Elaboratable
6
7 from nmutil.singlepipe import SimpleHandshake
8 import threading
9
10
11 class PipelineSpec:
12 """ Pipeline Specification base class.
13
14 :attribute width: the IEEE754 FP bitwidth
15 :attribute id_wid: the Reservation Station muxid bitwidth
16 :attribute op_wid: an "operand bitwidth" passed down all stages
17 :attribute opkls: an optional class that is instantiated as the "operand"
18
19 See ieee754/fpcommon/getop FPPipeContext for how (where) PipelineSpec
20 is used. FPPipeContext is passed down *every* stage of a pipeline
21 and contains the Reservation Station multiplexer ID as well as
22 an optional "operand". This "operand" may be used to *change*
23 the behaviour of the pipeline. In RISC-V terminology it would
24 typically be set to e.g. funct7 or parts thereof.
25
26 """
27
28 def __init__(self, width, id_width, op_wid=0, opkls=None, pipekls=None):
29 """ Create a PipelineSpec. """
30 self.width = width
31 self.id_wid = id_width
32 self.op_wid = op_wid
33 self.opkls = opkls
34 self.pipekls = pipekls or SimpleHandshakeRedir
35 self.stage = None
36 self.core_config = None
37 self.fpformat = None
38 self.n_comb_stages = None
39
40 # with many thanks to jsbueno on stackexchange for this one
41 # https://stackoverflow.com/questions/57273070/
42 # list post:
43 # http://lists.libre-riscv.org/pipermail/libre-riscv-dev/2019-July/002259.html
44
45 class Meta(ABCMeta):
46 registry = {}
47 recursing = threading.local()
48 recursing.check = False
49 mlock = threading.Lock()
50
51 def __call__(cls, *args, **kw):
52 mcls = cls.__class__
53 if mcls.recursing.check:
54 return super().__call__(*args, **kw)
55 spec = args[0]
56 base = spec.pipekls # pick up the dynamic class from PipelineSpec, HERE
57
58 if (cls, base) not in mcls.registry:
59 print ("__call__", args, kw, cls, base,
60 base.__bases__, cls.__bases__)
61 mcls.registry[cls, base] = type(
62 cls.__name__,
63 (cls, base) + cls.__bases__[1:],
64 {}
65 )
66 real_cls = mcls.registry[cls, base]
67
68 with mcls.mlock:
69 mcls.recursing.check = True
70 instance = real_cls.__class__.__call__(real_cls, *args, **kw)
71 mcls.recursing.check = False
72 return instance
73
74
75 # Inherit from this class instead of SimpleHandshake (or other ControlBase
76 # derivative), and the metaclass will instead *replace* DynamicPipe -
77 # *at runtime* - with the class that is specified *as a parameter*
78 # in PipelineSpec.
79 #
80 # as explained in the list posting and in the stackexchange post, this is
81 # needed to avoid a MASSIVE suite of duplicated multiple-inheritance classes
82 # that "Mix in" SimpleHandshake (or other).
83 #
84 # unfortunately, composition does not work in this instance
85 # (make an *instance* of SimpleHandshake or other class and pass it in)
86 # due to the multiple level inheritance, and in several places
87 # the inheriting class needs to do some setup that the deriving class
88 # needs in order to function correctly.
89
90 class DynamicPipe(metaclass=Meta):
91 def __init__(self, *args):
92 print ("DynamicPipe init", super(), args)
93 super().__init__(self, *args)
94
95
96 # bad hack: the DynamicPipe metaclass ends up creating an __init__ signature
97 # for the dynamically-derived class. luckily, SimpleHandshake only needs
98 # "self" as the 1st argument (it is its own "Stage"). anything else
99 # could hypothetically be passed through the pspec.
100 class SimpleHandshakeRedir(SimpleHandshake):
101 def __init__(self, mod, *args):
102 print ("redir", mod, args)
103 stage = self
104 if args and args[0].stage:
105 stage = args[0].stage
106 SimpleHandshake.__init__(self, stage)
107