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