tidyup, use FPModBaseChain and FPModBase
[ieee754fpu.git] / src / ieee754 / fcvt / int2float.py
1 # IEEE Floating Point Conversion
2 # Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3
4 from nmigen import Module, Signal, Cat
5 from nmigen.cli import main, verilog
6
7 from ieee754.fpcommon.modbase import FPModBase
8 from ieee754.fpcommon.getop import FPADDBaseData
9 from ieee754.fpcommon.postcalc import FPAddStage1Data
10 from ieee754.fpcommon.msbhigh import FPMSBHigh
11
12 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
13
14
15 class FPCVTIntToFloatMod(FPModBase):
16 """ FP integer conversion: copes with 16/32/64 int to 16/32/64 fp.
17
18 self.ctx.i.op & 0x1 == 0x1 : SIGNED int
19 self.ctx.i.op & 0x1 == 0x0 : UNSIGNED int
20 """
21 def __init__(self, in_pspec, out_pspec):
22 self.in_pspec = in_pspec
23 self.out_pspec = out_pspec
24 super().__init__(in_pspec, "intconvert")
25
26 def ispec(self):
27 return FPADDBaseData(self.in_pspec)
28
29 def ospec(self):
30 return FPAddStage1Data(self.out_pspec, e_extra=True)
31
32 def elaborate(self, platform):
33 m = Module()
34 comb = m.d.comb
35
36 #m.submodules.sc_out_z = self.o.z
37
38 # decode: XXX really should move to separate stage
39 print("in_width out", self.in_pspec.width,
40 self.out_pspec.width)
41 print("a1", self.in_pspec.width)
42 z1 = self.o.z
43 print("z1", z1.width, z1.rmw, z1.e_width, z1.e_start, z1.e_end)
44
45 me = self.in_pspec.width
46 mz = self.o.z.rmw
47 ms = mz - me
48 print("ms-me", ms, me, mz)
49
50 # 3 extra bits for guard/round/sticky
51 msb = FPMSBHigh(me+3, z1.e_width)
52 m.submodules.norm_msb = msb
53
54 # signed or unsigned, use operator context
55 signed = Signal(reset_less=True)
56 comb += signed.eq(self.i.ctx.op[0])
57
58 # copy of mantissa (one less bit if signed)
59 mantissa = Signal(me, reset_less=True)
60
61 # detect signed/unsigned. key case: -ve numbers need inversion
62 # to +ve because the FP sign says if it's -ve or not.
63 with m.If(signed):
64 comb += z1.s.eq(self.i.a[-1]) # sign in top bit of a
65 with m.If(z1.s):
66 comb += mantissa.eq(-self.i.a) # invert input if sign -ve
67 with m.Else():
68 comb += mantissa.eq(self.i.a) # leave as-is
69 with m.Else():
70 comb += mantissa.eq(self.i.a) # unsigned, use full a
71 comb += z1.s.eq(0)
72
73 # set input from full INT
74 comb += msb.m_in.eq(Cat(0, 0, 0, mantissa)) # g/r/s + input
75 comb += msb.e_in.eq(me) # exp = int width
76
77 # to do with FP16... not yet resolved why
78 alternative = ms < 0
79
80 if alternative:
81 comb += z1.e.eq(msb.e_out-1)
82 mmsb = msb.m_out[-mz-1:]
83 if mz == 16:
84 # larger int to smaller FP (uint32/64 -> fp16 most likely)
85 comb += z1.m[ms-1:].eq(mmsb)
86 else: # 32? XXX weirdness...
87 comb += z1.m.eq(mmsb)
88 else:
89 # smaller int to larger FP
90 comb += z1.e.eq(msb.e_out)
91 comb += z1.m[ms:].eq(msb.m_out[3:])
92 comb += z1.create(z1.s, z1.e, z1.m) # ... here
93
94 # note: post-normalisation actually appears to be capable of
95 # detecting overflow to infinity (FPPackMod). so it's ok to
96 # drop the bits into the mantissa (with a fixed exponent),
97 # do some rounding (which might result in exceeding the
98 # range of the target FP by re-increasing the exponent),
99 # and basically *not* have to do any kind of range-checking
100 # here: just set up guard/round/sticky, drop the INT into the
101 # mantissa, and away we go. XXX TODO: see if FPNormaliseMod
102 # is even necessary. it probably isn't
103
104 # initialise rounding (but only activate if needed)
105 if alternative:
106 # larger int to smaller FP (uint32/64 -> fp16 most likely)
107 comb += self.o.of.guard.eq(msb.m_out[-mz-2])
108 comb += self.o.of.round_bit.eq(msb.m_out[-mz-3])
109 comb += self.o.of.sticky.eq(msb.m_out[:-mz-3].bool())
110 comb += self.o.of.m0.eq(msb.m_out[-mz-1])
111 else:
112 # smaller int to larger FP
113 comb += self.o.of.guard.eq(msb.m_out[2])
114 comb += self.o.of.round_bit.eq(msb.m_out[1])
115 comb += self.o.of.sticky.eq(msb.m_out[:1].bool())
116 comb += self.o.of.m0.eq(msb.m_out[3])
117
118 # special cases active by default
119 comb += self.o.out_do_z.eq(1)
120
121 # detect zero
122 with m.If(~self.i.a.bool()):
123 comb += self.o.z.zero(0)
124 with m.Else():
125 comb += self.o.out_do_z.eq(0) # activate normalisation
126
127 # copy the context (muxid, operator)
128 comb += self.o.oz.eq(self.o.z.v)
129 comb += self.o.ctx.eq(self.i.ctx)
130
131 return m
132
133