2e61dcfb2fe4d8d9a80ef605a97a531a7d4563f6
[ieee754fpu.git] / src / ieee754 / fcvt / downsize.py
1 # IEEE754 Floating Point Conversion
2 # Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3
4 from nmigen import Module, Signal, Const
5 from nmigen.cli import main, verilog
6
7 from nmutil.pipemodbase import PipeModBase
8 from ieee754.fpcommon.basedata import FPBaseData
9 from ieee754.fpcommon.postcalc import FPPostCalcData
10 from ieee754.fpcommon.msbhigh import FPMSBHigh
11 from ieee754.fpcommon.exphigh import FPEXPHigh
12
13 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
14
15
16 class FPCVTDownConvertMod(PipeModBase):
17 """ FP down-conversion (higher to lower bitwidth)
18 """
19 def __init__(self, in_pspec, out_pspec):
20 self.in_pspec = in_pspec
21 self.out_pspec = out_pspec
22 super().__init__(in_pspec, "downconvert")
23
24 def ispec(self):
25 return FPBaseData(self.in_pspec)
26
27 def ospec(self):
28 return FPPostCalcData(self.out_pspec, e_extra=True)
29
30 def elaborate(self, platform):
31 m = Module()
32 comb = m.d.comb
33 print("in_width out", self.in_pspec.width, self.out_pspec.width)
34
35 # here we make room (in temporary constants / ospec) for extra
36 # bits in the exponent, at the size of the *incoming* number
37 # bitwidth. in this way it is possible to detect, in the
38 # *outgoing* number, if the exponent is too large and needs
39 # adjustment. otherwise we have to mess about with all sorts
40 # of width-detection and the normalisation, special cases etc.
41 # all become horribly complicated.
42
43 a1 = FPNumBaseRecord(self.in_pspec.width, False)
44 print("a1", a1.width, a1.rmw, a1.e_width, a1.e_start, a1.e_end)
45 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
46 comb += a1.v.eq(self.i.a)
47 z1 = self.o.z
48 print("z1", z1.width, z1.rmw, z1.e_width, z1.e_start, z1.e_end)
49
50 me = a1.rmw
51 ms = a1.rmw - self.o.z.rmw
52 print("ms-me", ms, me)
53
54 # intermediaries
55 exp_sub_n126 = Signal((a1.e_width, True), reset_less=True)
56 exp_gt127 = Signal(reset_less=True)
57 # constants from z1, at the bit-width of a1.
58 N126 = Const(z1.fp.N126.value, (a1.e_width, True))
59 P127 = Const(z1.fp.P127.value, (a1.e_width, True))
60 comb += exp_sub_n126.eq(a1.e - N126)
61 comb += exp_gt127.eq(a1.e > P127)
62
63 # bypass (always enabled except for normalisation, below)
64 comb += self.o.out_do_z.eq(1)
65
66 # if a zero, return zero (signed)
67 with m.If(a1.exp_n127):
68 comb += self.o.z.zero(a1.s)
69
70 # if a range outside z's min range (-126)
71 with m.Elif(exp_sub_n126 < 0):
72 comb += self.o.of.guard.eq(a1.m[ms-1])
73 comb += self.o.of.round_bit.eq(a1.m[ms-2])
74 comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
75 comb += self.o.of.m0.eq(a1.m[ms]) # bit of a1
76
77 comb += self.o.z.s.eq(a1.s)
78 comb += self.o.z.e.eq(a1.e)
79 comb += self.o.z.m.eq(a1.m[-self.o.z.rmw-1:])
80 comb += self.o.z.m[-1].eq(1)
81
82 # normalisation required
83 comb += self.o.out_do_z.eq(0)
84
85 # if a is inf return inf
86 with m.Elif(a1.is_inf):
87 comb += self.o.z.inf(a1.s)
88
89 # if a is NaN return NaN
90 with m.Elif(a1.is_nan):
91 comb += self.o.z.nan(0)
92
93 # if a mantissa greater than 127, return inf
94 with m.Elif(exp_gt127):
95 print("inf", self.o.z.inf(a1.s))
96 comb += self.o.z.inf(a1.s)
97
98 # ok after all that, anything else should fit fine (whew)
99 with m.Else():
100 comb += self.o.of.guard.eq(a1.m[ms-1])
101 comb += self.o.of.round_bit.eq(a1.m[ms-2])
102 comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
103 comb += self.o.of.m0.eq(a1.m[ms]) # bit of a1
104
105 # XXX TODO: this is basically duplicating FPRoundMod. hmmm...
106 print("alen", a1.e_start, z1.fp.N126, N126)
107 print("m1", self.o.z.rmw, a1.m[-self.o.z.rmw-1:])
108 mo = Signal(self.o.z.m_width-1)
109 comb += mo.eq(a1.m[ms:me])
110 with m.If(self.o.of.roundz):
111 with m.If((mo.bool())): # mantissa-out is all 1s
112 comb += self.o.z.create(a1.s, a1.e, mo+1)
113 with m.Else():
114 comb += self.o.z.create(a1.s, a1.e+1, mo+1)
115 with m.Else():
116 comb += self.o.z.create(a1.s, a1.e, a1.m[-self.o.z.rmw-1:])
117
118 # copy the context (muxid, operator)
119 comb += self.o.oz.eq(self.o.z.v)
120 comb += self.o.ctx.eq(self.i.ctx)
121
122 return m