sorting out unit tests, comply with IEEE754 on RISCV
[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
6 from nmigen.cli import main, verilog
7
8 from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase, FPNumBase
9
10
11 class FPState(FPBase):
12 def __init__(self, state_from):
13 self.state_from = state_from
14
15 def set_inputs(self, inputs):
16 self.inputs = inputs
17 for k,v in inputs.items():
18 setattr(self, k, v)
19
20 def set_outputs(self, outputs):
21 self.outputs = outputs
22 for k,v in outputs.items():
23 setattr(self, k, v)
24
25
26 class FPGetOpA(FPState):
27 """ gets operand a
28 """
29
30 def __init__(self, in_a, width):
31 FPState.__init__(self, "get_a")
32 self.in_a = in_a
33 self.a = FPNumIn(in_a, width)
34
35 def action(self, m):
36 self.get_op(m, self.in_a, self.a, "get_b")
37
38
39 class FPGetOpB(FPState):
40 """ gets operand b
41 """
42
43 def action(self, m):
44 self.get_op(m, self.in_b, self.b, "special_cases")
45
46
47 class FPAddSpecialCasesMod:
48 """ special cases: NaNs, infs, zeros, denormalised
49 NOTE: some of these are unique to add. see "Special Operations"
50 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
51 """
52
53 def __init__(self, width):
54 self.in_a = FPNumBase(width)
55 self.in_b = FPNumBase(width)
56 self.out_z = FPNumOut(width, False)
57 self.out_do_z = Signal(reset_less=True)
58
59 def setup(self, m, in_a, in_b, out_z, out_do_z):
60 """ links module to inputs and outputs
61 """
62 m.d.comb += self.in_a.copy(in_a)
63 m.d.comb += self.in_b.copy(in_b)
64 m.d.comb += out_z.v.eq(self.out_z.v)
65 m.d.comb += out_do_z.eq(self.out_do_z)
66
67 def elaborate(self, platform):
68 m = Module()
69
70 m.submodules.sc_in_a = self.in_a
71 m.submodules.sc_in_b = self.in_b
72 m.submodules.sc_out_z = self.out_z
73
74 s_nomatch = Signal()
75 m.d.comb += s_nomatch.eq(self.in_a.s != self.in_b.s)
76
77 m_match = Signal()
78 m.d.comb += m_match.eq(self.in_a.m == self.in_b.m)
79
80 # if a is NaN or b is NaN return NaN
81 with m.If(self.in_a.is_nan | self.in_b.is_nan):
82 m.d.comb += self.out_do_z.eq(1)
83 m.d.comb += self.out_z.nan(0)
84
85 # XXX WEIRDNESS for FP16 non-canonical NaN handling
86 # under review
87
88 ## if a is zero and b is NaN return -b
89 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
90 # m.d.comb += self.out_do_z.eq(1)
91 # m.d.comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
92
93 ## if b is zero and a is NaN return -a
94 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
95 # m.d.comb += self.out_do_z.eq(1)
96 # m.d.comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
97
98 ## if a is -zero and b is NaN return -b
99 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
100 # m.d.comb += self.out_do_z.eq(1)
101 # m.d.comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
102
103 ## if b is -zero and a is NaN return -a
104 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
105 # m.d.comb += self.out_do_z.eq(1)
106 # m.d.comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
107
108 # if a is inf return inf (or NaN)
109 with m.Elif(self.in_a.is_inf):
110 m.d.comb += self.out_do_z.eq(1)
111 m.d.comb += self.out_z.inf(self.in_a.s)
112 # if a is inf and signs don't match return NaN
113 with m.If(self.in_b.exp_128 & s_nomatch):
114 m.d.comb += self.out_z.nan(0)
115
116 # if b is inf return inf
117 with m.Elif(self.in_b.is_inf):
118 m.d.comb += self.out_do_z.eq(1)
119 m.d.comb += self.out_z.inf(self.in_b.s)
120
121 # if a is zero and b zero return signed-a/b
122 with m.Elif(self.in_a.is_zero & self.in_b.is_zero):
123 m.d.comb += self.out_do_z.eq(1)
124 m.d.comb += self.out_z.create(self.in_a.s & self.in_b.s,
125 self.in_b.e,
126 self.in_b.m[3:-1])
127
128 # if a is zero return b
129 with m.Elif(self.in_a.is_zero):
130 m.d.comb += self.out_do_z.eq(1)
131 m.d.comb += self.out_z.create(self.in_b.s, self.in_b.e,
132 self.in_b.m[3:-1])
133
134 # if b is zero return a
135 with m.Elif(self.in_b.is_zero):
136 m.d.comb += self.out_do_z.eq(1)
137 m.d.comb += self.out_z.create(self.in_a.s, self.in_a.e,
138 self.in_a.m[3:-1])
139
140 # if a equal to -b return zero (+ve zero)
141 with m.Elif(s_nomatch & m_match & (self.in_a.e == self.in_b.e)):
142 m.d.comb += self.out_do_z.eq(1)
143 m.d.comb += self.out_z.zero(0)
144
145 # Denormalised Number checks
146 with m.Else():
147 m.d.comb += self.out_do_z.eq(0)
148
149 return m
150
151
152 class FPAddSpecialCases(FPState):
153 """ special cases: NaNs, infs, zeros, denormalised
154 NOTE: some of these are unique to add. see "Special Operations"
155 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
156 """
157
158 def __init__(self, width):
159 FPState.__init__(self, "special_cases")
160 self.mod = FPAddSpecialCasesMod(width)
161 self.out_z = FPNumOut(width, False)
162 self.out_do_z = Signal(reset_less=True)
163
164 def action(self, m):
165 with m.If(self.out_do_z):
166 m.d.sync += self.z.v.eq(self.out_z.v) # only take the output
167 m.next = "put_z"
168 with m.Else():
169 m.next = "denormalise"
170
171
172 class FPAddDeNorm(FPState):
173
174 def action(self, m):
175 # Denormalised Number checks
176 m.next = "align"
177 self.denormalise(m, self.a)
178 self.denormalise(m, self.b)
179
180
181 class FPAddAlignMultiMod(FPState):
182
183 def __init__(self, width):
184 self.in_a = FPNumBase(width)
185 self.in_b = FPNumBase(width)
186 self.out_a = FPNumIn(None, width)
187 self.out_b = FPNumIn(None, width)
188 self.exp_eq = Signal(reset_less=True)
189
190 def setup(self, m, in_a, in_b, out_a, out_b, exp_eq):
191 """ links module to inputs and outputs
192 """
193 m.d.comb += self.in_a.copy(in_a)
194 m.d.comb += self.in_b.copy(in_b)
195 m.d.comb += out_a.copy(self.out_a)
196 m.d.comb += out_b.copy(self.out_b)
197 m.d.comb += exp_eq.eq(self.exp_eq)
198
199 def elaborate(self, platform):
200 # This one however (single-cycle) will do the shift
201 # in one go.
202
203 m = Module()
204
205 #m.submodules.align_in_a = self.in_a
206 #m.submodules.align_in_b = self.in_b
207 m.submodules.align_out_a = self.out_a
208 m.submodules.align_out_b = self.out_b
209
210 # NOTE: this does *not* do single-cycle multi-shifting,
211 # it *STAYS* in the align state until exponents match
212
213 # exponent of a greater than b: shift b down
214 m.d.comb += self.exp_eq.eq(0)
215 m.d.comb += self.out_a.copy(self.in_a)
216 m.d.comb += self.out_b.copy(self.in_b)
217 agtb = Signal(reset_less=True)
218 altb = Signal(reset_less=True)
219 m.d.comb += agtb.eq(self.in_a.e > self.in_b.e)
220 m.d.comb += altb.eq(self.in_a.e < self.in_b.e)
221 with m.If(agtb):
222 m.d.comb += self.out_b.shift_down(self.in_b)
223 # exponent of b greater than a: shift a down
224 with m.Elif(altb):
225 m.d.comb += self.out_a.shift_down(self.in_a)
226 # exponents equal: move to next stage.
227 with m.Else():
228 m.d.comb += self.exp_eq.eq(1)
229 return m
230
231
232 class FPAddAlignMulti(FPState):
233
234 def __init__(self, width):
235 FPState.__init__(self, "align")
236 self.mod = FPAddAlignMultiMod(width)
237 self.out_a = FPNumIn(None, width)
238 self.out_b = FPNumIn(None, width)
239 self.exp_eq = Signal(reset_less=True)
240
241 def action(self, m):
242 m.d.sync += self.a.copy(self.out_a)
243 m.d.sync += self.b.copy(self.out_b)
244 with m.If(self.exp_eq):
245 m.next = "add_0"
246
247
248 class FPAddAlignSingleMod:
249
250 def __init__(self, width):
251 self.in_a = FPNumBase(width)
252 self.in_b = FPNumBase(width)
253 self.out_a = FPNumIn(None, width)
254 self.out_b = FPNumIn(None, width)
255 #self.out_a = FPNumBase(width)
256 #self.out_b = FPNumBase(width)
257
258 def setup(self, m, in_a, in_b, out_a, out_b):
259 """ links module to inputs and outputs
260 """
261 m.d.comb += self.in_a.copy(in_a)
262 m.d.comb += self.in_b.copy(in_b)
263 m.d.comb += out_a.copy(self.out_a)
264 m.d.comb += out_b.copy(self.out_b)
265
266 def elaborate(self, platform):
267 # This one however (single-cycle) will do the shift
268 # in one go.
269
270 m = Module()
271
272 #m.submodules.align_in_a = self.in_a
273 #m.submodules.align_in_b = self.in_b
274 m.submodules.align_out_a = self.out_a
275 m.submodules.align_out_b = self.out_b
276
277 # XXX TODO: the shifter used here is quite expensive
278 # having only one would be better
279
280 ediff = Signal((len(self.in_a.e), True), reset_less=True)
281 ediffr = Signal((len(self.in_a.e), True), reset_less=True)
282 m.d.comb += ediff.eq(self.in_a.e - self.in_b.e)
283 m.d.comb += ediffr.eq(self.in_b.e - self.in_a.e)
284 m.d.comb += self.out_a.copy(self.in_a)
285 m.d.comb += self.out_b.copy(self.in_b)
286 with m.If(ediff > 0):
287 m.d.comb += self.out_b.shift_down_multi(ediff)
288 # exponent of b greater than a: shift a down
289 with m.Elif(ediff < 0):
290 m.d.comb += self.out_a.shift_down_multi(ediffr)
291 return m
292
293
294 class FPAddAlignSingle(FPState):
295
296 def __init__(self, width):
297 FPState.__init__(self, "align")
298 self.mod = FPAddAlignSingleMod(width)
299 self.out_a = FPNumIn(None, width)
300 self.out_b = FPNumIn(None, width)
301
302 def action(self, m):
303 m.d.sync += self.a.copy(self.out_a)
304 m.d.sync += self.b.copy(self.out_b)
305 m.next = "add_0"
306
307
308 class FPAddStage0Mod:
309
310 def __init__(self, width):
311 self.in_a = FPNumBase(width)
312 self.in_b = FPNumBase(width)
313 self.in_z = FPNumBase(width, False)
314 self.out_z = FPNumBase(width, False)
315 self.out_tot = Signal(self.out_z.m_width + 4, reset_less=True)
316
317 def setup(self, m, in_a, in_b, in_z, out_z, out_tot):
318 """ links module to inputs and outputs
319 """
320 m.d.comb += self.in_a.copy(in_a)
321 m.d.comb += self.in_b.copy(in_b)
322 m.d.comb += self.in_z.copy(in_z)
323 m.d.comb += out_z.copy(self.out_z)
324 m.d.comb += out_tot.eq(self.out_tot)
325
326 def elaborate(self, platform):
327 m = Module()
328 #m.submodules.add0_in_a = self.in_a
329 #m.submodules.add0_in_b = self.in_b
330 #m.submodules.add0_in_z = self.in_z
331 #m.submodules.add0_out_z = self.out_z
332
333 m.d.comb += self.out_z.e.eq(self.in_a.e)
334 # same-sign (both negative or both positive) add mantissas
335 with m.If(self.in_a.s == self.in_b.s):
336 m.d.comb += [
337 self.out_tot.eq(Cat(self.in_a.m, 0) + Cat(self.in_b.m, 0)),
338 self.out_z.s.eq(self.in_a.s)
339 ]
340 # a mantissa greater than b, use a
341 with m.Elif(self.in_a.m >= self.in_b.m):
342 m.d.comb += [
343 self.out_tot.eq(Cat(self.in_a.m, 0) - Cat(self.in_b.m, 0)),
344 self.out_z.s.eq(self.in_a.s)
345 ]
346 # b mantissa greater than a, use b
347 with m.Else():
348 m.d.comb += [
349 self.out_tot.eq(Cat(self.in_b.m, 0) - Cat(self.in_a.m, 0)),
350 self.out_z.s.eq(self.in_b.s)
351 ]
352 return m
353
354
355 class FPAddStage0(FPState):
356 """ First stage of add. covers same-sign (add) and subtract
357 special-casing when mantissas are greater or equal, to
358 give greatest accuracy.
359 """
360
361 def __init__(self, width):
362 FPState.__init__(self, "add_0")
363 self.mod = FPAddStage0Mod(width)
364 self.out_z = FPNumBase(width, False)
365 self.out_tot = Signal(self.out_z.m_width + 4, reset_less=True)
366
367 def action(self, m):
368 m.next = "add_1"
369 m.d.sync += self.z.copy(self.out_z)
370
371
372 class FPAddStage1Mod(FPState):
373 """ Second stage of add: preparation for normalisation.
374 detects when tot sum is too big (tot[27] is kinda a carry bit)
375 """
376
377 def __init__(self, width):
378 self.out_norm = Signal(reset_less=True)
379 self.in_z = FPNumBase(width, False)
380 self.in_tot = Signal(self.in_z.m_width + 4, reset_less=True)
381 self.out_z = FPNumBase(width, False)
382 self.out_of = Overflow()
383
384 def setup(self, m, in_tot, in_z, out_z, out_of):
385 """ links module to inputs and outputs
386 """
387 m.d.comb += self.in_z.copy(in_z)
388 m.d.comb += self.in_tot.eq(in_tot)
389 m.d.comb += out_z.copy(self.out_z)
390 m.d.comb += out_of.copy(self.out_of)
391
392 def elaborate(self, platform):
393 m = Module()
394 #m.submodules.norm1_in_overflow = self.in_of
395 #m.submodules.norm1_out_overflow = self.out_of
396 #m.submodules.norm1_in_z = self.in_z
397 #m.submodules.norm1_out_z = self.out_z
398 m.d.comb += self.out_z.copy(self.in_z)
399 # tot[27] gets set when the sum overflows. shift result down
400 with m.If(self.in_tot[-1]):
401 m.d.comb += [
402 self.out_z.m.eq(self.in_tot[4:]),
403 self.out_of.m0.eq(self.in_tot[4]),
404 self.out_of.guard.eq(self.in_tot[3]),
405 self.out_of.round_bit.eq(self.in_tot[2]),
406 self.out_of.sticky.eq(self.in_tot[1] | self.in_tot[0]),
407 self.out_z.e.eq(self.in_z.e + 1)
408 ]
409 # tot[27] zero case
410 with m.Else():
411 m.d.comb += [
412 self.out_z.m.eq(self.in_tot[3:]),
413 self.out_of.m0.eq(self.in_tot[3]),
414 self.out_of.guard.eq(self.in_tot[2]),
415 self.out_of.round_bit.eq(self.in_tot[1]),
416 self.out_of.sticky.eq(self.in_tot[0])
417 ]
418 return m
419
420
421 class FPAddStage1(FPState):
422
423 def __init__(self, width):
424 FPState.__init__(self, "add_1")
425 self.mod = FPAddStage1Mod(width)
426 self.out_z = FPNumBase(width, False)
427 self.out_of = Overflow()
428
429 def action(self, m):
430 m.d.sync += self.of.copy(self.out_of)
431 m.d.sync += self.z.copy(self.out_z)
432 m.next = "normalise_1"
433
434
435 class FPNorm1Mod:
436
437 def __init__(self, width):
438 self.out_norm = Signal(reset_less=True)
439 self.in_z = FPNumBase(width, False)
440 self.out_z = FPNumBase(width, False)
441 self.in_of = Overflow()
442 self.out_of = Overflow()
443
444 def setup(self, m, in_z, out_z, in_of, out_of, out_norm):
445 """ links module to inputs and outputs
446 """
447 m.d.comb += self.in_z.copy(in_z)
448 m.d.comb += out_z.copy(self.out_z)
449 m.d.comb += self.in_of.copy(in_of)
450 m.d.comb += out_of.copy(self.out_of)
451 m.d.comb += out_norm.eq(self.out_norm)
452
453 def elaborate(self, platform):
454 m = Module()
455 m.submodules.norm1_in_overflow = self.in_of
456 m.submodules.norm1_out_overflow = self.out_of
457 m.submodules.norm1_in_z = self.in_z
458 m.submodules.norm1_out_z = self.out_z
459 m.d.comb += self.out_z.copy(self.in_z)
460 m.d.comb += self.out_of.copy(self.in_of)
461 m.d.comb += self.out_norm.eq((self.in_z.m_msbzero) & \
462 (self.in_z.exp_gt_n126))
463 with m.If(self.out_norm):
464 m.d.comb += [
465 self.out_z.e.eq(self.in_z.e - 1), # DECREASE exponent
466 self.out_z.m.eq(self.in_z.m << 1), # shift mantissa UP
467 self.out_z.m[0].eq(self.in_of.guard), # steal guard (was tot[2])
468 self.out_of.guard.eq(self.in_of.round_bit), # round (was tot[1])
469 self.out_of.round_bit.eq(0), # reset round bit
470 self.out_of.m0.eq(self.in_of.guard),
471 ]
472
473 return m
474
475
476 class FPNorm1(FPState):
477
478 def __init__(self, width):
479 FPState.__init__(self, "normalise_1")
480 self.mod = FPNorm1Mod(width)
481 self.out_norm = Signal(reset_less=True)
482 self.out_z = FPNumBase(width)
483 self.out_of = Overflow()
484
485 def action(self, m):
486 m.d.sync += self.of.copy(self.out_of)
487 m.d.sync += self.z.copy(self.out_z)
488 with m.If(~self.out_norm):
489 m.next = "normalise_2"
490
491
492 class FPNorm2Mod:
493
494 def __init__(self, width):
495 self.out_norm = Signal(reset_less=True)
496 self.in_z = FPNumBase(width, False)
497 self.out_z = FPNumBase(width, False)
498 self.in_of = Overflow()
499 self.out_of = Overflow()
500
501 def setup(self, m, in_z, out_z, in_of, out_of, out_norm):
502 """ links module to inputs and outputs
503 """
504 m.d.comb += self.in_z.copy(in_z)
505 m.d.comb += out_z.copy(self.out_z)
506 m.d.comb += self.in_of.copy(in_of)
507 m.d.comb += out_of.copy(self.out_of)
508 m.d.comb += out_norm.eq(self.out_norm)
509
510 def elaborate(self, platform):
511 m = Module()
512 m.submodules.norm2_in_overflow = self.in_of
513 m.submodules.norm2_out_overflow = self.out_of
514 m.submodules.norm2_in_z = self.in_z
515 m.submodules.norm2_out_z = self.out_z
516 m.d.comb += self.out_z.copy(self.in_z)
517 m.d.comb += self.out_of.copy(self.in_of)
518 m.d.comb += self.out_norm.eq(self.in_z.exp_lt_n126)
519 with m.If(self.out_norm):
520 m.d.comb += [
521 self.out_z.e.eq(self.in_z.e + 1), # INCREASE exponent
522 self.out_z.m.eq(self.in_z.m >> 1), # shift mantissa DOWN
523 self.out_of.guard.eq(self.in_z.m[0]),
524 self.out_of.m0.eq(self.in_z.m[1]),
525 self.out_of.round_bit.eq(self.in_of.guard),
526 self.out_of.sticky.eq(self.in_of.sticky | self.in_of.round_bit)
527 ]
528
529 return m
530
531
532 class FPNorm2(FPState):
533
534 def __init__(self, width):
535 FPState.__init__(self, "normalise_2")
536 self.mod = FPNorm2Mod(width)
537 self.out_norm = Signal(reset_less=True)
538 self.out_z = FPNumBase(width)
539 self.out_of = Overflow()
540
541 def action(self, m):
542 m.d.sync += self.of.copy(self.out_of)
543 m.d.sync += self.z.copy(self.out_z)
544 with m.If(~self.out_norm):
545 m.next = "round"
546
547
548 class FPRoundMod:
549
550 def __init__(self, width):
551 self.in_roundz = Signal(reset_less=True)
552 self.in_z = FPNumBase(width, False)
553 self.out_z = FPNumBase(width, False)
554
555 def setup(self, m, in_z, out_z, in_of):
556 """ links module to inputs and outputs
557 """
558 m.d.comb += self.in_z.copy(in_z)
559 m.d.comb += out_z.copy(self.out_z)
560 m.d.comb += self.in_roundz.eq(in_of.roundz)
561
562 def elaborate(self, platform):
563 m = Module()
564 m.d.comb += self.out_z.copy(self.in_z)
565 with m.If(self.in_roundz):
566 m.d.comb += self.out_z.m.eq(self.in_z.m + 1) # mantissa rounds up
567 with m.If(self.in_z.m == self.in_z.m1s): # all 1s
568 m.d.comb += self.out_z.e.eq(self.in_z.e + 1) # exponent up
569 return m
570
571
572 class FPRound(FPState):
573
574 def __init__(self, width):
575 FPState.__init__(self, "round")
576 self.mod = FPRoundMod(width)
577 self.out_z = FPNumBase(width)
578
579 def action(self, m):
580 m.d.sync += self.z.copy(self.out_z)
581 m.next = "corrections"
582
583
584 class FPCorrectionsMod:
585
586 def __init__(self, width):
587 self.in_z = FPNumOut(width, False)
588 self.out_z = FPNumOut(width, False)
589
590 def setup(self, m, in_z, out_z):
591 """ links module to inputs and outputs
592 """
593 m.d.comb += self.in_z.copy(in_z)
594 m.d.comb += out_z.copy(self.out_z)
595
596 def elaborate(self, platform):
597 m = Module()
598 m.submodules.corr_in_z = self.in_z
599 m.submodules.corr_out_z = self.out_z
600 m.d.comb += self.out_z.copy(self.in_z)
601 with m.If(self.in_z.is_denormalised):
602 m.d.comb += self.out_z.e.eq(self.in_z.N127)
603
604 # with m.If(self.in_z.is_overflowed):
605 # m.d.comb += self.out_z.inf(self.in_z.s)
606 # with m.Else():
607 # m.d.comb += self.out_z.create(self.in_z.s, self.in_z.e, self.in_z.m)
608 return m
609
610
611 class FPCorrections(FPState):
612
613 def __init__(self, width):
614 FPState.__init__(self, "corrections")
615 self.mod = FPCorrectionsMod(width)
616 self.out_z = FPNumBase(width)
617
618 def action(self, m):
619 m.d.sync += self.z.copy(self.out_z)
620 m.next = "pack"
621
622
623 class FPPackMod:
624
625 def __init__(self, width):
626 self.in_z = FPNumOut(width, False)
627 self.out_z = FPNumOut(width, False)
628
629 def setup(self, m, in_z, out_z):
630 """ links module to inputs and outputs
631 """
632 m.d.comb += self.in_z.copy(in_z)
633 m.d.comb += out_z.v.eq(self.out_z.v)
634
635 def elaborate(self, platform):
636 m = Module()
637 m.submodules.pack_in_z = self.in_z
638 with m.If(self.in_z.is_overflowed):
639 m.d.comb += self.out_z.inf(self.in_z.s)
640 with m.Else():
641 m.d.comb += self.out_z.create(self.in_z.s, self.in_z.e, self.in_z.m)
642 return m
643
644
645 class FPPack(FPState):
646
647 def __init__(self, width):
648 FPState.__init__(self, "pack")
649 self.mod = FPPackMod(width)
650 self.out_z = FPNumOut(width, False)
651
652 def action(self, m):
653 m.d.sync += self.z.v.eq(self.out_z.v)
654 m.next = "put_z"
655
656
657 class FPPutZ(FPState):
658
659 def action(self, m):
660 self.put_z(m, self.z, self.out_z, "get_a")
661
662
663 class FPADD:
664
665 def __init__(self, width, single_cycle=False):
666 self.width = width
667 self.single_cycle = single_cycle
668
669 self.in_a = FPOp(width)
670 self.in_b = FPOp(width)
671 self.out_z = FPOp(width)
672
673 self.states = []
674
675 def add_state(self, state):
676 self.states.append(state)
677 return state
678
679 def get_fragment(self, platform=None):
680 """ creates the HDL code-fragment for FPAdd
681 """
682 m = Module()
683
684 # Latches
685 #a = FPNumIn(self.in_a, self.width)
686 b = FPNumIn(self.in_b, self.width)
687 z = FPNumOut(self.width, False)
688
689 m.submodules.fpnum_b = b
690 m.submodules.fpnum_z = z
691
692 w = z.m_width + 4
693
694 of = Overflow()
695 m.submodules.overflow = of
696
697 geta = self.add_state(FPGetOpA(self.in_a, self.width))
698 #geta.set_inputs({"in_a": self.in_a})
699 #geta.set_outputs({"a": a})
700 a = geta.a
701 # XXX m.d.comb += a.v.eq(self.in_a.v) # links in_a to a
702 m.submodules.fpnum_a = a
703
704 getb = self.add_state(FPGetOpB("get_b"))
705 getb.set_inputs({"in_b": self.in_b})
706 getb.set_outputs({"b": b})
707 # XXX m.d.comb += b.v.eq(self.in_b.v) # links in_b to b
708
709 sc = self.add_state(FPAddSpecialCases(self.width))
710 sc.set_inputs({"a": a, "b": b})
711 sc.set_outputs({"z": z})
712 sc.mod.setup(m, a, b, sc.out_z, sc.out_do_z)
713 m.submodules.specialcases = sc.mod
714
715 dn = self.add_state(FPAddDeNorm("denormalise"))
716 dn.set_inputs({"a": a, "b": b})
717 dn.set_outputs({"a": a, "b": b}) # XXX outputs same as inputs
718
719 if self.single_cycle:
720 alm = self.add_state(FPAddAlignSingle(self.width))
721 alm.set_inputs({"a": a, "b": b})
722 alm.set_outputs({"a": a, "b": b}) # XXX outputs same as inputs
723 alm.mod.setup(m, a, b, alm.out_a, alm.out_b)
724 else:
725 alm = self.add_state(FPAddAlignMulti(self.width))
726 alm.set_inputs({"a": a, "b": b})
727 alm.set_outputs({"a": a, "b": b}) # XXX outputs same as inputs
728 alm.mod.setup(m, a, b, alm.out_a, alm.out_b, alm.exp_eq)
729 m.submodules.align = alm.mod
730
731 add0 = self.add_state(FPAddStage0(self.width))
732 add0.set_inputs({"a": a, "b": b})
733 add0.set_outputs({"z": z})
734 add0.mod.setup(m, a, b, z, add0.out_z, add0.out_tot)
735 m.submodules.add0 = add0.mod
736
737 add1 = self.add_state(FPAddStage1(self.width))
738 add1.set_inputs({"tot": add0.out_tot, "z": add0.out_z})
739 add1.set_outputs({"z": z, "of": of}) # XXX Z as output
740 add1.mod.setup(m, add0.out_tot, z, add1.out_z, add1.out_of)
741 m.submodules.add1 = add1.mod
742
743 n1 = self.add_state(FPNorm1(self.width))
744 n1.set_inputs({"z": z, "of": of}) # XXX Z as output
745 n1.set_outputs({"z": z}) # XXX Z as output
746 n1.mod.setup(m, z, n1.out_z, of, n1.out_of, n1.out_norm)
747 m.submodules.normalise_1 = n1.mod
748
749 n2 = self.add_state(FPNorm2(self.width))
750 n2.set_inputs({"z": n1.out_z, "of": of})
751 n2.set_outputs({"z": z})
752 n2.mod.setup(m, n1.out_z, n2.out_z, of, n2.out_of, n2.out_norm)
753 m.submodules.normalise_2 = n2.mod
754
755 rn = self.add_state(FPRound(self.width))
756 rn.set_inputs({"z": n2.out_z, "of": of})
757 rn.set_outputs({"z": z})
758 rn.mod.setup(m, n2.out_z, rn.out_z, of)
759 m.submodules.roundz = rn.mod
760
761 cor = self.add_state(FPCorrections(self.width))
762 cor.set_inputs({"z": z}) # XXX Z as output
763 cor.set_outputs({"z": z}) # XXX Z as output
764 cor.mod.setup(m, z, cor.out_z)
765 m.submodules.corrections = cor.mod
766
767 pa = self.add_state(FPPack(self.width))
768 pa.set_inputs({"z": z}) # XXX Z as output
769 pa.set_outputs({"z": z}) # XXX Z as output
770 pa.mod.setup(m, z, pa.out_z)
771 m.submodules.pack = pa.mod
772
773 pz = self.add_state(FPPutZ("put_z"))
774 pz.set_inputs({"z": z})
775 pz.set_outputs({"out_z": self.out_z})
776
777 with m.FSM() as fsm:
778
779 for state in self.states:
780 with m.State(state.state_from):
781 state.action(m)
782
783 return m
784
785
786 if __name__ == "__main__":
787 alu = FPADD(width=32)
788 main(alu, ports=alu.in_a.ports() + alu.in_b.ports() + alu.out_z.ports())
789
790
791 # works... but don't use, just do "python fname.py convert -t v"
792 #print (verilog.convert(alu, ports=[
793 # ports=alu.in_a.ports() + \
794 # alu.in_b.ports() + \
795 # alu.out_z.ports())