config/setup/imports
[ieee754fpu.git] / src / ieee754 / fpdiv / pipeline.py
1 """IEEE Floating Point Divider Pipeline
2
3 Relevant bugreport: http://bugs.libre-riscv.org/show_bug.cgi?id=99
4
5 Stack looks like this:
6
7 scnorm - FPDIVSpecialCasesDeNorm ispec FPADDBaseData
8 ------ ospec FPSCData
9
10 StageChain: FPDIVSpecialCasesMod,
11 FPAddDeNormMod
12
13 pipediv0 - FPDivStagesSetup ispec FPSCData
14 -------- ospec DivPipeInterstageData
15
16 StageChain: FPDivStage0Mod,
17 DivPipeSetupStage,
18 DivPipeCalculateStage,
19 ...
20 DivPipeCalculateStage
21
22 pipediv1 - FPDivStagesIntermediate ispec DivPipeInterstageData
23 -------- ospec DivPipeInterstageData
24
25 StageChain: DivPipeCalculateStage,
26 ...
27 DivPipeCalculateStage
28 ...
29 ...
30
31 pipediv5 - FPDivStageFinal ispec FPDivStage0Data
32 -------- ospec FPAddStage1Data
33
34 StageChain: DivPipeCalculateStage,
35 ...
36 DivPipeCalculateStage,
37 DivPipeFinalStage,
38 FPDivStage2Mod
39
40 normpack - FPNormToPack ispec FPAddStage1Data
41 -------- ospec FPPackData
42
43 StageChain: Norm1ModSingle,
44 RoundMod,
45 CorrectionsMod,
46 PackMod
47
48 the number of combinatorial StageChains (n_comb_stages) in
49 FPDivStages is an argument arranged to get the length of the whole
50 pipeline down to sane numbers.
51
52 the reason for keeping the number of stages down is that for every
53 pipeline clock delay, a corresponding ReservationStation is needed.
54 if there are 24 pipeline stages, we need a whopping TWENTY FOUR
55 RS's. that's far too many. 6 is just about an acceptable number.
56 even 8 is starting to get alarmingly high.
57 """
58
59 from nmigen import Module
60 from nmigen.cli import main, verilog
61
62 from nmutil.singlepipe import ControlBase
63 from nmutil.concurrentunit import ReservationStations, num_bits
64
65 from ieee754.fpcommon.getop import FPADDBaseData
66 from ieee754.fpcommon.denorm import FPSCData
67 from ieee754.fpcommon.fpbase import FPFormat
68 from ieee754.fpcommon.pack import FPPackData
69 from ieee754.fpcommon.normtopack import FPNormToPack
70 from ieee754.fpdiv.specialcases import FPDIVSpecialCasesDeNorm
71 from ieee754.fpdiv.divstages import (FPDivStagesSetup,
72 FPDivStagesIntermediate,
73 FPDivStagesFinal)
74 from ieee754.pipeline import PipelineSpec
75 from ieee754.div_rem_sqrt_rsqrt.core import DivPipeCoreConfig
76
77
78 class FPDIVBasePipe(ControlBase):
79 def __init__(self, pspec):
80 self.pspec = pspec
81 ControlBase.__init__(self)
82
83 pipechain = []
84 max_n_comb_stages = 2 # TODO (depends on how many RS's we want)
85 n_stages = pspec.fpformat.m_width // max_n_comb_stages
86 stage_idx = 0
87 # to which the answer: "as few as possible"
88 # is required. too many ReservationStations
89 # means "big problems".
90
91 for i in range(n_stages):
92
93 n_comb_stages = max_n_comb_stages
94 # needs to convert input from pipestart ospec
95 if i == 0:
96 kls = FPDivStagesSetup
97 n_comb_stages -= 1 # reduce due to work done at start
98
99 # needs to convert output to pipeend ispec
100 elif i == n_stages - 1:
101 kls = FPDivStagesFinal
102 n_comb_stages -= 1 # FIXME - reduce due to work done at end?
103
104 # intermediary stage
105 else:
106 kls = FPDivStagesIntermediate
107
108 pipechain.append(kls(self.pspec, n_comb_stages, stage_idx))
109 stage_idx += n_comb_stages # increment so that each CalcStage
110 # gets a (correct) unique index
111
112 self.pipechain = pipechain
113
114 # start and end: unpack/specialcases then normalisation/packing
115 self.pipestart = pipestart = FPDIVSpecialCasesDeNorm(self.pspec)
116 self.pipeend = pipeend = FPNormToPack(self.pspec)
117
118 self._eqs = self.connect([pipestart] + pipechain + [pipeend])
119
120 def elaborate(self, platform):
121 m = ControlBase.elaborate(self, platform)
122
123 # add submodules
124 m.submodules.scnorm = self.pipestart
125 for i, p in enumerate(self.pipechain):
126 setattr(m.submodules, "pipediv%d" % i, p)
127 m.submodules.normpack = self.pipeend
128
129 # ControlBase.connect creates the "eqs" needed to connect each pipe
130 m.d.comb += self._eqs
131
132 return m
133
134
135 class FPDIVMuxInOut(ReservationStations):
136 """ Reservation-Station version of FPDIV pipeline.
137
138 * fan-in on inputs (an array of FPADDBaseData: a,b,mid)
139 * N-stage divider pipeline
140 * fan-out on outputs (an array of FPPackData: z,mid)
141
142 Fan-in and Fan-out are combinatorial.
143
144 :op_wid: - set this to the width of an operator which can
145 then be used to change the behaviour of the pipeline.
146 """
147
148 def __init__(self, width, num_rows, op_wid=0):
149 self.id_wid = num_bits(width)
150 self.pspec = PipelineSpec(width, self.id_wid, op_wid)
151 # get the standard mantissa width, store in the pspec
152 # (used in DivPipeBaseStage.get_core_config)
153 fpformat = FPFormat.standard(width)
154 log2_radix = 2
155
156 # 4 extra bits on the mantissa: MSB is zero, MSB-1 is 1
157 # then there is guard and round at the LSB end
158 cfg = DivPipeCoreConfig(width+4, fpformat.fraction_width, log2_radix)
159
160 self.pspec.fpformat = fpformat
161 self.pspec.log2_radix = log2_radix
162 self.pspec.core_config = cfg
163
164 # XXX TODO - a class (or function?) that takes the pspec (right here)
165 # and creates... "something". that "something" MUST have an eq function
166 # new_pspec = deepcopy(self.pspec)
167 # new_pspec.opkls = DivPipeCoreOperation
168 # self.alu = FPDIVBasePipe(new_pspec)
169 self.alu = FPDIVBasePipe(self.pspec)
170 ReservationStations.__init__(self, num_rows)
171
172 def i_specfn(self):
173 return FPADDBaseData(self.pspec)
174
175 def o_specfn(self):
176 return FPPackData(self.pspec)