resolve awful meta-class hacking (with thanks to jsbueno on stackexchange)
[ieee754fpu.git] / src / ieee754 / fcvt / downsize.py
1 # IEEE754 Floating Point Conversion
2 # Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3
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 FPCVTDownConvertMod(Elaboratable):
38 """ FP down-conversion (higher to lower bitwidth)
39 """
40 def __init__(self, in_pspec, out_pspec):
41 self.in_pspec = in_pspec
42 self.out_pspec = out_pspec
43 self.i = self.ispec()
44 self.o = self.ospec()
45
46 def ispec(self):
47 return FPADDBaseData(self.in_pspec)
48
49 def ospec(self):
50 return FPAddStage1Data(self.out_pspec, e_extra=True)
51
52 def setup(self, m, i):
53 """ links module to inputs and outputs
54 """
55 m.submodules.downconvert = self
56 m.d.comb += self.i.eq(i)
57
58 def process(self, i):
59 return self.o
60
61 def elaborate(self, platform):
62 m = Module()
63 comb = m.d.comb
64
65 #m.submodules.sc_out_z = self.o.z
66
67 # decode: XXX really should move to separate stage
68 print("in_width out", self.in_pspec.width,
69 self.out_pspec.width)
70 a1 = FPNumBaseRecord(self.in_pspec.width, False)
71 print("a1", a1.width, a1.rmw, a1.e_width, a1.e_start, a1.e_end)
72 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
73 comb += a1.v.eq(self.i.a)
74 z1 = self.o.z
75 print("z1", z1.width, z1.rmw, z1.e_width, z1.e_start, z1.e_end)
76
77 me = a1.rmw
78 ms = a1.rmw - self.o.z.rmw
79 print("ms-me", ms, me)
80
81 # intermediaries
82 exp_sub_n126 = Signal((a1.e_width, True), reset_less=True)
83 exp_gt127 = Signal(reset_less=True)
84 # constants from z1, at the bit-width of a1.
85 N126 = Const(z1.fp.N126.value, (a1.e_width, True))
86 P127 = Const(z1.fp.P127.value, (a1.e_width, True))
87 comb += exp_sub_n126.eq(a1.e - N126)
88 comb += exp_gt127.eq(a1.e > P127)
89
90 # if a zero, return zero (signed)
91 with m.If(a1.exp_n127):
92 comb += self.o.z.zero(a1.s)
93 comb += self.o.out_do_z.eq(1)
94
95 # if a range outside z's min range (-126)
96 with m.Elif(exp_sub_n126 < 0):
97 comb += self.o.of.guard.eq(a1.m[ms-1])
98 comb += self.o.of.round_bit.eq(a1.m[ms-2])
99 comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
100 comb += self.o.of.m0.eq(a1.m[ms]) # bit of a1
101
102 comb += self.o.z.s.eq(a1.s)
103 comb += self.o.z.e.eq(a1.e)
104 comb += self.o.z.m.eq(a1.m[-self.o.z.rmw-1:])
105 comb += self.o.z.m[-1].eq(1)
106
107 # if a is inf return inf
108 with m.Elif(a1.is_inf):
109 comb += self.o.z.inf(a1.s)
110 comb += self.o.out_do_z.eq(1)
111
112 # if a is NaN return NaN
113 with m.Elif(a1.is_nan):
114 comb += self.o.z.nan(0)
115 comb += self.o.out_do_z.eq(1)
116
117 # if a mantissa greater than 127, return inf
118 with m.Elif(exp_gt127):
119 print("inf", self.o.z.inf(a1.s))
120 comb += self.o.z.inf(a1.s)
121 comb += self.o.out_do_z.eq(1)
122
123 # ok after all that, anything else should fit fine (whew)
124 with m.Else():
125 comb += self.o.of.guard.eq(a1.m[ms-1])
126 comb += self.o.of.round_bit.eq(a1.m[ms-2])
127 comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
128 comb += self.o.of.m0.eq(a1.m[ms]) # bit of a1
129
130 # XXX TODO: this is basically duplicating FPRoundMod. hmmm...
131 print("alen", a1.e_start, z1.fp.N126, N126)
132 print("m1", self.o.z.rmw, a1.m[-self.o.z.rmw-1:])
133 mo = Signal(self.o.z.m_width-1)
134 comb += mo.eq(a1.m[ms:me])
135 with m.If(self.o.of.roundz):
136 with m.If((~mo == 0)): # all 1s
137 comb += self.o.z.create(a1.s, a1.e+1, mo+1)
138 with m.Else():
139 comb += self.o.z.create(a1.s, a1.e, mo+1)
140 with m.Else():
141 comb += self.o.z.create(a1.s, a1.e, a1.m[-self.o.z.rmw-1:])
142 comb += self.o.out_do_z.eq(1)
143
144 # copy the context (muxid, operator)
145 comb += self.o.oz.eq(self.o.z.v)
146 comb += self.o.ctx.eq(self.i.ctx)
147
148 return m
149
150