add in preliminary signed int conversion
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 19 Jul 2019 09:58:10 +0000 (10:58 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 19 Jul 2019 09:58:10 +0000 (10:58 +0100)
first example usage of FPPipeContext "operator"

src/ieee754/fcvt/pipeline.py
src/ieee754/fcvt/test/test_fcvt_int_pipe.py
src/ieee754/fpcommon/test/fpmux.py

index 3ba53148cf70ca1a6c9c2c68a3743c55c2f969ad..5487aa6520811e17ff6927b9a9f3458bcf53ff10 100644 (file)
@@ -30,9 +30,10 @@ from ieee754.pipeline import PipelineSpec
 
 
 class FPCVTIntToFloatMod(Elaboratable):
-    """ FP integer conversion.
+    """ FP integer conversion: copes with 16/32/64 int to 16/32/64 fp.
 
-        TODO: dynamic selection of signed/unsigned
+        self.ctx.i.op & 0x1 == 0x1 : SIGNED int
+        self.ctx.i.op & 0x1 == 0x0 : UNSIGNED int
     """
     def __init__(self, in_pspec, out_pspec):
         self.in_pspec = in_pspec
@@ -76,22 +77,38 @@ class FPCVTIntToFloatMod(Elaboratable):
         msb = FPMSBHigh(me+3, z1.e_width)
         m.submodules.norm_msb = msb
 
+        # signed or unsigned, use operator context
+        signed = Signal(reset_less=True)
+        m.d.comb += signed.eq(self.i.ctx.op[0])
+
+        # copy of mantissa (one less bit if signed)
+        mantissa = Signal(me, reset_less=True)
+
+        # detect signed/unsigned.  key case: -ve numbers need inversion
+        # to +ve because the FP sign says if it's -ve or not.
+        with m.If(signed):
+            m.d.comb += z1.s.eq(self.i.a[-1])      # sign in top bit of a
+            with m.If(z1.s):
+                m.d.comb += mantissa.eq(-self.i.a) # invert input if sign -ve
+            with m.Else():
+                m.d.comb += mantissa.eq(self.i.a)  # leave as-is
+        with m.Else():
+            m.d.comb += mantissa.eq(self.i.a)      # unsigned, use full a
+            m.d.comb += z1.s.eq(0)
+
         # set input from full INT
-        m.d.comb += msb.m_in.eq(Cat(0, 0, 0, self.i.a)) # g/r/s + input
+        m.d.comb += msb.m_in.eq(Cat(0, 0, 0, mantissa)) # g/r/s + input
         m.d.comb += msb.e_in.eq(me)                     # exp = int width
 
-        # conversion can mostly be done manually...
-        zo = self.o.z
-        m.d.comb += zo.s.eq(0)  # unsigned for now
         if ms < 0:
             # larger int to smaller FP (uint32/64 -> fp16 most likely)
-            m.d.comb += zo.e.eq(msb.e_out-1)
-            m.d.comb += zo.m[ms-1:].eq(msb.m_out[-mz-1:])
+            m.d.comb += z1.e.eq(msb.e_out-1)
+            m.d.comb += z1.m[ms-1:].eq(msb.m_out[-mz-1:])
         else:
             # smaller int to larger FP
-            m.d.comb += zo.e.eq(msb.e_out)
-            m.d.comb += zo.m[ms:].eq(msb.m_out[3:])
-        m.d.comb += zo.create(zo.s, zo.e, zo.m) # ... here
+            m.d.comb += z1.e.eq(msb.e_out)
+            m.d.comb += z1.m[ms:].eq(msb.m_out[3:])
+        m.d.comb += z1.create(z1.s, z1.e, z1.m) # ... here
 
         # note: post-normalisation actually appears to be capable of
         # detecting overflow to infinity (FPPackMod).  so it's ok to
index 29a7355cfbd644928951ebeb8a7312b16cae271e..ed48d17aa2a02331fa42538f9e3c522f9a590229 100644 (file)
@@ -7,6 +7,14 @@ from ieee754.fpcommon.test.fpmux import runfp
 import sfpy
 from sfpy import Float64, Float32, Float16
 
+def to_int16(x):
+    """ input: an unsigned int in the range 0..65535
+        output: a signed int in the range -32768..32767
+    """
+    if x > 32767:
+        return x-0x10000
+    return x
+
 def to_uint16(x):
     return x
 
@@ -19,6 +27,10 @@ def to_uint64(x):
 def fcvt_64(x):
     return sfpy.float.ui32_to_f64(x)
 
+def fcvt_i16_f32(x):
+    print ("fcvt i16_f32", x)
+    return sfpy.float.i32_to_f32(x) # XXX no i16_to_f32, it's ok though
+
 def fcvt_32(x):
     return sfpy.float.ui32_to_f32(x)
 
@@ -28,20 +40,35 @@ def fcvt_64_to_32(x):
 def fcvt_16(x):
     return sfpy.float.ui32_to_f16(x)
 
+######################
+# signed int to fp
+######################
+
+def test_int_pipe_i16_f32():
+    # XXX softfloat-3 doesn't have i16_to_xxx so use ui32 instead.
+    # should be fine.
+    dut = FPCVTIntMuxInOut(16, 32, 4, op_wid=1)
+    runfp(dut, 16, "test_fcvt_int_pipe_i16_f32", to_int16, fcvt_i16_f32, True,
+          n_vals=100, opcode=0x1)
+
+######################
+# unsigned int to fp
+######################
+
 def test_int_pipe_ui16_f32():
     # XXX softfloat-3 doesn't have ui16_to_xxx so use ui32 instead.
     # should be fine.
-    dut = FPCVTIntMuxInOut(16, 32, 4)
+    dut = FPCVTIntMuxInOut(16, 32, 4, op_wid=1)
     runfp(dut, 16, "test_fcvt_int_pipe_ui16_f32", to_uint16, fcvt_32, True,
           n_vals=100)
 
 def test_int_pipe_ui16_f64():
-    dut = FPCVTIntMuxInOut(16, 64, 4)
+    dut = FPCVTIntMuxInOut(16, 64, 4, op_wid=1)
     runfp(dut, 16, "test_fcvt_int_pipe_ui16_f64", to_uint16, fcvt_64, True,
           n_vals=100)
 
 def test_int_pipe_ui32_f64():
-    dut = FPCVTIntMuxInOut(32, 64, 4)
+    dut = FPCVTIntMuxInOut(32, 64, 4, op_wid=1)
     runfp(dut, 32, "test_fcvt_int_pipe_ui32_64", to_uint32, fcvt_64, True,
           n_vals=100)
 
@@ -49,7 +76,7 @@ def test_int_pipe_ui64_f32():
     # ok, doing 33 bits here because it's pretty pointless (not entirely)
     # to do random numbers statistically likely 99.999% of the time to be
     # converted to Inf
-    dut = FPCVTIntMuxInOut(64, 32, 4)
+    dut = FPCVTIntMuxInOut(64, 32, 4, op_wid=1)
     runfp(dut, 33, "test_fcvt_int_pipe_ui64_32", to_uint64, fcvt_64_to_32, True,
           n_vals=100)
 
@@ -57,7 +84,7 @@ def test_int_pipe_ui64_f16():
     # ok, doing 17 bits here because it's pretty pointless (not entirely)
     # to do random numbers statistically likely 99.999% of the time to be
     # converted to Inf
-    dut = FPCVTIntMuxInOut(64, 16, 4)
+    dut = FPCVTIntMuxInOut(64, 16, 4, op_wid=1)
     runfp(dut, 17, "test_fcvt_int_pipe_ui64_16", to_uint64, fcvt_16, True,
           n_vals=100)
 
@@ -65,16 +92,18 @@ def test_int_pipe_ui32_f16():
     # ok, doing 17 bits here because it's pretty pointless (not entirely)
     # to do random numbers statistically likely 99.999% of the time to be
     # converted to Inf
-    dut = FPCVTIntMuxInOut(32, 16, 4)
+    dut = FPCVTIntMuxInOut(32, 16, 4, op_wid=1)
     runfp(dut, 17, "test_fcvt_int_pipe_ui32_16", to_uint32, fcvt_16, True,
           n_vals=100)
 
 if __name__ == '__main__':
+    test_int_pipe_i16_f32()
+    test_int_pipe_ui16_f32()
+    exit()
     for i in range(200):
         test_int_pipe_ui64_f32()
         test_int_pipe_ui32_f16()
         test_int_pipe_ui64_f16()
-        test_int_pipe_ui16_f32()
         test_int_pipe_ui16_f64()
         test_int_pipe_ui32_f64()
 
index c15b09b502e5c270c7eb32175058da3ab55f03ad..c294f159252fe26d02ad727b58dbdf26fa61cefe 100644 (file)
@@ -11,11 +11,12 @@ from nmigen.cli import verilog, rtlil
 
 
 class MuxInOut:
-    def __init__(self, dut, width, fpkls, fpop, vals, single_op):
+    def __init__(self, dut, width, fpkls, fpop, vals, single_op, opcode):
         self.dut = dut
         self.fpkls = fpkls
         self.fpop = fpop
         self.single_op = single_op
+        self.opcode = opcode
         self.di = {}
         self.do = {}
         self.tlen = len(vals) // dut.num_rows
@@ -48,6 +49,8 @@ class MuxInOut:
             rs = self.dut.p[muxid]
             yield rs.valid_i.eq(1)
             yield rs.data_i.a.eq(op1)
+            if self.opcode is not None:
+                yield rs.data_i.ctx.op.eq(self.opcode)
             if not self.single_op:
                 yield rs.data_i.b.eq(op2)
             yield rs.data_i.muxid.eq(muxid)
@@ -188,7 +191,8 @@ def pipe_cornercases_repeat(dut, name, mod, fmod, width, fn, cc, fpfn, count,
                    fmod, fpfn, vals=vals, single_op=single_op)
 
 
-def runfp(dut, width, name, fpkls, fpop, single_op=False, n_vals=10, vals=None):
+def runfp(dut, width, name, fpkls, fpop, single_op=False, n_vals=10,
+          vals=None, opcode=None):
     vl = rtlil.convert(dut, ports=dut.ports())
     with open("%s.il" % name, "w") as f:
         f.write(vl)
@@ -196,7 +200,7 @@ def runfp(dut, width, name, fpkls, fpop, single_op=False, n_vals=10, vals=None):
     if vals is None:
         vals = create_random(dut.num_rows, width, single_op, n_vals)
 
-    test = MuxInOut(dut, width, fpkls, fpop, vals, single_op)
+    test = MuxInOut(dut, width, fpkls, fpop, vals, single_op, opcode=opcode)
     fns = []
     for i in range(dut.num_rows):
         fns.append(test.rcv(i))