From: Luke Kenneth Casson Leighton Date: Sat, 16 Feb 2019 11:24:12 +0000 (+0000) Subject: add div experiment X-Git-Tag: ls180-24jan2020~1947 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=0611568bd02236421ea892e81cddd857e38a2978;p=ieee754fpu.git add div experiment --- diff --git a/src/add/nmigen_div_experiment.py b/src/add/nmigen_div_experiment.py new file mode 100644 index 00000000..e6f68b84 --- /dev/null +++ b/src/add/nmigen_div_experiment.py @@ -0,0 +1,231 @@ +# IEEE Floating Point Adder (Single Precision) +# Copyright (C) Jonathan P Dawson 2013 +# 2013-12-12 + +from nmigen import Module, Signal, Const +from nmigen.cli import main, verilog + +from fpbase import FPNum, FPOp, Overflow, FPBase + +class Div: + def __init__(self, width): + self.width = width + self.quotient = Signal(width) + self.divisor = Signal(width) + self.dividend = Signal(width) + self.remainder = Signal(width) + self.count = Signal(6) + + self.czero = Const(0, width) + + def reset(self, m): + m.d.sync += [ + self.quotient.eq(self.czero), + self.remainder.eq(self.czero), + self.count.eq(Const(0, 6)) + ] + + +class FPDIV(FPBase): + + def __init__(self, width): + FPBase.__init__(self) + self.width = width + + self.in_a = FPOp(width) + self.in_b = FPOp(width) + self.out_z = FPOp(width) + + def get_fragment(self, platform=None): + """ creates the HDL code-fragment for FPAdd + """ + m = Module() + + # Latches + a = FPNum(self.width, 24) + b = FPNum(self.width, 24) + z = FPNum(self.width, 24) + + div = Div(50) + + of = Overflow() + + with m.FSM() as fsm: + + # ****** + # gets operand a + + with m.State("get_a"): + self.get_op(m, self.in_a, a, "get_b") + + # ****** + # gets operand b + + with m.State("get_b"): + self.get_op(m, self.in_b, b, "special_cases") + + # ****** + # special cases: NaNs, infs, zeros, denormalised + # NOTE: some of these are unique to add. see "Special Operations" + # https://steve.hollasch.net/cgindex/coding/ieeefloat.html + + with m.State("special_cases"): + + # if a is NaN or b is NaN return NaN + with m.If(a.is_nan() | b.is_nan()): + m.next = "put_z" + m.d.sync += z.nan(1) + + # if a is Inf and b is Inf return NaN + with m.Elif(a.is_inf() | b.is_inf()): + m.next = "put_z" + m.d.sync += z.nan(1) + + # if a is inf return inf (or NaN if b is zero) + with m.Elif(a.is_inf()): + m.next = "put_z" + # if b is zero return NaN + with m.If(b.is_zero()): + m.d.sync += z.nan(1) + with m.Else(): + m.d.sync += z.inf(a.s ^ b.s) + + # if b is inf return zero + with m.Elif(b.is_inf()): + m.next = "put_z" + m.d.sync += z.zero(a.s ^ b.s) + + # if a is inf return zero (or NaN if b is zero) + with m.Elif(a.is_inf()): + m.next = "put_z" + # if b is zero return NaN + with m.If(b.is_zero()): + m.d.sync += z.nan(1) + with m.Else(): + m.d.sync += z.inf(a.s ^ b.s) + + # if b is zero return Inf + with m.Elif(b.is_zero()): + m.next = "put_z" + m.d.sync += z.zero(a.s ^ b.s) + + # Denormalised Number checks + with m.Else(): + m.next = "normalise_a" + self.denormalise(m, a) + self.denormalise(m, b) + + # ****** + # normalise_a + + with m.State("normalise_a"): + self.op_normalise(m, a, "normalise_b") + + # ****** + # normalise_b + + with m.State("normalise_b"): + self.op_normalise(m, b, "divide_0") + + # ****** + # First stage of divide. initialise state + + with m.State("divide_0"): + m.next = "divide_1" + m.d.sync += [ + z.s.eq(a.s ^ b.s), # sign + z.e.eq(a.e - b.e), # exponent + div.dividend.eq(a.m<<27), + div.divisor.eq(b.m), + ] + div.reset(m) + + # ****** + # Second stage of divide. + + with m.State("divide_1"): + m.next = "divide_2" + m.d.sync += [ + div.quotient.eq(div.quotient << 1), + div.remainder.eq(Cat(dividend[0], div.remainder[2:])), + div.dividend.eq(div.dividend << 1), + ] + + # ****** + # Third stage of divide. + + with m.State("divide_2"): + with m.If(div.remainder >= div.divisor): + m.d.sync += [ + div.quotient[0].eq(1), + div.remainder.eq(div.remainder - div.divisor), + ] + with m.If(count == div.width-1): + m.next = "divide_3" + with m.Else(): + m.next = "divide_1" + m.d.sync += [ + div.count.eq(div.count + 1), + ] + + # ****** + # Fourth stage of divide. + + with m.State("divide_3"): + m.next = "normalise_1" + m.d.sync += [ + z.m.eq(div.quotient[3:27]), + of.guard.eq(div.quotient[2]), + of.round_bit.eq(div.quotient[1]), + of.sticky.eq(div.quotient[0] | div.remainder != 0) + ] + + # ****** + # First stage of normalisation. + + with m.State("normalise_1"): + self.normalise_1(m, z, of, "normalise_2") + + # ****** + # Second stage of normalisation. + + with m.State("normalise_2"): + self.normalise_2(m, z, of, "round") + + # ****** + # rounding stage + + with m.State("round"): + self.roundz(m, z, of, "corrections") + + # ****** + # correction stage + + with m.State("corrections"): + self.corrections(m, z, "pack") + + # ****** + # pack stage + + with m.State("pack"): + self.pack(m, z, "put_z") + + # ****** + # put_z stage + + with m.State("put_z"): + self.put_z(m, z, self.out_z, "get_a") + + return m + + +if __name__ == "__main__": + alu = FPDIV(width=32) + main(alu, ports=alu.in_a.ports() + alu.in_b.ports() + alu.out_z.ports()) + + + # works... but don't use, just do "python fname.py convert -t v" + #print (verilog.convert(alu, ports=[ + # ports=alu.in_a.ports() + \ + # alu.in_b.ports() + \ + # alu.out_z.ports()) diff --git a/src/add/test_div.py b/src/add/test_div.py new file mode 100644 index 00000000..def8c475 --- /dev/null +++ b/src/add/test_div.py @@ -0,0 +1,81 @@ +from nmigen import Module, Signal +from nmigen.compat.sim import run_simulation + +from nmigen_add_experiment import FPADD + +class ORGate: + def __init__(self): + self.a = Signal() + self.b = Signal() + self.x = Signal() + + def get_fragment(self, platform=None): + + m = Module() + m.d.comb += self.x.eq(self.a | self.b) + + return m + +def check_case(dut, a, b, z): + yield dut.in_a.v.eq(a) + yield dut.in_a.stb.eq(1) + yield + yield + a_ack = (yield dut.in_a.ack) + assert a_ack == 0 + yield dut.in_b.v.eq(b) + yield dut.in_b.stb.eq(1) + b_ack = (yield dut.in_b.ack) + assert b_ack == 0 + + while True: + yield + out_z_stb = (yield dut.out_z.stb) + if not out_z_stb: + continue + yield dut.in_a.stb.eq(0) + yield dut.in_b.stb.eq(0) + yield dut.out_z.ack.eq(1) + yield + yield dut.out_z.ack.eq(0) + yield + yield + break + + out_z = yield dut.out_z.v + assert out_z == z, "Output z 0x%x not equal to expected 0x%x" % (out_z, z) + +def testbench(dut): + yield from check_case(dut, 0, 0, 0) + yield from check_case(dut, 0x3F800000, 0x40000000, 0x40400000) + yield from check_case(dut, 0x40000000, 0x3F800000, 0x40400000) + yield from check_case(dut, 0x447A0000, 0x4488B000, 0x4502D800) + yield from check_case(dut, 0x463B800A, 0x42BA8A3D, 0x463CF51E) + yield from check_case(dut, 0x42BA8A3D, 0x463B800A, 0x463CF51E) + yield from check_case(dut, 0x463B800A, 0xC2BA8A3D, 0x463A0AF6) + yield from check_case(dut, 0xC2BA8A3D, 0x463B800A, 0x463A0AF6) + yield from check_case(dut, 0xC63B800A, 0x42BA8A3D, 0xC63A0AF6) + yield from check_case(dut, 0x42BA8A3D, 0xC63B800A, 0xC63A0AF6) + yield from check_case(dut, 0xFFFFFFFF, 0xC63B800A, 0xFFC00000) + yield from check_case(dut, 0x7F800000, 0x00000000, 0x7F800000) + yield from check_case(dut, 0x00000000, 0x7F800000, 0x7F800000) + yield from check_case(dut, 0xFF800000, 0x00000000, 0xFF800000) + yield from check_case(dut, 0x00000000, 0xFF800000, 0xFF800000) + yield from check_case(dut, 0x7F800000, 0x7F800000, 0x7F800000) + yield from check_case(dut, 0xFF800000, 0xFF800000, 0xFF800000) + yield from check_case(dut, 0x7F800000, 0xFF800000, 0xFFC00000) + yield from check_case(dut, 0xFF800000, 0x7F800000, 0x7FC00000) + yield from check_case(dut, 0x00018643, 0x00FA72A4, 0x00FBF8E7) + yield from check_case(dut, 0x001A2239, 0x00FA72A4, 0x010A4A6E) + yield from check_case(dut, 0x3F7FFFFE, 0x3F7FFFFE, 0x3FFFFFFE) + yield from check_case(dut, 0x7EFFFFEE, 0x7EFFFFEE, 0x7F7FFFEE) + yield from check_case(dut, 0x7F7FFFEE, 0xFEFFFFEE, 0x7EFFFFEE) + yield from check_case(dut, 0x7F7FFFEE, 0x756CA884, 0x7F7FFFFD) + yield from check_case(dut, 0x7F7FFFEE, 0x758A0CF8, 0x7F7FFFFF) + #yield from check_case(dut, 1, 0, 1) + #yield from check_case(dut, 1, 1, 1) + +if __name__ == '__main__': + dut = FPADD(width=32) + run_simulation(dut, testbench(dut), vcd_name="test_add.vcd") +