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