# Copyright (C) Jonathan P Dawson 2013
# 2013-12-12
-from nmigen import Module, Signal, Cat, Const
-from nmigen.cli import main, verilog
-from math import log
+from nmigen import Module, Signal, Cat, Mux
-from nmutil.pipemodbase import FPModBase, FPModBaseChain
-from ieee754.fpcommon.fpbase import FPNumDecode
+from nmutil.pipemodbase import PipeModBase, PipeModBaseChain
+from ieee754.fpcommon.fpbase import FPFormat, FPNumDecode, FPRoundingMode
from ieee754.fpcommon.fpbase import FPNumBaseRecord
-from ieee754.fpcommon.getop import FPADDBaseData
+from ieee754.fpcommon.basedata import FPBaseData
from ieee754.fpcommon.denorm import (FPSCData, FPAddDeNormMod)
-class FPAddSpecialCasesMod(FPModBase):
+class FPAddInputData(FPBaseData):
+ def __init__(self, pspec):
+ super().__init__(pspec)
+ self.is_sub = Signal(reset=False)
+
+ def eq(self, i):
+ ret = super().eq(i)
+ ret.append(self.is_sub.eq(i.is_sub))
+ return ret
+
+ def __iter__(self):
+ yield from super().__iter__()
+ yield self.is_sub
+
+ def ports(self):
+ return list(self)
+
+
+class FPAddSpecialCasesMod(PipeModBase):
""" special cases: NaNs, infs, zeros, denormalised
NOTE: some of these are unique to add. see "Special Operations"
https://steve.hollasch.net/cgindex/coding/ieeefloat.html
super().__init__(pspec, "specialcases")
def ispec(self):
- return FPADDBaseData(self.pspec)
+ return FPAddInputData(self.pspec)
def ospec(self):
return FPSCData(self.pspec, True)
b1 = FPNumBaseRecord(width)
m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1)
- comb += [a1.v.eq(self.i.a),
- b1.v.eq(self.i.b),
- self.o.a.eq(a1),
- self.o.b.eq(b1)
- ]
+ flip_b_sign = Signal()
+ b_is_nan = Signal()
+ comb += [
+ b_is_nan.eq(FPFormat.standard(width).is_nan(self.i.b)),
+ flip_b_sign.eq(self.i.is_sub & ~b_is_nan),
+ a1.v.eq(self.i.a),
+ b1.v.eq(self.i.b ^ (flip_b_sign << (width - 1))),
+ self.o.a.eq(a1),
+ self.o.b.eq(b1)
+ ]
+
+ zero_sign_array = FPRoundingMode.make_array(FPRoundingMode.zero_sign)
# temporaries used below
s_nomatch = Signal(reset_less=True)
+ s_match = Signal(reset_less=True)
m_match = Signal(reset_less=True)
e_match = Signal(reset_less=True)
- aeqmb = Signal(reset_less=True)
- abz = Signal(reset_less=True)
- abnan = Signal(reset_less=True)
+ absa = Signal(reset_less=True) # a1.s & b1.s
+ t_aeqmb = Signal(reset_less=True)
+ t_a1inf = Signal(reset_less=True)
+ t_b1inf = Signal(reset_less=True)
+ t_a1zero = Signal(reset_less=True)
+ t_b1zero = Signal(reset_less=True)
+ t_abz = Signal(reset_less=True)
+ t_abnan = Signal(reset_less=True)
bexp128s = Signal(reset_less=True)
+ t_special = Signal(reset_less=True)
comb += s_nomatch.eq(a1.s != b1.s)
+ comb += s_match.eq(a1.s == b1.s)
comb += m_match.eq(a1.m == b1.m)
comb += e_match.eq(a1.e == b1.e)
- comb += aeqmb.eq(s_nomatch & m_match & e_match)
- comb += abz.eq(a1.is_zero & b1.is_zero)
- comb += abnan.eq(a1.is_nan | b1.is_nan)
- comb += bexp128s.eq(b1.exp_128 & s_nomatch)
- # default bypass
- comb += self.o.out_do_z.eq(1)
+ # logic-chain (matches comments, below) gives an if-elif-elif-elif...
+ comb += t_abnan.eq(a1.is_nan | b1.is_nan)
+ comb += t_a1inf.eq(a1.is_inf)
+ comb += t_b1inf.eq(b1.is_inf)
+ comb += t_abz.eq(a1.is_zero & b1.is_zero)
+ comb += t_a1zero.eq(a1.is_zero)
+ comb += t_b1zero.eq(b1.is_zero)
+ comb += t_aeqmb.eq(s_nomatch & m_match & e_match)
+ comb += t_special.eq(Cat(t_aeqmb, t_b1zero, t_a1zero, t_abz,
+ t_b1inf, t_a1inf, t_abnan).bool())
+
+ comb += absa.eq(a1.s & b1.s)
+ comb += bexp128s.eq(b1.exp_128 & s_nomatch)
+ # prepare inf/zero/nans
+ z_zero = FPNumBaseRecord(width, False, name="z_zero")
+ z_default_zero = FPNumBaseRecord(width, False, name="z_default_zero")
+ z_default_nan = FPNumBaseRecord(width, False, name="z_default_nan")
+ z_quieted_a = FPNumBaseRecord(width, False, name="z_quieted_a")
+ z_quieted_b = FPNumBaseRecord(width, False, name="z_quieted_b")
+ z_infa = FPNumBaseRecord(width, False, name="z_infa")
+ z_infb = FPNumBaseRecord(width, False, name="z_infb")
+ comb += z_zero.zero(0)
+ comb += z_default_zero.zero(zero_sign_array[self.i.rm])
+ comb += z_default_nan.nan(0)
+ comb += z_quieted_a.quieted_nan(a1)
+ comb += z_quieted_b.quieted_nan(b1)
+ comb += z_infa.inf(a1.s)
+ comb += z_infb.inf(b1.s)
+
+ # any special-cases it's a "special".
+ comb += self.o.out_do_z.eq(t_special)
+
+ # this is the logic-decision-making for special-cases:
# if a is NaN or b is NaN return NaN
- with m.If(abnan):
- comb += self.o.z.nan(0)
-
- # XXX WEIRDNESS for FP16 non-canonical NaN handling
- # under review
-
- ## if a is zero and b is NaN return -b
- #with m.If(a.is_zero & (a.s==0) & b.is_nan):
- # comb += self.o.out_do_z.eq(1)
- # comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
-
- ## if b is zero and a is NaN return -a
- #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
- # comb += self.o.out_do_z.eq(1)
- # comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
-
- ## if a is -zero and b is NaN return -b
- #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
- # comb += self.o.out_do_z.eq(1)
- # comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
-
- ## if b is -zero and a is NaN return -a
- #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
- # comb += self.o.out_do_z.eq(1)
- # comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
-
- # if a is inf return inf (or NaN)
- with m.Elif(a1.is_inf):
- comb += self.o.z.inf(a1.s)
- # if a is inf and signs don't match return NaN
- with m.If(bexp128s):
- comb += self.o.z.nan(0)
-
- # if b is inf return inf
- with m.Elif(b1.is_inf):
- comb += self.o.z.inf(b1.s)
-
- # if a is zero and b zero return signed-a/b
- with m.Elif(abz):
- comb += self.o.z.create(a1.s & b1.s, b1.e, b1.m[3:-1])
-
- # if a is zero return b
- with m.Elif(a1.is_zero):
- comb += self.o.z.create(b1.s, b1.e, b1.m[3:-1])
-
- # if b is zero return a
- with m.Elif(b1.is_zero):
- comb += self.o.z.create(a1.s, a1.e, a1.m[3:-1])
-
- # if a equal to -b return zero (+ve zero)
- with m.Elif(aeqmb):
- comb += self.o.z.zero(0)
-
- # Denormalised Number checks next, so pass a/b data through
- with m.Else():
- comb += self.o.out_do_z.eq(0)
-
- comb += self.o.oz.eq(self.o.z.v)
+ # if a is NaN return quieted_nan(a)
+ # else return quieted_nan(b)
+ # elif a is inf return inf (or NaN)
+ # if a is inf and signs don't match return NaN
+ # else return inf(a)
+ # elif b is inf return inf(b)
+ # elif a is zero and b zero with same sign return a
+ # elif a equal to -b return zero (sign determined by rounding-mode)
+ # elif a is zero return b
+ # elif b is zero return a
+
+ # XXX *sigh* there are better ways to do this...
+ # one of them: use a priority-picker!
+ # in reverse-order, accumulate Muxing
+
+ oz = 0
+ oz = Mux(t_b1zero, a1.v, oz)
+ oz = Mux(t_a1zero, b1.v, oz)
+ oz = Mux(t_aeqmb, z_default_zero.v, oz)
+ oz = Mux(t_abz & s_match, a1.v, oz)
+ oz = Mux(t_b1inf, z_infb.v, oz)
+ oz = Mux(t_a1inf, Mux(bexp128s, z_default_nan.v, z_infa.v), oz)
+ oz = Mux(t_abnan, Mux(a1.is_nan, z_quieted_a.v, z_quieted_b.v), oz)
+
+ comb += self.o.oz.eq(oz)
+
comb += self.o.ctx.eq(self.i.ctx)
+ comb += self.o.rm.eq(self.i.rm)
+
return m
-class FPAddSpecialCasesDeNorm(FPModBaseChain):
+class FPAddSpecialCasesDeNorm(PipeModBaseChain):
""" special cases chain
"""