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