add div experiment
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 16 Feb 2019 11:24:12 +0000 (11:24 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 16 Feb 2019 11:25:21 +0000 (11:25 +0000)
src/add/nmigen_div_experiment.py [new file with mode: 0644]
src/add/test_div.py [new file with mode: 0644]

diff --git a/src/add/nmigen_div_experiment.py b/src/add/nmigen_div_experiment.py
new file mode 100644 (file)
index 0000000..e6f68b8
--- /dev/null
@@ -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 (file)
index 0000000..def8c47
--- /dev/null
@@ -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")
+