-# IEEE Floating Point Adder (Single Precision)
-# Copyright (C) Jonathan P Dawson 2013
-# 2013-12-12
+"""IEEE754 Floating Point Conversion
-from nmigen import Module
-from nmigen.cli import main, verilog
+Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
+"""
+
+import sys
+import functools
from nmutil.singlepipe import ControlBase
from nmutil.concurrentunit import ReservationStations, num_bits
-from ieee754.fpcommon.getop import FPADDBaseData
-from ieee754.fpcommon.denorm import FPSCData
-from ieee754.fpcommon.pack import FPPackData
from ieee754.fpcommon.normtopack import FPNormToPack
-from ieee754.fpcommon.postcalc import FPAddStage1Data
-
-
-from nmigen import Module, Signal, Elaboratable
-from math import log
+from ieee754.pipeline import PipelineSpec, DynamicPipe
-from ieee754.fpcommon.fpbase import FPNumIn, FPNumOut, FPNumBaseRecord
-from ieee754.fpcommon.fpbase import FPState, FPNumBase
-from ieee754.fpcommon.getop import FPPipeContext
+from ieee754.fcvt.float2int import FPCVTFloatToIntMod
+from ieee754.fcvt.int2float import FPCVTIntToFloatMod
+from ieee754.fcvt.upsize import FPCVTUpConvertMod
+from ieee754.fcvt.downsize import FPCVTDownConvertMod
-from nmigen import Module, Signal, Cat, Const, Elaboratable
-from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
-from nmutil.singlepipe import SimpleHandshake, StageChain
+# not used, yet
+# from nmigen import Signal
+class SignedOp:
+ def __init__(self):
+ self.signed = Signal(reset_less=True)
-from ieee754.fpcommon.fpbase import FPState, FPID
-from ieee754.fpcommon.getop import FPADDBaseData
+ def eq(self, i):
+ return [self.signed.eq(i)]
-class FPCVTSpecialCasesMod(Elaboratable):
- """ special cases: NaNs, infs, zeros, denormalised
- see "Special Operations"
- https://steve.hollasch.net/cgindex/coding/ieeefloat.html
+class FPCVTConvertDeNorm(DynamicPipe):
+ """ FPConversion and De-norm
"""
- def __init__(self, in_width, out_width, in_pspec, out_pspec):
- self.in_width = in_width
- self.out_width = out_width
- self.in_pspec = in_pspec
- self.out_pspec = out_pspec
- self.i = self.ispec()
- self.o = self.ospec()
+ def __init__(self, in_pspec, out_pspec, modkls):
+ print ("cvtdenorm")
+ sc = modkls(in_pspec, out_pspec)
+ in_pspec.stage = sc
+ super().__init__(in_pspec)
- def ispec(self):
- return FPADDBaseData(self.in_width, self.in_pspec)
- def ospec(self):
- return FPAddStage1Data(self.out_width, self.out_pspec)
-
- def setup(self, m, i):
- """ links module to inputs and outputs
- """
- m.submodules.specialcases = self
- m.d.comb += self.i.eq(i)
+# this one is slightly weird-looking because of course the INT output
+# is, duh, an INT, so of course does not get "FP normalised".
+class FPCVTFtoIntBasePipe(ControlBase):
+ def __init__(self, modkls, e_extra, in_pspec, out_pspec):
+ ControlBase.__init__(self)
+ self.pipe1 = FPCVTConvertDeNorm(in_pspec, out_pspec, modkls)
+ #self.pipe2 = FPNormToPack(out_pspec, e_extra=e_extra)
- def process(self, i):
- return self.o
+ #self._eqs = self.connect([self.pipe1, self.pipe2])
+ self._eqs = self.connect([self.pipe1, ])
def elaborate(self, platform):
- m = Module()
-
- #m.submodules.sc_out_z = self.o.z
-
- # decode: XXX really should move to separate stage
- print ("in_width out", self.in_width, self.out_width)
- a1 = FPNumBaseRecord(self.in_width, False)
- m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
- m.d.comb += a1.v.eq(self.i.a)
- z1 = self.o.z
-
- # set sign
- m.d.comb += self.o.z.s.eq(a1.s)
-
- # intermediaries
- exp_sub_n126 = Signal((a1.e_width, True), reset_less=True)
- exp_gt127 = Signal(reset_less=True)
- # constants from z1, at the bit-width of a1.
- N126 = Const(z1.fp.N126.value, (a1.e_width, True))
- P127 = Const(z1.fp.P127.value, (a1.e_width, True))
- m.d.comb += exp_sub_n126.eq(a1.e - N126)
- m.d.comb += exp_gt127.eq(a1.e > P127)
-
- # if a zero, return zero (signed)
- with m.If(a1.exp_n127):
- _, ze, zm = self.o.z._zero(a1.s)
- m.d.comb += self.o.z.e.eq(ze)
- m.d.comb += self.o.z.m.eq(zm)
-
- # if a range within z min range (-126)
- with m.Elif(exp_sub_n126 < 0):
- m.d.comb += self.o.z.e.eq(a1.e)
- m.d.comb += self.o.z.m.eq(a1.m[-self.o.z.rmw:])
- m.d.comb += self.o.of.guard.eq(a1.m[-self.o.z.rmw-1])
- m.d.comb += self.o.of.round_bit.eq(a1.m[-self.o.z.rmw-2])
- m.d.comb += self.o.of.sticky.eq(a1.m[:-self.o.z.rmw-2] != 0)
-
- # if a is inf return inf
- with m.Elif(a1.is_inf):
- _, ze, zm = self.o.z._inf(a1.s)
- m.d.comb += self.o.z.e.eq(ze)
- m.d.comb += self.o.z.m.eq(zm)
-
- # if a is NaN return NaN
- with m.Elif(a1.is_nan):
- _, ze, zm = self.o.z._nan(a1.s)
- m.d.comb += self.o.z.e.eq(ze)
- m.d.comb += self.o.z.m.eq(zm)
-
- # if a mantissa greater than 127, return inf
- with m.Elif(exp_gt127):
- _, ze, zm = self.o.z._inf(a1.s)
- m.d.comb += self.o.z.e.eq(ze)
- m.d.comb += self.o.z.m.eq(zm)
-
- # ok after all that, anything else should fit fine (whew)
- with m.Else():
- m.d.comb += self.o.z.e.eq(a1.e)
- print ("alen", a1.e_start, z1.fp.N126, N126)
- print ("m1", self.o.z.rmw, a1.m[-self.o.z.rmw:])
- m.d.comb += self.o.z.m.eq(a1.m[-self.o.z.rmw:])
-
- # copy the context (muxid, operator)
- m.d.comb += self.o.ctx.eq(self.i.ctx)
-
+ m = ControlBase.elaborate(self, platform)
+ m.submodules.down = self.pipe1
+ #m.submodules.normpack = self.pipe2
+ m.d.comb += self._eqs
return m
-class FPCVTSpecialCases(FPState):
- """ special cases: NaNs, infs, zeros, denormalised
- """
-
- def __init__(self, in_width, out_width, id_wid):
- FPState.__init__(self, "special_cases")
- self.mod = FPCVTSpecialCasesMod(in_width, out_width)
- self.out_z = self.mod.ospec()
- self.out_do_z = Signal(reset_less=True)
-
- def setup(self, m, i):
- """ links module to inputs and outputs
- """
- self.mod.setup(m, i, self.out_do_z)
- m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
- m.d.sync += self.out_z.ctx.eq(self.mod.o.ctx) # (and context)
-
- def action(self, m):
- self.idsync(m)
- with m.If(self.out_do_z):
- m.next = "put_z"
- with m.Else():
- m.next = "denormalise"
-
-
-class FPCVTSpecialCasesDeNorm(FPState, SimpleHandshake):
- """ special cases: NaNs, infs, zeros, denormalised
- """
-
- def __init__(self, in_width, out_width, in_pspec, out_pspec):
- FPState.__init__(self, "special_cases")
- sc = FPCVTSpecialCasesMod(in_width, out_width, in_pspec, out_pspec)
- SimpleHandshake.__init__(self, sc)
- self.out = self.ospec(None)
-
-
class FPCVTBasePipe(ControlBase):
- def __init__(self, in_width, out_width, in_pspec, out_pspec):
+ def __init__(self, modkls, e_extra, in_pspec, out_pspec):
ControlBase.__init__(self)
- self.pipe1 = FPCVTSpecialCasesDeNorm(in_width, out_width,
- in_pspec, out_pspec)
- self.pipe2 = FPNormToPack(out_width, out_pspec)
+ self.pipe1 = FPCVTConvertDeNorm(in_pspec, out_pspec, modkls)
+ self.pipe2 = FPNormToPack(out_pspec, e_extra=e_extra)
self._eqs = self.connect([self.pipe1, self.pipe2])
def elaborate(self, platform):
m = ControlBase.elaborate(self, platform)
- m.submodules.scnorm = self.pipe1
+ m.submodules.down = self.pipe1
m.submodules.normpack = self.pipe2
m.d.comb += self._eqs
return m
-class FPCVTMuxInOut(ReservationStations):
+class FPCVTMuxInOutBase(ReservationStations):
+ """ Reservation-Station version of FPCVT pipeline.
+
+ * fan-in on inputs (an array of FPBaseData: a,b,mid)
+ * converter pipeline (alu)
+ * fan-out on outputs (an array of FPPackData: z,mid)
+
+ Fan-in and Fan-out are combinatorial.
+ """
+
+ def __init__(self, modkls, e_extra, in_width, out_width,
+ num_rows, op_wid=0, pkls=FPCVTBasePipe):
+ self.op_wid = op_wid
+ self.id_wid = num_bits(num_rows)
+
+ self.in_pspec = PipelineSpec(in_width, self.id_wid, self.op_wid)
+ self.out_pspec = PipelineSpec(out_width, self.id_wid, op_wid)
+
+ self.alu = pkls(modkls, e_extra, self.in_pspec, self.out_pspec)
+ ReservationStations.__init__(self, num_rows)
+
+
+class FPCVTF2IntMuxInOut(FPCVTMuxInOutBase):
""" Reservation-Station version of FPCVT pipeline.
- * fan-in on inputs (an array of FPADDBaseData: a,b,mid)
+ * fan-in on inputs (an array of FPBaseData: a,b,mid)
* 2-stage multiplier pipeline
* fan-out on outputs (an array of FPPackData: z,mid)
Fan-in and Fan-out are combinatorial.
"""
+
def __init__(self, in_width, out_width, num_rows, op_wid=0):
- self.in_width = in_width
- self.out_width = out_width
- self.op_wid = op_wid
- self.id_wid = num_bits(in_width)
- self.out_id_wid = num_bits(out_width)
+ FPCVTMuxInOutBase.__init__(self, FPCVTFloatToIntMod, False,
+ in_width, out_width,
+ num_rows, op_wid,
+ pkls=FPCVTFtoIntBasePipe)
- self.in_pspec = {}
- self.in_pspec['id_wid'] = self.id_wid
- self.in_pspec['op_wid'] = self.op_wid
- self.out_pspec = {}
- self.out_pspec['id_wid'] = self.out_id_wid
- self.out_pspec['op_wid'] = self.op_wid
+# factory which creates near-identical class structures that differ by
+# the module and the e_extra argument. at some point it would be good
+# to merge these into a single dynamic "thing" that takes an operator.
+# however, the difference(s) in the bitwidths makes that a little less
+# straightforward.
+muxfactoryinput = [("FPCVTDownMuxInOut", FPCVTDownConvertMod, True, ),
+ ("FPCVTUpMuxInOut", FPCVTUpConvertMod, False, ),
+ ("FPCVTIntMuxInOut", FPCVTIntToFloatMod, True, ),
+ ]
- self.alu = FPCVTBasePipe(in_width, out_width,
- self.in_pspec, self.out_pspec)
- ReservationStations.__init__(self, num_rows)
+def getkls(*args, **kwargs):
+ print ("getkls", args, kwargs)
+ return FPCVTMuxInOutBase(*args, **kwargs)
+
+for (name, kls, e_extra) in muxfactoryinput:
+ fn = functools.partial(getkls, kls, e_extra)
+ setattr(sys.modules[__name__], name, fn)
- def i_specfn(self):
- return FPADDBaseData(self.in_width, self.in_pspec)
- def o_specfn(self):
- return FPPackData(self.out_width, self.out_pspec)