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