From 894971e2b53da272eb5bdb4d3b900ee87752469e Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Wed, 17 Jul 2019 12:09:30 +0100 Subject: [PATCH] add first version fcvt int to fp16/32/64 --- src/ieee754/fcvt/pipeline.py | 161 ++++++++++++++++++++ src/ieee754/fcvt/test/test_fcvt_int_pipe.py | 42 +++++ 2 files changed, 203 insertions(+) create mode 100644 src/ieee754/fcvt/test/test_fcvt_int_pipe.py diff --git a/src/ieee754/fcvt/pipeline.py b/src/ieee754/fcvt/pipeline.py index aa28b7da..28b5e0e2 100644 --- a/src/ieee754/fcvt/pipeline.py +++ b/src/ieee754/fcvt/pipeline.py @@ -12,6 +12,7 @@ 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 nmigen import Module, Signal, Elaboratable @@ -28,6 +29,85 @@ from ieee754.fpcommon.fpbase import FPState from ieee754.pipeline import PipelineSpec +class FPCVTIntToFloatMod(Elaboratable): + """ FP integer conversion. + + TODO: dynamic selection of signed/unsigned + """ + 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() + + def ispec(self): + return FPADDBaseData(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.upconvert = self + m.d.comb += self.i.eq(i) + + def process(self, i): + return self.o + + 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_pspec.width, + self.out_pspec.width) + print("a1", self.in_pspec.width) + z1 = self.o.z + print("z1", z1.width, z1.rmw, z1.e_width, z1.e_start, z1.e_end) + + me = self.in_pspec.width + ms = self.o.z.rmw - me + print("ms-me", ms, me, self.o.z.rmw) + + # 3 extra bits for guard/round/sticky + msb = FPMSBHigh(me+3, z1.e_width) + m.submodules.norm_msb = msb + + # 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.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 + 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 + + # initialise rounding (but only activate if needed) + m.d.comb += self.o.of.guard.eq(msb.m_out[2]) + m.d.comb += self.o.of.round_bit.eq(msb.m_out[1]) + m.d.comb += self.o.of.sticky.eq(msb.m_out[1]) + m.d.comb += self.o.of.m0.eq(msb.m_out[3]) + + # special cases active by default + m.d.comb += self.o.out_do_z.eq(1) + + # detect zero + with m.If(~self.i.a.bool()): + m.d.comb += self.o.z.zero(0) + with m.Else(): + m.d.comb += self.o.out_do_z.eq(0) # activate normalisation + + # copy the context (muxid, operator) + m.d.comb += self.o.oz.eq(self.o.z.v) + m.d.comb += self.o.ctx.eq(self.i.ctx) + + return m + + class FPCVTUpConvertMod(Elaboratable): """ FP up-conversion (lower to higher bitwidth) """ @@ -221,6 +301,31 @@ class FPCVTDownConvertMod(Elaboratable): return m +class FPCVTIntToFloat(FPState): + """ Up-conversion + """ + + def __init__(self, in_width, out_width, id_wid): + FPState.__init__(self, "inttofloat") + self.mod = FPCVTIntToFloatMod(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 FPCVTUpConvert(FPState): """ Up-conversion """ @@ -271,6 +376,17 @@ class FPCVTDownConvert(FPState): m.next = "denormalise" +class FPCVTIntToFloatDeNorm(FPState, SimpleHandshake): + """ Upconvert + """ + + def __init__(self, in_pspec, out_pspec): + FPState.__init__(self, "inttofloat") + sc = FPCVTIntToFloatMod(in_pspec, out_pspec) + SimpleHandshake.__init__(self, sc) + self.out = self.ospec(None) + + class FPCVTUpConvertDeNorm(FPState, SimpleHandshake): """ Upconvert """ @@ -293,6 +409,22 @@ class FPCVTDownConvertDeNorm(FPState, SimpleHandshake): self.out = self.ospec(None) +class FPCVTIntBasePipe(ControlBase): + def __init__(self, in_pspec, out_pspec): + ControlBase.__init__(self) + self.pipe1 = FPCVTIntToFloatDeNorm(in_pspec, out_pspec) + self.pipe2 = FPNormToPack(out_pspec, e_extra=True) + + self._eqs = self.connect([self.pipe1, self.pipe2]) + + def elaborate(self, platform): + m = ControlBase.elaborate(self, platform) + m.submodules.toint = self.pipe1 + m.submodules.normpack = self.pipe2 + m.d.comb += self._eqs + return m + + class FPCVTUpBasePipe(ControlBase): def __init__(self, in_pspec, out_pspec): ControlBase.__init__(self) @@ -325,6 +457,34 @@ class FPCVTDownBasePipe(ControlBase): return m +class FPCVTIntMuxInOut(ReservationStations): + """ Reservation-Station version of FPCVT int-to-float pipeline. + + * fan-in on inputs (an array of FPADDBaseData: 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.op_wid = op_wid + self.id_wid = num_bits(in_width) + self.out_id_wid = num_bits(out_width) + + self.in_pspec = PipelineSpec(in_width, self.id_wid, self.op_wid) + self.out_pspec = PipelineSpec(out_width, self.out_id_wid, op_wid) + + self.alu = FPCVTIntBasePipe(self.in_pspec, self.out_pspec) + ReservationStations.__init__(self, num_rows) + + def i_specfn(self): + return FPADDBaseData(self.in_pspec) + + def o_specfn(self): + return FPPackData(self.out_pspec) + + class FPCVTUpMuxInOut(ReservationStations): """ Reservation-Station version of FPCVT up pipeline. @@ -352,6 +512,7 @@ class FPCVTUpMuxInOut(ReservationStations): def o_specfn(self): return FPPackData(self.out_pspec) + class FPCVTDownMuxInOut(ReservationStations): """ Reservation-Station version of FPCVT pipeline. diff --git a/src/ieee754/fcvt/test/test_fcvt_int_pipe.py b/src/ieee754/fcvt/test/test_fcvt_int_pipe.py new file mode 100644 index 00000000..1b407dcf --- /dev/null +++ b/src/ieee754/fcvt/test/test_fcvt_int_pipe.py @@ -0,0 +1,42 @@ +""" test of FPCVTMuxInOut +""" + +from ieee754.fcvt.pipeline import (FPCVTIntMuxInOut,) +from ieee754.fpcommon.test.fpmux import runfp + +import sfpy +from sfpy import Float64, Float32, Float16 + +def to_uint16(x): + return x + +def to_uint32(x): + return x + +def fcvt_64(x): + return sfpy.float.ui32_to_f64(x) + +def fcvt_32(x): + return sfpy.float.ui32_to_f32(x) + +def test_int_pipe_fp16_32(): + dut = FPCVTIntMuxInOut(16, 32, 4) + runfp(dut, 16, "test_fcvt_int_pipe_fp16_32", to_uint16, fcvt_32, True, + n_vals=100) + +def test_int_pipe_fp16_64(): + dut = FPCVTIntMuxInOut(16, 64, 4) + runfp(dut, 16, "test_fcvt_int_pipe_fp16_64", to_uint16, fcvt_64, True, + n_vals=100) + +def test_int_pipe_fp32_64(): + dut = FPCVTIntMuxInOut(32, 64, 4) + runfp(dut, 32, "test_fcvt_int_pipe_fp32_64", to_uint32, fcvt_64, True, + n_vals=100) + +if __name__ == '__main__': + for i in range(200): + test_int_pipe_fp16_32() + test_int_pipe_fp16_64() + test_int_pipe_fp32_64() + -- 2.30.2