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
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
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
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)
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)
# 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)
# 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)
# 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()
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
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)
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)
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))