X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fopenpower%2Ftest%2Frunner.py;h=656e2b5bf948f15162c6d37ead2df22201f62582;hb=af3624a79b0506bfcb2ec96b33f651793eb93fa3;hp=c429ee14bea49b69412444964773d42e67ac6cc0;hpb=8fc6050bf747fb5d367b406013b9d6652b772344;p=openpower-isa.git diff --git a/src/openpower/test/runner.py b/src/openpower/test/runner.py index c429ee14..656e2b5b 100644 --- a/src/openpower/test/runner.py +++ b/src/openpower/test/runner.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: LGPL-2-or-later """TestRunner class, part of the Test API SPDX-License: LGPLv2+ @@ -14,8 +15,10 @@ related bugs: * https://bugs.libre-soc.org/show_bug.cgi?id=686#c51 """ +from unittest.mock import Mock from nmigen import Module, ClockSignal -from copy import copy +from copy import copy, deepcopy +from pprint import pformat # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell # Also, check out the cxxsim nmigen branch, and latest yosys from git @@ -28,22 +31,27 @@ from openpower.endian import bigendian from openpower.decoder.power_decoder2 import PowerDecode2 -from soc.config.test.test_loadstore import TestMemPspec from nmutil.util import wrap -from soc.experiment.test.test_mmu_dcache import wb_get +from openpower.test.wb_get import wb_get +import openpower.test.wb_get as wbget from openpower.test.state import TestState, StateRunner, ExpectedState +from openpower.util import log, LogKind class SimRunner(StateRunner): """SimRunner: Implements methods for the setup, preparation, and running of tests using ISACaller simulation """ + def __init__(self, dut, m, pspec): super().__init__("sim", SimRunner) self.dut = dut + self.mmu = pspec.mmu == True + fp_en = pspec.fp_en == True regreduce_en = pspec.regreduce_en == True - self.simdec2 = simdec2 = PowerDecode2(None, regreduce_en=regreduce_en) + self.simdec2 = simdec2 = PowerDecode2( + None, regreduce_en=regreduce_en, fp_en=fp_en) m.submodules.simdec2 = simdec2 # pain in the neck def prepare_for_test(self, test): @@ -64,15 +72,24 @@ class SimRunner(StateRunner): initial_insns=gen, respect_pc=True, disassembly=insncode, bigendian=bigendian, - initial_svstate=test.svstate) + initial_svstate=test.svstate, + mmu=self.mmu, + fpregfile=test.fpregs, + initial_fpscr=test.initial_fpscr) # run the loop of the instructions on the current test index = sim.pc.CIA.value//4 while index < len(instructions): ins, code = instructions[index] - print("sim instr: 0x{:X}".format(ins & 0xffffffff)) - print(index, code) + # extra new-line so it's easier to visually separate each + # instruction in output + log(f"\n0x{sim.pc.CIA.value:04X}: {ins % (1 << 32):08X} {code}", + kind=LogKind.InstrInOuts) + + log("sim instr: 0x{:X} pc=0x{:X}".format(ins & 0xffffffff, + sim.pc.CIA.value)) + log(index, code) # set up simulated instruction (in simdec2) try: @@ -82,7 +99,7 @@ class SimRunner(StateRunner): yield Settle() # call simulated operation - print("sim", code) + log("sim", code) yield from sim.execute_one() yield Settle() index = sim.pc.CIA.value//4 @@ -91,6 +108,13 @@ class SimRunner(StateRunner): state = yield from TestState("sim", sim, dut, code) sim_states.append(state) + log(f"final pc: 0x{sim.pc.CIA.value:X}", kind=LogKind.InstrInOuts) + + if self.dut.allow_overlap: + # get last state, at end of run + state = yield from TestState("sim", sim, dut, code) + sim_states.append(state) + return sim_states @@ -104,39 +128,55 @@ class TestRunnerBase(FHDLTestCase): When using an Expected state to test with, the expected state is passed in with tst_data. """ + def __init__(self, tst_data, microwatt_mmu=False, rom=None, - svp64=True, run_hdl=None, run_sim=True): + svp64=True, run_hdl=None, run_sim=True, + allow_overlap=False, inorder=False, fp=False): super().__init__("run_all") self.test_data = tst_data self.microwatt_mmu = microwatt_mmu self.rom = rom self.svp64 = svp64 + self.allow_overlap = allow_overlap + self.inorder = inorder self.run_hdl = run_hdl self.run_sim = run_sim + self.fp = fp def run_all(self): m = Module() comb = m.d.comb if self.microwatt_mmu: - ldst_ifacetype = 'test_mmu_cache_wb' + # do not wire these up to anything if wb_get is to be used + if self.rom is not None: + ldst_ifacetype = 'mmu_cache_wb' + imem_ifacetype = 'mmu_cache_wb' + else: + ldst_ifacetype = 'test_mmu_cache_wb' + imem_ifacetype = 'test_bare_wb' else: ldst_ifacetype = 'test_bare_wb' - imem_ifacetype = 'test_bare_wb' - - pspec = TestMemPspec(ldst_ifacetype=ldst_ifacetype, - imem_ifacetype=imem_ifacetype, - addr_wid=48, - mask_wid=8, - imem_reg_wid=64, - # wb_data_width=32, - use_pll=False, - nocore=False, - xics=False, - gpio=False, - regreduce=True, - svp64=self.svp64, - mmu=self.microwatt_mmu, - reg_wid=64) + imem_ifacetype = 'test_bare_wb' + + pspec = Mock(ldst_ifacetype=ldst_ifacetype, + imem_ifacetype=imem_ifacetype, + addr_wid=64, + mask_wid=8, + XLEN=64, + imem_reg_wid=64, + # wb_data_width=32, + use_pll=False, + nocore=False, + xics=False, + gpio=False, + regreduce=not self.allow_overlap, + core_domain="sync", # no alternative domain + svp64=self.svp64, + allow_overlap=self.allow_overlap, + inorder=self.inorder, + mmu=self.microwatt_mmu, + reg_wid=64, + fp_en=self.fp) ###### SETUP PHASE ####### # Determine the simulations needed and add to state_list @@ -144,6 +184,10 @@ class TestRunnerBase(FHDLTestCase): # The methods contained in the respective Runner classes are # called using this list when possible + # allow wb_get to run + if self.rom is not None: + wbget.stop = False + state_list = [] if self.run_hdl: @@ -159,6 +203,8 @@ class TestRunnerBase(FHDLTestCase): # but Simulation-only fails without it intclk = ClockSignal("coresync") comb += intclk.eq(ClockSignal()) + dbgclk = ClockSignal("dbgsync") + comb += dbgclk.eq(ClockSignal()) # nmigen Simulation - everything runs around this, so it # still has to be created. @@ -175,22 +221,60 @@ class TestRunnerBase(FHDLTestCase): # get each test, completely reset the core, and run it for test in self.test_data: - - with self.subTest(test.name): + with self.subTest(test.name, **test.subtest_args): ###### PREPARATION PHASE AT START OF TEST ####### + # HACK: if there is test memory and wb_get is in use, + # overwrite (reset) the wb_get memory dictionary with + # the test's memory contents (oh, and put the wb_get + # memory back in as well) + self.default_mem.clear() + if self.rom is not None: + self.default_mem.update(deepcopy(self.rom)) + if test.mem is not None: + self.default_mem.update(deepcopy(test.mem)) + for runner in state_list: yield from runner.prepare_for_test(test) - print(test.name) + log("running test: ", test.name, test.subtest_args, + kind=LogKind.InstrInOuts) program = test.program - print("regs", test.regs) - print("sprs", test.sprs) - print("cr", test.cr) - print("mem", test.mem) - print("msr", test.msr) - print("assem", program.assembly) + + def format_regs(regs): + # type: (list[int]) -> str + out = [] + for i, v in enumerate(regs): + values = "" + for sz in (32, 64): + for signed in ("u", "i"): + value = v % (1 << sz) + if signed == "i" and \ + value & (1 << (sz - 1)) != 0: + value -= 1 << sz + values += f" {signed}{sz}:{value}" + out.append(f"r{i} = 0x{v:X} {values}") + return "\n".join(out) + log("regs:", format_regs(test.regs), + kind=LogKind.InstrInOuts) + log("sprs", test.sprs, kind=LogKind.InstrInOuts) + log("cr", test.cr, kind=LogKind.InstrInOuts) + log("mem", test.mem) + log("msr", test.msr, kind=LogKind.InstrInOuts) + + def format_assembly(assembly): + # type: (str) -> str + pc = 0 + out = [] + for line in assembly.splitlines(): + out.append(f"pc=0x{pc:04X}: {line}") + if not line.startswith(".set ") and \ + line.partition('#')[0].strip() != "": + pc += 4 + return "\n".join(out) + log("assembly:\n" + format_assembly(program.assembly), + kind=LogKind.InstrInOuts) gen = list(program.generate_instructions()) insncode = program.assembly.splitlines() instructions = list(zip(gen, insncode)) @@ -219,8 +303,8 @@ class TestRunnerBase(FHDLTestCase): if self.run_sim: sim_states = yield from simrun.run_test( - instructions, gen, - insncode) + instructions, gen, + insncode) ###### COMPARING THE TESTS ####### @@ -237,30 +321,40 @@ class TestRunnerBase(FHDLTestCase): elif self.run_hdl: last_sim = copy(hdl_states[-1]) else: - last_sim = None # err what are you doing?? + last_sim = None # err what are you doing?? if self.run_hdl: - print ("hdl_states") + log("hdl_states") for state in hdl_states: - print (state) + log(state) - if self.run_sim: - print ("sim_states") - for state in sim_states: - print (state) + # FIXME: commented until SimState has a __repr__ + # if self.run_sim: + # log("sim_states") + # for state in sim_states: + # log(state) # compare the states if self.run_hdl and self.run_sim: + # if allow_overlap is enabled, because allow_overlap + # can commit out-of-order, only compare the last ones + if self.allow_overlap: + log("allow_overlap: truncating %d %d " + "states to last" % (len(sim_states), + len(hdl_states))) + sim_states = sim_states[-1:] + hdl_states = hdl_states[-1:] + sim_states[-1].dump_state_tofile() + log("allow_overlap: last hdl_state") + hdl_states[-1].dump_state_tofile() for simstate, hdlstate in zip(sim_states, hdl_states): simstate.compare(hdlstate) # register check - simstate.compare_mem(hdlstate) # memory check + simstate.compare_mem(hdlstate) # memory check # if no expected, create /tmp/case_name.py with code # setting expected state to last_sim if test.expected is None: - e = ExpectedState() - e.dump_state_tofile(last_sim, test.name, - test.test_file) + last_sim.dump_state_tofile(test.name, test.test_file) # compare against expected results if test.expected is not None: @@ -272,21 +366,29 @@ class TestRunnerBase(FHDLTestCase): # do actual comparison, against last item last_sim.compare(test.expected) + # check number of instructions run (sanity) if self.run_hdl and self.run_sim: - self.assertTrue(len(hdl_states) == len(sim_states), - "number of instructions run not the same") + n_hdl = len(hdl_states) + n_sim = len(sim_states) + self.assertTrue(n_hdl == n_sim, + "number of instructions %d %d " + "run not the same" % (n_hdl, n_sim)) ###### END OF A TEST ####### # StateRunner.end_test() for runner in state_list: - yield from runner.end_test() # TODO, some arguments? + yield from runner.end_test() # TODO, some arguments? ###### END OF EVERYTHING (but none needs doing, still call fn) #### # StateRunner.cleanup() for runner in state_list: - yield from runner.cleanup() # TODO, some arguments? + yield from runner.cleanup() # TODO, some arguments? + + # finally stop wb_get from going + if self.rom is not None: + wbget.stop = True styles = { 'dec': {'base': 'dec'}, @@ -370,7 +472,14 @@ class TestRunnerBase(FHDLTestCase): 'core.int.rp_src1.memory(7)[63:0]', 'core.int.rp_src1.memory(9)[63:0]', 'core.int.rp_src1.memory(10)[63:0]', - 'core.int.rp_src1.memory(13)[63:0]' + 'core.int.rp_src1.memory(13)[63:0]', + # Exceptions: see list archive for description of the chain + # http://lists.libre-soc.org/pipermail/libre-soc-dev/2021-December/004220.html + ('exceptions', 'closed', [ + 'exc_happened', + 'pdecode2.exc_happened', + 'core.exc_happened', + 'core.fus.ldst0.exc_o_happened']), ] # PortInterface module path varies depending on MMU option @@ -381,7 +490,7 @@ class TestRunnerBase(FHDLTestCase): traces += [('ld/st port interface', {'submodule': pi_module}, [ 'oper_r__insn_type', - 'oper_r__msr', + 'oper_r__msr[63:0]', 'ldst_port0_is_ld_i', 'ldst_port0_is_st_i', 'ldst_port0_busy_o', @@ -393,6 +502,7 @@ class TestRunnerBase(FHDLTestCase): 'ldst_port0_st_data_i_ok', 'ldst_port0_ld_data_o[63:0]', 'ldst_port0_ld_data_o_ok', + 'ldst_port0_msr_pr', 'exc_o_happened', 'cancel' ])] @@ -406,16 +516,26 @@ class TestRunnerBase(FHDLTestCase): 'core.fus.mmu0.alu_mmu0.mmu.pid[31:0]', 'core.fus.mmu0.alu_mmu0.mmu.prtbl[63:0]', {'comment': 'wishbone_memory'}, - 'core.fus.mmu0.alu_mmu0.dcache.stb', - 'core.fus.mmu0.alu_mmu0.dcache.cyc', - 'core.fus.mmu0.alu_mmu0.dcache.we', - 'core.fus.mmu0.alu_mmu0.dcache.ack', - 'core.fus.mmu0.alu_mmu0.dcache.stall,' + 'core.l0.pimem.bus__ack', + 'core.l0.pimem.bus__adr[4:0]', + 'core.l0.pimem.bus__bte', + 'core.l0.pimem.bus__cti', + 'core.l0.pimem.bus__cyc', + 'core.l0.pimem.bus__dat_r[63:0]', + 'core.l0.pimem.bus__dat_w[63:0]', + 'core.l0.pimem.bus__dat_err', + 'core.l0.pimem.bus__dat_sel[7:0]', + 'core.l0.pimem.bus__dat_stb', + 'core.l0.pimem.bus__dat_we', ] - write_gtkw("issuer_simulator.gtkw", - "issuer_simulator.vcd", - traces, styles, module='top.issuer') + gtkname = "issuer_simulator" + if self.rom: + gtkname += "_mmu" + + write_gtkw("%s.gtkw" % gtkname, + "%s.vcd" % gtkname, + traces, styles, module='bench.top.issuer') # add run of instructions sim.add_sync_process(process) @@ -426,10 +546,17 @@ class TestRunnerBase(FHDLTestCase): # optionally, if a wishbone-based ROM is passed in, run that as an # extra emulated process + self.default_mem = {} if self.rom is not None: + log("TestRunner with MMU ROM") + log(pformat(self.rom)) dcache = hdlrun.issuer.core.fus.fus["mmu0"].alu.dcache - default_mem = self.rom - sim.add_sync_process(wrap(wb_get(dcache, default_mem, "DCACHE"))) - - with sim.write_vcd("issuer_simulator.vcd"): + icache = hdlrun.issuer.core.fus.fus["mmu0"].alu.icache + self.default_mem = deepcopy(self.rom) + sim.add_sync_process(wrap(wb_get(dcache.bus, + self.default_mem, "DCACHE"))) + sim.add_sync_process(wrap(wb_get(icache.ibus, + self.default_mem, "ICACHE"))) + + with sim.write_vcd("%s.vcd" % gtkname): sim.run()