add FPSCR to Test API (ExpectedState, SimState). untested
[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 self.dut.assertEqual(self.fpscr, s2.fpscr,
136 "fpscr mismatch (%s != %s) %s" %
137 (self.state_type, s2.state_type, repr(self.code)))
138
139 def compare_mem(self, s2):
140 # copy dics to preserve state mem then pad empty locs since
141 # different Power ISA objects may differ how theystore memory
142 s1mem, s2mem = self.mem.copy(), s2.mem.copy()
143 for i in set(self.mem).difference(set(s2.mem)):
144 s2mem[i] = 0
145 for i in set(s2.mem).difference(set(self.mem)):
146 s1mem[i] = 0
147 for i in s1mem:
148 self.dut.assertEqual(s1mem[i], s2mem[i],
149 "mem mismatch location %d %s" % (i, self.code))
150
151 def dump_state_tofile(self, testname=None, testfile=None):
152 """dump_state_tofile: Takes a passed in teststate object along
153 with a test name and generates a code file located at
154 /tmp/testfile/testname to set an expected state object
155 """
156 lindent = ' '*8 # indent for code
157 # create the path
158 if testname is not None:
159 path = "/tmp/expected/"
160 if testfile is not None:
161 path += testfile + '/'
162 os.makedirs(path, exist_ok=True)
163 sout = open("%s%s.py" % (path, testname), "a+")
164 else:
165 sout = sys.stdout
166
167 # pc and intregs
168 sout.write("%se = ExpectedState(pc=%d)\n" % (lindent, self.pc))
169 for i, reg in enumerate(self.intregs):
170 if(reg != 0):
171 msg = "%se.intregs[%d] = 0x%x\n"
172 sout.write( msg % (lindent, i, reg))
173 for i, reg in enumerate(self.fpregs):
174 if reg != 0:
175 msg = "%se.fpregs[%d] = 0x%x\n"
176 sout.write(msg % (lindent, i, reg))
177 # CR fields
178 for i in range(8):
179 cri = self.crregs[i]
180 if(cri != 0):
181 msg = "%se.crregs[%d] = 0x%x\n"
182 sout.write( msg % (lindent, i, cri))
183 # XER
184 if(self.so != 0):
185 sout.write("%se.so = 0x%x\n" % (lindent, self.so))
186 if(self.ov != 0):
187 sout.write("%se.ov = 0x%x\n" % (lindent, self.ov))
188 if(self.ca != 0):
189 sout.write("%se.ca = 0x%x\n" % (lindent, self.ca))
190
191 if sout != sys.stdout:
192 sout.close()
193
194
195 def _get_regs(regs, asint=lambda v: v.asint()):
196 retval = []
197 while True:
198 try:
199 retval.append(asint(regs[len(retval)]))
200 except (IndexError, KeyError):
201 break
202 return retval
203
204
205 class SimState(State):
206 """SimState: Obtains registers and memory from an ISACaller object.
207 Note that yields are "faked" to maintain consistency and compatibility
208 within the API.
209 """
210 def __init__(self, sim):
211 self.sim = sim
212
213 def get_fpregs(self):
214 if False:
215 yield
216 self.fpregs = _get_regs(self.sim.fpr)
217 log("class sim fp regs", list(map(hex, self.fpregs)))
218
219 def get_fpscr(self):
220 if False:
221 yield
222 self.fpscr = self.sim.fpscr.value
223 log("class sim fpscr", hex(self.fpscr))
224
225 def get_intregs(self):
226 if False:
227 yield
228 self.intregs = _get_regs(self.sim.gpr)
229 log("class sim int regs", list(map(hex, self.intregs)))
230
231 def get_crregs(self):
232 if False:
233 yield
234 self.crregs = _get_regs(self.sim.crl, lambda v: v.get_range().value)
235 log("class sim cr regs", list(map(hex, self.crregs)))
236
237 def get_xregs(self):
238 if False:
239 yield
240 self.xregs = []
241 self.so = self.sim.spr['XER'][XER_bits['SO']].value
242 self.ov = self.sim.spr['XER'][XER_bits['OV']].value
243 self.ov32 = self.sim.spr['XER'][XER_bits['OV32']].value
244 self.ca = self.sim.spr['XER'][XER_bits['CA']].value
245 self.ca32 = self.sim.spr['XER'][XER_bits['CA32']].value
246 self.ov = self.ov | (self.ov32 << 1)
247 self.ca = self.ca | (self.ca32 << 1)
248 self.xregs.extend((self.so, self.ov, self.ca))
249 log("class sim xregs", list(map(hex, self.xregs)))
250
251 def get_pc(self):
252 if False:
253 yield
254 self.pcl = []
255 self.pc = self.sim.pc.CIA.value
256 self.pcl.append(self.pc)
257 log("class sim pc", hex(self.pc))
258
259 def get_mem(self):
260 if False:
261 yield
262 mem = self.sim.mem
263 if isinstance(mem, RADIX):
264 mem = mem.mem
265 keys = list(mem.mem.keys())
266 self.mem = {}
267 # from each address in the underlying mem-simulated dictionary
268 # issue a 64-bit LD (with no byte-swapping)
269 for k in keys:
270 data = mem.ld(k*8, 8, False)
271 self.mem[k*8] = data
272
273
274 class ExpectedState(State):
275 """ExpectedState: A user defined state set manually.
276 No methods, just pass into what expected values you want to test
277 with against other states.
278
279 see openpower/test/shift_rot/shift_rot_cases2.py for examples
280 """
281 def __init__(self, int_regs=None, pc=0, crregs=None,
282 so=0, ov=0, ca=0, fp_regs=None, fpscr=0):
283 if fp_regs is None:
284 fp_regs = 32
285 if isinstance(fp_regs, int):
286 fp_regs = [0] * fp_regs
287 self.fpregs = deepcopy(fp_regs)
288 self.fpscr = fpscr
289 if int_regs is None:
290 int_regs = 32
291 if isinstance(int_regs, int):
292 int_regs = [0] * int_regs
293 self.intregs = deepcopy(int_regs)
294 self.pc = pc
295 if crregs is None:
296 crregs = 8
297 if isinstance(crregs, int):
298 crregs = [0] * crregs
299 self.crregs = deepcopy(crregs)
300 self.so = so
301 self.ov = ov
302 self.ca = ca
303
304 def get_fpregs(self):
305 if False: yield
306 def get_fpscr(self):
307 if False: yield
308 def get_intregs(self):
309 if False: yield
310 def get_crregs(self):
311 if False: yield
312 def get_xregs(self):
313 if False: yield
314 def get_pc(self):
315 if False: yield
316 def get_mem(self):
317 if False: yield
318
319
320 global state_factory
321 state_factory = {'sim': SimState, 'expected': ExpectedState}
322
323
324 def state_add(name, kls):
325 log("state_add", name, kls)
326 state_factory[name] = kls
327
328
329 def TestState(state_type, to_test, dut, code=0):
330 """TestState: Factory that returns a TestState object loaded with
331 registers and memory that can then be compared.
332
333 state_type: Type of state to create from global state_factory dictionary
334 to_test: The Power ISA object to test
335 dut: The unittest object
336 code: Actual machine code of what is being tested
337
338 The state_type can be added to the factory types using the state_add
339 function in this module.
340 """
341 state_class = state_factory[state_type]
342 state = state_class(to_test)
343 state.to_test = to_test
344 state.dut = dut
345 state.state_type = state_type
346 state.code = code
347 yield from state.get_state()
348 return state
349
350
351 def teststate_check_regs(dut, states, test, code):
352 """teststate_check_regs: compares a set of Power ISA objects
353 to check if they have the same "state" (registers only, at the moment)
354 """
355 slist = []
356 # create one TestState per "thing"
357 for stype, totest in states.items():
358 state = yield from TestState(stype, totest, dut, code)
359 slist.append(state)
360 # compare each "thing" against the next "thing" in the list.
361 # (no need to do an O(N^2) comparison here, they *all* have to be the same
362 for i in range(len(slist)-1):
363 state, against = slist[i], slist[i+1]
364 state.compare(against)
365
366
367 def teststate_check_mem(dut, states, test, code):
368 """teststate_check_mem: compares a set of Power ISA objects
369 to check if they have the same "state" (memory)
370 """
371 slist = []
372 # create one TestState per "thing"
373 for stype, totest in states.items():
374 state = yield from TestState(stype, totest, dut, code)
375 slist.append(state)
376 # compare each "thing" against the next "thing" in the list.
377 # (no need to do an O(N^2) comparison here, they *all* have to be the same
378 for i in range(len(slist)-1):
379 state, against = slist[i], slist[i+1]
380 state.compare_mem(against)