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