pysvp64db: fix traversal
[openpower-isa.git] / src / openpower / test / state.py
1 """ Power ISA test API
2
3 This module implements the creation, inspection and comparison
4 of test states from different sources.
5
6 The basic premise is to create a test state using the TestState method.
7 The TestState method returns a test state object initialized with a
8 basic set of registers pulled from the 'to_test' object. The
9 state created can then be tested against other test states using the
10 'compare' method.
11
12 The SimState class provides an example of needed registers and naming.
13
14 The TestState method relies on the 'state_factory' dictionary for lookup
15 of associated test class creation. The dictionary can be added to using
16 the state_add method.
17
18 Also note when creating and accessing test state classes and object
19 methods, the use of yield from/yield is required.
20
21
22 """
23
24
25 from openpower.decoder.power_enums import XER_bits, SPRfull
26 from openpower.decoder.isa.radixmmu import RADIX
27 from openpower.util import log
28 from openpower.fpscr import FPSCRState
29 from openpower.decoder.selectable_int import SelectableInt
30 from openpower.consts import DEFAULT_MSR
31 import os
32 import sys
33 from copy import deepcopy
34
35 global staterunner_factory
36 staterunner_factory = {}
37
38
39 def staterunner_add(name, kls):
40 log("staterunner_add", name, kls)
41 staterunner_factory[name] = kls
42
43
44 # TBD an Abstract Base Class
45 class StateRunner:
46 """StateRunner: an Abstract Base Class for preparing and running "State".
47 near-identical in concept to python unittest.TestCase
48 """
49 def __init__(self, name, kls):
50 staterunner_add(name, kls)
51 self.name = name
52
53 def setup_for_test(self):
54 if False: yield
55 def setup_during_test(self):
56 if False: yield
57 def prepare_for_test(self, test):
58 if False: yield
59 def run_test(self):
60 if False: yield
61 def end_test(self):
62 if False: yield
63 def cleanup(self):
64 if False: yield
65
66
67 class StateSPRs:
68 KEYS = tuple(i for i in SPRfull if i != SPRfull.XER)
69 __EMPTY_VALUES = {k: 0 for k in KEYS}
70
71 def __init__(self, values=None):
72 if isinstance(values, StateSPRs):
73 self.__values = values.__values.copy()
74 return
75 self.__values = self.__EMPTY_VALUES.copy()
76 if values is not None:
77 for k, v in values.items():
78 self[k] = v
79
80 @staticmethod
81 def __key(k, raise_if_invalid=True):
82 try:
83 if isinstance(k, str):
84 retval = SPRfull.__members__[k]
85 else:
86 retval = SPRfull(k)
87 except (ValueError, KeyError):
88 retval = None
89 if retval == SPRfull.XER: # XER is not stored in StateSPRs
90 retval = None
91 if retval is None and raise_if_invalid:
92 raise KeyError(k)
93 return retval
94
95 def items(self):
96 for k in StateSPRs.KEYS:
97 yield (k, self[k])
98
99 def __iter__(self):
100 return iter(StateSPRs.KEYS)
101
102 def __len__(self):
103 return len(StateSPRs.KEYS)
104
105 def __contains__(self, k):
106 return self.__key(k, raise_if_invalid=False) is not None
107
108 def __getitem__(self, k):
109 return self.__values[self.__key(k)]
110
111 def __setitem__(self, k, v):
112 k = self.__key(k)
113 if v is not None:
114 v = int(v)
115 self.__values[k] = v
116
117 def nonzero(self):
118 return {k: v for k, v in self.__values.items() if v != 0}
119
120 def __repr__(self):
121 return repr(self.nonzero())
122
123
124 class State:
125 """State: Base class for the "state" of the Power ISA object to be tested
126 including methods to compare various registers and memory between
127 them.
128
129 All methods implemented must be generators.
130
131 GPRs and CRs - stored as lists
132 XERs/PC - simple members
133 SO/CA[32]/OV[32] are stored in so/ca/ov members,
134 xer_other is all other XER bits.
135 SPRs - stored in self.sprs as a StateSPRs
136 memory - stored as a dictionary {location: data}
137 """
138
139 @property
140 def sprs(self):
141 return self.__sprs
142
143 @sprs.setter
144 def sprs(self, value):
145 self.__sprs = StateSPRs(value)
146
147 def get_state(self):
148 yield from self.get_fpscr()
149 yield from self.get_fpregs()
150 yield from self.get_intregs()
151 yield from self.get_crregs()
152 yield from self.get_xregs()
153 yield from self.get_pc()
154 yield from self.get_msr()
155 yield from self.get_sprs()
156 yield from self.get_mem()
157
158 def compare(self, s2):
159 # Compare FP registers
160 for i, (fpreg, fpreg2) in enumerate(
161 zip(self.fpregs, s2.fpregs)):
162 log("asserting...reg", i, fpreg, fpreg2)
163 log("code, frepr(code)", self.code, repr(self.code))
164 self.dut.assertEqual(fpreg, fpreg2,
165 "fp reg %d (%s) not equal (%s) %s. "
166 " got %x expected %x at pc %x %x\n" %
167 (i, self.state_type, s2.state_type, repr(self.code),
168 fpreg, fpreg2, self.pc, s2.pc))
169
170 # Compare int registers
171 for i, (intreg, intreg2) in enumerate(
172 zip(self.intregs, s2.intregs)):
173 if intreg is None or intreg2 is None:
174 continue
175 log("asserting...reg", i, intreg, intreg2)
176 log("code, frepr(code)", self.code, repr(self.code))
177 self.dut.assertEqual(intreg, intreg2,
178 "int reg %d (%s) not equal (%s) %s. "
179 " got %x expected %x at pc %x %x\n" %
180 (i, self.state_type, s2.state_type, repr(self.code),
181 intreg, intreg2, self.pc, s2.pc))
182
183 # CR registers
184 for i, (crreg, crreg2) in enumerate(
185 zip(self.crregs, s2.crregs)):
186 log("asserting...cr", i, crreg, crreg2)
187
188 for i, (crreg, crreg2) in enumerate(
189 zip(self.crregs, s2.crregs)):
190 self.dut.assertEqual(crreg, crreg2,
191 "cr reg %d (%s) not equal (%s) %s. got %x expected %x" %
192 (i, self.state_type, s2.state_type, repr(self.code),
193 crreg, crreg2))
194
195 # XER
196 if self.so is not None and s2.so is not None:
197 self.dut.assertEqual(self.so, s2.so, "so mismatch (%s != %s) %s" %
198 (self.state_type, s2.state_type, repr(self.code)))
199 if self.ov is not None and s2.ov is not None:
200 self.dut.assertEqual(self.ov, s2.ov, "ov mismatch (%s != %s) %s" %
201 (self.state_type, s2.state_type, repr(self.code)))
202 if self.ca is not None and s2.ca is not None:
203 self.dut.assertEqual(self.ca, s2.ca, "ca mismatch (%s != %s) %s" %
204 (self.state_type, s2.state_type, repr(self.code)))
205 if self.xer_other is not None and s2.xer_other is not None:
206 self.dut.assertEqual(
207 hex(self.xer_other), hex(s2.xer_other),
208 "xer_other mismatch (%s != %s) %s" %
209 (self.state_type, s2.state_type, repr(self.code)))
210
211 # pc
212 self.dut.assertEqual(self.pc, s2.pc, "pc mismatch (%s != %s) %s" %
213 (self.state_type, s2.state_type, repr(self.code)))
214
215 # fpscr
216 if self.fpscr is not None and s2.fpscr is not None:
217 if self.fpscr != s2.fpscr:
218 # use FPSCRState.fsi since that's much easier to read than a
219 # decimal integer and since unittest has fancy dict diffs.
220
221 # use auto_update_summary_bits=False since HDL might
222 # mis-compute those summary bits and we want to show the
223 # actual bits, not the corrected bits
224 fpscr1 = FPSCRState(self.fpscr, auto_update_summary_bits=False)
225 fpscr2 = FPSCRState(s2.fpscr, auto_update_summary_bits=False)
226 # FieldSelectableInt.__repr__ is too long
227 fpscr1 = {k: hex(int(v)) for k, v in fpscr1.fsi.items()}
228 fpscr2 = {k: hex(int(v)) for k, v in fpscr2.fsi.items()}
229 old_max_diff = self.dut.maxDiff
230 self.dut.maxDiff = None # show full diff
231 try:
232 self.dut.assertEqual(
233 fpscr1, fpscr2, "fpscr mismatch (%s != %s) %s\n" %
234 (self.state_type, s2.state_type, repr(self.code)))
235 finally:
236 self.dut.maxDiff = old_max_diff
237
238 for spr in self.sprs:
239 spr1 = self.sprs[spr]
240 spr2 = s2.sprs[spr]
241
242 if spr1 == spr2:
243 continue
244
245 if spr1 is not None and spr2 is not None:
246 # if not explicitly ignored
247
248 self.dut.fail(
249 f"{spr1:#x} != {spr2:#x}: {spr} mismatch "
250 f"({self.state_type} != {s2.state_type}) {self.code!r}\n")
251
252 if self.msr is not None and s2.msr is not None:
253 self.dut.assertEqual(
254 hex(self.msr), hex(s2.msr), "msr mismatch (%s != %s) %s" %
255 (self.state_type, s2.state_type, repr(self.code)))
256
257 def compare_mem(self, s2):
258 # copy dicts to preserve state mem then pad empty locs since
259 # different Power ISA objects may differ how they store memory
260 if getattr(self, "mem", None) is None:
261 return
262 if getattr(s2, "mem", None) is None:
263 return
264 s1mem, s2mem = self.mem.copy(), s2.mem.copy()
265 for i in set(self.mem).difference(set(s2.mem)):
266 s2mem[i] = 0
267 for i in set(s2.mem).difference(set(self.mem)):
268 s1mem[i] = 0
269 for i in s1mem:
270 if s1mem[i] != s2mem[i]:
271 self.dut.assertEqual(hex(s1mem[i]), hex(s2mem[i]),
272 "mem mismatch location 0x%X %s" % (i, self.code))
273
274 def dump_state_tofile(self, testname=None, testfile=None):
275 """dump_state_tofile: Takes a passed in teststate object along
276 with a test name and generates a code file located at
277 /tmp/testfile/testname to set an expected state object
278 """
279 single_indent = ' ' * 4
280 lindent = single_indent * 2 # indent for code
281 # create the path
282 if testname is not None:
283 path = "/tmp/expected/"
284 if testfile is not None:
285 path += testfile + '/'
286 os.makedirs(path, exist_ok=True)
287 sout = open("%s%s.py" % (path, testname), "a+")
288 else:
289 sout = sys.stdout
290
291 # pc and intregs
292 sout.write("%se = ExpectedState(pc=%d)\n" % (lindent, self.pc))
293 for i, reg in enumerate(self.intregs):
294 if(reg != 0):
295 msg = "%se.intregs[%d] = 0x%x\n"
296 sout.write( msg % (lindent, i, reg))
297 for i, reg in enumerate(self.fpregs):
298 if reg != 0:
299 msg = "%se.fpregs[%d] = 0x%x\n"
300 sout.write(msg % (lindent, i, reg))
301 # CR fields
302 for i in range(8):
303 cri = self.crregs[i]
304 if(cri != 0):
305 msg = "%se.crregs[%d] = 0x%x\n"
306 sout.write( msg % (lindent, i, cri))
307 # XER
308 if(self.so != 0):
309 sout.write("%se.so = 0x%x\n" % (lindent, self.so))
310 if(self.ov != 0):
311 sout.write("%se.ov = 0x%x\n" % (lindent, self.ov))
312 if(self.ca != 0):
313 sout.write("%se.ca = 0x%x\n" % (lindent, self.ca))
314 if self.xer_other != 0:
315 sout.write("%se.xer_other = 0x%x\n" % (lindent, self.xer_other))
316
317 # FPSCR
318 if self.fpscr != 0:
319 sout.write("%se.fpscr = 0x%x\n" % (lindent, self.fpscr))
320
321 # SPRs
322 for k, v in self.sprs.nonzero().items():
323 sout.write("%se.sprs[%r] = 0x%x\n" % (lindent, k.name, v))
324
325 # MSR
326 if self.msr != 0:
327 sout.write("%se.msr = 0x%x\n" % (lindent, self.msr))
328
329 # memory
330 if getattr(self, "mem", None) is not None:
331 sout.write(lindent + "e.mem = {\n")
332 lindent2 = lindent + single_indent
333 for k, v in self.mem.items():
334 vh = (v >> 48) & 0xFFFF
335 vhm = (v >> 32) & 0xFFFF
336 vlm = (v >> 16) & 0xFFFF
337 vl = v & 0xFFFF
338 sout.write("%s0x%04X: 0x%04X_%04X_%04X_%04X,\n" % (
339 lindent2, k, vh, vhm, vlm, vl))
340 sout.write(lindent + "}\n")
341
342 if sout != sys.stdout:
343 sout.close()
344
345
346 def _get_regs(regs, asint=lambda v: v.asint()):
347 retval = []
348 while True:
349 try:
350 retval.append(asint(regs[len(retval)]))
351 except (IndexError, KeyError):
352 break
353 return retval
354
355
356 class SimState(State):
357 """SimState: Obtains registers and memory from an ISACaller object.
358 Note that yields are "faked" to maintain consistency and compatibility
359 within the API.
360 """
361 def __init__(self, sim):
362 self.sim = sim
363
364 def get_fpregs(self):
365 if False:
366 yield
367 self.fpregs = _get_regs(self.sim.fpr)
368 log("class sim fp regs", list(map(hex, self.fpregs)))
369
370 def get_fpscr(self):
371 if False:
372 yield
373 self.fpscr = int(self.sim.fpscr)
374 log("class sim fpscr", hex(self.fpscr))
375
376 def get_msr(self):
377 if False:
378 yield
379 self.msr = int(self.sim.msr)
380 log("class sim msr", hex(self.msr))
381
382 def get_intregs(self):
383 if False:
384 yield
385 self.intregs = _get_regs(self.sim.gpr)
386 log("class sim int regs", list(map(hex, self.intregs)))
387
388 def get_crregs(self):
389 if False:
390 yield
391 self.crregs = _get_regs(self.sim.crl, lambda v: v.get_range().value)
392 log("class sim cr regs", list(map(hex, self.crregs)))
393
394 def get_xregs(self):
395 if False:
396 yield
397 self.xregs = []
398 self.so = self.sim.spr['XER'][XER_bits['SO']].value
399 self.ov = self.sim.spr['XER'][XER_bits['OV']].value
400 self.ov32 = self.sim.spr['XER'][XER_bits['OV32']].value
401 self.ca = self.sim.spr['XER'][XER_bits['CA']].value
402 self.ca32 = self.sim.spr['XER'][XER_bits['CA32']].value
403 self.ov = self.ov | (self.ov32 << 1)
404 self.ca = self.ca | (self.ca32 << 1)
405 xer_other = SelectableInt(self.sim.spr['XER'])
406 for i in 'SO', 'OV', 'OV32', 'CA', 'CA32':
407 xer_other[XER_bits[i]] = 0
408 self.xer_other = int(xer_other)
409 self.xregs.extend((self.so, self.ov, self.ca))
410 log("class sim xregs", list(map(hex, self.xregs)))
411
412 def get_sprs(self):
413 if False:
414 yield
415 self.sprs = StateSPRs()
416 for spr in self.sprs:
417 # hacky workaround to workaround luke's hack in caller.py that
418 # aliases HSRR[01] to SRR[01] -- we temporarily clear SRR[01] while
419 # trying to read HSRR[01]
420 clear_srr = spr == SPRfull.HSRR0 or spr == SPRfull.HSRR1
421 if clear_srr:
422 old_srr0 = self.sim.spr['SRR0']
423 old_srr1 = self.sim.spr['SRR1']
424 self.sim.spr['SRR0'] = 0
425 self.sim.spr['SRR1'] = 0
426
427 self.sprs[spr] = self.sim.spr[spr.name] # setitem converts to int
428
429 if clear_srr:
430 self.sim.spr['SRR0'] = old_srr0
431 self.sim.spr['SRR1'] = old_srr1
432
433 def get_pc(self):
434 if False:
435 yield
436 self.pcl = []
437 self.pc = self.sim.pc.CIA.value
438 self.pcl.append(self.pc)
439 log("class sim pc", hex(self.pc))
440
441 def get_mem(self):
442 if False:
443 yield
444 mem = self.sim.mem
445 if isinstance(mem, RADIX):
446 mem = mem.mem
447 self.mem = mem.make_sim_state_dict()
448
449
450 class ExpectedState(State):
451 """ExpectedState: A user defined state set manually.
452 No methods, just pass into what expected values you want to test
453 with against other states.
454
455 see openpower/test/shift_rot/shift_rot_cases2.py for examples
456 """
457 def __init__(self, int_regs=None, pc=0, crregs=None,
458 so=0, ov=0, ca=0, fp_regs=None, fpscr=0, sprs=None,
459 msr=DEFAULT_MSR, xer_other=0, mem=None):
460 if fp_regs is None:
461 fp_regs = 32
462 if isinstance(fp_regs, int):
463 fp_regs = [0] * fp_regs
464 else:
465 assert isinstance(fp_regs, list), \
466 "fp_regs must be int | list[int] | None"
467 # don't use deepcopy, it's slow
468 fp_regs = fp_regs.copy()
469 self.fpregs = fp_regs
470 self.fpscr = fpscr
471 if int_regs is None:
472 int_regs = 32
473 if isinstance(int_regs, int):
474 int_regs = [0] * int_regs
475 else:
476 assert isinstance(int_regs, list), \
477 "int_regs must be int | list[int] | None"
478 # don't use deepcopy, it's slow
479 int_regs = int_regs.copy()
480 self.intregs = int_regs
481 self.pc = pc
482 if crregs is None:
483 crregs = 8
484 if isinstance(crregs, int):
485 crregs = [0] * crregs
486 else:
487 assert isinstance(crregs, list), \
488 "crregs must be int | list[int] | None"
489 # don't use deepcopy, it's slow
490 crregs = crregs.copy()
491 self.crregs = crregs
492 self.so = so
493 self.ov = ov
494 self.ca = ca
495 self.xer_other = xer_other
496 self.sprs = StateSPRs(sprs)
497 self.msr = msr
498 if mem is not None:
499 mem = dict(mem)
500 self.mem = mem
501
502 def get_fpregs(self):
503 if False: yield
504 def get_fpscr(self):
505 if False: yield
506 def get_intregs(self):
507 if False: yield
508 def get_crregs(self):
509 if False: yield
510 def get_xregs(self):
511 if False: yield
512 def get_pc(self):
513 if False: yield
514
515 def get_msr(self):
516 if False:
517 yield
518
519 def get_sprs(self):
520 if False:
521 yield
522
523 def get_mem(self):
524 if False: yield
525
526
527 global state_factory
528 state_factory = {'sim': SimState, 'expected': ExpectedState}
529
530
531 def state_add(name, kls):
532 log("state_add", name, kls)
533 state_factory[name] = kls
534
535
536 def TestState(state_type, to_test, dut, code=0):
537 """TestState: Factory that returns a TestState object loaded with
538 registers and memory that can then be compared.
539
540 state_type: Type of state to create from global state_factory dictionary
541 to_test: The Power ISA object to test
542 dut: The unittest object
543 code: Actual machine code of what is being tested
544
545 The state_type can be added to the factory types using the state_add
546 function in this module.
547 """
548 state_class = state_factory[state_type]
549 state = state_class(to_test)
550 state.to_test = to_test
551 state.dut = dut
552 state.state_type = state_type
553 state.code = code
554 yield from state.get_state()
555 return state
556
557
558 def teststate_check_regs(dut, states, test, code):
559 """teststate_check_regs: compares a set of Power ISA objects
560 to check if they have the same "state" (registers only, at the moment)
561 """
562 slist = []
563 # create one TestState per "thing"
564 for stype, totest in states.items():
565 state = yield from TestState(stype, totest, dut, code)
566 slist.append(state)
567 # compare each "thing" against the next "thing" in the list.
568 # (no need to do an O(N^2) comparison here, they *all* have to be the same
569 for i in range(len(slist)-1):
570 state, against = slist[i], slist[i+1]
571 state.compare(against)
572
573
574 def teststate_check_mem(dut, states, test, code):
575 """teststate_check_mem: compares a set of Power ISA objects
576 to check if they have the same "state" (memory)
577 """
578 slist = []
579 # create one TestState per "thing"
580 for stype, totest in states.items():
581 state = yield from TestState(stype, totest, dut, code)
582 slist.append(state)
583 # compare each "thing" against the next "thing" in the list.
584 # (no need to do an O(N^2) comparison here, they *all* have to be the same
585 for i in range(len(slist)-1):
586 state, against = slist[i], slist[i+1]
587 state.compare_mem(against)