allow ignoring FPSCR in tests
[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
26 from openpower.decoder.isa.radixmmu import RADIX
27 from openpower.util import log
28 import os
29 import sys
30 from copy import deepcopy
31
32 global staterunner_factory
33 staterunner_factory = {}
34
35
36 def staterunner_add(name, kls):
37 log("staterunner_add", name, kls)
38 staterunner_factory[name] = kls
39
40
41 # TBD an Abstract Base Class
42 class StateRunner:
43 """StateRunner: an Abstract Base Class for preparing and running "State".
44 near-identical in concept to python unittest.TestCase
45 """
46 def __init__(self, name, kls):
47 staterunner_add(name, kls)
48 self.name = name
49
50 def setup_for_test(self):
51 if False: yield
52 def setup_during_test(self):
53 if False: yield
54 def prepare_for_test(self, test):
55 if False: yield
56 def run_test(self):
57 if False: yield
58 def end_test(self):
59 if False: yield
60 def cleanup(self):
61 if False: yield
62
63
64 class State:
65 """State: Base class for the "state" of the Power ISA object to be tested
66 including methods to compare various registers and memory between
67 them.
68
69 All methods implemented must be generators.
70
71 GPRs and CRs - stored as lists
72 XERs/PC - simple members
73 memory - stored as a dictionary {location: data}
74 """
75 def get_state(self):
76 yield from self.get_fpscr()
77 yield from self.get_fpregs()
78 yield from self.get_intregs()
79 yield from self.get_crregs()
80 yield from self.get_xregs()
81 yield from self.get_pc()
82 yield from self.get_mem()
83
84 def compare(self, s2):
85 # Compare FP registers
86 for i, (fpreg, fpreg2) in enumerate(
87 zip(self.fpregs, s2.fpregs)):
88 log("asserting...reg", i, fpreg, fpreg2)
89 log("code, frepr(code)", self.code, repr(self.code))
90 self.dut.assertEqual(fpreg, fpreg2,
91 "fp reg %d (%s) not equal (%s) %s. "
92 " got %x expected %x at pc %x %x\n" %
93 (i, self.state_type, s2.state_type, repr(self.code),
94 fpreg, fpreg2, self.pc, s2.pc))
95
96 # Compare int registers
97 for i, (intreg, intreg2) in enumerate(
98 zip(self.intregs, s2.intregs)):
99 log("asserting...reg", i, intreg, intreg2)
100 log("code, frepr(code)", self.code, repr(self.code))
101 self.dut.assertEqual(intreg, intreg2,
102 "int reg %d (%s) not equal (%s) %s. "
103 " got %x expected %x at pc %x %x\n" %
104 (i, self.state_type, s2.state_type, repr(self.code),
105 intreg, intreg2, self.pc, s2.pc))
106
107 # CR registers
108 for i, (crreg, crreg2) in enumerate(
109 zip(self.crregs, s2.crregs)):
110 log("asserting...cr", i, crreg, crreg2)
111
112 for i, (crreg, crreg2) in enumerate(
113 zip(self.crregs, s2.crregs)):
114 self.dut.assertEqual(crreg, crreg2,
115 "cr reg %d (%s) not equal (%s) %s. got %x expected %x" %
116 (i, self.state_type, s2.state_type, repr(self.code),
117 crreg, crreg2))
118
119 # XER
120 if self.so is not None and s2.so is not None:
121 self.dut.assertEqual(self.so, s2.so, "so mismatch (%s != %s) %s" %
122 (self.state_type, s2.state_type, repr(self.code)))
123 if self.ov is not None and s2.ov is not None:
124 self.dut.assertEqual(self.ov, s2.ov, "ov mismatch (%s != %s) %s" %
125 (self.state_type, s2.state_type, repr(self.code)))
126 if self.ca is not None and s2.ca is not None:
127 self.dut.assertEqual(self.ca, s2.ca, "ca mismatch (%s != %s) %s" %
128 (self.state_type, s2.state_type, repr(self.code)))
129
130 # pc
131 self.dut.assertEqual(self.pc, s2.pc, "pc mismatch (%s != %s) %s" %
132 (self.state_type, s2.state_type, repr(self.code)))
133
134 # fpscr
135 if self.fpscr is not None and s2.fpscr is not None:
136 self.dut.assertEqual(
137 self.fpscr, s2.fpscr, "fpscr mismatch (%s != %s) %s" %
138 (self.state_type, s2.state_type, repr(self.code)))
139
140 def compare_mem(self, s2):
141 # copy dics to preserve state mem then pad empty locs since
142 # different Power ISA objects may differ how theystore memory
143 s1mem, s2mem = self.mem.copy(), s2.mem.copy()
144 for i in set(self.mem).difference(set(s2.mem)):
145 s2mem[i] = 0
146 for i in set(s2.mem).difference(set(self.mem)):
147 s1mem[i] = 0
148 for i in s1mem:
149 self.dut.assertEqual(s1mem[i], s2mem[i],
150 "mem mismatch location %d %s" % (i, self.code))
151
152 def dump_state_tofile(self, testname=None, testfile=None):
153 """dump_state_tofile: Takes a passed in teststate object along
154 with a test name and generates a code file located at
155 /tmp/testfile/testname to set an expected state object
156 """
157 lindent = ' '*8 # indent for code
158 # create the path
159 if testname is not None:
160 path = "/tmp/expected/"
161 if testfile is not None:
162 path += testfile + '/'
163 os.makedirs(path, exist_ok=True)
164 sout = open("%s%s.py" % (path, testname), "a+")
165 else:
166 sout = sys.stdout
167
168 # pc and intregs
169 sout.write("%se = ExpectedState(pc=%d)\n" % (lindent, self.pc))
170 for i, reg in enumerate(self.intregs):
171 if(reg != 0):
172 msg = "%se.intregs[%d] = 0x%x\n"
173 sout.write( msg % (lindent, i, reg))
174 for i, reg in enumerate(self.fpregs):
175 if reg != 0:
176 msg = "%se.fpregs[%d] = 0x%x\n"
177 sout.write(msg % (lindent, i, reg))
178 # CR fields
179 for i in range(8):
180 cri = self.crregs[i]
181 if(cri != 0):
182 msg = "%se.crregs[%d] = 0x%x\n"
183 sout.write( msg % (lindent, i, cri))
184 # XER
185 if(self.so != 0):
186 sout.write("%se.so = 0x%x\n" % (lindent, self.so))
187 if(self.ov != 0):
188 sout.write("%se.ov = 0x%x\n" % (lindent, self.ov))
189 if(self.ca != 0):
190 sout.write("%se.ca = 0x%x\n" % (lindent, self.ca))
191
192 if sout != sys.stdout:
193 sout.close()
194
195
196 def _get_regs(regs, asint=lambda v: v.asint()):
197 retval = []
198 while True:
199 try:
200 retval.append(asint(regs[len(retval)]))
201 except (IndexError, KeyError):
202 break
203 return retval
204
205
206 class SimState(State):
207 """SimState: Obtains registers and memory from an ISACaller object.
208 Note that yields are "faked" to maintain consistency and compatibility
209 within the API.
210 """
211 def __init__(self, sim):
212 self.sim = sim
213
214 def get_fpregs(self):
215 if False:
216 yield
217 self.fpregs = _get_regs(self.sim.fpr)
218 log("class sim fp regs", list(map(hex, self.fpregs)))
219
220 def get_fpscr(self):
221 if False:
222 yield
223 self.fpscr = self.sim.fpscr.value
224 log("class sim fpscr", hex(self.fpscr))
225
226 def get_intregs(self):
227 if False:
228 yield
229 self.intregs = _get_regs(self.sim.gpr)
230 log("class sim int regs", list(map(hex, self.intregs)))
231
232 def get_crregs(self):
233 if False:
234 yield
235 self.crregs = _get_regs(self.sim.crl, lambda v: v.get_range().value)
236 log("class sim cr regs", list(map(hex, self.crregs)))
237
238 def get_xregs(self):
239 if False:
240 yield
241 self.xregs = []
242 self.so = self.sim.spr['XER'][XER_bits['SO']].value
243 self.ov = self.sim.spr['XER'][XER_bits['OV']].value
244 self.ov32 = self.sim.spr['XER'][XER_bits['OV32']].value
245 self.ca = self.sim.spr['XER'][XER_bits['CA']].value
246 self.ca32 = self.sim.spr['XER'][XER_bits['CA32']].value
247 self.ov = self.ov | (self.ov32 << 1)
248 self.ca = self.ca | (self.ca32 << 1)
249 self.xregs.extend((self.so, self.ov, self.ca))
250 log("class sim xregs", list(map(hex, self.xregs)))
251
252 def get_pc(self):
253 if False:
254 yield
255 self.pcl = []
256 self.pc = self.sim.pc.CIA.value
257 self.pcl.append(self.pc)
258 log("class sim pc", hex(self.pc))
259
260 def get_mem(self):
261 if False:
262 yield
263 mem = self.sim.mem
264 if isinstance(mem, RADIX):
265 mem = mem.mem
266 keys = list(mem.mem.keys())
267 self.mem = {}
268 # from each address in the underlying mem-simulated dictionary
269 # issue a 64-bit LD (with no byte-swapping)
270 for k in keys:
271 data = mem.ld(k*8, 8, False)
272 self.mem[k*8] = data
273
274
275 class ExpectedState(State):
276 """ExpectedState: A user defined state set manually.
277 No methods, just pass into what expected values you want to test
278 with against other states.
279
280 see openpower/test/shift_rot/shift_rot_cases2.py for examples
281 """
282 def __init__(self, int_regs=None, pc=0, crregs=None,
283 so=0, ov=0, ca=0, fp_regs=None, fpscr=0):
284 if fp_regs is None:
285 fp_regs = 32
286 if isinstance(fp_regs, int):
287 fp_regs = [0] * fp_regs
288 self.fpregs = deepcopy(fp_regs)
289 self.fpscr = fpscr
290 if int_regs is None:
291 int_regs = 32
292 if isinstance(int_regs, int):
293 int_regs = [0] * int_regs
294 self.intregs = deepcopy(int_regs)
295 self.pc = pc
296 if crregs is None:
297 crregs = 8
298 if isinstance(crregs, int):
299 crregs = [0] * crregs
300 self.crregs = deepcopy(crregs)
301 self.so = so
302 self.ov = ov
303 self.ca = ca
304
305 def get_fpregs(self):
306 if False: yield
307 def get_fpscr(self):
308 if False: yield
309 def get_intregs(self):
310 if False: yield
311 def get_crregs(self):
312 if False: yield
313 def get_xregs(self):
314 if False: yield
315 def get_pc(self):
316 if False: yield
317 def get_mem(self):
318 if False: yield
319
320
321 global state_factory
322 state_factory = {'sim': SimState, 'expected': ExpectedState}
323
324
325 def state_add(name, kls):
326 log("state_add", name, kls)
327 state_factory[name] = kls
328
329
330 def TestState(state_type, to_test, dut, code=0):
331 """TestState: Factory that returns a TestState object loaded with
332 registers and memory that can then be compared.
333
334 state_type: Type of state to create from global state_factory dictionary
335 to_test: The Power ISA object to test
336 dut: The unittest object
337 code: Actual machine code of what is being tested
338
339 The state_type can be added to the factory types using the state_add
340 function in this module.
341 """
342 state_class = state_factory[state_type]
343 state = state_class(to_test)
344 state.to_test = to_test
345 state.dut = dut
346 state.state_type = state_type
347 state.code = code
348 yield from state.get_state()
349 return state
350
351
352 def teststate_check_regs(dut, states, test, code):
353 """teststate_check_regs: compares a set of Power ISA objects
354 to check if they have the same "state" (registers only, at the moment)
355 """
356 slist = []
357 # create one TestState per "thing"
358 for stype, totest in states.items():
359 state = yield from TestState(stype, totest, dut, code)
360 slist.append(state)
361 # compare each "thing" against the next "thing" in the list.
362 # (no need to do an O(N^2) comparison here, they *all* have to be the same
363 for i in range(len(slist)-1):
364 state, against = slist[i], slist[i+1]
365 state.compare(against)
366
367
368 def teststate_check_mem(dut, states, test, code):
369 """teststate_check_mem: compares a set of Power ISA objects
370 to check if they have the same "state" (memory)
371 """
372 slist = []
373 # create one TestState per "thing"
374 for stype, totest in states.items():
375 state = yield from TestState(stype, totest, dut, code)
376 slist.append(state)
377 # compare each "thing" against the next "thing" in the list.
378 # (no need to do an O(N^2) comparison here, they *all* have to be the same
379 for i in range(len(slist)-1):
380 state, against = slist[i], slist[i+1]
381 state.compare_mem(against)