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