From fca1093babd57f9cba2e4b1093a8f97dd0450728 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Fri, 19 Jul 2019 10:58:10 +0100 Subject: [PATCH] add in preliminary signed int conversion first example usage of FPPipeContext "operator" --- src/ieee754/fcvt/pipeline.py | 39 +++++++++++++------ src/ieee754/fcvt/test/test_fcvt_int_pipe.py | 43 +++++++++++++++++---- src/ieee754/fpcommon/test/fpmux.py | 10 +++-- 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/ieee754/fcvt/pipeline.py b/src/ieee754/fcvt/pipeline.py index 3ba53148..5487aa65 100644 --- a/src/ieee754/fcvt/pipeline.py +++ b/src/ieee754/fcvt/pipeline.py @@ -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 diff --git a/src/ieee754/fcvt/test/test_fcvt_int_pipe.py b/src/ieee754/fcvt/test/test_fcvt_int_pipe.py index 29a7355c..ed48d17a 100644 --- a/src/ieee754/fcvt/test/test_fcvt_int_pipe.py +++ b/src/ieee754/fcvt/test/test_fcvt_int_pipe.py @@ -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() diff --git a/src/ieee754/fpcommon/test/fpmux.py b/src/ieee754/fpcommon/test/fpmux.py index c15b09b5..c294f159 100644 --- a/src/ieee754/fpcommon/test/fpmux.py +++ b/src/ieee754/fpcommon/test/fpmux.py @@ -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)) -- 2.30.2