add beginning unit tests for 64-bit add
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 17 Feb 2019 07:16:22 +0000 (07:16 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 17 Feb 2019 07:16:22 +0000 (07:16 +0000)
src/add/fpbase.py
src/add/nmigen_add_experiment.py
src/add/test_add64.py [new file with mode: 0644]

index c10e5d9bd370ee60c5d39745817e831c06c6a238..aba8c9676e33b1aac1527a78b298138f2ecd2e5b 100644 (file)
@@ -22,12 +22,19 @@ class FPNum:
         m_width = {32: 24, 64: 53}[width]
         e_width = {32: 10, 64: 13}[width]
         e_max = 1<<(e_width-3)
+        self.rmw = m_width # real mantissa width (not including extras)
         if m_extra:
             # mantissa extra bits (top,guard,round)
-            m_width += 3
-        print (m_width, e_width, e_max)
+            self.m_extra = 3
+            m_width += self.m_extra
+        else:
+            self.m_extra = 0
+        print (m_width, e_width, e_max, self.rmw, self.m_extra)
         self.m_width = m_width
         self.e_width = e_width
+        self.e_start = self.rmw - 1
+        self.e_end = self.rmw + self.e_width - 3 # for decoding
+
         self.v = Signal(width)      # Latched copy of value
         self.m = Signal(m_width)    # Mantissa
         self.e = Signal((e_width, True)) # Exponent: 10 bits, signed
@@ -47,10 +54,11 @@ class FPNum:
             is extended to 10 bits so that subtract 127 is done on
             a 10-bit number
         """
-        args = [0] * (self.m_width-24) + [v[0:23]] # pad with extra zeros
+        args = [0] * self.m_extra + [v[0:self.e_start]] # pad with extra zeros
+        print (self.e_end)
         return [self.m.eq(Cat(*args)), # mantissa
-                self.e.eq(v[23:31] - self.P127), # exp (minus bias)
-                self.s.eq(v[31]),                 # sign
+                self.e.eq(v[self.e_start:self.e_end] - self.P127), # exp
+                self.s.eq(v[-1]),                 # sign
                 ]
 
     def create(self, s, e, m):
@@ -59,9 +67,9 @@ class FPNum:
             bias is added here, to the exponent
         """
         return [
-          self.v[31].eq(s),          # sign
-          self.v[23:31].eq(e + self.P127), # exp (add on bias)
-          self.v[0:23].eq(m)         # mantissa
+          self.v[-1].eq(s),          # sign
+          self.v[self.e_start:self.e_end].eq(e + self.P127), # exp (add on bias)
+          self.v[0:self.e_start].eq(m)         # mantissa
         ]
 
     def shift_down(self):
@@ -75,7 +83,7 @@ class FPNum:
                ]
 
     def nan(self, s):
-        return self.create(s, self.P128, 1<<22)
+        return self.create(s, self.P128, 1<<(self.e_start-1))
 
     def inf(self, s):
         return self.create(s, self.P128, 0)
@@ -96,7 +104,7 @@ class FPNum:
         return (self.e > self.P127)
 
     def is_denormalised(self):
-        return (self.e == self.N126) & (self.m[23] == 0)
+        return (self.e == self.N126) & (self.m[self.e_start] == 0)
 
 
 class FPOp:
@@ -144,7 +152,7 @@ class FPBase:
         """ denormalises a number
         """
         with m.If(a.e == a.N127):
-            m.d.sync += a.e.eq(-126) # limit a exponent
+            m.d.sync += a.e.eq(a.N126) # limit a exponent
         with m.Else():
             m.d.sync += a.m[-1].eq(1) # set top mantissa bit
 
@@ -214,7 +222,7 @@ class FPBase:
         m.next = next_state
         # denormalised, correct exponent to zero
         with m.If(z.is_denormalised()):
-            m.d.sync += z.m.eq(-127)
+            m.d.sync += z.m.eq(z.N127)
         # FIX SIGN BUG: -a + a = +0.
         with m.If((z.e == z.N126) & (z.m[0:] == 0)):
             m.d.sync += z.s.eq(0)
index efb0c8bfa1de2bd8526d213e8426f934841f112a..da3726828913024b7e4b3c0121100af7a7cf0fe1 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright (C) Jonathan P Dawson 2013
 # 2013-12-12
 
-from nmigen import Module, Signal
+from nmigen import Module, Signal, Cat
 from nmigen.cli import main, verilog
 
 from fpbase import FPNum, FPOp, Overflow, FPBase
@@ -28,7 +28,8 @@ class FPADD(FPBase):
         b = FPNum(self.width)
         z = FPNum(self.width, False)
 
-        tot = Signal(28)     # sticky/round/guard bits, 23 result, 1 overflow
+        w = {32: 28, 64:57}[self.width]
+        tot = Signal(w) # sticky/round/guard, {mantissa} result, 1 overflow
 
         of = Overflow()
 
@@ -74,17 +75,17 @@ class FPADD(FPBase):
                 # if a is zero and b zero return signed-a/b
                 with m.Elif(a.is_zero() & b.is_zero()):
                     m.next = "put_z"
-                    m.d.sync += z.create(a.s & b.s, b.e[0:8], b.m[3:-1])
+                    m.d.sync += z.create(a.s & b.s, b.e, b.m[3:-1])
 
                 # if a is zero return b
                 with m.Elif(a.is_zero()):
                     m.next = "put_z"
-                    m.d.sync += z.create(b.s, b.e[0:8], b.m[3:-1])
+                    m.d.sync += z.create(b.s, b.e, b.m[3:-1])
 
                 # if b is zero return a
                 with m.Elif(b.is_zero()):
                     m.next = "put_z"
-                    m.d.sync += z.create(a.s, a.e[0:8], a.m[3:-1])
+                    m.d.sync += z.create(a.s, a.e, a.m[3:-1])
 
                 # Denormalised Number checks
                 with m.Else():
@@ -118,19 +119,19 @@ class FPADD(FPBase):
                 # same-sign (both negative or both positive) add mantissas
                 with m.If(a.s == b.s):
                     m.d.sync += [
-                        tot.eq(a.m + b.m),
+                        tot.eq(Cat(a.m, 0) + Cat(b.m, 0)),
                         z.s.eq(a.s)
                     ]
                 # a mantissa greater than b, use a
                 with m.Elif(a.m >= b.m):
                     m.d.sync += [
-                        tot.eq(a.m - b.m),
+                        tot.eq(Cat(a.m, 0) - Cat(b.m, 0)),
                         z.s.eq(a.s)
                     ]
                 # b mantissa greater than a, use b
                 with m.Else():
                     m.d.sync += [
-                        tot.eq(b.m - a.m),
+                        tot.eq(Cat(b.m, 0) - Cat(a.m, 0)),
                         z.s.eq(b.s)
                 ]
 
@@ -141,9 +142,9 @@ class FPADD(FPBase):
             with m.State("add_1"):
                 m.next = "normalise_1"
                 # tot[27] gets set when the sum overflows. shift result down
-                with m.If(tot[27]):
+                with m.If(tot[-1]):
                     m.d.sync += [
-                        z.m.eq(tot[4:28]),
+                        z.m.eq(tot[4:]),
                         of.guard.eq(tot[3]),
                         of.round_bit.eq(tot[2]),
                         of.sticky.eq(tot[1] | tot[0]),
@@ -152,7 +153,7 @@ class FPADD(FPBase):
                 # tot[27] zero case
                 with m.Else():
                     m.d.sync += [
-                        z.m.eq(tot[3:27]),
+                        z.m.eq(tot[3:]),
                         of.guard.eq(tot[2]),
                         of.round_bit.eq(tot[1]),
                         of.sticky.eq(tot[0])
diff --git a/src/add/test_add64.py b/src/add/test_add64.py
new file mode 100644 (file)
index 0000000..c797716
--- /dev/null
@@ -0,0 +1,90 @@
+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, 0x3FF0000000000000, 0x4000000000000000,
+                               0x4008000000000000)
+    yield from check_case(dut, 0x4000000000000000, 0x3FF0000000000000,
+                               0x4008000000000000)
+    yield from check_case(dut, 0x4056C00000000000, 0x4042800000000000,
+                               0x4060000000000000)
+    yield from check_case(dut, 0x4056C00000000000, 0x4042EA3D70A3D70A,
+                               0x40601A8F5C28F5C2)
+
+    if False:
+        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=64)
+    run_simulation(dut, testbench(dut), vcd_name="test_add64.vcd")
+