switch to using nmutil's FHDLTestCase
[ieee754fpu.git] / src / ieee754 / fpmul / specialcases.py
1 """IEEE754 Floating Point Multiplier
2
3 Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
4 Copyright (C) 2019 Jake Lifshay
5
6 """
7
8 from nmigen import Module, Signal, Cat, Const, Mux
9 from nmigen.cli import main, verilog
10 from math import log
11
12 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
13
14 from nmutil.pipemodbase import PipeModBase, PipeModBaseChain
15 from ieee754.fpcommon.basedata import FPBaseData
16 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
17 from ieee754.fpmul.align import FPAlignModSingle
18
19
20 class FPMulSpecialCasesMod(PipeModBase):
21 """ special cases: NaNs, infs, zeros, denormalised
22 see "Special Operations"
23 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
24 """
25
26 def __init__(self, pspec):
27 super().__init__(pspec, "specialcases")
28
29 def ispec(self):
30 return FPBaseData(self.pspec)
31
32 def ospec(self):
33 return FPSCData(self.pspec, False)
34
35 def elaborate(self, platform):
36 m = Module()
37 comb = m.d.comb
38
39 # decode a/b
40 width = self.pspec.width
41 a1 = FPNumBaseRecord(width, False)
42 b1 = FPNumBaseRecord(width, False)
43 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
44 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
45 comb += [a1.v.eq(self.i.a),
46 b1.v.eq(self.i.b),
47 self.o.a.eq(a1),
48 self.o.b.eq(b1)
49 ]
50
51 # intermediaries / tests
52 t_obz = Signal(reset_less=True)
53 t_a1inf = Signal(reset_less=True)
54 t_b1inf = Signal(reset_less=True)
55 t_abnan = Signal(reset_less=True)
56 t_special = Signal(reset_less=True)
57 sabx = Signal(reset_less=True) # sign a xor b (sabx, get it?)
58
59 comb += sabx.eq(a1.s ^ b1.s)
60 comb += t_obz.eq(a1.is_zero | b1.is_zero)
61 comb += t_a1inf.eq(a1.is_inf)
62 comb += t_b1inf.eq(b1.is_inf)
63 comb += t_abnan.eq(a1.is_nan | b1.is_nan)
64 comb += t_special.eq(Cat(t_obz, t_abnan, t_b1inf, t_a1inf).bool())
65
66 # prepare inf/zero/nans
67 z_zero = FPNumBaseRecord(width, False, name="z_zero")
68 z_nan = FPNumBaseRecord(width, False, name="z_nan")
69 z_inf = FPNumBaseRecord(width, False, name="z_inf")
70 comb += z_zero.zero(sabx)
71 comb += z_nan.nan(0)
72 comb += z_inf.inf(sabx)
73
74 # special case pipeline bypass enabled y/n
75 comb += self.o.out_do_z.eq(t_special)
76
77 # if a is NaN or b is NaN return NaN
78 # if a is inf return inf (or NaN)
79 # if b is zero return NaN
80 # if b is inf return inf (or NaN)
81 # if a is zero return NaN
82 # if a is zero or b zero return signed-a/b
83
84 # invert the sequence above to create the Mux tree
85 # XXX TODO: use PriorityPicker?
86 oz = 0
87 oz = Mux(t_obz, z_zero.v, oz)
88 oz = Mux(t_b1inf, Mux(a1.is_zero, z_nan.v, z_inf.v), oz)
89 oz = Mux(t_a1inf, Mux(b1.is_zero, z_nan.v, z_inf.v), oz)
90 oz = Mux(t_abnan, z_nan.v, oz)
91 comb += self.o.oz.eq(oz)
92
93 # pass through context
94 comb += self.o.ctx.eq(self.i.ctx)
95
96 return m
97
98
99 class FPMulSpecialCasesDeNorm(PipeModBaseChain):
100 """ special cases: NaNs, infs, zeros, denormalised
101 """
102
103 def get_chain(self):
104 """ gets chain of modules
105 """
106 smod = FPMulSpecialCasesMod(self.pspec)
107 dmod = FPAddDeNormMod(self.pspec, False)
108 amod = FPAlignModSingle(self.pspec, False)
109
110 return [smod, dmod, amod]
111