+# SPDX-License-Identifier: LGPL-2-or-later
"""TestRunner class, part of the Test API
SPDX-License: LGPLv2+
* 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
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):
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:
yield Settle()
# call simulated operation
- print("sim", code)
+ log("sim", code)
yield from sim.execute_one()
yield Settle()
index = sim.pc.CIA.value//4
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)
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):
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=not self.allow_overlap,
- svp64=self.svp64,
- allow_overlap=self.allow_overlap,
- 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
# 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:
# 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.
# 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))
if self.run_sim:
sim_states = yield from simrun.run_test(
- instructions, gen,
- insncode)
+ instructions, gen,
+ insncode)
###### COMPARING THE TESTS #######
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
# 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'},
if self.microwatt_mmu:
traces += [
{'comment': 'microwatt_mmu'},
- 'core.mmu0.illegal',
- 'core.mmu0.debug0[3:0]',
- 'core.mmu.state',
- 'core.mmu.pid[31:0]',
- 'core.mmu.prtbl[63:0]',
- 'core.state', # actually LoadStore1
+ 'core.fus.mmu0.alu_mmu0.illegal',
+ 'core.fus.mmu0.alu_mmu0.debug0[3:0]',
+ 'core.fus.mmu0.alu_mmu0.mmu.state',
+ 'core.fus.mmu0.alu_mmu0.mmu.pid[31:0]',
+ 'core.fus.mmu0.alu_mmu0.mmu.prtbl[63:0]',
{'comment': 'wishbone_memory'},
- 'core.dcache.wb_in_ack',
- 'core.dcache.wb_in_stall,'
- 'core.dcache.wb_in_dat,'
- 'core.dcache.wb_out_cyc',
- 'core.dcache.wb_out_stb',
- 'core.dcache.wb_out_we',
- 'core.dcache.wb_out_adr',
- 'core.dcache.wb_out_dat',
- 'core.dcache.wb_out_sel',
+ '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)
# 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()