bug 1228: dcbt* to match against nop, not dc* in ISACaller
[openpower-isa.git] / src / openpower / test / runner.py
index 8987c3f13522e33038ed0ee88f06e49614b8a7a3..bff484b37252d90f9f3208d5bf26fd821158d1e8 100644 (file)
@@ -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()