Massively spead up test_pipe_caller.py
authorMichael Nolan <mtnolan2640@gmail.com>
Mon, 11 May 2020 22:45:22 +0000 (18:45 -0400)
committerMichael Nolan <mtnolan2640@gmail.com>
Mon, 11 May 2020 22:45:22 +0000 (18:45 -0400)
src/soc/alu/test/test_pipe_caller.py

index 55ce27d4576614c341e4f82d816ffbdf0f8a17d3..3dcab75bc3e25af78f54abe650fe824801d45fee 100644 (file)
@@ -17,6 +17,13 @@ from soc.alu.alu_input_record import CompALUOpSubset
 from soc.alu.pipe_data import ALUPipeSpec
 import random
 
+class TestCase:
+    def __init__(self, program, regs, sprs, name):
+        self.program = program
+        self.regs = regs
+        self.sprs = sprs
+        self.name = name
+
 def get_rec_width(rec):
     recwidth = 0
     # Setup random inputs for dut.op
@@ -63,82 +70,34 @@ def set_extra_alu_inputs(alu, dec2, sim):
     yield alu.p.data_i.so.eq(so)
     
 
-class ALUTestCase(FHDLTestCase):
-    def run_tst(self, program, initial_regs, initial_sprs):
-        m = Module()
-        comb = m.d.comb
-        instruction = Signal(32)
-
-        pdecode = create_pdecode()
+# This test bench is a bit different than is usual. Initially when I
+# was writing it, I had all of the tests call a function to create a
+# device under test and simulator, initialize the dut, run the
+# simulation for ~2 cycles, and assert that the dut output what it
+# should have. However, this was really slow, since it needed to
+# create and tear down the dut and simulator for every test case.
 
-        m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
-
-        rec = CompALUOpSubset()
-
-        pspec = ALUPipeSpec(id_wid=2, op_wid=get_rec_width(rec))
-        m.submodules.alu = alu = ALUBasePipe(pspec)
-
-        comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
-        comb += alu.p.valid_i.eq(1)
-        comb += alu.n.ready_i.eq(1)
-        simulator = ISA(pdecode2, initial_regs, initial_sprs)
-        comb += pdecode2.dec.raw_opcode_in.eq(instruction)
-        sim = Simulator(m)
-        gen = program.generate_instructions()
+# Now, instead of doing that, every test case in ALUTestCase puts some
+# data into the test_data list below, describing the instructions to
+# be tested and the initial state. Once all the tests have been run,
+# test_data gets passed to TestRunner which then sets up the DUT and
+# simulator once, runs all the data through it, and asserts that the
+# results match the pseudocode sim at every cycle.
 
-        sim.add_clock(1e-6)
-        def process():
-            instructions = list(zip(gen, program.assembly.splitlines()))
-
-            index = simulator.pc.CIA.value//4
-            while index < len(instructions):
-                ins, code = instructions[index]
-
-                print("0x{:X}".format(ins & 0xffffffff))
-                print(code)
-
-                # ask the decoder to decode this binary data (endian'd)
-                yield pdecode2.dec.bigendian.eq(0)  # little / big?
-                yield instruction.eq(ins)          # raw binary instr.
-                yield Settle()
-                yield from set_alu_inputs(alu, pdecode2, simulator)
-                yield from set_extra_alu_inputs(alu, pdecode2, simulator)
-                yield 
-                opname = code.split(' ')[0]
-                yield from simulator.call(opname)
-                index = simulator.pc.CIA.value//4
+# By doing this, I've reduced the time it takes to run the test suite
+# massively. Before, it took around 1 minute on my computer, now it
+# takes around 3 seconds
 
-                vld = yield alu.n.valid_o
-                while not vld:
-                    yield
-                    vld = yield alu.n.valid_o
-                yield
-                alu_out = yield alu.n.data_o.o
-                out_reg_valid = yield pdecode2.e.write_reg.ok
-                if out_reg_valid:
-                    write_reg_idx = yield pdecode2.e.write_reg.data
-                    expected = simulator.gpr(write_reg_idx).value
-                    print(f"expected {expected:x}, actual: {alu_out:x}")
-                    self.assertEqual(expected, alu_out)
-                yield from self.check_extra_alu_outputs(alu, pdecode2,
-                                                        simulator)
+test_data = []
 
-        sim.add_sync_process(process)
-        with sim.write_vcd("simulator.vcd", "simulator.gtkw",
-                           traces=[]):
-            sim.run()
-        return simulator
-    def check_extra_alu_outputs(self, alu, dec2, sim):
-        rc = yield dec2.e.rc.data
-        if rc:
-            cr_expected = sim.crl[0].get_range().value
-            cr_actual = yield alu.n.data_o.cr0
-            self.assertEqual(cr_expected, cr_actual)
 
+class ALUTestCase(FHDLTestCase):
+    def __init__(self, name):
+        super().__init__(name)
+        self.test_name = name
     def run_tst_program(self, prog, initial_regs=[0] * 32, initial_sprs={}):
-        simulator = self.run_tst(prog, initial_regs, initial_sprs)
-        simulator.gpr.dump()
-        return simulator
+        tc = TestCase(prog, initial_regs, initial_sprs, self.test_name)
+        test_data.append(tc)
 
     def test_rand(self):
         insns = ["add", "add.", "and", "or", "xor", "subf"]
@@ -148,8 +107,7 @@ class ALUTestCase(FHDLTestCase):
             initial_regs = [0] * 32
             initial_regs[1] = random.randint(0, (1<<64)-1)
             initial_regs[2] = random.randint(0, (1<<64)-1)
-            with Program(lst) as program:
-                sim = self.run_tst_program(program, initial_regs)
+            self.run_tst_program(Program(lst), initial_regs)
 
     def test_rand_imm(self):
         insns = ["addi", "addis", "subfic"]
@@ -160,8 +118,7 @@ class ALUTestCase(FHDLTestCase):
             print(lst)
             initial_regs = [0] * 32
             initial_regs[1] = random.randint(0, (1<<64)-1)
-            with Program(lst) as program:
-                sim = self.run_tst_program(program, initial_regs)
+            self.run_tst_program(Program(lst), initial_regs)
 
     def test_rand_imm_logical(self):
         insns = ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
@@ -172,8 +129,7 @@ class ALUTestCase(FHDLTestCase):
             print(lst)
             initial_regs = [0] * 32
             initial_regs[1] = random.randint(0, (1<<64)-1)
-            with Program(lst) as program:
-                sim = self.run_tst_program(program, initial_regs)
+            self.run_tst_program(Program(lst), initial_regs)
 
     def test_shift(self):
         insns = ["slw", "sld", "srw", "srd", "sraw", "srad"]
@@ -184,8 +140,7 @@ class ALUTestCase(FHDLTestCase):
             initial_regs[1] = random.randint(0, (1<<64)-1)
             initial_regs[2] = random.randint(0, 63)
             print(initial_regs[1], initial_regs[2])
-            with Program(lst) as program:
-                sim = self.run_tst_program(program, initial_regs)
+            self.run_tst_program(Program(lst), initial_regs)
 
 
     def test_shift_arith(self):
@@ -194,8 +149,7 @@ class ALUTestCase(FHDLTestCase):
         initial_regs[1] = random.randint(0, (1<<64)-1)
         initial_regs[2] = random.randint(0, 63)
         print(initial_regs[1], initial_regs[2])
-        with Program(lst) as program:
-            sim = self.run_tst_program(program, initial_regs)
+        self.run_tst_program(Program(lst), initial_regs)
 
     def test_rlwinm(self):
         for i in range(10):
@@ -205,24 +159,21 @@ class ALUTestCase(FHDLTestCase):
             lst = [f"rlwinm 3, 1, {mb}, {me}, {sh}"]
             initial_regs = [0] * 32
             initial_regs[1] = random.randint(0, (1<<64)-1)
-            with Program(lst) as program:
-                sim = self.run_tst_program(program, initial_regs)
+            self.run_tst_program(Program(lst), initial_regs)
 
     def test_rlwimi(self):
         lst = ["rlwimi 3, 1, 5, 20, 6"]
         initial_regs = [0] * 32
         initial_regs[1] = 0xdeadbeef
         initial_regs[3] = 0x12345678
-        with Program(lst) as program:
-            sim = self.run_tst_program(program, initial_regs)
+        self.run_tst_program(Program(lst), initial_regs)
 
     def test_rlwnm(self):
         lst = ["rlwnm 3, 1, 2, 20, 6"]
         initial_regs = [0] * 32
         initial_regs[1] = random.randint(0, (1<<64)-1)
         initial_regs[2] = random.randint(0, 63)
-        with Program(lst) as program:
-            sim = self.run_tst_program(program, initial_regs)
+        self.run_tst_program(Program(lst), initial_regs)
         
     def test_adde(self):
         lst = ["adde. 5, 6, 7"]
@@ -233,8 +184,7 @@ class ALUTestCase(FHDLTestCase):
         xer = SelectableInt(0, 64)
         xer[XER_bits['CA']] = 1
         initial_sprs[special_sprs['XER']] = xer
-        with Program(lst) as program:
-            sim = self.run_tst_program(program, initial_regs, initial_sprs)
+        self.run_tst_program(Program(lst), initial_regs, initial_sprs)
 
     def test_ilang(self):
         rec = CompALUOpSubset()
@@ -245,5 +195,91 @@ class ALUTestCase(FHDLTestCase):
         with open("pipeline.il", "w") as f:
             f.write(vl)
 
+
+class TestRunner(FHDLTestCase):
+    def __init__(self, test_data):
+        super().__init__("run_all")
+        self.test_data = test_data
+
+    def run_all(self):
+        m = Module()
+        comb = m.d.comb
+        instruction = Signal(32)
+
+        pdecode = create_pdecode()
+
+        m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
+
+        rec = CompALUOpSubset()
+
+        pspec = ALUPipeSpec(id_wid=2, op_wid=get_rec_width(rec))
+        m.submodules.alu = alu = ALUBasePipe(pspec)
+
+        comb += alu.p.data_i.ctx.op.eq_from_execute1(pdecode2.e)
+        comb += alu.p.valid_i.eq(1)
+        comb += alu.n.ready_i.eq(1)
+        comb += pdecode2.dec.raw_opcode_in.eq(instruction)
+        sim = Simulator(m)
+
+        sim.add_clock(1e-6)
+        def process():
+            for test in self.test_data:
+                print(test.name)
+                program = test.program
+                self.subTest(test.name)
+                simulator = ISA(pdecode2, test.regs, test.sprs)
+                gen = program.generate_instructions()
+                instructions = list(zip(gen, program.assembly.splitlines()))
+
+                index = simulator.pc.CIA.value//4
+                while index < len(instructions):
+                    ins, code = instructions[index]
+
+                    print("0x{:X}".format(ins & 0xffffffff))
+                    print(code)
+
+                    # ask the decoder to decode this binary data (endian'd)
+                    yield pdecode2.dec.bigendian.eq(0)  # little / big?
+                    yield instruction.eq(ins)          # raw binary instr.
+                    yield Settle()
+                    yield from set_alu_inputs(alu, pdecode2, simulator)
+                    yield from set_extra_alu_inputs(alu, pdecode2, simulator)
+                    yield 
+                    opname = code.split(' ')[0]
+                    yield from simulator.call(opname)
+                    index = simulator.pc.CIA.value//4
+
+                    vld = yield alu.n.valid_o
+                    while not vld:
+                        yield
+                        vld = yield alu.n.valid_o
+                    yield
+                    alu_out = yield alu.n.data_o.o
+                    out_reg_valid = yield pdecode2.e.write_reg.ok
+                    if out_reg_valid:
+                        write_reg_idx = yield pdecode2.e.write_reg.data
+                        expected = simulator.gpr(write_reg_idx).value
+                        print(f"expected {expected:x}, actual: {alu_out:x}")
+                        self.assertEqual(expected, alu_out)
+                    yield from self.check_extra_alu_outputs(alu, pdecode2,
+                                                            simulator)
+
+        sim.add_sync_process(process)
+        with sim.write_vcd("simulator.vcd", "simulator.gtkw",
+                            traces=[]):
+            sim.run()
+    def check_extra_alu_outputs(self, alu, dec2, sim):
+        rc = yield dec2.e.rc.data
+        if rc:
+            cr_expected = sim.crl[0].get_range().value
+            cr_actual = yield alu.n.data_o.cr0
+            self.assertEqual(cr_expected, cr_actual)
+
+
 if __name__ == "__main__":
-    unittest.main()
+    unittest.main(exit=False)
+    suite = unittest.TestSuite()
+    suite.addTest(TestRunner(test_data))
+
+    runner = unittest.TextTestRunner()
+    runner.run(suite)