From 2932f7f7c8afae7ac055fe2dc99ca1d8f2a3cb61 Mon Sep 17 00:00:00 2001 From: Michael Nolan Date: Mon, 11 May 2020 18:45:22 -0400 Subject: [PATCH] Massively spead up test_pipe_caller.py --- src/soc/alu/test/test_pipe_caller.py | 214 ++++++++++++++++----------- 1 file changed, 125 insertions(+), 89 deletions(-) diff --git a/src/soc/alu/test/test_pipe_caller.py b/src/soc/alu/test/test_pipe_caller.py index 55ce27d4..3dcab75b 100644 --- a/src/soc/alu/test/test_pipe_caller.py +++ b/src/soc/alu/test/test_pipe_caller.py @@ -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) -- 2.30.2