add feedback_width argument to runfp for testing
[ieee754fpu.git] / src / ieee754 / fcvt / downsize.py
index 64fd09626a73c67d61f580c5831d2d4aab3407d9..cfc8a69e03c3fb9828aa2f5a200eeb028dc3a299 100644 (file)
@@ -1,72 +1,43 @@
 # IEEE754 Floating Point Conversion
 # Copyright (C) 2019 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
 
-
-import sys
-import functools
-
-from nmigen import Module, Signal, Cat, Const, Mux, Elaboratable
+from nmigen import Module, Signal, Const
 from nmigen.cli import main, verilog
 
-from nmutil.singlepipe import ControlBase
-from nmutil.concurrentunit import ReservationStations, num_bits
-
-from ieee754.fpcommon.fpbase import Overflow
-from ieee754.fpcommon.getop import FPADDBaseData
-from ieee754.fpcommon.pack import FPPackData
-from ieee754.fpcommon.normtopack import FPNormToPack
-from ieee754.fpcommon.postcalc import FPAddStage1Data
-from ieee754.fpcommon.msbhigh import FPMSBHigh
-from ieee754.fpcommon.exphigh import FPEXPHigh
-
-
-from nmigen import Module, Signal, Elaboratable
-from math import log
-
-from ieee754.fpcommon.fpbase import FPNumIn, FPNumOut, FPNumBaseRecord
-from ieee754.fpcommon.fpbase import FPState, FPNumBase
-from ieee754.fpcommon.getop import FPPipeContext
+from nmutil.pipemodbase import PipeModBase
+from ieee754.fpcommon.basedata import FPBaseData
+from ieee754.fpcommon.postcalc import FPPostCalcData
 
 from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord
-from nmutil.singlepipe import SimpleHandshake, StageChain
-
-from ieee754.fpcommon.fpbase import FPState
-from ieee754.pipeline import PipelineSpec
 
 
-class FPCVTDownConvertMod(Elaboratable):
+class FPCVTDownConvertMod(PipeModBase):
     """ FP down-conversion (higher to lower bitwidth)
     """
     def __init__(self, in_pspec, out_pspec):
         self.in_pspec = in_pspec
         self.out_pspec = out_pspec
-        self.i = self.ispec()
-        self.o = self.ospec()
+        super().__init__(in_pspec, "downconvert")
 
     def ispec(self):
-        return FPADDBaseData(self.in_pspec)
+        return FPBaseData(self.in_pspec)
 
     def ospec(self):
-        return FPAddStage1Data(self.out_pspec, e_extra=True)
-
-    def setup(self, m, i):
-        """ links module to inputs and outputs
-        """
-        m.submodules.downconvert = self
-        m.d.comb += self.i.eq(i)
-
-    def process(self, i):
-        return self.o
+        return FPPostCalcData(self.out_pspec, e_extra=True)
 
     def elaborate(self, platform):
         m = Module()
         comb = m.d.comb
+        print("in_width out", self.in_pspec.width, self.out_pspec.width)
 
-        #m.submodules.sc_out_z = self.o.z
+        # here we make room (in temporary constants / ospec) for extra
+        # bits in the exponent, at the size of the *incoming* number
+        # bitwidth.  in this way it is possible to detect, in the
+        # *outgoing* number, if the exponent is too large and needs
+        # adjustment.  otherwise we have to mess about with all sorts
+        # of width-detection and the normalisation, special cases etc.
+        # all become horribly complicated.
 
-        # decode: XXX really should move to separate stage
-        print("in_width out", self.in_pspec.width,
-              self.out_pspec.width)
         a1 = FPNumBaseRecord(self.in_pspec.width, False)
         print("a1", a1.width, a1.rmw, a1.e_width, a1.e_start, a1.e_end)
         m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1)
@@ -87,38 +58,40 @@ class FPCVTDownConvertMod(Elaboratable):
         comb += exp_sub_n126.eq(a1.e - N126)
         comb += exp_gt127.eq(a1.e > P127)
 
+        # bypass (always enabled except for normalisation, below)
+        comb += self.o.out_do_z.eq(1)
+
         # if a zero, return zero (signed)
         with m.If(a1.exp_n127):
             comb += self.o.z.zero(a1.s)
-            comb += self.o.out_do_z.eq(1)
 
         # if a range outside z's min range (-126)
         with m.Elif(exp_sub_n126 < 0):
             comb += self.o.of.guard.eq(a1.m[ms-1])
             comb += self.o.of.round_bit.eq(a1.m[ms-2])
             comb += self.o.of.sticky.eq(a1.m[:ms-2].bool())
-            comb += self.o.of.m0.eq(a1.m[ms])  # bit of a1
+            comb += self.o.of.m0.eq(a1.m[ms])  # LSB bit of a1
 
             comb += self.o.z.s.eq(a1.s)
             comb += self.o.z.e.eq(a1.e)
             comb += self.o.z.m.eq(a1.m[-self.o.z.rmw-1:])
             comb += self.o.z.m[-1].eq(1)
 
+            # normalisation required
+            comb += self.o.out_do_z.eq(0)
+
         # if a is inf return inf
         with m.Elif(a1.is_inf):
             comb += self.o.z.inf(a1.s)
-            comb += self.o.out_do_z.eq(1)
 
         # if a is NaN return NaN
         with m.Elif(a1.is_nan):
             comb += self.o.z.nan(0)
-            comb += self.o.out_do_z.eq(1)
 
         # if a mantissa greater than 127, return inf
         with m.Elif(exp_gt127):
             print("inf", self.o.z.inf(a1.s))
             comb += self.o.z.inf(a1.s)
-            comb += self.o.out_do_z.eq(1)
 
         # ok after all that, anything else should fit fine (whew)
         with m.Else():
@@ -133,18 +106,15 @@ class FPCVTDownConvertMod(Elaboratable):
             mo = Signal(self.o.z.m_width-1)
             comb += mo.eq(a1.m[ms:me])
             with m.If(self.o.of.roundz):
-                with m.If((~mo == 0)):  # all 1s
+                with m.If((mo.all())):  # mantissa-out is all 1s
                     comb += self.o.z.create(a1.s, a1.e+1, mo+1)
                 with m.Else():
                     comb += self.o.z.create(a1.s, a1.e, mo+1)
             with m.Else():
                 comb += self.o.z.create(a1.s, a1.e, a1.m[-self.o.z.rmw-1:])
-            comb += self.o.out_do_z.eq(1)
 
         # copy the context (muxid, operator)
         comb += self.o.oz.eq(self.o.z.v)
         comb += self.o.ctx.eq(self.i.ctx)
 
         return m
-
-