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