X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fopenpower%2Ftest%2Fstate.py;h=b2eb250ad9a4ce90e192e646fae748f91a89094c;hb=1ede1d3464b184bae24668bf58d92a9bca73fce4;hp=7995a8bcb65f1391168d84cc777cf2f50b06f073;hpb=10e0db2f4b1167a74289187a0ea745d2e537d906;p=openpower-isa.git diff --git a/src/openpower/test/state.py b/src/openpower/test/state.py index 7995a8bc..b2eb250a 100644 --- a/src/openpower/test/state.py +++ b/src/openpower/test/state.py @@ -22,9 +22,15 @@ methods, the use of yield from/yield is required. """ -from openpower.decoder.power_enums import XER_bits +from openpower.decoder.power_enums import XER_bits, SPRfull +from openpower.decoder.isa.radixmmu import RADIX from openpower.util import log +from openpower.fpscr import FPSCRState +from openpower.decoder.selectable_int import SelectableInt +from openpower.consts import DEFAULT_MSR import os +import sys +from copy import deepcopy global staterunner_factory staterunner_factory = {} @@ -58,6 +64,63 @@ class StateRunner: if False: yield +class StateSPRs: + KEYS = tuple(i for i in SPRfull if i != SPRfull.XER) + __EMPTY_VALUES = {k: 0 for k in KEYS} + + def __init__(self, values=None): + if isinstance(values, StateSPRs): + self.__values = values.__values.copy() + return + self.__values = self.__EMPTY_VALUES.copy() + if values is not None: + for k, v in values.items(): + self[k] = v + + @staticmethod + def __key(k, raise_if_invalid=True): + try: + if isinstance(k, str): + retval = SPRfull.__members__[k] + else: + retval = SPRfull(k) + except (ValueError, KeyError): + retval = None + if retval == SPRfull.XER: # XER is not stored in StateSPRs + retval = None + if retval is None and raise_if_invalid: + raise KeyError(k) + return retval + + def items(self): + for k in StateSPRs.KEYS: + yield (k, self[k]) + + def __iter__(self): + return iter(StateSPRs.KEYS) + + def __len__(self): + return len(StateSPRs.KEYS) + + def __contains__(self, k): + return self.__key(k, raise_if_invalid=False) is not None + + def __getitem__(self, k): + return self.__values[self.__key(k)] + + def __setitem__(self, k, v): + k = self.__key(k) + if v is not None: + v = int(v) + self.__values[k] = v + + def nonzero(self): + return {k: v for k, v in self.__values.items() if v != 0} + + def __repr__(self): + return repr(self.nonzero()) + + class State: """State: Base class for the "state" of the Power ISA object to be tested including methods to compare various registers and memory between @@ -67,47 +130,128 @@ class State: GPRs and CRs - stored as lists XERs/PC - simple members + SO/CA[32]/OV[32] are stored in so/ca/ov members, + xer_other is all other XER bits. + SPRs - stored in self.sprs as a StateSPRs memory - stored as a dictionary {location: data} """ + + @property + def sprs(self): + return self.__sprs + + @sprs.setter + def sprs(self, value): + self.__sprs = StateSPRs(value) + def get_state(self): + yield from self.get_fpscr() + yield from self.get_fpregs() yield from self.get_intregs() yield from self.get_crregs() yield from self.get_xregs() yield from self.get_pc() + yield from self.get_msr() + yield from self.get_sprs() yield from self.get_mem() def compare(self, s2): + # Compare FP registers + for i, (fpreg, fpreg2) in enumerate( + zip(self.fpregs, s2.fpregs)): + log("asserting...reg", i, fpreg, fpreg2) + log("code, frepr(code)", self.code, repr(self.code)) + self.dut.assertEqual(fpreg, fpreg2, + "fp reg %d (%s) not equal (%s) %s. " + " got %x expected %x at pc %x %x\n" % + (i, self.state_type, s2.state_type, repr(self.code), + fpreg, fpreg2, self.pc, s2.pc)) + # Compare int registers - for i, (self.intregs, s2.intregs) in enumerate( + for i, (intreg, intreg2) in enumerate( zip(self.intregs, s2.intregs)): - log("asserting...reg", i, self.intregs, s2.intregs) + log("asserting...reg", i, intreg, intreg2) log("code, frepr(code)", self.code, repr(self.code)) - self.dut.assertEqual(self.intregs, s2.intregs, - "int reg %d (%s) not equal (%s) %s. got %x expected %x" % + self.dut.assertEqual(intreg, intreg2, + "int reg %d (%s) not equal (%s) %s. " + " got %x expected %x at pc %x %x\n" % (i, self.state_type, s2.state_type, repr(self.code), - self.intregs, s2.intregs)) + intreg, intreg2, self.pc, s2.pc)) # CR registers - for i, (self.crregs, s2.crregs) in enumerate( + for i, (crreg, crreg2) in enumerate( + zip(self.crregs, s2.crregs)): + log("asserting...cr", i, crreg, crreg2) + + for i, (crreg, crreg2) in enumerate( zip(self.crregs, s2.crregs)): - log("asserting...cr", i, self.crregs, s2.crregs) - self.dut.assertEqual(self.crregs, s2.crregs, + self.dut.assertEqual(crreg, crreg2, "cr reg %d (%s) not equal (%s) %s. got %x expected %x" % (i, self.state_type, s2.state_type, repr(self.code), - self.crregs, s2.crregs)) + crreg, crreg2)) # XER - self.dut.assertEqual(self.so, s2.so, "so mismatch (%s != %s) %s" % - (self.state_type, s2.state_type, repr(self.code))) - self.dut.assertEqual(self.ov, s2.ov, "ov mismatch (%s != %s) %s" % - (self.state_type, s2.state_type, repr(self.code))) - self.dut.assertEqual(self.ca, s2.ca, "ca mismatch (%s != %s) %s" % - (self.state_type, s2.state_type, repr(self.code))) + if self.so is not None and s2.so is not None: + self.dut.assertEqual(self.so, s2.so, "so mismatch (%s != %s) %s" % + (self.state_type, s2.state_type, repr(self.code))) + if self.ov is not None and s2.ov is not None: + self.dut.assertEqual(self.ov, s2.ov, "ov mismatch (%s != %s) %s" % + (self.state_type, s2.state_type, repr(self.code))) + if self.ca is not None and s2.ca is not None: + self.dut.assertEqual(self.ca, s2.ca, "ca mismatch (%s != %s) %s" % + (self.state_type, s2.state_type, repr(self.code))) + if self.xer_other is not None and s2.xer_other is not None: + self.dut.assertEqual( + hex(self.xer_other), hex(s2.xer_other), + "xer_other mismatch (%s != %s) %s" % + (self.state_type, s2.state_type, repr(self.code))) # pc self.dut.assertEqual(self.pc, s2.pc, "pc mismatch (%s != %s) %s" % (self.state_type, s2.state_type, repr(self.code))) + # fpscr + if self.fpscr is not None and s2.fpscr is not None: + if self.fpscr != s2.fpscr: + # use FPSCRState.fsi since that's much easier to read than a + # decimal integer and since unittest has fancy dict diffs. + + # use auto_update_summary_bits=False since HDL might + # mis-compute those summary bits and we want to show the + # actual bits, not the corrected bits + fpscr1 = FPSCRState(self.fpscr, auto_update_summary_bits=False) + fpscr2 = FPSCRState(s2.fpscr, auto_update_summary_bits=False) + # FieldSelectableInt.__repr__ is too long + fpscr1 = {k: hex(int(v)) for k, v in fpscr1.fsi.items()} + fpscr2 = {k: hex(int(v)) for k, v in fpscr2.fsi.items()} + old_max_diff = self.dut.maxDiff + self.dut.maxDiff = None # show full diff + try: + self.dut.assertEqual( + fpscr1, fpscr2, "fpscr mismatch (%s != %s) %s\n" % + (self.state_type, s2.state_type, repr(self.code))) + finally: + self.dut.maxDiff = old_max_diff + + for spr in self.sprs: + spr1 = self.sprs[spr] + spr2 = s2.sprs[spr] + + if spr1 == spr2: + continue + + if spr1 is not None and spr2 is not None: + # if not explicitly ignored + + self.dut.fail( + f"{spr1:#x} != {spr2:#x}: {spr} mismatch " + f"({self.state_type} != {s2.state_type}) {self.code!r}\n") + + if self.msr is not None and s2.msr is not None: + self.dut.assertEqual( + hex(self.msr), hex(s2.msr), "msr mismatch (%s != %s) %s" % + (self.state_type, s2.state_type, repr(self.code))) + def compare_mem(self, s2): # copy dics to preserve state mem then pad empty locs since # different Power ISA objects may differ how theystore memory @@ -120,31 +264,110 @@ class State: self.dut.assertEqual(s1mem[i], s2mem[i], "mem mismatch location %d %s" % (i, self.code)) + def dump_state_tofile(self, testname=None, testfile=None): + """dump_state_tofile: Takes a passed in teststate object along + with a test name and generates a code file located at + /tmp/testfile/testname to set an expected state object + """ + lindent = ' '*8 # indent for code + # create the path + if testname is not None: + path = "/tmp/expected/" + if testfile is not None: + path += testfile + '/' + os.makedirs(path, exist_ok=True) + sout = open("%s%s.py" % (path, testname), "a+") + else: + sout = sys.stdout + + # pc and intregs + sout.write("%se = ExpectedState(pc=%d)\n" % (lindent, self.pc)) + for i, reg in enumerate(self.intregs): + if(reg != 0): + msg = "%se.intregs[%d] = 0x%x\n" + sout.write( msg % (lindent, i, reg)) + for i, reg in enumerate(self.fpregs): + if reg != 0: + msg = "%se.fpregs[%d] = 0x%x\n" + sout.write(msg % (lindent, i, reg)) + # CR fields + for i in range(8): + cri = self.crregs[i] + if(cri != 0): + msg = "%se.crregs[%d] = 0x%x\n" + sout.write( msg % (lindent, i, cri)) + # XER + if(self.so != 0): + sout.write("%se.so = 0x%x\n" % (lindent, self.so)) + if(self.ov != 0): + sout.write("%se.ov = 0x%x\n" % (lindent, self.ov)) + if(self.ca != 0): + sout.write("%se.ca = 0x%x\n" % (lindent, self.ca)) + if self.xer_other != 0: + sout.write("%se.xer_other = 0x%x\n" % (lindent, self.xer_other)) + + # FPSCR + if self.fpscr != 0: + sout.write(f"{lindent}e.fpscr = {self.fpscr:#x}\n") + + # SPRs + for k, v in self.sprs.nonzero().items(): + sout.write(f"{lindent}e.sprs[{k.name!r}] = {v:#x}\n") + + # MSR + if self.msr != 0: + sout.write(f"{lindent}e.msr = {self.msr:#x}\n") + + if sout != sys.stdout: + sout.close() + + +def _get_regs(regs, asint=lambda v: v.asint()): + retval = [] + while True: + try: + retval.append(asint(regs[len(retval)])) + except (IndexError, KeyError): + break + return retval + class SimState(State): """SimState: Obtains registers and memory from an ISACaller object. - Note that yields are "faked" to maintain consistency and compatability + Note that yields are "faked" to maintain consistency and compatibility within the API. """ def __init__(self, sim): self.sim = sim + def get_fpregs(self): + if False: + yield + self.fpregs = _get_regs(self.sim.fpr) + log("class sim fp regs", list(map(hex, self.fpregs))) + + def get_fpscr(self): + if False: + yield + self.fpscr = int(self.sim.fpscr) + log("class sim fpscr", hex(self.fpscr)) + + def get_msr(self): + if False: + yield + self.msr = int(self.sim.msr) + log("class sim msr", hex(self.msr)) + def get_intregs(self): if False: yield - self.intregs = [] - for i in range(32): - simregval = self.sim.gpr[i].asint() - self.intregs.append(simregval) + self.intregs = _get_regs(self.sim.gpr) log("class sim int regs", list(map(hex, self.intregs))) def get_crregs(self): if False: yield - self.crregs = [] - for i in range(8): - cri = self.sim.crl[7 - i].get_range().value - self.crregs.append(cri) + self.crregs = _get_regs(self.sim.crl, lambda v: v.get_range().value) log("class sim cr regs", list(map(hex, self.crregs))) def get_xregs(self): @@ -158,9 +381,34 @@ class SimState(State): self.ca32 = self.sim.spr['XER'][XER_bits['CA32']].value self.ov = self.ov | (self.ov32 << 1) self.ca = self.ca | (self.ca32 << 1) + xer_other = SelectableInt(self.sim.spr['XER']) + for i in 'SO', 'OV', 'OV32', 'CA', 'CA32': + xer_other[XER_bits[i]] = 0 + self.xer_other = int(xer_other) self.xregs.extend((self.so, self.ov, self.ca)) log("class sim xregs", list(map(hex, self.xregs))) + def get_sprs(self): + if False: + yield + self.sprs = StateSPRs() + for spr in self.sprs: + # hacky workaround to workaround luke's hack in caller.py that + # aliases HSRR[01] to SRR[01] -- we temporarily clear SRR[01] while + # trying to read HSRR[01] + clear_srr = spr == SPRfull.HSRR0 or spr == SPRfull.HSRR1 + if clear_srr: + old_srr0 = self.sim.spr['SRR0'] + old_srr1 = self.sim.spr['SRR1'] + self.sim.spr['SRR0'] = 0 + self.sim.spr['SRR1'] = 0 + + self.sprs[spr] = self.sim.spr[spr.name] # setitem converts to int + + if clear_srr: + self.sim.spr['SRR0'] = old_srr0 + self.sim.spr['SRR1'] = old_srr1 + def get_pc(self): if False: yield @@ -172,12 +420,15 @@ class SimState(State): def get_mem(self): if False: yield - keys = list(self.sim.mem.mem.keys()) + mem = self.sim.mem + if isinstance(mem, RADIX): + mem = mem.mem + keys = list(mem.mem.keys()) self.mem = {} # from each address in the underlying mem-simulated dictionary # issue a 64-bit LD (with no byte-swapping) for k in keys: - data = self.sim.mem.ld(k*8, 8, False) + data = mem.ld(k*8, 8, False) self.mem[k*8] = data @@ -189,22 +440,51 @@ class ExpectedState(State): see openpower/test/shift_rot/shift_rot_cases2.py for examples """ def __init__(self, int_regs=None, pc=0, crregs=None, - so=0, ov=0, ca=0): + so=0, ov=0, ca=0, fp_regs=None, fpscr=0, sprs=None, + msr=DEFAULT_MSR, xer_other=0): + if fp_regs is None: + fp_regs = 32 + if isinstance(fp_regs, int): + fp_regs = [0] * fp_regs + else: + assert isinstance(fp_regs, list), \ + "fp_regs must be int | list[int] | None" + # don't use deepcopy, it's slow + fp_regs = fp_regs.copy() + self.fpregs = fp_regs + self.fpscr = fpscr if int_regs is None: int_regs = 32 if isinstance(int_regs, int): int_regs = [0] * int_regs + else: + assert isinstance(int_regs, list), \ + "int_regs must be int | list[int] | None" + # don't use deepcopy, it's slow + int_regs = int_regs.copy() self.intregs = int_regs self.pc = pc if crregs is None: crregs = 8 if isinstance(crregs, int): crregs = [0] * crregs + else: + assert isinstance(crregs, list), \ + "crregs must be int | list[int] | None" + # don't use deepcopy, it's slow + crregs = crregs.copy() self.crregs = crregs self.so = so self.ov = ov self.ca = ca + self.xer_other = xer_other + self.sprs = StateSPRs(sprs) + self.msr = msr + def get_fpregs(self): + if False: yield + def get_fpscr(self): + if False: yield def get_intregs(self): if False: yield def get_crregs(self): @@ -213,42 +493,18 @@ class ExpectedState(State): if False: yield def get_pc(self): if False: yield + + def get_msr(self): + if False: + yield + + def get_sprs(self): + if False: + yield + def get_mem(self): if False: yield - def dump_state_tofile(self, state, testname, testfile): - """dump_state_tofile: Takes a passed in teststate object along - with a test name and generates a code file located at - /tmp/testfile/testname to set an expected state object - """ - lindent = ' '*8 # indent for code - # create the path - path = "/tmp/expected/" - if testfile is not None: - path += testfile + '/' - os.makedirs(path, exist_ok=True) - - with open("%s%s.py" % (path, testname), "a") as sout: - # pc and intregs - sout.write("%se = ExpectedState(pc=%d)\n" % (lindent, state.pc)) - for i, reg in enumerate(state.intregs): - if(reg != 0): - msg = "%se.intregs[%d] = 0x%x\n" - sout.write( msg % (lindent, i, reg)) - # cr - for i in range(8): - cri = state.crregs[7 - i] - if(cri != 0): - msg = "%se.crregs[%d] = 0x%x\n" - sout.write( msg % (lindent, i, cri)) - # XER - if(state.so != 0): - sout.write("%se.so = 0x%x\n" % (lindent, state.so)) - if(state.ov != 0): - sout.write("%se.ov = 0x%x\n" % (lindent, state.ov)) - if(state.ca != 0): - sout.write("%se.ca = 0x%x\n" % (lindent, state.ca)) - global state_factory state_factory = {'sim': SimState, 'expected': ExpectedState}