-# IEEE Floating Point Adder (Single Precision)
-# Copyright (C) Jonathan P Dawson 2013
-# 2013-12-12
+"""IEEE754 Floating Point Library
-from nmigen import Module, Signal, Elaboratable
+Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
+"""
+
+from nmigen import Module, Signal, Mux
from nmigen.cli import main, verilog
-from ieee754.fpcommon.fpbase import FPNumOut, FPNumIn, FPNumBase
+from nmutil.pipemodbase import PipeModBase
+from ieee754.fpcommon.fpbase import FPNumBaseRecord
from ieee754.fpcommon.fpbase import MultiShiftRMerge
-from ieee754.fpcommon.fpbase import FPState
from ieee754.fpcommon.denorm import FPSCData
+from ieee754.fpcommon.getop import FPPipeContext
+from ieee754.fpcommon.pscdata import FPSCData
-class FPNumIn2Ops:
-
- def __init__(self, width, id_wid):
- self.a = FPNumIn(None, width)
- self.b = FPNumIn(None, width)
- self.z = FPNumOut(width, False)
- self.out_do_z = Signal(reset_less=True)
- self.oz = Signal(width, reset_less=True)
- self.mid = Signal(id_wid, reset_less=True)
-
- def eq(self, i):
- return [self.z.eq(i.z), self.out_do_z.eq(i.out_do_z), self.oz.eq(i.oz),
- self.a.eq(i.a), self.b.eq(i.b), self.mid.eq(i.mid)]
-
-
-
-class FPAddAlignMultiMod(FPState):
-
+class FPAddAlignMultiMod:
+ """Module to do mantissa alignment shift in multiple cycles
+ """
def __init__(self, width):
- self.in_a = FPNumBase(width)
- self.in_b = FPNumBase(width)
- self.out_a = FPNumIn(None, width)
- self.out_b = FPNumIn(None, width)
+ self.in_a = FPNumBaseRecord(width)
+ self.in_b = FPNumBaseRecord(width)
+ self.out_a = FPNumBaseRecord(width)
+ self.out_b = FPNumBaseRecord(width)
self.exp_eq = Signal(reset_less=True)
def elaborate(self, platform):
- # This one however (single-cycle) will do the shift
- # in one go.
-
m = Module()
-
- m.submodules.align_in_a = self.in_a
- m.submodules.align_in_b = self.in_b
- m.submodules.align_out_a = self.out_a
- m.submodules.align_out_b = self.out_b
-
- # NOTE: this does *not* do single-cycle multi-shifting,
- # it *STAYS* in the align state until exponents match
+ comb = m.d.comb
# exponent of a greater than b: shift b down
- m.d.comb += self.exp_eq.eq(0)
- m.d.comb += self.out_a.eq(self.in_a)
- m.d.comb += self.out_b.eq(self.in_b)
+ comb += self.exp_eq.eq(0)
+ comb += self.out_a.eq(self.in_a)
+ comb += self.out_b.eq(self.in_b)
agtb = Signal(reset_less=True)
altb = Signal(reset_less=True)
- m.d.comb += agtb.eq(self.in_a.e > self.in_b.e)
- m.d.comb += altb.eq(self.in_a.e < self.in_b.e)
+ comb += agtb.eq(self.in_a.e > self.in_b.e)
+ comb += altb.eq(self.in_a.e < self.in_b.e)
with m.If(agtb):
- m.d.comb += self.out_b.shift_down(self.in_b)
+ comb += self.out_b.shift_down(self.in_b)
# exponent of b greater than a: shift a down
with m.Elif(altb):
- m.d.comb += self.out_a.shift_down(self.in_a)
+ comb += self.out_a.shift_down(self.in_a)
# exponents equal: move to next stage.
with m.Else():
- m.d.comb += self.exp_eq.eq(1)
+ comb += self.exp_eq.eq(1)
return m
-class FPAddAlignMulti(FPState):
-
- def __init__(self, width, id_wid):
- FPState.__init__(self, "align")
- self.mod = FPAddAlignMultiMod(width)
- self.out_a = FPNumIn(None, width)
- self.out_b = FPNumIn(None, width)
- self.exp_eq = Signal(reset_less=True)
-
- def setup(self, m, in_a, in_b):
- """ links module to inputs and outputs
- """
- m.submodules.align = self.mod
- m.d.comb += self.mod.in_a.eq(in_a)
- m.d.comb += self.mod.in_b.eq(in_b)
- m.d.comb += self.exp_eq.eq(self.mod.exp_eq)
- m.d.sync += self.out_a.eq(self.mod.out_a)
- m.d.sync += self.out_b.eq(self.mod.out_b)
-
- def action(self, m):
- with m.If(self.exp_eq):
- m.next = "add_0"
+class FPAddAlignSingleMod(PipeModBase):
-
-class FPAddAlignSingleMod(Elaboratable):
-
- def __init__(self, width, id_wid):
- self.width = width
- self.id_wid = id_wid
- self.i = self.ispec()
- self.o = self.ospec()
+ def __init__(self, pspec):
+ super().__init__(pspec, "align")
def ispec(self):
- return FPSCData(self.width, self.id_wid)
+ return FPSCData(self.pspec, True)
def ospec(self):
- return FPNumIn2Ops(self.width, self.id_wid)
-
- def process(self, i):
- return self.o
-
- def setup(self, m, i):
- """ links module to inputs and outputs
- """
- m.submodules.align = self
- m.d.comb += self.i.eq(i)
+ return FPSCData(self.pspec, True)
def elaborate(self, platform):
""" Aligns A against B or B against A, depending on which has the
the shifter used here is quite expensive in terms of gates.
Mux A or B in (and out) into temporaries, as only one of them
- needs to be aligned against the other
+ needs to be aligned against the other.
+
+ code is therefore slightly complex because after testing which
+ exponent is greater, a and b get mux-routed into the multi-shifter
+ and so does the output.
"""
m = Module()
+ comb = m.d.comb
- m.submodules.align_in_a = self.i.a
- m.submodules.align_in_b = self.i.b
- m.submodules.align_out_a = self.o.a
- m.submodules.align_out_b = self.o.b
+ ai = self.i.a
+ bi = self.i.b
+ width = self.pspec.width
+ espec = (len(ai.e), True)
# temporary (muxed) input and output to be shifted
- t_inp = FPNumBase(self.width)
- t_out = FPNumIn(None, self.width)
- espec = (len(self.i.a.e), True)
- msr = MultiShiftRMerge(self.i.a.m_width, espec)
- m.submodules.align_t_in = t_inp
- m.submodules.align_t_out = t_out
+ t_inp = FPNumBaseRecord(width)
+ t_out = FPNumBaseRecord(width)
+ msr = MultiShiftRMerge(ai.m_width, espec)
m.submodules.multishift_r = msr
+ # temporaries
ediff = Signal(espec, reset_less=True)
ediffr = Signal(espec, reset_less=True)
tdiff = Signal(espec, reset_less=True)
egz = Signal(reset_less=True)
# connect multi-shifter to t_inp/out mantissa (and tdiff)
- m.d.comb += msr.inp.eq(t_inp.m)
- m.d.comb += msr.diff.eq(tdiff)
- m.d.comb += t_out.m.eq(msr.m)
- m.d.comb += t_out.e.eq(t_inp.e + tdiff)
- m.d.comb += t_out.s.eq(t_inp.s)
-
- m.d.comb += ediff.eq(self.i.a.e - self.i.b.e)
- m.d.comb += ediffr.eq(self.i.b.e - self.i.a.e)
- m.d.comb += elz.eq(self.i.a.e < self.i.b.e)
- m.d.comb += egz.eq(self.i.a.e > self.i.b.e)
-
- # default: A-exp == B-exp, A and B untouched (fall through)
- m.d.comb += self.o.a.eq(self.i.a)
- m.d.comb += self.o.b.eq(self.i.b)
- # only one shifter (muxed)
- #m.d.comb += t_out.shift_down_multi(tdiff, t_inp)
- # exponent of a greater than b: shift b down
- with m.If(~self.i.out_do_z):
- with m.If(egz):
- m.d.comb += [t_inp.eq(self.i.b),
- tdiff.eq(ediff),
- self.o.b.eq(t_out),
- self.o.b.s.eq(self.i.b.s), # whoops forgot sign
- ]
- # exponent of b greater than a: shift a down
- with m.Elif(elz):
- m.d.comb += [t_inp.eq(self.i.a),
- tdiff.eq(ediffr),
- self.o.a.eq(t_out),
- self.o.a.s.eq(self.i.a.s), # whoops forgot sign
- ]
-
- m.d.comb += self.o.mid.eq(self.i.mid)
- m.d.comb += self.o.z.eq(self.i.z)
- m.d.comb += self.o.out_do_z.eq(self.i.out_do_z)
- m.d.comb += self.o.oz.eq(self.i.oz)
+ # (only one: input/output is muxed)
+ comb += msr.inp.eq(t_inp.m)
+ comb += msr.diff.eq(tdiff)
+ comb += t_out.m.eq(msr.m)
+ comb += t_out.e.eq(Mux(egz, ai.e, bi.e))
+ comb += t_out.s.eq(t_inp.s)
+
+ # work out exponent difference, set up mux-tests if a > b or b > a
+ comb += ediff.eq(ai.e - bi.e) # a - b
+ comb += ediffr.eq(-ediff) # b - a
+ comb += elz.eq(ediffr > 0) # ae < be
+ comb += egz.eq(ediff > 0) # ae > be
+
+ # decide what to input into the multi-shifter
+ comb += [t_inp.s.eq(Mux(egz, bi.s, ai.s)), # a/b sign
+ t_inp.m.eq(Mux(egz, bi.m, ai.m)), # a/b mantissa
+ t_inp.e.eq(Mux(egz, bi.e, ai.e)), # a/b exponent
+ tdiff.eq(Mux(egz, ediff, ediffr)),
+ ]
+
+ # now decide where (if) to route the *output* of the multi-shifter
+
+ # if a exponent greater, route mshifted-out to b? otherwise just b
+ comb += [self.o.b.e.eq(Mux(egz, t_out.e, bi.e)), # exponent
+ self.o.b.m.eq(Mux(egz, t_out.m, bi.m)), # mantissa
+ self.o.b.s.eq(bi.s), # sign as-is
+ ]
+ # if b exponent greater, route mshifted-out to a? otherwise just a
+ comb += [self.o.a.e.eq(Mux(elz, t_out.e, ai.e)), # exponent
+ self.o.a.m.eq(Mux(elz, t_out.m, ai.m)), # mantissa
+ self.o.a.s.eq(ai.s), # sign as-is
+ ]
+
+ # pass context through
+ comb += self.o.ctx.eq(self.i.ctx)
+ comb += self.o.z.eq(self.i.z)
+ comb += self.o.out_do_z.eq(self.i.out_do_z)
+ comb += self.o.oz.eq(self.i.oz)
+ comb += self.o.rm.eq(self.i.rm)
return m
-
-class FPAddAlignSingle(FPState):
-
- def __init__(self, width, id_wid):
- FPState.__init__(self, "align")
- self.mod = FPAddAlignSingleMod(width, id_wid)
- self.out_a = FPNumIn(None, width)
- self.out_b = FPNumIn(None, width)
-
- def setup(self, m, i):
- """ links module to inputs and outputs
- """
- self.mod.setup(m, i)
-
- # NOTE: could be done as comb
- m.d.sync += self.out_a.eq(self.mod.out_a)
- m.d.sync += self.out_b.eq(self.mod.out_b)
-
- def action(self, m):
- m.next = "add_0"
-
-