deepcopy is really slow and unnecessary here
[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 log("asserting...reg", i, intreg, intreg2)
174 log("code, frepr(code)", self.code, repr(self.code))
175 self.dut.assertEqual(intreg, intreg2,
176 "int reg %d (%s) not equal (%s) %s. "
177 " got %x expected %x at pc %x %x\n" %
178 (i, self.state_type, s2.state_type, repr(self.code),
179 intreg, intreg2, self.pc, s2.pc))
180
181 # CR registers
182 for i, (crreg, crreg2) in enumerate(
183 zip(self.crregs, s2.crregs)):
184 log("asserting...cr", i, crreg, crreg2)
185
186 for i, (crreg, crreg2) in enumerate(
187 zip(self.crregs, s2.crregs)):
188 self.dut.assertEqual(crreg, crreg2,
189 "cr reg %d (%s) not equal (%s) %s. got %x expected %x" %
190 (i, self.state_type, s2.state_type, repr(self.code),
191 crreg, crreg2))
192
193 # XER
194 if self.so is not None and s2.so is not None:
195 self.dut.assertEqual(self.so, s2.so, "so mismatch (%s != %s) %s" %
196 (self.state_type, s2.state_type, repr(self.code)))
197 if self.ov is not None and s2.ov is not None:
198 self.dut.assertEqual(self.ov, s2.ov, "ov mismatch (%s != %s) %s" %
199 (self.state_type, s2.state_type, repr(self.code)))
200 if self.ca is not None and s2.ca is not None:
201 self.dut.assertEqual(self.ca, s2.ca, "ca mismatch (%s != %s) %s" %
202 (self.state_type, s2.state_type, repr(self.code)))
203 if self.xer_other is not None and s2.xer_other is not None:
204 self.dut.assertEqual(
205 hex(self.xer_other), hex(s2.xer_other),
206 "xer_other mismatch (%s != %s) %s" %
207 (self.state_type, s2.state_type, repr(self.code)))
208
209 # pc
210 self.dut.assertEqual(self.pc, s2.pc, "pc mismatch (%s != %s) %s" %
211 (self.state_type, s2.state_type, repr(self.code)))
212
213 # fpscr
214 if self.fpscr is not None and s2.fpscr is not None:
215 if self.fpscr != s2.fpscr:
216 # use FPSCRState.fsi since that's much easier to read than a
217 # decimal integer and since unittest has fancy dict diffs.
218
219 # use auto_update_summary_bits=False since HDL might
220 # mis-compute those summary bits and we want to show the
221 # actual bits, not the corrected bits
222 fpscr1 = FPSCRState(self.fpscr, auto_update_summary_bits=False)
223 fpscr2 = FPSCRState(s2.fpscr, auto_update_summary_bits=False)
224 # FieldSelectableInt.__repr__ is too long
225 fpscr1 = {k: hex(int(v)) for k, v in fpscr1.fsi.items()}
226 fpscr2 = {k: hex(int(v)) for k, v in fpscr2.fsi.items()}
227 old_max_diff = self.dut.maxDiff
228 self.dut.maxDiff = None # show full diff
229 try:
230 self.dut.assertEqual(
231 fpscr1, fpscr2, "fpscr mismatch (%s != %s) %s\n" %
232 (self.state_type, s2.state_type, repr(self.code)))
233 finally:
234 self.dut.maxDiff = old_max_diff
235
236 for spr in self.sprs:
237 spr1 = self.sprs[spr]
238 spr2 = s2.sprs[spr]
239
240 if spr1 == spr2:
241 continue
242
243 if spr1 is not None and spr2 is not None:
244 # if not explicitly ignored
245
246 self.dut.fail(
247 f"{spr1:#x} != {spr2:#x}: {spr} mismatch "
248 f"({self.state_type} != {s2.state_type}) {self.code!r}\n")
249
250 if self.msr is not None and s2.msr is not None:
251 self.dut.assertEqual(
252 hex(self.msr), hex(s2.msr), "msr mismatch (%s != %s) %s" %
253 (self.state_type, s2.state_type, repr(self.code)))
254
255 def compare_mem(self, s2):
256 # copy dics to preserve state mem then pad empty locs since
257 # different Power ISA objects may differ how theystore memory
258 s1mem, s2mem = self.mem.copy(), s2.mem.copy()
259 for i in set(self.mem).difference(set(s2.mem)):
260 s2mem[i] = 0
261 for i in set(s2.mem).difference(set(self.mem)):
262 s1mem[i] = 0
263 for i in s1mem:
264 self.dut.assertEqual(s1mem[i], s2mem[i],
265 "mem mismatch location %d %s" % (i, self.code))
266
267 def dump_state_tofile(self, testname=None, testfile=None):
268 """dump_state_tofile: Takes a passed in teststate object along
269 with a test name and generates a code file located at
270 /tmp/testfile/testname to set an expected state object
271 """
272 lindent = ' '*8 # indent for code
273 # create the path
274 if testname is not None:
275 path = "/tmp/expected/"
276 if testfile is not None:
277 path += testfile + '/'
278 os.makedirs(path, exist_ok=True)
279 sout = open("%s%s.py" % (path, testname), "a+")
280 else:
281 sout = sys.stdout
282
283 # pc and intregs
284 sout.write("%se = ExpectedState(pc=%d)\n" % (lindent, self.pc))
285 for i, reg in enumerate(self.intregs):
286 if(reg != 0):
287 msg = "%se.intregs[%d] = 0x%x\n"
288 sout.write( msg % (lindent, i, reg))
289 for i, reg in enumerate(self.fpregs):
290 if reg != 0:
291 msg = "%se.fpregs[%d] = 0x%x\n"
292 sout.write(msg % (lindent, i, reg))
293 # CR fields
294 for i in range(8):
295 cri = self.crregs[i]
296 if(cri != 0):
297 msg = "%se.crregs[%d] = 0x%x\n"
298 sout.write( msg % (lindent, i, cri))
299 # XER
300 if(self.so != 0):
301 sout.write("%se.so = 0x%x\n" % (lindent, self.so))
302 if(self.ov != 0):
303 sout.write("%se.ov = 0x%x\n" % (lindent, self.ov))
304 if(self.ca != 0):
305 sout.write("%se.ca = 0x%x\n" % (lindent, self.ca))
306 if self.xer_other != 0:
307 sout.write("%se.xer_other = 0x%x\n" % (lindent, self.xer_other))
308
309 # FPSCR
310 if self.fpscr != 0:
311 sout.write(f"{lindent}e.fpscr = {self.fpscr:#x}\n")
312
313 # SPRs
314 for k, v in self.sprs.nonzero().items():
315 sout.write(f"{lindent}e.sprs[{k.name!r}] = {v:#x}\n")
316
317 # MSR
318 if self.msr != 0:
319 sout.write(f"{lindent}e.msr = {self.msr:#x}\n")
320
321 if sout != sys.stdout:
322 sout.close()
323
324
325 def _get_regs(regs, asint=lambda v: v.asint()):
326 retval = []
327 while True:
328 try:
329 retval.append(asint(regs[len(retval)]))
330 except (IndexError, KeyError):
331 break
332 return retval
333
334
335 class SimState(State):
336 """SimState: Obtains registers and memory from an ISACaller object.
337 Note that yields are "faked" to maintain consistency and compatibility
338 within the API.
339 """
340 def __init__(self, sim):
341 self.sim = sim
342
343 def get_fpregs(self):
344 if False:
345 yield
346 self.fpregs = _get_regs(self.sim.fpr)
347 log("class sim fp regs", list(map(hex, self.fpregs)))
348
349 def get_fpscr(self):
350 if False:
351 yield
352 self.fpscr = int(self.sim.fpscr)
353 log("class sim fpscr", hex(self.fpscr))
354
355 def get_msr(self):
356 if False:
357 yield
358 self.msr = int(self.sim.msr)
359 log("class sim msr", hex(self.msr))
360
361 def get_intregs(self):
362 if False:
363 yield
364 self.intregs = _get_regs(self.sim.gpr)
365 log("class sim int regs", list(map(hex, self.intregs)))
366
367 def get_crregs(self):
368 if False:
369 yield
370 self.crregs = _get_regs(self.sim.crl, lambda v: v.get_range().value)
371 log("class sim cr regs", list(map(hex, self.crregs)))
372
373 def get_xregs(self):
374 if False:
375 yield
376 self.xregs = []
377 self.so = self.sim.spr['XER'][XER_bits['SO']].value
378 self.ov = self.sim.spr['XER'][XER_bits['OV']].value
379 self.ov32 = self.sim.spr['XER'][XER_bits['OV32']].value
380 self.ca = self.sim.spr['XER'][XER_bits['CA']].value
381 self.ca32 = self.sim.spr['XER'][XER_bits['CA32']].value
382 self.ov = self.ov | (self.ov32 << 1)
383 self.ca = self.ca | (self.ca32 << 1)
384 xer_other = SelectableInt(self.sim.spr['XER'])
385 for i in 'SO', 'OV', 'OV32', 'CA', 'CA32':
386 xer_other[XER_bits[i]] = 0
387 self.xer_other = int(xer_other)
388 self.xregs.extend((self.so, self.ov, self.ca))
389 log("class sim xregs", list(map(hex, self.xregs)))
390
391 def get_sprs(self):
392 if False:
393 yield
394 self.sprs = StateSPRs()
395 for spr in self.sprs:
396 # hacky workaround to workaround luke's hack in caller.py that
397 # aliases HSRR[01] to SRR[01] -- we temporarily clear SRR[01] while
398 # trying to read HSRR[01]
399 clear_srr = spr == SPRfull.HSRR0 or spr == SPRfull.HSRR1
400 if clear_srr:
401 old_srr0 = self.sim.spr['SRR0']
402 old_srr1 = self.sim.spr['SRR1']
403 self.sim.spr['SRR0'] = 0
404 self.sim.spr['SRR1'] = 0
405
406 self.sprs[spr] = self.sim.spr[spr.name] # setitem converts to int
407
408 if clear_srr:
409 self.sim.spr['SRR0'] = old_srr0
410 self.sim.spr['SRR1'] = old_srr1
411
412 def get_pc(self):
413 if False:
414 yield
415 self.pcl = []
416 self.pc = self.sim.pc.CIA.value
417 self.pcl.append(self.pc)
418 log("class sim pc", hex(self.pc))
419
420 def get_mem(self):
421 if False:
422 yield
423 mem = self.sim.mem
424 if isinstance(mem, RADIX):
425 mem = mem.mem
426 keys = list(mem.mem.keys())
427 self.mem = {}
428 # from each address in the underlying mem-simulated dictionary
429 # issue a 64-bit LD (with no byte-swapping)
430 for k in keys:
431 data = mem.ld(k*8, 8, False)
432 self.mem[k*8] = data
433
434
435 class ExpectedState(State):
436 """ExpectedState: A user defined state set manually.
437 No methods, just pass into what expected values you want to test
438 with against other states.
439
440 see openpower/test/shift_rot/shift_rot_cases2.py for examples
441 """
442 def __init__(self, int_regs=None, pc=0, crregs=None,
443 so=0, ov=0, ca=0, fp_regs=None, fpscr=0, sprs=None,
444 msr=DEFAULT_MSR, xer_other=0):
445 if fp_regs is None:
446 fp_regs = 32
447 if isinstance(fp_regs, int):
448 fp_regs = [0] * fp_regs
449 else:
450 assert isinstance(fp_regs, list), \
451 "fp_regs must be int | list[int] | None"
452 # don't use deepcopy, it's slow
453 fp_regs = fp_regs.copy()
454 self.fpregs = fp_regs
455 self.fpscr = fpscr
456 if int_regs is None:
457 int_regs = 32
458 if isinstance(int_regs, int):
459 int_regs = [0] * int_regs
460 else:
461 assert isinstance(int_regs, list), \
462 "int_regs must be int | list[int] | None"
463 # don't use deepcopy, it's slow
464 int_regs = int_regs.copy()
465 self.intregs = int_regs
466 self.pc = pc
467 if crregs is None:
468 crregs = 8
469 if isinstance(crregs, int):
470 crregs = [0] * crregs
471 else:
472 assert isinstance(crregs, list), \
473 "crregs must be int | list[int] | None"
474 # don't use deepcopy, it's slow
475 crregs = crregs.copy()
476 self.crregs = crregs
477 self.so = so
478 self.ov = ov
479 self.ca = ca
480 self.xer_other = xer_other
481 self.sprs = StateSPRs(sprs)
482 self.msr = msr
483
484 def get_fpregs(self):
485 if False: yield
486 def get_fpscr(self):
487 if False: yield
488 def get_intregs(self):
489 if False: yield
490 def get_crregs(self):
491 if False: yield
492 def get_xregs(self):
493 if False: yield
494 def get_pc(self):
495 if False: yield
496
497 def get_msr(self):
498 if False:
499 yield
500
501 def get_sprs(self):
502 if False:
503 yield
504
505 def get_mem(self):
506 if False: yield
507
508
509 global state_factory
510 state_factory = {'sim': SimState, 'expected': ExpectedState}
511
512
513 def state_add(name, kls):
514 log("state_add", name, kls)
515 state_factory[name] = kls
516
517
518 def TestState(state_type, to_test, dut, code=0):
519 """TestState: Factory that returns a TestState object loaded with
520 registers and memory that can then be compared.
521
522 state_type: Type of state to create from global state_factory dictionary
523 to_test: The Power ISA object to test
524 dut: The unittest object
525 code: Actual machine code of what is being tested
526
527 The state_type can be added to the factory types using the state_add
528 function in this module.
529 """
530 state_class = state_factory[state_type]
531 state = state_class(to_test)
532 state.to_test = to_test
533 state.dut = dut
534 state.state_type = state_type
535 state.code = code
536 yield from state.get_state()
537 return state
538
539
540 def teststate_check_regs(dut, states, test, code):
541 """teststate_check_regs: compares a set of Power ISA objects
542 to check if they have the same "state" (registers only, at the moment)
543 """
544 slist = []
545 # create one TestState per "thing"
546 for stype, totest in states.items():
547 state = yield from TestState(stype, totest, dut, code)
548 slist.append(state)
549 # compare each "thing" against the next "thing" in the list.
550 # (no need to do an O(N^2) comparison here, they *all* have to be the same
551 for i in range(len(slist)-1):
552 state, against = slist[i], slist[i+1]
553 state.compare(against)
554
555
556 def teststate_check_mem(dut, states, test, code):
557 """teststate_check_mem: compares a set of Power ISA objects
558 to check if they have the same "state" (memory)
559 """
560 slist = []
561 # create one TestState per "thing"
562 for stype, totest in states.items():
563 state = yield from TestState(stype, totest, dut, code)
564 slist.append(state)
565 # compare each "thing" against the next "thing" in the list.
566 # (no need to do an O(N^2) comparison here, they *all* have to be the same
567 for i in range(len(slist)-1):
568 state, against = slist[i], slist[i+1]
569 state.compare_mem(against)