resolve awful meta-class hacking (with thanks to jsbueno on stackexchange)
[ieee754fpu.git] / src / ieee754 / 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, Elaboratable
6 from nmigen.cli import main, verilog
7
8 from ieee754.fpcommon.fpbase import FPNumOut, FPNumIn, FPNumBase
9 from ieee754.fpcommon.fpbase import FPNumBaseRecord
10 from ieee754.fpcommon.fpbase import MultiShiftRMerge
11 from ieee754.fpcommon.fpbase import FPState
12 from ieee754.fpcommon.denorm import FPSCData
13 from ieee754.fpcommon.getop import FPPipeContext
14
15
16 class FPNumIn2Ops:
17
18 def __init__(self, pspec):
19 width = pspec.width
20 self.a = FPNumBaseRecord(width)
21 self.b = FPNumBaseRecord(width)
22 self.z = FPNumBaseRecord(width, False)
23 self.out_do_z = Signal(reset_less=True)
24 self.oz = Signal(width, reset_less=True)
25 self.ctx = FPPipeContext(pspec)
26 self.muxid = self.ctx.muxid
27
28 def eq(self, i):
29 return [self.z.eq(i.z), self.out_do_z.eq(i.out_do_z), self.oz.eq(i.oz),
30 self.a.eq(i.a), self.b.eq(i.b), self.ctx.eq(i.ctx)]
31
32
33
34 class FPAddAlignMultiMod(FPState):
35
36 def __init__(self, width):
37 self.in_a = FPNumBaseRecord(width)
38 self.in_b = FPNumBaseRecord(width)
39 self.out_a = FPNumBaseRecord(width)
40 self.out_b = FPNumBaseRecord(width)
41 self.exp_eq = Signal(reset_less=True)
42
43 def elaborate(self, platform):
44 # This one however (single-cycle) will do the shift
45 # in one go.
46
47 m = Module()
48
49 #m.submodules.align_in_a = self.in_a
50 #m.submodules.align_in_b = self.in_b
51 #m.submodules.align_out_a = self.out_a
52 #m.submodules.align_out_b = self.out_b
53
54 # NOTE: this does *not* do single-cycle multi-shifting,
55 # it *STAYS* in the align state until exponents match
56
57 # exponent of a greater than b: shift b down
58 m.d.comb += self.exp_eq.eq(0)
59 m.d.comb += self.out_a.eq(self.in_a)
60 m.d.comb += self.out_b.eq(self.in_b)
61 agtb = Signal(reset_less=True)
62 altb = Signal(reset_less=True)
63 m.d.comb += agtb.eq(self.in_a.e > self.in_b.e)
64 m.d.comb += altb.eq(self.in_a.e < self.in_b.e)
65 with m.If(agtb):
66 m.d.comb += self.out_b.shift_down(self.in_b)
67 # exponent of b greater than a: shift a down
68 with m.Elif(altb):
69 m.d.comb += self.out_a.shift_down(self.in_a)
70 # exponents equal: move to next stage.
71 with m.Else():
72 m.d.comb += self.exp_eq.eq(1)
73 return m
74
75
76 class FPAddAlignMulti(FPState):
77
78 def __init__(self, pspec):
79 FPState.__init__(self, "align")
80 self.mod = FPAddAlignMultiMod(pspec)
81 self.out_a = FPNumBaseRecord(width)
82 self.out_b = FPNumBaseRecord(width)
83 self.exp_eq = Signal(reset_less=True)
84
85 def setup(self, m, in_a, in_b):
86 """ links module to inputs and outputs
87 """
88 m.submodules.align = self.mod
89 m.d.comb += self.mod.in_a.eq(in_a)
90 m.d.comb += self.mod.in_b.eq(in_b)
91 m.d.comb += self.exp_eq.eq(self.mod.exp_eq)
92 m.d.sync += self.out_a.eq(self.mod.out_a)
93 m.d.sync += self.out_b.eq(self.mod.out_b)
94
95 def action(self, m):
96 with m.If(self.exp_eq):
97 m.next = "add_0"
98
99
100 class FPAddAlignSingleMod(Elaboratable):
101
102 def __init__(self, pspec):
103 self.pspec = pspec
104 self.i = self.ispec()
105 self.o = self.ospec()
106
107 def ispec(self):
108 return FPSCData(self.pspec, True)
109
110 def ospec(self):
111 return FPNumIn2Ops(self.pspec)
112
113 def process(self, i):
114 return self.o
115
116 def setup(self, m, i):
117 """ links module to inputs and outputs
118 """
119 m.submodules.align = self
120 m.d.comb += self.i.eq(i)
121
122 def elaborate(self, platform):
123 """ Aligns A against B or B against A, depending on which has the
124 greater exponent. This is done in a *single* cycle using
125 variable-width bit-shift
126
127 the shifter used here is quite expensive in terms of gates.
128 Mux A or B in (and out) into temporaries, as only one of them
129 needs to be aligned against the other
130 """
131 m = Module()
132
133 #m.submodules.align_in_a = self.i.a
134 #m.submodules.align_in_b = self.i.b
135 #m.submodules.align_out_a = self.o.a
136 #m.submodules.align_out_b = self.o.b
137
138 # temporary (muxed) input and output to be shifted
139 width = self.pspec.width
140 t_inp = FPNumBaseRecord(width)
141 t_out = FPNumBaseRecord(width)
142 espec = (len(self.i.a.e), True)
143 msr = MultiShiftRMerge(self.i.a.m_width, espec)
144 #m.submodules.align_t_in = t_inp
145 #m.submodules.align_t_out = t_out
146 m.submodules.multishift_r = msr
147
148 ediff = Signal(espec, reset_less=True)
149 ediffr = Signal(espec, reset_less=True)
150 tdiff = Signal(espec, reset_less=True)
151 elz = Signal(reset_less=True)
152 egz = Signal(reset_less=True)
153
154 # connect multi-shifter to t_inp/out mantissa (and tdiff)
155 m.d.comb += msr.inp.eq(t_inp.m)
156 m.d.comb += msr.diff.eq(tdiff)
157 m.d.comb += t_out.m.eq(msr.m)
158 m.d.comb += t_out.e.eq(t_inp.e + tdiff)
159 m.d.comb += t_out.s.eq(t_inp.s)
160
161 m.d.comb += ediff.eq(self.i.a.e - self.i.b.e)
162 m.d.comb += ediffr.eq(self.i.b.e - self.i.a.e)
163 m.d.comb += elz.eq(self.i.a.e < self.i.b.e)
164 m.d.comb += egz.eq(self.i.a.e > self.i.b.e)
165
166 # default: A-exp == B-exp, A and B untouched (fall through)
167 m.d.comb += self.o.a.eq(self.i.a)
168 m.d.comb += self.o.b.eq(self.i.b)
169 # only one shifter (muxed)
170 #m.d.comb += t_out.shift_down_multi(tdiff, t_inp)
171 # exponent of a greater than b: shift b down
172 with m.If(~self.i.out_do_z):
173 with m.If(egz):
174 m.d.comb += [t_inp.eq(self.i.b),
175 tdiff.eq(ediff),
176 self.o.b.eq(t_out),
177 self.o.b.s.eq(self.i.b.s), # whoops forgot sign
178 ]
179 # exponent of b greater than a: shift a down
180 with m.Elif(elz):
181 m.d.comb += [t_inp.eq(self.i.a),
182 tdiff.eq(ediffr),
183 self.o.a.eq(t_out),
184 self.o.a.s.eq(self.i.a.s), # whoops forgot sign
185 ]
186
187 m.d.comb += self.o.ctx.eq(self.i.ctx)
188 m.d.comb += self.o.z.eq(self.i.z)
189 m.d.comb += self.o.out_do_z.eq(self.i.out_do_z)
190 m.d.comb += self.o.oz.eq(self.i.oz)
191
192 return m
193
194
195 class FPAddAlignSingle(FPState):
196
197 def __init__(self, pspec):
198 FPState.__init__(self, "align")
199 width = pspec.width
200 self.mod = FPAddAlignSingleMod(pspec)
201 self.out_a = FPNumIn(None, width)
202 self.out_b = FPNumIn(None, width)
203
204 def setup(self, m, i):
205 """ links module to inputs and outputs
206 """
207 self.mod.setup(m, i)
208
209 # NOTE: could be done as comb
210 m.d.sync += self.out_a.eq(self.mod.out_a)
211 m.d.sync += self.out_b.eq(self.mod.out_b)
212
213 def action(self, m):
214 m.next = "add_0"
215
216