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