convert to DynamicPipe (default class in PipelineSpec: SimpleHandshake)
[ieee754fpu.git] / src / ieee754 / fpmul / specialcases.py
1 # IEEE Floating Point Multiplier
2
3 from nmigen import Module, Signal, Cat, Const, Elaboratable
4 from nmigen.cli import main, verilog
5 from math import log
6
7 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
8 from nmutil.singlepipe import StageChain
9
10 from ieee754.pipeline import DynamicPipe
11 from ieee754.fpcommon.getop import FPADDBaseData
12 from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
13 from ieee754.fpmul.align import FPAlignModSingle
14
15
16 class FPMulSpecialCasesMod(Elaboratable):
17 """ special cases: NaNs, infs, zeros, denormalised
18 see "Special Operations"
19 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
20 """
21
22 def __init__(self, pspec):
23 self.pspec = pspec
24 self.i = self.ispec()
25 self.o = self.ospec()
26
27 def ispec(self):
28 return FPADDBaseData(self.pspec)
29
30 def ospec(self):
31 return FPSCData(self.pspec, False)
32
33 def setup(self, m, i):
34 """ links module to inputs and outputs
35 """
36 m.submodules.specialcases = self
37 m.d.comb += self.i.eq(i)
38
39 def process(self, i):
40 return self.o
41
42 def elaborate(self, platform):
43 m = Module()
44
45 #m.submodules.sc_out_z = self.o.z
46
47 # decode: XXX really should move to separate stage
48 width = self.pspec.width
49 a1 = FPNumBaseRecord(width, False)
50 b1 = FPNumBaseRecord(width, False)
51 m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
52 m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
53 m.d.comb += [a1.v.eq(self.i.a),
54 b1.v.eq(self.i.b),
55 self.o.a.eq(a1),
56 self.o.b.eq(b1)
57 ]
58
59 obz = Signal(reset_less=True)
60 m.d.comb += obz.eq(a1.is_zero | b1.is_zero)
61
62 sabx = Signal(reset_less=True) # sign a xor b (sabx, get it?)
63 m.d.comb += sabx.eq(a1.s ^ b1.s)
64
65 abnan = Signal(reset_less=True)
66 m.d.comb += abnan.eq(a1.is_nan | b1.is_nan)
67
68 # if a is NaN or b is NaN return NaN
69 with m.If(abnan):
70 m.d.comb += self.o.out_do_z.eq(1)
71 m.d.comb += self.o.z.nan(0)
72
73 # if a is inf return inf (or NaN)
74 with m.Elif(a1.is_inf):
75 m.d.comb += self.o.out_do_z.eq(1)
76 m.d.comb += self.o.z.inf(sabx)
77 # b is zero return NaN
78 with m.If(b1.is_zero):
79 m.d.comb += self.o.z.nan(0)
80
81 # if b is inf return inf (or NaN)
82 with m.Elif(b1.is_inf):
83 m.d.comb += self.o.out_do_z.eq(1)
84 m.d.comb += self.o.z.inf(sabx)
85 # a is zero return NaN
86 with m.If(a1.is_zero):
87 m.d.comb += self.o.z.nan(0)
88
89 # if a is zero or b zero return signed-a/b
90 with m.Elif(obz):
91 m.d.comb += self.o.out_do_z.eq(1)
92 m.d.comb += self.o.z.zero(sabx)
93
94 # Denormalised Number checks next, so pass a/b data through
95 with m.Else():
96 m.d.comb += self.o.out_do_z.eq(0)
97
98 m.d.comb += self.o.oz.eq(self.o.z.v)
99 m.d.comb += self.o.ctx.eq(self.i.ctx)
100
101 return m
102
103
104 class FPMulSpecialCasesDeNorm(DynamicPipe):
105 """ special cases: NaNs, infs, zeros, denormalised
106 """
107
108 def __init__(self, pspec):
109 self.pspec = pspec
110 super().__init__(pspec)
111 self.out = self.ospec()
112
113 def ispec(self):
114 return FPADDBaseData(self.pspec)
115
116 def ospec(self):
117 return FPSCData(self.pspec, False)
118
119 def setup(self, m, i):
120 """ links module to inputs and outputs
121 """
122 smod = FPMulSpecialCasesMod(self.pspec)
123 dmod = FPAddDeNormMod(self.pspec, False)
124 amod = FPAlignModSingle(self.pspec, False)
125
126 chain = StageChain([smod, dmod, amod])
127 chain.setup(m, i)
128
129 # only needed for break-out (early-out)
130 # self.out_do_z = smod.o.out_do_z
131
132 self.o = amod.o # output is from last .o in the chain
133
134 def process(self, i):
135 return self.o
136