4cb32a6b57475fc4bdb1e6f7bd1240a038dd6dcf
[openpower-isa.git] / src / openpower / decoder / isa / pypowersim.py
1 from nmigen import Module, Signal
2 from nmigen.sim import Simulator, Delay, Settle
3 import sys
4 import getopt
5 import struct
6 from openpower.decoder.power_decoder import create_pdecode
7 from openpower.decoder.power_decoder2 import (PowerDecode2)
8 from openpower.simulator.program import Program
9 from openpower.decoder.selectable_int import SelectableInt
10 from openpower.decoder.orderedset import OrderedSet
11 from openpower.decoder.isa.all import ISA
12 from openpower.util import log
13 from openpower.simulator.qemu import run_program
14
15
16 def read_data(fname, offset=0):
17 """reads binary data and returns a dictionary of address: contents,
18 each entry is 8 bytes: input file *must* contain a multiple of 8 bytes.
19 data to be considered *binary* (raw)
20 """
21 res = {}
22 with open(fname, "rb") as f:
23 while True:
24 b = f.read(8)
25 log (repr(b))
26 if not b:
27 return res
28 res[offset] = struct.unpack('<Q', b)[0] # unsigned long
29 offset += 8
30
31 def write_data(mem, fname, offset, sz):
32 """writes binary data to a file, each entry must be 8 bytes
33 """
34 with open(fname, "wb") as f:
35 for i in range(0, sz, 8):
36 addr = offset + i
37 val = mem.ld(addr, 8)
38 f.write(struct.pack('>Q', val)) # unsigned long
39
40 def sim_check_data(simulator, qemu, check_addr, msg):
41 addr = check_addr & ~0x7 # align
42 sim_data = simulator.mem.ld(addr, 8, swap=False)
43 qdata = qemu.get_mem(addr, 8)[0]
44 log ("last", msg, hex(check_addr), hex(sim_data), hex(qdata))
45 if sim_data != qdata:
46 log("expect mem %x, %x got %x" % (addr, qdata, sim_data))
47 exit(0)
48
49 def convert_to_num(num):
50 # detect number types
51 if num.isdigit():
52 return int(num)
53 if num.startswith("0b"):
54 return int(num[2:], 2)
55 if num.startswith("0x"):
56 return int(num[2:], 16)
57 return num
58
59
60 def read_entries(fname, listqty=None):
61 """read_entries: reads values from a file of the format "x: y", per line
62 can be used for memory, or regfiles (GPR, SPR, FP). regs and entries may
63 start with "0x" or "0b" for hex or binary
64 """
65 regs = {}
66 allints = True
67 with open(fname) as f:
68 for line in f.readlines():
69 # split out comments
70 if line.startswith("#"):
71 continue
72 line = line.split("#")[0]
73 # split line "x : y" into ["x", "y"], remove spaces
74 line = list(map(str.strip, line.strip().split(":")))
75 assert len(line) == 2, "regfile line must be formatted 'x : y'"
76 # check and convert format
77 reg, val = line
78 reg = convert_to_num(reg)
79 val = convert_to_num(val)
80 assert reg not in regs, "duplicate entry %s" % (repr(reg))
81 allints = allints and isinstance(reg, int)
82 regs[reg] = val
83
84 # SPRs can be named
85 if not allints:
86 return regs
87
88 # post-process into a list.
89 if listqty is None:
90 return regs
91 result = [0] * listqty
92 for reg, value in regs.items():
93 assert isinstance(reg, int), "list must be int, %s found" % reg
94 assert reg < listqty, "entry %s too large for list %s" % (reg, listqty)
95 result[reg] = value
96
97 return result
98
99
100 def qemu_register_compare(sim, q, regs, fprs):
101 qpc, qxer, qcr, qlr = q.get_pc(), q.get_xer(), q.get_cr(), q.get_lr()
102 sim_cr = sim.cr.value
103 sim_pc = sim.pc.CIA.value
104 sim_xer = sim.spr['XER'].value
105 sim_lr = sim.spr['LR'].value
106 print("qemu pc", hex(qpc))
107 print("qemu cr", hex(qcr))
108 print("qemu lr", hex(qlr))
109 print("qemu xer", bin(qxer))
110 print("sim nia", hex(sim.pc.NIA.value))
111 print("sim pc", hex(sim.pc.CIA.value))
112 print("sim cr", hex(sim_cr))
113 print("sim xer", hex(sim_xer))
114 print("sim lr", hex(sim_lr))
115 #self.assertEqual(qpc, sim_pc)
116 for reg in regs:
117 qemu_val = q.get_gpr(reg)
118 sim_val = sim.gpr(reg).value
119 if qemu_val != sim_val:
120 log("expect gpr %d %x got %x" % (reg, qemu_val, sim_val))
121 #self.assertEqual(qemu_val, sim_val,
122 # "expect %x got %x" % (qemu_val, sim_val))
123 for fpr in fprs:
124 qemu_val = q.get_fpr(fpr)
125 sim_val = sim.fpr(fpr).value
126 if qemu_val != sim_val:
127 log("expect fpr %d %x got %x" % (fpr, qemu_val, sim_val))
128 #self.assertEqual(qemu_val, sim_val,
129 # "expect %x got %x" % (qemu_val, sim_val))
130 #self.assertEqual(qcr, sim_cr)
131
132
133 def run_tst(args, generator, qemu,
134 initial_regs,
135 initial_sprs=None, svstate=0, mmu=False,
136 initial_cr=0, mem=None,
137 initial_fprs=None,
138 initial_pc=0):
139 if initial_regs is None:
140 initial_regs = [0] * 32
141 if initial_sprs is None:
142 initial_sprs = {}
143
144 if qemu:
145 log("qemu program", generator.binfile.name)
146 qemu = run_program(generator, initial_mem=mem,
147 bigendian=False, start_addr=initial_pc,
148 continuous_run=False, initial_sprs=initial_sprs,
149 initial_regs=initial_regs, initial_fprs=initial_fprs)
150 for reg, val in qemu._get_registers().items():
151 log ("qemu reg", reg, hex(val))
152 if True:
153 offs, length = 0x200000, 0x200
154 qmem = qemu.get_mem(offs, length)
155 log("qemu mem pre-dump", hex(offs), length)
156 for i, data in enumerate(qmem):
157 log(hex(offs+i*8), hex(data))
158
159 m = Module()
160 comb = m.d.comb
161 instruction = Signal(32)
162
163 pdecode = create_pdecode(include_fp=initial_fprs is not None)
164
165 gen = list(generator.generate_instructions())
166 log ("instructions gen", gen)
167 insncode = generator.assembly.splitlines()
168 if insncode:
169 instructions = list(zip(gen, insncode))
170 else:
171 instructions = gen
172
173 log ("instructions", instructions)
174
175 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
176 simulator = ISA(pdecode2, initial_regs, initial_sprs, initial_cr,
177 initial_insns=(initial_pc, gen), respect_pc=True,
178 initial_svstate=svstate,
179 initial_mem=mem,
180 initial_pc=initial_pc,
181 fpregfile=initial_fprs,
182 disassembly=insncode,
183 bigendian=0,
184 mmu=mmu)
185 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
186 sim = Simulator(m)
187
188 def process():
189
190 yield pdecode2.dec.bigendian.eq(0) # little / big?
191 pc = simulator.pc.CIA.value
192 index = pc//4
193
194 # rather awkward: qemu will go wonky if stepped over the
195 # last instruction. use ISACaller to check if this is
196 # the last instruction, and leave qemu pointing at it
197 # rather than trigger an exception in the remote-qemu program
198 try:
199 _pc, _ins = simulator.get_next_insn()
200 except KeyError: # indicates instruction not in imem: stop
201 return
202
203 while not simulator.halted:
204 log("instr pc", pc)
205 yield from simulator.setup_next_insn(_pc, _ins)
206 yield Settle()
207
208 if False:
209 ins = instructions[index]
210 if isinstance(ins, list):
211 ins, code = ins
212 log(" 0x{:X}".format(ins & 0xffffffff))
213 opname = code.split(' ')[0]
214 log(code, opname)
215 else:
216 log(" 0x{:X}".format(ins & 0xffffffff))
217
218 # ask the decoder to decode this binary data (endian'd)
219 yield from simulator.execute_one()
220 #pc = simulator.pc.CIA.value
221 #index = pc//4
222
223 if not qemu:
224 try:
225 _pc, _ins = simulator.get_next_insn()
226 except KeyError: # indicates instruction not in imem: stop
227 return
228 continue
229
230 # check qemu co-sim: run one instruction, but first check
231 # in ISACaller if there *is* a next instruction. if there
232 # is, "recover" qemu by switching bigendian back
233 try:
234 _pc, _ins = simulator.get_next_insn()
235 except KeyError: # indicates instruction not in imem: stop
236 _pc, _insn = (None, None)
237 qemu.step()
238 if not _pc or simulator.halted:
239 qemu.set_endian(False)
240 qemu_register_compare(simulator, qemu, range(32), range(32))
241 # check last store address
242 check_addr = None
243 if simulator.last_st_addr is not None:
244 check_addr = simulator.last_st_addr
245 msg = "st"
246 if simulator.last_ld_addr is not None:
247 check_addr = simulator.last_ld_addr
248 msg = "ld"
249 if check_addr is not None:
250 sim_check_data(simulator, qemu, check_addr, msg)
251 sim_check_data(simulator, qemu, 0x600800, "dbgld")
252 if _pc is None:
253 break
254
255 sim.add_process(process)
256 sim.run()
257
258 return simulator, qemu
259
260
261 def help():
262 print ("-i --binary= raw (non-ELF) bare metal executable, loaded at 0x0")
263 print ("-a --listing= file containing bare-metal assembler (no macros)")
264 print ("-g --intregs= colon-separated file with GPR values")
265 print ("-f --fpregs= colon-separated file with FPR values")
266 print ("-s --spregs= colon-separated file with SPR values")
267 print ("-l --load= filename:address to load binary into memory")
268 print ("-d --dump= filename:address:len to binary save from memory")
269 print ("-q --qemu= run qemu co-simulation")
270 print ("-p --pc= set initial program counter")
271 print ("-h --help prints this message")
272 print ("notes:")
273 print ("load and dump may be given multiple times")
274 print ("load and dump must be 8-byte aligned sizes")
275 print ("loading SPRs accepts SPR names (e.g. LR, CTR, SRR0)")
276 print ("numbers may be integer, binary (0bNNN) or hex (0xMMM) but not FP")
277 print ("running ELF binaries: load SPRs, LR set to 0xffffffffffffffff")
278 print ("TODO: dump registers")
279 print ("TODO: load/dump PC, MSR, CR")
280 print ("TODO: print exec and sub-exec counters at end")
281 exit(-1)
282
283
284 def run_a_simulation(binary, bigendian, prog,
285 qemu_cosim, initial_regs,
286 initial_sprs, svstate, mmu,
287 initial_cr, initial_mem,
288 initial_fprs, initial_pc):
289
290 log ("binary", binary)
291 log ("bigendian", bigendian)
292 log ("prog", prog)
293 log ("qemu_cosim", qemu_cosim)
294 log ("initial_regs", initial_regs)
295 log ("initial_sprs", initial_sprs)
296 log ("svstate", svstate)
297 log ("mmu", mmu)
298 log ("initial_cr", initial_cr)
299 log ("initial_mem", initial_mem)
300 log ("initial_fprs", initial_fprs)
301 log ("initial_pc", initial_pc)
302
303 with Program(binary, bigendian=bigendian, orig_filename=None) as prog:
304 simulator, qemu = run_tst(None, prog, qemu_cosim,
305 initial_regs,
306 initial_sprs=initial_sprs,
307 svstate=svstate, mmu=mmu,
308 initial_cr=initial_cr, mem=initial_mem,
309 initial_fprs=initial_fprs,
310 initial_pc=initial_pc)
311 #print("GPRs")
312 #simulator.gpr.dump()
313 #print("memory")
314 #simulator.mem.dump()
315
316 return simulator
317
318 def run_simulation():
319
320 binaryname = None
321 initial_regs = [0]*128
322 initial_fprs = [0]*128
323 initial_sprs = None
324 initial_mem = {}
325 initial_pc = 0x0
326 lst = None
327 qemu_cosim = False
328 write_to = []
329
330 try:
331 opts, args = getopt.getopt(sys.argv[1:], "qhi:a:g:f:s:l:d:p:",
332 ["qemu", "help", "pc=",
333 "binary=", "listing=",
334 "intregs=", "fpregs=", "sprs=",
335 "load=", "dump="])
336
337 except:
338 sys.stderr.write("Command-line Error\n")
339 help()
340
341 for opt, arg in opts:
342 if opt in ['-h', '--help']:
343 help()
344 elif opt in ['-q', '--qemu']:
345 qemu_cosim = True
346 elif opt in ['-i', '--binary']:
347 binaryname = arg
348 elif opt in ['-p', '--pc']:
349 initial_pc = convert_to_num(arg)
350 elif opt in ['-a', '--listing']:
351 lst = arg
352 elif opt in ['-g', '--intregs']:
353 initial_regs = read_entries(arg, 128)
354 elif opt in ['-f', '--fpregs']:
355 initial_fprs = read_entries(arg, 128)
356 elif opt in ['-s', '--sprs']:
357 initial_sprs = read_entries(arg, 32)
358 elif opt in ['-l', '--load']:
359 arg = list(map(str.strip, arg.split(":")))
360 if len(arg) == 1:
361 fname, offs = arg[0], 0
362 else:
363 fname, offs = arg
364 offs = convert_to_num(offs)
365 log ("offs load", fname, hex(offs))
366 mem = read_data(fname, offs)
367 initial_mem.update(mem)
368 elif opt in ['-d', '--dump']:
369 arg = list(map(str.strip, arg.split(":")))
370 assert len(arg) == 3, \
371 "dump '%s' must contain file:offset:length" % repr(arg)
372 fname, offs, length = arg
373 offs = convert_to_num(offs)
374 length = convert_to_num(length)
375 assert length % 8 == 0, "length %d must align on 8-byte" % length
376 log ("dump", fname, offs, length)
377 write_to.append((fname, offs, length))
378
379 log (initial_mem)
380
381 if binaryname is None and lst is None:
382 sys.stderr.write("Must give binary or listing\n")
383 help()
384
385 if lst:
386 with open(lst, "r") as f:
387 lst = list(map(str.strip, f.readlines()))
388
389 if binaryname:
390 with open(binaryname, "rb") as f:
391 lst = f.read()
392
393 with Program(lst, bigendian=False, orig_filename=binaryname) as prog:
394 simulator, qemu = run_tst(None, prog, qemu_cosim,
395 initial_regs,
396 initial_sprs=initial_sprs,
397 svstate=0, mmu=False,
398 initial_cr=0, mem=initial_mem,
399 initial_fprs=initial_fprs,
400 initial_pc=initial_pc)
401 print ("GPRs")
402 simulator.gpr.dump()
403 print ("FPRs")
404 simulator.fpr.dump()
405 print ("SPRs")
406 simulator.spr.dump()
407 print ("Mem")
408 simulator.mem.dump(asciidump=True)
409
410 for fname, offs, length in write_to:
411 write_data(simulator.mem, fname, offs, length)
412 if qemu:
413 qmem = qemu.get_mem(offs, length)
414 log("qemu mem", hex(offs), length)
415 for i, mem in enumerate(qmem):
416 log(hex(offs+i*8), hex(mem))
417 if qemu:
418 log("final complete qemu reg dump")
419 for reg, val in qemu._get_registers().items():
420 log ("qemu reg", reg, hex(val))
421
422 # cleanup
423 if qemu:
424 qemu.exit()
425
426 if __name__ == "__main__":
427 run_simulation()
428