1 from nmigen
import Module
, Signal
2 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
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
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)
22 with
open(fname
, "rb") as f
:
28 res
[offset
] = struct
.unpack('<Q', b
)[0] # unsigned long
31 def write_data(mem
, fname
, offset
, sz
):
32 """writes binary data to a file, each entry must be 8 bytes
34 with
open(fname
, "wb") as f
:
35 for i
in range(0, sz
, 8):
38 f
.write(struct
.pack('>Q', val
)) # unsigned long
41 def convert_to_num(num
):
45 if num
.startswith("0b"):
46 return int(num
[2:], 2)
47 if num
.startswith("0x"):
48 return int(num
[2:], 16)
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
59 with
open(fname
) as f
:
60 for line
in f
.readlines():
62 if line
.startswith("#"):
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
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)
80 # post-process into a list.
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
)
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()
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)
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))
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)
125 def run_tst(args
, generator
, qemu
,
127 initial_sprs
=None, svstate
=0, mmu
=False,
128 initial_cr
=0, mem
=None,
131 if initial_regs
is None:
132 initial_regs
= [0] * 32
133 if initial_sprs
is None:
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
))
147 instruction
= Signal(32)
149 pdecode
= create_pdecode(include_fp
=initial_fprs
is not None)
151 gen
= list(generator
.generate_instructions())
152 log ("instructions gen", gen
)
153 insncode
= generator
.assembly
.splitlines()
155 instructions
= list(zip(gen
, insncode
))
159 log ("instructions", instructions
)
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
,
166 initial_pc
=initial_pc
,
167 fpregfile
=initial_fprs
,
168 disassembly
=insncode
,
171 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
176 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
177 pc
= simulator
.pc
.CIA
.value
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
185 _pc
, _ins
= simulator
.get_next_insn()
186 except KeyError: # indicates instruction not in imem: stop
189 while not simulator
.halted
:
191 yield from simulator
.setup_next_insn(_pc
, _ins
)
195 ins
= instructions
[index
]
196 if isinstance(ins
, list):
198 log(" 0x{:X}".format(ins
& 0xffffffff))
199 opname
= code
.split(' ')[0]
202 log(" 0x{:X}".format(ins
& 0xffffffff))
204 # ask the decoder to decode this binary data (endian'd)
205 yield from simulator
.execute_one()
206 #pc = simulator.pc.CIA.value
211 _pc
, _ins
= simulator
.get_next_insn()
212 except KeyError: # indicates instruction not in imem: stop
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
220 _pc
, _ins
= simulator
.get_next_insn()
221 except KeyError: # indicates instruction not in imem: stop
222 _pc
, _insn
= (None, None)
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
))
238 sim
.add_process(process
)
241 return simulator
, qemu
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")
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")
267 def run_simulation():
270 initial_regs
= [0]*32
271 initial_fprs
= [0]*32
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=",
287 sys
.stderr
.write("Command-line Error\n")
290 for opt
, arg
in opts
:
291 if opt
in ['-h', '--help']:
293 elif opt
in ['-q', '--qemu']:
295 elif opt
in ['-i', '--binary']:
297 elif opt
in ['-p', '--pc']:
298 initial_pc
= convert_to_num(arg
)
299 elif opt
in ['-a', '--listing']:
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(":")))
310 fname
, offs
= arg
[0], 0
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
))
330 if binaryname
is None and lst
is None:
331 sys
.stderr
.write("Must give binary or listing\n")
335 with
open(lst
, "r") as f
:
336 lst
= list(map(str.strip
, f
.readlines()))
339 with
open(binaryname
, "rb") as f
:
342 with
Program(lst
, bigendian
=False, orig_filename
=binaryname
) as prog
:
343 simulator
, qemu
= run_tst(None, prog
, qemu_cosim
,
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
)
359 for fname
, offs
, length
in write_to
:
360 write_data(simulator
.mem
, fname
, offs
, length
)
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
))
372 if __name__
== "__main__":