X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fopenpower%2Ftest%2Frunner.py;h=bff484b37252d90f9f3208d5bf26fd821158d1e8;hb=42101b3e34e96dc3cc36973fa1470758df9aa0c6;hp=8987c3f13522e33038ed0ee88f06e49614b8a7a3;hpb=1418c44189e4fbcaa60d352d48b5595b6c8af007;p=openpower-isa.git diff --git a/src/openpower/test/runner.py b/src/openpower/test/runner.py index 8987c3f1..bff484b3 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,9 +15,12 @@ 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 pprint import pprint +from copy import copy, deepcopy +from pprint import pformat +import os +from elftools.elf.elffile import ELFFile # for isinstance # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell # Also, check out the cxxsim nmigen branch, and latest yosys from git @@ -25,29 +29,37 @@ from nmutil.sim_tmp_alternative import Simulator, Settle from nmutil.formaltest import FHDLTestCase from nmutil.gtkw import write_gtkw from openpower.decoder.isa.all import ISA +from openpower.decoder.isa.caller import ExitSyscallCalled 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 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, LogType class SimRunner(StateRunner): """SimRunner: Implements methods for the setup, preparation, and running of tests using ISACaller simulation """ - def __init__(self, dut, m, pspec): + + def __init__(self, dut, m, pspec, + use_mmap_mem=False, + use_syscall_emu=False): 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 + self.use_mmap_mem = use_mmap_mem + self.use_syscall_emu = use_syscall_emu def prepare_for_test(self, test): self.test = test @@ -68,16 +80,43 @@ class SimRunner(StateRunner): disassembly=insncode, bigendian=bigendian, initial_svstate=test.svstate, - mmu=self.mmu) + mmu=self.mmu, + fpregfile=test.fpregs, + initial_fpscr=test.initial_fpscr, + use_mmap_mem=self.use_mmap_mem, + use_syscall_emu=self.use_syscall_emu) - # run the loop of the instructions on the current test - index = sim.pc.CIA.value//4 - while index < len(instructions): + index = ins = code = 0 # variables for nonlocal + + def next_insn(): + nonlocal index, ins, code + index = sim.pc.CIA.value//4 + if index >= len(instructions): + return False 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("\n0x%04X: %08X %s" % (sim.pc.CIA.value, + ins % (1 << 32), code), + kind=LogType.InstrInOuts) + + log(index, code) + return True + + if isinstance(gen, ELFFile): + def next_insn(): + nonlocal index, ins, code + index = code = None + ins = sim.imem.ld(sim.pc.CIA.value, width=4, swap=False, + check_in_mem=True, instr_fetch=True) + ins_str = "None" if ins is None else "%08X" % ins + log("\n0x%04X: %s" % (sim.pc.CIA.value, ins_str), + kind=LogType.InstrInOuts) + return not sim.halted + # run the loop of the instructions on the current test + while next_insn(): # set up simulated instruction (in simdec2) try: yield from sim.setup_one() @@ -86,15 +125,19 @@ class SimRunner(StateRunner): yield Settle() # call simulated operation - print("sim", code) - yield from sim.execute_one() + log("sim", code) + try: + yield from sim.execute_one() + except ExitSyscallCalled: + break yield Settle() - index = sim.pc.CIA.value//4 # get sim register and memory TestState, add to list state = yield from TestState("sim", sim, dut, code) sim_states.append(state) + log(f"final pc: 0x{sim.pc.CIA.value:X}", kind=LogType.InstrInOuts) + if self.dut.allow_overlap: # get last state, at end of run state = yield from TestState("sim", sim, dut, code) @@ -113,17 +156,24 @@ 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, - allow_overlap=False): + svp64=True, run_hdl=None, run_sim=True, + allow_overlap=False, inorder=False, fp=False, + use_mmap_mem=False, + use_syscall_emu=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 + self.use_mmap_mem = use_mmap_mem + self.use_syscall_emu = use_syscall_emu def run_all(self): m = Module() @@ -140,22 +190,25 @@ class TestRunnerBase(FHDLTestCase): 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=not self.allow_overlap, - core_domain="sync", # no alternative domain - svp64=self.svp64, - allow_overlap=self.allow_overlap, - mmu=self.microwatt_mmu, - reg_wid=64) + 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 @@ -174,7 +227,9 @@ class TestRunnerBase(FHDLTestCase): state_list.append(hdlrun) if self.run_sim: - simrun = SimRunner(self, m, pspec) + simrun = SimRunner(self, m, pspec, + use_mmap_mem=self.use_mmap_mem, + use_syscall_emu=self.use_syscall_emu) state_list.append(simrun) # run core clock at same rate as test clock @@ -200,25 +255,74 @@ 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=LogType.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) - gen = list(program.generate_instructions()) - insncode = program.assembly.splitlines() - instructions = list(zip(gen, insncode)) + + 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=LogType.InstrInOuts) + log("sprs", test.sprs, kind=LogType.InstrInOuts) + log("cr", test.cr, kind=LogType.InstrInOuts) + log("mem", test.mem) + if test.msr is None: + log("msr", "None", kind=LogType.InstrInOuts) + else: + log("msr", hex(test.msr), kind=LogType.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) + if isinstance(program, ELFFile): + f = os.readlink( + "/proc/self/fd/%d" % program.stream.fileno()) + log("using program: " + f, kind=LogType.InstrInOuts) + instructions = program + gen = program + insncode = None + else: + log("assembly:\n" + format_assembly(program.assembly), + kind=LogType.InstrInOuts) + gen = list(program.generate_instructions()) + insncode = program.assembly.splitlines() + instructions = list(zip(gen, insncode)) ###### RUNNING OF EACH TEST ####### # StateRunner.step_test() @@ -244,8 +348,8 @@ class TestRunnerBase(FHDLTestCase): if self.run_sim: sim_states = yield from simrun.run_test( - instructions, gen, - insncode) + instructions, gen, + insncode) ###### COMPARING THE TESTS ####### @@ -262,34 +366,35 @@ 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: - print ("allow_overlap: truncating %d %d " - "states to last" % (len(sim_states), - len(hdl_states))) + 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() - print ("allow_overlap: last hdl_state") + 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 @@ -311,20 +416,20 @@ class TestRunnerBase(FHDLTestCase): 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)) + "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: @@ -469,9 +574,13 @@ class TestRunnerBase(FHDLTestCase): '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) @@ -482,16 +591,20 @@ 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: - print ("TestRunner with MMU ROM") - pprint (self.rom) + log("TestRunner with MMU ROM") + log(pformat(self.rom)) dcache = hdlrun.issuer.core.fus.fus["mmu0"].alu.dcache icache = hdlrun.issuer.core.fus.fus["mmu0"].alu.icache - default_mem = self.rom + self.default_mem = deepcopy(self.rom) sim.add_sync_process(wrap(wb_get(dcache.bus, - default_mem, "DCACHE"))) + self.default_mem, "DCACHE"))) sim.add_sync_process(wrap(wb_get(icache.ibus, - default_mem, "ICACHE"))) + self.default_mem, "ICACHE"))) - with sim.write_vcd("issuer_simulator.vcd"): + if "SIM_NO_VCD" in os.environ: sim.run() + else: + with sim.write_vcd("%s.vcd" % gtkname): + sim.run()