845a4e007b2d695ca4084a0c9a140fbd19baeb2a
[ieee754fpu.git] / src / ieee754 / fcvt / float2int.py
1 # IEEE754 Floating Point Converter
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.fpbase import Overflow
8 from ieee754.fpcommon.getop import FPADDBaseData
9 from ieee754.fpcommon.postcalc import FPPostCalcData
10 from ieee754.fpcommon.exphigh import FPEXPHigh
11
12 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
13
14
15 class FPCVTFloatToIntMod(Elaboratable):
16 """ integer to FP conversion: copes with 16/32/64 fp to 16/32/64 int/uint
17
18 self.ctx.i.op & 0x1 == 0x1 : SIGNED int
19 self.ctx.i.op & 0x1 == 0x0 : UNSIGNED int
20
21 Note: this is a single-stage conversion that goes direct to FPPackData
22 """
23 def __init__(self, in_pspec, out_pspec):
24 self.in_pspec = in_pspec
25 self.out_pspec = out_pspec
26 self.i = self.ispec()
27 self.o = self.ospec()
28
29 def ispec(self):
30 return FPADDBaseData(self.in_pspec)
31
32 def ospec(self):
33 return FPPackData(self.out_pspec)
34
35 def setup(self, m, i):
36 """ links module to inputs and outputs
37 """
38 m.submodules.upconvert = self
39 m.d.comb += self.i.eq(i)
40
41 def process(self, i):
42 return self.o
43
44 def elaborate(self, platform):
45 m = Module()
46 comb = m.d.comb
47
48 # set up FP Num decoder
49 print("in_width out", self.in_pspec.width,
50 self.out_pspec.width)
51 a1 = FPNumBaseRecord(self.in_pspec.width, False)
52 print("a1", a1.width, a1.rmw, a1.e_width, a1.e_start, a1.e_end)
53 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
54 comb += a1.v.eq(self.i.a)
55 z1 = self.o.z
56 mz = len(z1)
57 print("z1", mz)
58
59 me = a1.rmw
60 ms = mz - me
61 print("ms-me", ms, me)
62
63 espec = (a1.e_width, True)
64
65 signed = Signal(reset_less=True)
66 comb += signed.eq(self.i.ctx.op[0])
67
68 # special cases
69 with m.If(a1.is_nan):
70 with m.If(signed):
71 comb += self.o.z.eq((1<<(mz-1))-1) # signed NaN overflow
72 with m.Else():
73 comb += self.o.z.eq((1<<mz)-1) # NaN overflow
74
75 # zero exponent: definitely out of range of INT. zero...
76 with m.Elif(a1.exp_n127):
77 comb += self.o.z.eq(0)
78
79 # unsigned, -ve, return 0
80 with m.Elif((~signed) & a1.s):
81 comb += self.o.z.eq(0)
82
83 # signed, exp too big
84 with m.Elif(signed & (a1.e >= Const(mz-1, espec))):
85 with m.If(a1.s): # negative FP, so negative overrun
86 comb += self.o.z.eq(-(1<<(mz-1)))
87 with m.Else(): # positive FP, so positive overrun
88 comb += self.o.z.eq((1<<(mz-1))-1)
89
90 # unsigned, exp too big
91 with m.Elif((~signed) & (a1.e >= Const(mz, espec))):
92 with m.If(a1.s): # negative FP, so negative overrun (zero)
93 comb += self.o.z.eq(0)
94 with m.Else(): # positive FP, so positive overrun (max INT)
95 comb += self.o.z.eq((1<<(mz))-1)
96
97 # ok exp should be in range: shift and round it
98 with m.Else():
99 mlen = max(a1.m_width, mz) + 5
100 mantissa = Signal(mlen, reset_less=True)
101 l = [0] * 2 + [a1.m[:-1]] + [1]
102 comb += mantissa[-a1.m_width-3:].eq(Cat(*l))
103 comb += self.o.z.eq(mantissa)
104
105 # shift
106 msr = FPEXPHigh(mlen, espec[0])
107 m.submodules.norm_exp = msr
108 comb += [msr.m_in.eq(mantissa),
109 msr.e_in.eq(a1.e),
110 msr.ediff.eq(Mux(signed, mz, mz)-a1.e)
111 ]
112
113 of = Overflow()
114 comb += of.guard.eq(msr.m_out[2])
115 comb += of.round_bit.eq(msr.m_out[1])
116 comb += of.sticky.eq(msr.m_out[0])
117 comb += of.m0.eq(msr.m_out[3])
118
119 # XXX TODO: check if this overflows the mantissa
120 mround = Signal(mlen, reset_less=True)
121 with m.If(of.roundz):
122 comb += mround.eq(msr.m_out[3:]+1)
123 with m.Else():
124 comb += mround.eq(msr.m_out[3:])
125
126 # check sign
127 with m.If(signed & a1.s):
128 comb += self.o.z.eq(-mround) # inverted
129 with m.Else():
130 comb += self.o.z.eq(mround)
131
132 # copy the context (muxid, operator)
133 #comb += self.o.oz.eq(self.o.z.v)
134 comb += self.o.ctx.eq(self.i.ctx)
135
136 return m