add comment about simulation bugs
[ieee754fpu.git] / src / ieee754 / fcvt / pipeline.py
index 7218be4e00420e1263124f9dd7ee1875f9c1123f..5e6cc19dee7f98c971d1ea7e2266339b4bf4d2ec 100644 (file)
-# 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)