667941649afdbd642bee6cd618742eac6c7aab3a
[ieee754fpu.git] / src / add / fpadd / align.py
1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Module, Signal, Cat, Mux, Array, Const
6 from nmigen.lib.coding import PriorityEncoder
7 from nmigen.cli import main, verilog
8 from math import log
9
10 from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase, FPNumBase
11 from fpbase import MultiShiftRMerge, Trigger
12 from singlepipe import (ControlBase, StageChain, UnbufferedPipeline,
13 PassThroughStage)
14 from multipipe import CombMuxOutPipe
15 from multipipe import PriorityCombMuxInPipe
16
17 from fpbase import FPState, FPID
18 from fpcommon.getop import (FPGetOpMod, FPGetOp, FPNumBase2Ops, FPADDBaseData,
19 FPGet2OpMod, FPGet2Op)
20 from fpadd.specialcases import (FPAddSpecialCasesMod, FPAddSpecialCases,
21 FPAddSpecialCasesDeNorm)
22 from fpcommon.denorm import (FPSCData, FPAddDeNormMod, FPAddDeNorm)
23 from fpcommon.postcalc import FPAddStage1Data
24 from fpcommon.postnormalise import (FPNorm1Data, FPNorm1ModSingle,
25 FPNorm1ModMulti, FPNorm1Single, FPNorm1Multi)
26 from fpcommon.roundz import (FPRoundData, FPRoundMod, FPRound)
27 from fpcommon.corrections import (FPCorrectionsMod, FPCorrections)
28 from fpcommon.pack import (FPPackData, FPPackMod, FPPack)
29 from fpcommon.normtopack import FPNormToPack
30 from fpcommon.putz import (FPPutZ, FPPutZIdx)
31
32
33 class FPNumIn2Ops:
34
35 def __init__(self, width, id_wid):
36 self.a = FPNumIn(None, width)
37 self.b = FPNumIn(None, width)
38 self.z = FPNumOut(width, False)
39 self.out_do_z = Signal(reset_less=True)
40 self.oz = Signal(width, reset_less=True)
41 self.mid = Signal(id_wid, reset_less=True)
42
43 def eq(self, i):
44 return [self.z.eq(i.z), self.out_do_z.eq(i.out_do_z), self.oz.eq(i.oz),
45 self.a.eq(i.a), self.b.eq(i.b), self.mid.eq(i.mid)]
46
47
48
49 class FPAddAlignMultiMod(FPState):
50
51 def __init__(self, width):
52 self.in_a = FPNumBase(width)
53 self.in_b = FPNumBase(width)
54 self.out_a = FPNumIn(None, width)
55 self.out_b = FPNumIn(None, width)
56 self.exp_eq = Signal(reset_less=True)
57
58 def elaborate(self, platform):
59 # This one however (single-cycle) will do the shift
60 # in one go.
61
62 m = Module()
63
64 m.submodules.align_in_a = self.in_a
65 m.submodules.align_in_b = self.in_b
66 m.submodules.align_out_a = self.out_a
67 m.submodules.align_out_b = self.out_b
68
69 # NOTE: this does *not* do single-cycle multi-shifting,
70 # it *STAYS* in the align state until exponents match
71
72 # exponent of a greater than b: shift b down
73 m.d.comb += self.exp_eq.eq(0)
74 m.d.comb += self.out_a.eq(self.in_a)
75 m.d.comb += self.out_b.eq(self.in_b)
76 agtb = Signal(reset_less=True)
77 altb = Signal(reset_less=True)
78 m.d.comb += agtb.eq(self.in_a.e > self.in_b.e)
79 m.d.comb += altb.eq(self.in_a.e < self.in_b.e)
80 with m.If(agtb):
81 m.d.comb += self.out_b.shift_down(self.in_b)
82 # exponent of b greater than a: shift a down
83 with m.Elif(altb):
84 m.d.comb += self.out_a.shift_down(self.in_a)
85 # exponents equal: move to next stage.
86 with m.Else():
87 m.d.comb += self.exp_eq.eq(1)
88 return m
89
90
91 class FPAddAlignMulti(FPState):
92
93 def __init__(self, width, id_wid):
94 FPState.__init__(self, "align")
95 self.mod = FPAddAlignMultiMod(width)
96 self.out_a = FPNumIn(None, width)
97 self.out_b = FPNumIn(None, width)
98 self.exp_eq = Signal(reset_less=True)
99
100 def setup(self, m, in_a, in_b):
101 """ links module to inputs and outputs
102 """
103 m.submodules.align = self.mod
104 m.d.comb += self.mod.in_a.eq(in_a)
105 m.d.comb += self.mod.in_b.eq(in_b)
106 m.d.comb += self.exp_eq.eq(self.mod.exp_eq)
107 m.d.sync += self.out_a.eq(self.mod.out_a)
108 m.d.sync += self.out_b.eq(self.mod.out_b)
109
110 def action(self, m):
111 with m.If(self.exp_eq):
112 m.next = "add_0"
113
114
115 class FPAddAlignSingleMod:
116
117 def __init__(self, width, id_wid):
118 self.width = width
119 self.id_wid = id_wid
120 self.i = self.ispec()
121 self.o = self.ospec()
122
123 def ispec(self):
124 return FPSCData(self.width, self.id_wid)
125
126 def ospec(self):
127 return FPNumIn2Ops(self.width, self.id_wid)
128
129 def process(self, i):
130 return self.o
131
132 def setup(self, m, i):
133 """ links module to inputs and outputs
134 """
135 m.submodules.align = self
136 m.d.comb += self.i.eq(i)
137
138 def elaborate(self, platform):
139 """ Aligns A against B or B against A, depending on which has the
140 greater exponent. This is done in a *single* cycle using
141 variable-width bit-shift
142
143 the shifter used here is quite expensive in terms of gates.
144 Mux A or B in (and out) into temporaries, as only one of them
145 needs to be aligned against the other
146 """
147 m = Module()
148
149 m.submodules.align_in_a = self.i.a
150 m.submodules.align_in_b = self.i.b
151 m.submodules.align_out_a = self.o.a
152 m.submodules.align_out_b = self.o.b
153
154 # temporary (muxed) input and output to be shifted
155 t_inp = FPNumBase(self.width)
156 t_out = FPNumIn(None, self.width)
157 espec = (len(self.i.a.e), True)
158 msr = MultiShiftRMerge(self.i.a.m_width, espec)
159 m.submodules.align_t_in = t_inp
160 m.submodules.align_t_out = t_out
161 m.submodules.multishift_r = msr
162
163 ediff = Signal(espec, reset_less=True)
164 ediffr = Signal(espec, reset_less=True)
165 tdiff = Signal(espec, reset_less=True)
166 elz = Signal(reset_less=True)
167 egz = Signal(reset_less=True)
168
169 # connect multi-shifter to t_inp/out mantissa (and tdiff)
170 m.d.comb += msr.inp.eq(t_inp.m)
171 m.d.comb += msr.diff.eq(tdiff)
172 m.d.comb += t_out.m.eq(msr.m)
173 m.d.comb += t_out.e.eq(t_inp.e + tdiff)
174 m.d.comb += t_out.s.eq(t_inp.s)
175
176 m.d.comb += ediff.eq(self.i.a.e - self.i.b.e)
177 m.d.comb += ediffr.eq(self.i.b.e - self.i.a.e)
178 m.d.comb += elz.eq(self.i.a.e < self.i.b.e)
179 m.d.comb += egz.eq(self.i.a.e > self.i.b.e)
180
181 # default: A-exp == B-exp, A and B untouched (fall through)
182 m.d.comb += self.o.a.eq(self.i.a)
183 m.d.comb += self.o.b.eq(self.i.b)
184 # only one shifter (muxed)
185 #m.d.comb += t_out.shift_down_multi(tdiff, t_inp)
186 # exponent of a greater than b: shift b down
187 with m.If(~self.i.out_do_z):
188 with m.If(egz):
189 m.d.comb += [t_inp.eq(self.i.b),
190 tdiff.eq(ediff),
191 self.o.b.eq(t_out),
192 self.o.b.s.eq(self.i.b.s), # whoops forgot sign
193 ]
194 # exponent of b greater than a: shift a down
195 with m.Elif(elz):
196 m.d.comb += [t_inp.eq(self.i.a),
197 tdiff.eq(ediffr),
198 self.o.a.eq(t_out),
199 self.o.a.s.eq(self.i.a.s), # whoops forgot sign
200 ]
201
202 m.d.comb += self.o.mid.eq(self.i.mid)
203 m.d.comb += self.o.z.eq(self.i.z)
204 m.d.comb += self.o.out_do_z.eq(self.i.out_do_z)
205 m.d.comb += self.o.oz.eq(self.i.oz)
206
207 return m
208
209
210 class FPAddAlignSingle(FPState):
211
212 def __init__(self, width, id_wid):
213 FPState.__init__(self, "align")
214 self.mod = FPAddAlignSingleMod(width, id_wid)
215 self.out_a = FPNumIn(None, width)
216 self.out_b = FPNumIn(None, width)
217
218 def setup(self, m, i):
219 """ links module to inputs and outputs
220 """
221 self.mod.setup(m, i)
222
223 # NOTE: could be done as comb
224 m.d.sync += self.out_a.eq(self.mod.out_a)
225 m.d.sync += self.out_b.eq(self.mod.out_b)
226
227 def action(self, m):
228 m.next = "add_0"
229
230