comment cleanup, record last LD/ST address in simulator
[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 # check last store address
228 if simulator.last_st_addr is not None:
229 addr = simulator.last_st_addr & ~0x7 # align
230 sim_data = simulator.mem.ld(addr, 8, swap=False)
231 qdata = qemu.get_mem(addr, 8)[0]
232 log ("last st", simulator.last_st_addr, sim_data, qdata)
233 if sim_data !=qdata :
234 log("expect mem %x, %x got %x" % (addr, qdata, sim_data))
235 if _pc is None:
236 break
237
238 sim.add_process(process)
239 sim.run()
240
241 return simulator, qemu
242
243
244 def help():
245 print ("-i --binary= raw (non-ELF) bare metal executable, loaded at 0x0")
246 print ("-a --listing= file containing bare-metal assembler (no macros)")
247 print ("-g --intregs= colon-separated file with GPR values")
248 print ("-f --fpregs= colon-separated file with FPR values")
249 print ("-s --spregs= colon-separated file with SPR values")
250 print ("-l --load= filename:address to load binary into memory")
251 print ("-d --dump= filename:address:len to binary save from memory")
252 print ("-q --qemu= run qemu co-simulation")
253 print ("-p --pc= set initial program counter")
254 print ("-h --help prints this message")
255 print ("notes:")
256 print ("load and dump may be given multiple times")
257 print ("load and dump must be 8-byte aligned sizes")
258 print ("loading SPRs accepts SPR names (e.g. LR, CTR, SRR0)")
259 print ("numbers may be integer, binary (0bNNN) or hex (0xMMM) but not FP")
260 print ("running ELF binaries: load SPRs, LR set to 0xffffffffffffffff")
261 print ("TODO: dump registers")
262 print ("TODO: load/dump PC, MSR, CR")
263 print ("TODO: print exec and sub-exec counters at end")
264 exit(-1)
265
266
267 def run_simulation():
268
269 binaryname = None
270 initial_regs = [0]*32
271 initial_fprs = [0]*32
272 initial_sprs = None
273 initial_mem = {}
274 initial_pc = 0x0
275 lst = None
276 qemu_cosim = False
277 write_to = []
278
279 try:
280 opts, args = getopt.getopt(sys.argv[1:], "qhi:a:g:f:s:l:d:p:",
281 ["qemu", "help", "pc=",
282 "binary=", "listing=",
283 "intregs=", "fpregs=", "sprs=",
284 "load=", "dump="])
285
286 except:
287 sys.stderr.write("Command-line Error\n")
288 help()
289
290 for opt, arg in opts:
291 if opt in ['-h', '--help']:
292 help()
293 elif opt in ['-q', '--qemu']:
294 qemu_cosim = True
295 elif opt in ['-i', '--binary']:
296 binaryname = arg
297 elif opt in ['-p', '--pc']:
298 initial_pc = convert_to_num(arg)
299 elif opt in ['-a', '--listing']:
300 lst = arg
301 elif opt in ['-g', '--intregs']:
302 initial_regs = read_entries(arg, 32)
303 elif opt in ['-f', '--fpregs']:
304 initial_fprs = read_entries(arg, 32)
305 elif opt in ['-s', '--sprs']:
306 initial_sprs = read_entries(arg, 32)
307 elif opt in ['-l', '--load']:
308 arg = list(map(str.strip, arg.split(":")))
309 if len(arg) == 1:
310 fname, offs = arg[0], 0
311 else:
312 fname, offs = arg
313 offs = convert_to_num(offs)
314 log ("offs load", fname, hex(offs))
315 mem = read_data(fname, offs)
316 initial_mem.update(mem)
317 elif opt in ['-d', '--dump']:
318 arg = list(map(str.strip, arg.split(":")))
319 assert len(arg) == 3, \
320 "dump '%s' must contain file:offset:length" % repr(arg)
321 fname, offs, length = arg
322 offs = convert_to_num(offs)
323 length = convert_to_num(length)
324 assert length % 8 == 0, "length %d must align on 8-byte" % length
325 log ("dump", fname, offs, length)
326 write_to.append((fname, offs, length))
327
328 log (initial_mem)
329
330 if binaryname is None and lst is None:
331 sys.stderr.write("Must give binary or listing\n")
332 help()
333
334 if lst:
335 with open(lst, "r") as f:
336 lst = list(map(str.strip, f.readlines()))
337
338 if binaryname:
339 with open(binaryname, "rb") as f:
340 lst = f.read()
341
342 with Program(lst, bigendian=False, orig_filename=binaryname) as prog:
343 simulator, qemu = run_tst(None, prog, qemu_cosim,
344 initial_regs,
345 initial_sprs=initial_sprs,
346 svstate=0, mmu=False,
347 initial_cr=0, mem=initial_mem,
348 initial_fprs=initial_fprs,
349 initial_pc=initial_pc)
350 print ("GPRs")
351 simulator.gpr.dump()
352 print ("FPRs")
353 simulator.fpr.dump()
354 print ("SPRs")
355 simulator.spr.dump()
356 print ("Mem")
357 simulator.mem.dump()
358
359 for fname, offs, length in write_to:
360 write_data(simulator.mem, fname, offs, length)
361 if qemu:
362 qmem = qemu.get_mem(offs, length)
363 for i, mem in enumerate(qmem):
364 log(hex(offs+i*8), hex(mem))
365 for reg, val in qemu._get_registers().items():
366 log ("qemu reg", reg, hex(val))
367
368 # cleanup
369 if qemu:
370 qemu.exit()
371
372 if __name__ == "__main__":
373 run_simulation()
374