rename FPADDBaseData to FPBaseData and move to separate module
[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: FPDivStage0Mod,
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 FPDivStage2Mod
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.basedata import FPBaseData
70 from ieee754.fpcommon.denorm import FPSCData
71 from ieee754.fpcommon.fpbase import FPFormat
72 from ieee754.fpcommon.pack import FPPackData
73 from ieee754.fpcommon.normtopack import FPNormToPack
74 from ieee754.fpdiv.specialcases import FPDIVSpecialCasesDeNorm
75 from ieee754.fpdiv.divstages import (FPDivStagesSetup,
76 FPDivStagesIntermediate,
77 FPDivStagesFinal)
78 from ieee754.pipeline import PipelineSpec
79 from ieee754.div_rem_sqrt_rsqrt.core import DivPipeCoreConfig
80
81
82 class FPDIVBasePipe(ControlBase):
83 def __init__(self, pspec):
84 self.pspec = pspec
85 ControlBase.__init__(self)
86
87 pipechain = []
88 # to which the answer: "as few as possible"
89 # is required. too many ReservationStations
90 # means "big problems".
91
92 # get number of stages, set up loop.
93 n_stages = pspec.core_config.n_stages
94 max_n_comb_stages = self.pspec.n_comb_stages
95 print("n_stages", n_stages)
96 stage_idx = 0
97
98 end = False
99 while not end:
100
101 n_comb_stages = max_n_comb_stages
102 # needs to convert input from pipestart ospec
103 if stage_idx == 0:
104 n_comb_stages -= 1
105 kls = FPDivStagesSetup # does n_comb_stages-1 calcs as well
106
107 # needs to convert output to pipeend ispec
108 elif stage_idx + n_comb_stages >= n_stages:
109 kls = FPDivStagesFinal # does n_comb_stages-1 calcs as well
110 end = True
111 n_comb_stages = n_stages - stage_idx
112
113 # intermediary stage
114 else:
115 kls = FPDivStagesIntermediate # does n_comb_stages calcs
116
117 # create (in each pipe) a StageChain n_comb_stages in length
118 pipechain.append(kls(self.pspec, n_comb_stages, stage_idx))
119 stage_idx += n_comb_stages # increment so that each CalcStage
120 # gets a (correct) unique index
121
122 self.pipechain = pipechain
123
124 # start and end: unpack/specialcases then normalisation/packing
125 self.pipestart = pipestart = FPDIVSpecialCasesDeNorm(self.pspec)
126 self.pipeend = pipeend = FPNormToPack(self.pspec)
127
128 self._eqs = self.connect([pipestart] + pipechain + [pipeend])
129
130 def elaborate(self, platform):
131 m = ControlBase.elaborate(self, platform)
132
133 # add submodules
134 m.submodules.scnorm = self.pipestart
135 for i, p in enumerate(self.pipechain):
136 setattr(m.submodules, "pipediv%d" % i, p)
137 m.submodules.normpack = self.pipeend
138
139 # ControlBase.connect creates the "eqs" needed to connect each pipe
140 m.d.comb += self._eqs
141
142 return m
143
144
145 def roundup(x, mod):
146 return x if x % mod == 0 else x + mod - x % mod
147
148
149 class FPDIVMuxInOut(ReservationStations):
150 """ Reservation-Station version of FPDIV pipeline.
151
152 * fan-in on inputs (an array of FPBaseData: a,b,mid)
153 * N-stage divider pipeline
154 * fan-out on outputs (an array of FPPackData: z,mid)
155
156 Fan-in and Fan-out are combinatorial.
157
158 :op_wid: - set this to the width of an operator which can
159 then be used to change the behaviour of the pipeline.
160 """
161
162 def __init__(self, width, num_rows, op_wid=2):
163 self.id_wid = num_bits(num_rows)
164 self.pspec = PipelineSpec(width, self.id_wid, op_wid)
165
166 # get the standard mantissa width, store in the pspec
167 fmt = FPFormat.standard(width)
168 log2_radix = 3 # tested options so far: 1, 2 and 3.
169 n_comb_stages = 2 # 2 compute stages per pipeline stage
170
171 # extra bits needed: guard + round (sticky comes from remainer.bool())
172 fraction_width = fmt.fraction_width
173 fraction_width += 2
174
175 # rounding width to a multiple of log2_radix is not needed,
176 # DivPipeCoreCalculateStage just internally reduces log2_radix on
177 # the last stage
178 cfg = DivPipeCoreConfig(fmt.width, fraction_width, log2_radix)
179
180 self.pspec.fpformat = fmt
181 self.pspec.n_comb_stages = n_comb_stages
182 self.pspec.core_config = cfg
183
184 # XXX TODO - a class (or function?) that takes the pspec (right here)
185 # and creates... "something". that "something" MUST have an eq function
186 # new_pspec = deepcopy(self.pspec)
187 # new_pspec.opkls = DivPipeCoreOperation
188 # self.alu = FPDIVBasePipe(new_pspec)
189 self.alu = FPDIVBasePipe(self.pspec)
190 ReservationStations.__init__(self, num_rows)
191
192 def i_specfn(self):
193 return FPBaseData(self.pspec)
194
195 def o_specfn(self):
196 return FPPackData(self.pspec)