From e768533532bb2035e9cbc78e2db86affc694e290 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Wed, 6 Mar 2019 06:09:15 +0000 Subject: [PATCH] split out main stages of add to separate class, FPADDBase --- src/add/fpbase.py | 22 ++++- src/add/nmigen_add_experiment.py | 155 +++++++++++++++++++++++++++---- src/add/test_add_base.py | 94 +++++++++++++++++++ 3 files changed, 248 insertions(+), 23 deletions(-) create mode 100644 src/add/test_add_base.py diff --git a/src/add/fpbase.py b/src/add/fpbase.py index dd2179f5..7f4f2cc8 100644 --- a/src/add/fpbase.py +++ b/src/add/fpbase.py @@ -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: diff --git a/src/add/nmigen_add_experiment.py b/src/add/nmigen_add_experiment.py index a77868c6..edcb17dd 100644 --- a/src/add/nmigen_add_experiment.py +++ b/src/add/nmigen_add_experiment.py @@ -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 index 00000000..d1552c19 --- /dev/null +++ b/src/add/test_add_base.py @@ -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") + -- 2.30.2