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))
230 sim
.add_process(process
)
233 return simulator
, qemu
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")
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")
259 def run_simulation():
262 initial_regs
= [0]*32
263 initial_fprs
= [0]*32
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=",
279 sys
.stderr
.write("Command-line Error\n")
282 for opt
, arg
in opts
:
283 if opt
in ['-h', '--help']:
285 elif opt
in ['-q', '--qemu']:
287 elif opt
in ['-i', '--binary']:
289 elif opt
in ['-p', '--pc']:
290 initial_pc
= convert_to_num(arg
)
291 elif opt
in ['-a', '--listing']:
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(":")))
302 fname
, offs
= arg
[0], 0
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
))
322 if binaryname
is None and lst
is None:
323 sys
.stderr
.write("Must give binary or listing\n")
327 with
open(lst
, "r") as f
:
328 lst
= list(map(str.strip
, f
.readlines()))
331 with
open(binaryname
, "rb") as f
:
334 with
Program(lst
, bigendian
=False, orig_filename
=binaryname
) as prog
:
335 simulator
, qemu
= run_tst(None, prog
, qemu_cosim
,
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
)
351 for fname
, offs
, length
in write_to
:
352 write_data(simulator
.mem
, fname
, offs
, length
)
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
))
364 if __name__
== "__main__":