split out main stages of add to separate class, FPADDBase
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 6 Mar 2019 06:09:15 +0000 (06:09 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 6 Mar 2019 06:09:15 +0000 (06:09 +0000)
src/add/fpbase.py
src/add/nmigen_add_experiment.py
src/add/test_add_base.py [new file with mode: 0644]

index dd2179f577ea22c8a56d319cd4f0c2384e4fdbb5..7f4f2cc88d57813c22d70f31837290fcd2753e7e 100644 (file)
@@ -389,11 +389,9 @@ class FPNumIn(FPNumBase):
                 self.m.eq(sm.lshift(self.m, maxslen))
                ]
 
-class FPOp:
-    def __init__(self, width):
-        self.width = width
+class Trigger:
+    def __init__(self):
 
-        self.v   = Signal(width)
         self.stb = Signal(reset=0)
         self.ack = Signal()
         self.trigger = Signal(reset_less=True)
@@ -403,6 +401,22 @@ class FPOp:
         m.d.sync += self.trigger.eq(self.stb & self.ack)
         return m
 
+    def copy(self, inp):
+        return [self.stb.eq(inp.stb),
+                self.ack.eq(inp.ack)
+               ]
+
+    def ports(self):
+        return [self.stb, self.ack]
+
+
+class FPOp(Trigger):
+    def __init__(self, width):
+        Trigger.__init__(self)
+        self.width = width
+
+        self.v   = Signal(width)
+
     def chain_inv(self, in_op, extra=None):
         stb = in_op.stb
         if extra is not None:
index a77868c667f572ee0b6b3cbec2ba93623c85cd7c..edcb17dd62461eba301b44e5239d0ea57c67fca8 100644 (file)
@@ -7,7 +7,7 @@ from nmigen.lib.coding import PriorityEncoder
 from nmigen.cli import main, verilog
 
 from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase, FPNumBase
-from fpbase import MultiShiftRMerge
+from fpbase import MultiShiftRMerge, Trigger
 #from fpbase import FPNumShiftMultiRight
 
 class FPState(FPBase):
@@ -74,6 +74,69 @@ class FPGetOp(FPState):
             m.d.sync += self.in_op.ack.eq(1)
 
 
+class FPGet2OpMod(Trigger):
+    def __init__(self, width):
+        Trigger.__init__(self)
+        self.in_op1 = Signal(width, reset_less=True)
+        self.in_op2 = Signal(width, reset_less=True)
+        self.out_op1 = FPNumIn(None, width)
+        self.out_op2 = FPNumIn(None, width)
+
+    def elaborate(self, platform):
+        m = Trigger.elaborate(self, platform)
+        #m.submodules.get_op_in = self.in_op
+        m.submodules.get_op1_out = self.out_op1
+        m.submodules.get_op2_out = self.out_op2
+        with m.If(self.trigger):
+            m.d.comb += [
+                self.out_op1.decode(self.in_op1),
+                self.out_op2.decode(self.in_op2),
+            ]
+        return m
+
+
+class FPGet2Op(FPState):
+    """ gets operands
+    """
+
+    def __init__(self, in_state, out_state, in_op1, in_op2, width):
+        FPState.__init__(self, in_state)
+        self.out_state = out_state
+        self.mod = FPGet2OpMod(width)
+        self.in_op1 = in_op1
+        self.in_op2 = in_op2
+        self.out_op1 = FPNumIn(None, width)
+        self.out_op2 = FPNumIn(None, width)
+        self.in_stb = Signal(reset_less=True)
+        self.out_ack = Signal(reset_less=True)
+        self.out_decode = Signal(reset_less=True)
+
+    def setup(self, m, in_op1, in_op2, in_stb):
+        """ links module to inputs and outputs
+        """
+        m.submodules.get_ops = self.mod
+        m.d.comb += self.mod.in_op1.eq(in_op1)
+        m.d.comb += self.mod.in_op2.eq(in_op2)
+        m.d.comb += self.mod.stb.eq(in_stb)
+        m.d.comb += self.out_ack.eq(self.mod.ack)
+        m.d.comb += self.out_decode.eq(self.mod.trigger)
+        #m.d.comb += self.out_op1.v.eq(self.mod.out_op1.v)
+        #m.d.comb += self.out_op2.v.eq(self.mod.out_op2.v)
+
+    def action(self, m):
+        with m.If(self.out_decode):
+            m.next = self.out_state
+            m.d.sync += [
+                self.mod.ack.eq(0),
+                #self.out_op1.v.eq(self.mod.out_op1.v),
+                #self.out_op2.v.eq(self.mod.out_op2.v),
+                self.out_op1.copy(self.mod.out_op1),
+                self.out_op2.copy(self.mod.out_op2)
+            ]
+        with m.Else():
+            m.d.sync += self.mod.ack.eq(1)
+
+
 class FPAddSpecialCasesMod:
     """ special cases: NaNs, infs, zeros, denormalised
         NOTE: some of these are unique to add.  see "Special Operations"
@@ -958,12 +1021,12 @@ class FPPutZ(FPState):
         ]
         with m.If(self.out_z.stb & self.out_z.ack):
             m.d.sync += self.out_z.stb.eq(0)
-            m.next = "get_a"
+            m.next = "get_ops"
         with m.Else():
             m.d.sync += self.out_z.stb.eq(1)
 
 
-class FPADD(FPID):
+class FPADDBase(FPID):
 
     def __init__(self, width, id_wid=None, single_cycle=False):
         """ IEEE754 FP Add
@@ -976,8 +1039,9 @@ class FPADD(FPID):
         self.width = width
         self.single_cycle = single_cycle
 
-        self.in_a  = FPOp(width)
-        self.in_b  = FPOp(width)
+        self.in_t = Trigger()
+        self.in_a  = Signal(width)
+        self.in_b  = Signal(width)
         self.out_z = FPOp(width)
 
         self.states = []
@@ -990,19 +1054,15 @@ class FPADD(FPID):
         """ creates the HDL code-fragment for FPAdd
         """
         m = Module()
-        m.submodules.in_a = self.in_a
-        m.submodules.in_b = self.in_b
         m.submodules.out_z = self.out_z
+        m.submodules.in_t = self.in_t
 
-        geta = self.add_state(FPGetOp("get_a", "get_b",
-                                      self.in_a, self.width))
-        geta.setup(m, self.in_a)
-        a = geta.out_op
-
-        getb = self.add_state(FPGetOp("get_b", "special_cases",
-                                      self.in_b, self.width))
-        getb.setup(m, self.in_b)
-        b = getb.out_op
+        get = self.add_state(FPGet2Op("get_ops", "special_cases",
+                                      self.in_a, self.in_b, self.width))
+        get.setup(m, self.in_a, self.in_b, self.in_t.stb)
+        m.d.comb += self.in_t.ack.eq(get.mod.ack)
+        a = get.out_op1
+        b = get.out_op2
 
         sc = self.add_state(FPAddSpecialCases(self.width, self.id_wid))
         sc.setup(m, a, b, self.in_mid)
@@ -1050,10 +1110,67 @@ class FPADD(FPID):
         return m
 
 
+class FPADD(FPID):
+
+    def __init__(self, width, id_wid=None, single_cycle=False):
+        """ IEEE754 FP Add
+
+            * width: bit-width of IEEE754.  supported: 16, 32, 64
+            * id_wid: an identifier that is sync-connected to the input
+            * single_cycle: True indicates each stage to complete in 1 clock
+        """
+        FPID.__init__(self, id_wid)
+        self.width = width
+        self.single_cycle = single_cycle
+
+        self.in_a  = FPOp(width)
+        self.in_b  = FPOp(width)
+        self.out_z = FPOp(width)
+
+        self.states = []
+
+    def add_state(self, state):
+        self.states.append(state)
+        return state
+
+    def get_fragment(self, platform=None):
+        """ creates the HDL code-fragment for FPAdd
+        """
+        m = Module()
+        m.submodules.in_a = self.in_a
+        m.submodules.in_b = self.in_b
+        m.submodules.out_z = self.out_z
+
+        geta = self.add_state(FPGetOp("get_a", "get_b",
+                                      self.in_a, self.width))
+        geta.setup(m, self.in_a)
+        a = geta.out_op
+
+        getb = self.add_state(FPGetOp("get_b", "special_cases",
+                                      self.in_b, self.width))
+        getb.setup(m, self.in_b)
+        b = getb.out_op
+
+        ab = FPADDBase()
+        #pa = self.add_state(FPPack(self.width, self.id_wid))
+        #pa.setup(m, cor.out_z, rn.in_mid)
+
+        pz = self.add_state(FPPutZ("put_z", sc.out_z, self.out_z,
+                                    pa.in_mid, self.out_mid))
+
+        with m.FSM() as fsm:
+
+            for state in self.states:
+                with m.State(state.state_from):
+                    state.action(m)
+
+        return m
+
+
 if __name__ == "__main__":
-    alu = FPADD(width=32, in_wid=5, single_cycle=True)
-    main(alu, ports=alu.in_a.ports() + \
-                    alu.in_b.ports() + \
+    alu = FPADDBase(width=32, id_wid=5, single_cycle=True)
+    main(alu, ports=[alu.in_a, alu.in_b] + \
+                    alu.in_t.ports() + \
                     alu.out_z.ports() + \
                     [alu.in_mid, alu.out_mid])
 
diff --git a/src/add/test_add_base.py b/src/add/test_add_base.py
new file mode 100644 (file)
index 0000000..d1552c1
--- /dev/null
@@ -0,0 +1,94 @@
+from random import randint
+from operator import add
+
+from nmigen import Module, Signal
+from nmigen.compat.sim import run_simulation
+
+from nmigen_add_experiment import FPADDBase
+
+def get_case(dut, a, b, mid):
+    yield dut.in_mid.eq(mid)
+    yield dut.in_a.eq(a)
+    yield dut.in_b.eq(b)
+    yield dut.in_t.stb.eq(1)
+    yield
+    yield
+    yield
+    yield
+    ack = (yield dut.in_t.ack)
+    assert ack == 0
+
+    yield dut.in_t.stb.eq(0)
+
+    yield dut.out_z.ack.eq(1)
+
+    while True:
+        out_z_stb = (yield dut.out_z.stb)
+        if not out_z_stb:
+            yield
+            continue
+        out_z = yield dut.out_z.v
+        out_mid = yield dut.out_mid
+        yield dut.out_z.ack.eq(0)
+        yield
+        break
+
+    return out_z, out_mid
+
+def check_case(dut, a, b, z, mid=None):
+    if mid is None:
+        mid = randint(0, 6)
+    out_z, out_mid = yield from get_case(dut, a, b, mid)
+    assert out_z == z, "Output z 0x%x not equal to expected 0x%x" % (out_z, z)
+    assert out_mid == mid, "Output mid 0x%x != expected 0x%x" % (out_mid, mid)
+
+
+
+def testbench(dut):
+    yield from check_case(dut, 0x36093399, 0x7f6a12f1, 0x7f6a12f1)
+    yield from check_case(dut, 0x006CE3EE, 0x806CE3EC, 0x00000002)
+    yield from check_case(dut, 0x00000047, 0x80000048, 0x80000001)
+    yield from check_case(dut, 0x000116C2, 0x8001170A, 0x80000048)
+    yield from check_case(dut, 0x7ed01f25, 0xff559e2c, 0xfedb1d33)
+    yield from check_case(dut, 0, 0, 0)
+    yield from check_case(dut, 0xFFFFFFFF, 0xC63B800A, 0x7FC00000)
+    yield from check_case(dut, 0xFF800000, 0x7F800000, 0x7FC00000)
+    #yield from check_case(dut, 0xFF800000, 0x7F800000, 0x7FC00000)
+    yield from check_case(dut, 0x7F800000, 0xFF800000, 0x7FC00000)
+    yield from check_case(dut, 0x42540000, 0xC2540000, 0x00000000)
+    yield from check_case(dut, 0xC2540000, 0x42540000, 0x00000000)
+    yield from check_case(dut, 0xfe34f995, 0xff5d59ad, 0xff800000)
+    yield from check_case(dut, 0x82471f51, 0x243985f, 0x801c3790)
+    yield from check_case(dut, 0x40000000, 0xc0000000, 0x00000000)
+    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, 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, 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, 0x42500000, 0x51A7A358, 0x51A7A358)
+    yield from check_case(dut, 0x51A7A358, 0x42500000, 0x51A7A358)
+    yield from check_case(dut, 0x4E5693A4, 0x42500000, 0x4E5693A5)
+    yield from check_case(dut, 0x42500000, 0x4E5693A4, 0x4E5693A5)
+
+if __name__ == '__main__':
+    dut = FPADDBase(width=32, id_wid=5, single_cycle=True)
+    run_simulation(dut, testbench(dut), vcd_name="test_add.vcd")
+