create get_op function
[ieee754fpu.git] / src / add / nmigen_add_experiment.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, Const
6 from nmigen.cli import main, verilog
7
8
9 class FPNum:
10 """ Floating-point Number Class, variable-width TODO (currently 32-bit)
11
12 Contains signals for an incoming copy of the value, decoded into
13 sign / exponent / mantissa.
14 Also contains encoding functions, creation and recognition of
15 zero, NaN and inf (all signed)
16
17 Four extra bits are included in the mantissa: the top bit
18 (m[-1]) is effectively a carry-overflow. The other three are
19 guard (m[2]), round (m[1]), and sticky (m[0])
20 """
21 def __init__(self, width, m_width=None):
22 self.width = width
23 if m_width is None:
24 m_width = width - 5 # mantissa extra bits (top,guard,round)
25 self.v = Signal(width) # Latched copy of value
26 self.m = Signal(m_width) # Mantissa
27 self.e = Signal((10, True)) # Exponent: 10 bits, signed
28 self.s = Signal() # Sign bit
29
30 self.mzero = Const(0, (m_width, False))
31 self.m1s = Const(-1, (m_width, False))
32 self.P128 = Const(128, (10, True))
33 self.P127 = Const(127, (10, True))
34 self.N127 = Const(-127, (10, True))
35 self.N126 = Const(-126, (10, True))
36
37 def decode(self):
38 """ decodes a latched value into sign / exponent / mantissa
39
40 bias is subtracted here, from the exponent. exponent
41 is extended to 10 bits so that subtract 127 is done on
42 a 10-bit number
43 """
44 v = self.v
45 return [self.m.eq(Cat(0, 0, 0, v[0:23])), # mantissa
46 self.e.eq(v[23:31] - self.P127), # exp (minus bias)
47 self.s.eq(v[31]), # sign
48 ]
49
50 def create(self, s, e, m):
51 """ creates a value from sign / exponent / mantissa
52
53 bias is added here, to the exponent
54 """
55 return [
56 self.v[31].eq(s), # sign
57 self.v[23:31].eq(e + self.P127), # exp (add on bias)
58 self.v[0:23].eq(m) # mantissa
59 ]
60
61 def shift_down(self):
62 """ shifts a mantissa down by one. exponent is increased to compensate
63
64 accuracy is lost as a result in the mantissa however there are 3
65 guard bits (the latter of which is the "sticky" bit)
66 """
67 return [self.e.eq(self.e + 1),
68 self.m.eq(Cat(self.m[0] | self.m[1], self.m[2:], 0))
69 ]
70
71 def nan(self, s):
72 return self.create(s, self.P128, 1<<22)
73
74 def inf(self, s):
75 return self.create(s, self.P128, 0)
76
77 def zero(self, s):
78 return self.create(s, self.N127, 0)
79
80 def is_nan(self):
81 return (self.e == self.P128) & (self.m != 0)
82
83 def is_inf(self):
84 return (self.e == self.P128) & (self.m == 0)
85
86 def is_zero(self):
87 return (self.e == self.N127) & (self.m == self.mzero)
88
89 def is_overflowed(self):
90 return (self.e > self.P127)
91
92 def is_denormalised(self):
93 return (self.e == self.N126) & (self.m[23] == 0)
94
95 class FPOp:
96 def __init__(self, width):
97 self.width = width
98
99 self.v = Signal(width)
100 self.stb = Signal()
101 self.ack = Signal()
102
103 def ports(self):
104 return [self.v, self.stb, self.ack]
105
106
107 class FPADD:
108 def __init__(self, width):
109 self.width = width
110
111 self.in_a = FPOp(width)
112 self.in_b = FPOp(width)
113 self.out_z = FPOp(width)
114
115 def get_op(self, m, op, v, next_state):
116 with m.If((op.ack) & (op.stb)):
117 m.next = next_state
118 m.d.sync += [
119 v.eq(op.v),
120 op.ack.eq(0)
121 ]
122 with m.Else():
123 m.d.sync += op.ack.eq(1)
124
125 def get_fragment(self, platform=None):
126 m = Module()
127
128 # Latches
129 a = FPNum(self.width)
130 b = FPNum(self.width)
131 z = FPNum(self.width, 24)
132
133 tot = Signal(28) # sticky/round/guard bits, 23 result, 1 overflow
134
135 guard = Signal() # tot[2]
136 round_bit = Signal() # tot[1]
137 sticky = Signal() # tot[0]
138
139 with m.FSM() as fsm:
140
141 # ******
142 # gets operand a
143
144 with m.State("get_a"):
145 self.get_op(m, self.in_a, a.v, "get_b")
146
147 # ******
148 # gets operand b
149
150 with m.State("get_b"):
151 with m.If((self.in_b.ack) & (self.in_b.stb)):
152 m.next = "unpack"
153 m.d.sync += [
154 b.v.eq(self.in_b.v),
155 self.in_b.ack.eq(0)
156 ]
157 with m.Else():
158 m.d.sync += self.in_b.ack.eq(1)
159
160 # ******
161 # unpacks operands into sign, mantissa and exponent
162
163 with m.State("unpack"):
164 m.next = "special_cases"
165 m.d.sync += a.decode()
166 m.d.sync += b.decode()
167
168 # ******
169 # special cases: NaNs, infs, zeros, denormalised
170
171 with m.State("special_cases"):
172
173 # if a is NaN or b is NaN return NaN
174 with m.If(a.is_nan() | b.is_nan()):
175 m.next = "put_z"
176 m.d.sync += z.nan(1)
177
178 # if a is inf return inf (or NaN)
179 with m.Elif(a.is_inf()):
180 m.next = "put_z"
181 m.d.sync += z.inf(a.s)
182 # if a is inf and signs don't match return NaN
183 with m.If((b.e == b.P128) & (a.s != b.s)):
184 m.d.sync += z.nan(b.s)
185
186 # if b is inf return inf
187 with m.Elif(b.is_inf()):
188 m.next = "put_z"
189 m.d.sync += z.inf(b.s)
190
191 # if a is zero and b zero return signed-a/b
192 with m.Elif(a.is_zero() & b.is_zero()):
193 m.next = "put_z"
194 m.d.sync += z.create(a.s & b.s, b.e[0:8], b.m[3:-1])
195
196 # if a is zero return b
197 with m.Elif(a.is_zero()):
198 m.next = "put_z"
199 m.d.sync += z.create(b.s, b.e[0:8], b.m[3:-1])
200
201 # if b is zero return a
202 with m.Elif(b.is_zero()):
203 m.next = "put_z"
204 m.d.sync += z.create(a.s, a.e[0:8], a.m[3:-1])
205
206 # Denormalised Number checks
207 with m.Else():
208 m.next = "align"
209 # denormalise a check
210 with m.If(a.e == a.N127):
211 m.d.sync += a.e.eq(-126) # limit a exponent
212 with m.Else():
213 m.d.sync += a.m[-1].eq(1) # set top mantissa bit
214 # denormalise b check
215 with m.If(b.e == a.N127):
216 m.d.sync += b.e.eq(-126) # limit b exponent
217 with m.Else():
218 m.d.sync += b.m[-1].eq(1) # set top mantissa bit
219
220 # ******
221 # align. NOTE: this does *not* do single-cycle multi-shifting,
222 # it *STAYS* in the align state until the exponents match
223
224 with m.State("align"):
225 # exponent of a greater than b: increment b exp, shift b mant
226 with m.If(a.e > b.e):
227 m.d.sync += b.shift_down()
228 # exponent of b greater than a: increment a exp, shift a mant
229 with m.Elif(a.e < b.e):
230 m.d.sync += a.shift_down()
231 # exponents equal: move to next stage.
232 with m.Else():
233 m.next = "add_0"
234
235 # ******
236 # First stage of add. covers same-sign (add) and subtract
237 # special-casing when mantissas are greater or equal, to
238 # give greatest accuracy.
239
240 with m.State("add_0"):
241 m.next = "add_1"
242 m.d.sync += z.e.eq(a.e)
243 # same-sign (both negative or both positive) add mantissas
244 with m.If(a.s == b.s):
245 m.d.sync += [
246 tot.eq(a.m + b.m),
247 z.s.eq(a.s)
248 ]
249 # a mantissa greater than b, use a
250 with m.Elif(a.m >= b.m):
251 m.d.sync += [
252 tot.eq(a.m - b.m),
253 z.s.eq(a.s)
254 ]
255 # b mantissa greater than a, use b
256 with m.Else():
257 m.d.sync += [
258 tot.eq(b.m - a.m),
259 z.s.eq(b.s)
260 ]
261
262 # ******
263 # Second stage of add: preparation for normalisation.
264 # detects when tot sum is too big (tot[27] is kinda a carry bit)
265
266 with m.State("add_1"):
267 m.next = "normalise_1"
268 # tot[27] gets set when the sum overflows. shift result down
269 with m.If(tot[27]):
270 m.d.sync += [
271 z.m.eq(tot[4:28]),
272 guard.eq(tot[3]),
273 round_bit.eq(tot[2]),
274 sticky.eq(tot[1] | tot[0]),
275 z.e.eq(z.e + 1)
276 ]
277 # tot[27] zero case
278 with m.Else():
279 m.d.sync += [
280 z.m.eq(tot[3:27]),
281 guard.eq(tot[2]),
282 round_bit.eq(tot[1]),
283 sticky.eq(tot[0])
284 ]
285
286 # ******
287 # First stage of normalisation.
288 # NOTE: just like "align", this one keeps going round every clock
289 # until the result's exponent is within acceptable "range"
290 # NOTE: the weirdness of reassigning guard and round is due to
291 # the extra mantissa bits coming from tot[0..2]
292
293 with m.State("normalise_1"):
294 with m.If((z.m[-1] == 0) & (z.e > z.N126)):
295 m.d.sync +=[
296 z.e.eq(z.e - 1), # DECREASE exponent
297 z.m.eq(z.m << 1), # shift mantissa UP
298 z.m[0].eq(guard), # steal guard bit (was tot[2])
299 guard.eq(round_bit), # steal round_bit (was tot[1])
300 ]
301 with m.Else():
302 m.next = "normalise_2"
303
304 # ******
305 # Second stage of normalisation.
306 # NOTE: just like "align", this one keeps going round every clock
307 # until the result's exponent is within acceptable "range"
308 # NOTE: the weirdness of reassigning guard and round is due to
309 # the extra mantissa bits coming from tot[0..2]
310
311 with m.State("normalise_2"):
312 with m.If(z.e < z.N126):
313 m.d.sync +=[
314 z.e.eq(z.e + 1), # INCREASE exponent
315 z.m.eq(z.m >> 1), # shift mantissa DOWN
316 guard.eq(z.m[0]),
317 round_bit.eq(guard),
318 sticky.eq(sticky | round_bit)
319 ]
320 with m.Else():
321 m.next = "round"
322
323 # ******
324 # rounding stage
325
326 with m.State("round"):
327 m.next = "corrections"
328 with m.If(guard & (round_bit | sticky | z.m[0])):
329 m.d.sync += z.m.eq(z.m + 1) # mantissa rounds up
330 with m.If(z.m == z.m1s): # all 1s
331 m.d.sync += z.e.eq(z.e + 1) # exponent rounds up
332
333 # ******
334 # correction stage
335
336 with m.State("corrections"):
337 m.next = "pack"
338 # denormalised, correct exponent to zero
339 with m.If(z.is_denormalised()):
340 m.d.sync += z.m.eq(-127)
341 # FIX SIGN BUG: -a + a = +0.
342 with m.If((z.e == z.N126) & (z.m[0:] == 0)):
343 m.d.sync += z.s.eq(0)
344
345 # ******
346 # pack stage
347
348 with m.State("pack"):
349 m.next = "put_z"
350 # if overflow occurs, return inf
351 with m.If(z.is_overflowed()):
352 m.d.sync += z.inf(0)
353 with m.Else():
354 m.d.sync += z.create(z.s, z.e, z.m)
355
356 # ******
357 # put_z stage
358
359 with m.State("put_z"):
360 m.d.sync += [
361 self.out_z.stb.eq(1),
362 self.out_z.v.eq(z.v)
363 ]
364 with m.If(self.out_z.stb & self.out_z.ack):
365 m.d.sync += self.out_z.stb.eq(0)
366 m.next = "get_a"
367
368 return m
369
370
371 if __name__ == "__main__":
372 alu = FPADD(width=32)
373 main(alu, ports=alu.in_a.ports() + alu.in_b.ports() + alu.out_z.ports())
374
375
376 # works... but don't use, just do "python fname.py convert -t v"
377 #print (verilog.convert(alu, ports=[
378 # ports=alu.in_a.ports() + \
379 # alu.in_b.ports() + \
380 # alu.out_z.ports())