switch to exact version of cython
[ieee754fpu.git] / src / ieee754 / fpdiv / pipeline.py
1 """IEEE754 Floating Point Divider Pipeline
2
3 Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
4 Copyright (C) 2019 Jacob Lifshay
5
6 Relevant bugreports:
7 * http://bugs.libre-riscv.org/show_bug.cgi?id=99
8 * http://bugs.libre-riscv.org/show_bug.cgi?id=43
9 * http://bugs.libre-riscv.org/show_bug.cgi?id=44
10
11 Stack looks like this:
12
13 scnorm - FPDIVSpecialCasesDeNorm ispec FPBaseData
14 ------ ospec FPSCData
15
16 StageChain: FPDIVSpecialCasesMod,
17 FPAddDeNormMod
18
19 pipediv0 - FPDivStagesSetup ispec FPSCData
20 -------- ospec DivPipeInterstageData
21
22 StageChain: FPDivPreFPAdjust,
23 DivPipeSetupStage,
24 DivPipeCalculateStage,
25 ...
26 DivPipeCalculateStage
27
28 pipediv1 - FPDivStagesIntermediate ispec DivPipeInterstageData
29 -------- ospec DivPipeInterstageData
30
31 StageChain: DivPipeCalculateStage,
32 ...
33 DivPipeCalculateStage
34 ...
35 ...
36
37 pipediv5 - FPDivStageFinal ispec FPDivStage0Data
38 -------- ospec FPPostCalcData
39
40 StageChain: DivPipeCalculateStage,
41 ...
42 DivPipeCalculateStage,
43 DivPipeFinalStage,
44 FPDivPostToFPFormat
45
46 normpack - FPNormToPack ispec FPPostCalcData
47 -------- ospec FPPackData
48
49 StageChain: Norm1ModSingle,
50 RoundMod,
51 CorrectionsMod,
52 PackMod
53
54 the number of combinatorial StageChains (n_comb_stages) in
55 FPDivStages is an argument arranged to get the length of the whole
56 pipeline down to sane numbers. it specifies the number of "blocks"
57 that will be combinatorially chained together.
58
59 the reason for keeping the number of stages down is that for every
60 pipeline clock delay, a corresponding ReservationStation is needed.
61 if there are 24 pipeline stages, we need a whopping TWENTY FOUR
62 RS's. that's far too many. 6 is just about an acceptable number.
63 even 8 is starting to get alarmingly high.
64 """
65
66 from nmutil.singlepipe import ControlBase
67 from nmutil.concurrentunit import ReservationStations, num_bits
68
69 from ieee754.fpcommon.fpbase import FPFormat
70 from ieee754.fpcommon.normtopack import FPNormToPack
71 from ieee754.fpdiv.specialcases import FPDIVSpecialCasesDeNorm
72 from ieee754.fpdiv.divstages import (FPDivStagesSetup,
73 FPDivStagesIntermediate,
74 FPDivStagesFinal)
75 from ieee754.pipeline import PipelineSpec
76 from ieee754.div_rem_sqrt_rsqrt.core import DivPipeCoreConfig
77 from nmutil.dynamicpipe import MaskCancellableRedir
78
79
80 class FPDIVBasePipe(ControlBase):
81 def __init__(self, pspec):
82 self.pspec = pspec
83 ControlBase.__init__(self, maskwid=pspec.maskwid)
84
85 pipechain = []
86 # to which the answer: "as few as possible"
87 # is required. too many ReservationStations
88 # means "big problems".
89
90 # get number of stages, set up loop.
91 n_stages = pspec.core_config.n_stages
92 max_n_comb_stages = self.pspec.n_comb_stages
93 print("n_stages", n_stages)
94 stage_idx = 0
95
96 end = False
97 while not end:
98
99 n_comb_stages = max_n_comb_stages
100 # needs to convert input from pipestart ospec
101 if stage_idx == 0:
102 n_comb_stages -= 1
103 kls = FPDivStagesSetup # does n_comb_stages-1 calcs as well
104
105 # needs to convert output to pipeend ispec
106 elif stage_idx + n_comb_stages >= n_stages:
107 kls = FPDivStagesFinal # does n_comb_stages-1 calcs as well
108 end = True
109 n_comb_stages = n_stages - stage_idx
110
111 # intermediary stage
112 else:
113 kls = FPDivStagesIntermediate # does n_comb_stages calcs
114
115 # create (in each pipe) a StageChain n_comb_stages in length
116 pipechain.append(kls(self.pspec, n_comb_stages, stage_idx))
117 stage_idx += n_comb_stages # increment so that each CalcStage
118 # gets a (correct) unique index
119
120 self.pipechain = pipechain
121
122 # start and end: unpack/specialcases then normalisation/packing
123 self.pipestart = pipestart = FPDIVSpecialCasesDeNorm(self.pspec)
124 self.pipeend = pipeend = FPNormToPack(self.pspec)
125
126 self._eqs = self.connect([pipestart] + pipechain + [pipeend])
127
128 def elaborate(self, platform):
129 m = ControlBase.elaborate(self, platform)
130
131 # add submodules
132 m.submodules.scnorm = self.pipestart
133 for i, p in enumerate(self.pipechain):
134 setattr(m.submodules, "pipediv%d" % i, p)
135 m.submodules.normpack = self.pipeend
136
137 # ControlBase.connect creates the "eqs" needed to connect each pipe
138 m.d.comb += self._eqs
139
140 return m
141
142
143 def roundup(x, mod):
144 return x if x % mod == 0 else x + mod - x % mod
145
146
147 class FPDIVMuxInOut(ReservationStations):
148 """ Reservation-Station version of FPDIV pipeline.
149
150 * fan-in on inputs (an array of FPBaseData: a,b,mid)
151 * N-stage divider pipeline
152 * fan-out on outputs (an array of FPPackData: z,mid)
153
154 Fan-in and Fan-out are combinatorial.
155
156 :op_wid: - set this to the width of an operator which can
157 then be used to change the behaviour of the pipeline.
158 """
159
160 def __init__(self, width, num_rows, op_wid=2):
161 self.id_wid = num_bits(num_rows)
162 self.pspec = PipelineSpec(width, self.id_wid, op_wid)
163
164 # get the standard mantissa width, store in the pspec
165 fmt = FPFormat.standard(width)
166 log2_radix = 3 # tested options so far: 1, 2 and 3.
167 n_comb_stages = 2 # 2 compute stages per pipeline stage
168 maskwid = 1 # SIMD width effectively
169
170 # extra bits needed: guard + round (sticky comes from remainer.bool())
171 fraction_width = fmt.fraction_width
172 fraction_width += 2
173
174 # rounding width to a multiple of log2_radix is not needed,
175 # DivPipeCoreCalculateStage just internally reduces log2_radix on
176 # the last stage
177 cfg = DivPipeCoreConfig(fmt.width, fraction_width, log2_radix)
178
179 self.pspec.pipekls = MaskCancellableRedir
180 self.pspec.maskwid = maskwid * num_rows # RS gets just maskwid
181 self.pspec.fpformat = fmt
182 self.pspec.n_comb_stages = n_comb_stages
183 self.pspec.core_config = cfg
184
185 # XXX TODO - a class (or function?) that takes the pspec (right here)
186 # and creates... "something". that "something" MUST have an eq function
187 # new_pspec = deepcopy(self.pspec)
188 # new_pspec.opkls = DivPipeCoreOperation
189 # self.alu = FPDIVBasePipe(new_pspec)
190 self.alu = FPDIVBasePipe(self.pspec)
191 ReservationStations.__init__(self, num_rows, maskwid=maskwid)