this is part of a cycle-accurate POWER9 simulator. its primary purpose is
not speed, it is for both learning and educational purposes, as well as
a method of verifying the HDL.
+
+related bugs:
+
+* https://bugs.libre-soc.org/show_bug.cgi?id=424
"""
from functools import wraps
from soc.decoder.orderedset import OrderedSet
from soc.decoder.selectable_int import (FieldSelectableInt, SelectableInt,
selectconcat)
-from soc.decoder.power_enums import spr_dict, XER_bits, insns, InternalOp
+from soc.decoder.power_enums import (spr_dict, spr_byname, XER_bits,
+ insns, InternalOp)
from soc.decoder.helpers import exts
+from soc.consts import PI, MSR
+
from collections import namedtuple
import math
import sys
def __init__(self, dec2, initial_sprs={}):
self.sd = dec2
dict.__init__(self)
- self.update(initial_sprs)
+ for key, v in initial_sprs.items():
+ if isinstance(key, SelectableInt):
+ key = key.value
+ key = special_sprs.get(key, key)
+ if isinstance(key, int):
+ info = spr_dict[key]
+ else:
+ info = spr_byname[key]
+ if not isinstance(v, SelectableInt):
+ v = SelectableInt(v, info.length)
+ self[key] = v
def __getitem__(self, key):
+ print ("get spr", key)
+ print ("dict", self.items())
# if key in special_sprs get the special spr, otherwise return key
if isinstance(key, SelectableInt):
key = key.value
+ if isinstance(key, int):
+ key = spr_dict[key].SPR
key = special_sprs.get(key, key)
if key in self:
- return dict.__getitem__(self, key)
+ res = dict.__getitem__(self, key)
else:
- info = spr_dict[key]
+ if isinstance(key, int):
+ info = spr_dict[key]
+ else:
+ info = spr_byname[key]
dict.__setitem__(self, key, SelectableInt(0, info.length))
- return dict.__getitem__(self, key)
+ res = dict.__getitem__(self, key)
+ print ("spr returning", key, res)
+ return res
def __setitem__(self, key, value):
if isinstance(key, SelectableInt):
key = key.value
+ if isinstance(key, int):
+ key = spr_dict[key].SPR
+ print ("spr key", key)
key = special_sprs.get(key, key)
+ print ("setting spr", key, value)
dict.__setitem__(self, key, value)
def __call__(self, ridx):
def __init__(self, decoder2, regfile, initial_sprs=None, initial_cr=0,
initial_mem=None, initial_msr=0,
initial_insns=None, respect_pc=False,
- disassembly=None):
+ disassembly=None,
+ initial_pc=0,
+ bigendian=True):
+ self.bigendian = bigendian
+ self.halted = False
self.respect_pc = respect_pc
if initial_sprs is None:
initial_sprs = {}
# "fake program counter" mode (for unit testing)
self.fake_pc = 0
+ disasm_start = 0
if not respect_pc:
if isinstance(initial_mem, tuple):
self.fake_pc = initial_mem[0]
+ disasm_start = self.fake_pc
+ else:
+ disasm_start = initial_pc
# disassembly: we need this for now (not given from the decoder)
self.disassembly = {}
if disassembly:
for i, code in enumerate(disassembly):
- self.disassembly[i*4 + self.fake_pc] = code
+ self.disassembly[i*4 + disasm_start] = code
# set up registers, instruction memory, data memory, PC, SPRs, MSR
self.gpr = GPR(decoder2, regfile)
# "undefined", just set to variable-bit-width int (use exts "max")
self.undefined = SelectableInt(0, 256) # TODO, not hard-code 256!
- self.namespace = {'GPR': self.gpr,
+ self.namespace = {}
+ self.namespace.update(self.spr)
+ self.namespace.update({'GPR': self.gpr,
'MEM': self.mem,
'SPR': self.spr,
'memassign': self.memassign,
'undefined': self.undefined,
'mode_is_64bit': True,
'SO': XER_bits['SO']
- }
+ })
+
+ # update pc to requested start point
+ self.set_pc(initial_pc)
# field-selectable versions of Condition Register TODO check bitranges?
self.crl = []
self.decoder = decoder2.dec
self.dec2 = decoder2
- def TRAP(self, trap_addr=0x700):
- print ("TRAP: TODO")
+ def TRAP(self, trap_addr=0x700, trap_bit=PI.TRAP):
+ print ("TRAP:", hex(trap_addr))
# store CIA(+4?) in SRR0, set NIA to 0x700
# store MSR in SRR1, set MSR to um errr something, have to check spec
+ self.spr['SRR0'] = self.pc.CIA
+ self.spr['SRR1'] = self.namespace['MSR']
+ self.trap_nia = SelectableInt(trap_addr, 64)
+ self.namespace['MSR'][63-trap_bit] = 1
def memassign(self, ea, sz, val):
self.mem.memassign(ea, sz, val)
self.namespace['CA32'] = self.spr['XER'][XER_bits['CA32']].value
def handle_carry_(self, inputs, outputs, already_done):
- inv_a = yield self.dec2.e.invert_a
+ inv_a = yield self.dec2.e.do.invert_a
if inv_a:
inputs[0] = ~inputs[0]
- imm_ok = yield self.dec2.e.imm_data.ok
+ imm_ok = yield self.dec2.e.do.imm_data.ok
if imm_ok:
- imm = yield self.dec2.e.imm_data.data
+ imm = yield self.dec2.e.do.imm_data.data
inputs.append(SelectableInt(imm, 64))
assert len(outputs) >= 1
print ("outputs", repr(outputs))
if not (2 & already_done):
self.spr['XER'][XER_bits['CA32']] = cy32
- def handle_overflow(self, inputs, outputs):
- inv_a = yield self.dec2.e.invert_a
+ def handle_overflow(self, inputs, outputs, div_overflow):
+ inv_a = yield self.dec2.e.do.invert_a
if inv_a:
inputs[0] = ~inputs[0]
- imm_ok = yield self.dec2.e.imm_data.ok
+ imm_ok = yield self.dec2.e.do.imm_data.ok
if imm_ok:
- imm = yield self.dec2.e.imm_data.data
+ imm = yield self.dec2.e.do.imm_data.data
inputs.append(SelectableInt(imm, 64))
assert len(outputs) >= 1
- print ("handle_overflow", inputs, outputs)
- if len(inputs) >= 2:
+ print ("handle_overflow", inputs, outputs, div_overflow)
+ if len(inputs) < 2 and div_overflow is None:
+ return
+
+ # div overflow is different: it's returned by the pseudo-code
+ # because it's more complex than can be done by analysing the output
+ if div_overflow is not None:
+ ov, ov32 = div_overflow, div_overflow
+ # arithmetic overflow can be done by analysing the input and output
+ elif len(inputs) >= 2:
output = outputs[0]
# OV (64-bit)
ov32 = 1 if input32_sgn[0] == input32_sgn[1] and \
output32_sgn != input32_sgn[0] else 0
- self.spr['XER'][XER_bits['OV']] = ov
- self.spr['XER'][XER_bits['OV32']] = ov32
- so = self.spr['XER'][XER_bits['SO']]
- so = so | ov
- self.spr['XER'][XER_bits['SO']] = so
+ self.spr['XER'][XER_bits['OV']] = ov
+ self.spr['XER'][XER_bits['OV32']] = ov32
+ so = self.spr['XER'][XER_bits['SO']]
+ so = so | ov
+ self.spr['XER'][XER_bits['SO']] = so
def handle_comparison(self, outputs):
out = outputs[0]
+ print ("handle_comparison", out.bits, hex(out.value))
+ # TODO - XXX *processor* in 32-bit mode
+ # https://bugs.libre-soc.org/show_bug.cgi?id=424
+ #if is_32bit:
+ # o32 = exts(out.value, 32)
+ # print ("handle_comparison exts 32 bit", hex(o32))
out = exts(out.value, out.bits)
+ print ("handle_comparison exts", hex(out))
zero = SelectableInt(out == 0, 1)
positive = SelectableInt(out > 0, 1)
negative = SelectableInt(out < 0, 1)
SO = self.spr['XER'][XER_bits['SO']]
+ print ("handle_comparison SO", SO)
cr_field = selectconcat(negative, positive, zero, SO)
self.crl[0].eq(cr_field)
if ins is None:
raise KeyError("no instruction at 0x%x" % pc)
print("setup: 0x%x 0x%x %s" % (pc, ins & 0xffffffff, bin(ins)))
- print ("NIA, CIA", self.pc.CIA.value, self.pc.NIA.value)
+ print ("CIA NIA", self.respect_pc, self.pc.CIA.value, self.pc.NIA.value)
yield self.dec2.dec.raw_opcode_in.eq(ins & 0xffffffff)
- yield self.dec2.dec.bigendian.eq(0) # little / big?
+ yield self.dec2.dec.bigendian.eq(self.bigendian)
def execute_one(self):
"""execute one instruction
if not self.respect_pc:
self.fake_pc += 4
- print ("NIA, CIA", self.pc.CIA.value, self.pc.NIA.value)
+ print ("execute one, CIA NIA", self.pc.CIA.value, self.pc.NIA.value)
def get_assembly_name(self):
# TODO, asmregs is from the spec, e.g. add RT,RA,RB
# see http://bugs.libre-riscv.org/show_bug.cgi?id=282
asmcode = yield self.dec2.dec.op.asmcode
+ print ("get assembly name asmcode", asmcode)
asmop = insns.get(asmcode, None)
# sigh reconstruct the assembly instruction name
- ov_en = yield self.dec2.e.oe.oe
- ov_ok = yield self.dec2.e.oe.ok
+ ov_en = yield self.dec2.e.do.oe.oe
+ ov_ok = yield self.dec2.e.do.oe.ok
if ov_en & ov_ok:
asmop += "."
- lk = yield self.dec2.e.lk
+ lk = yield self.dec2.e.do.lk
if lk:
asmop += "l"
int_op = yield self.dec2.dec.op.internal_op
if AA:
asmop += "a"
if int_op == InternalOp.OP_MFCR.value:
- dec_insn = yield self.dec2.e.insn
+ dec_insn = yield self.dec2.e.do.insn
if dec_insn & (1<<20) != 0: # sigh
asmop = 'mfocrf'
else:
# XXX TODO: for whatever weird reason this doesn't work
# https://bugs.libre-soc.org/show_bug.cgi?id=390
if int_op == InternalOp.OP_MTCRF.value:
- dec_insn = yield self.dec2.e.insn
+ dec_insn = yield self.dec2.e.do.insn
if dec_insn & (1<<20) != 0: # sigh
asmop = 'mtocrf'
else:
return asmop
def call(self, name):
+ name = name.strip() # remove spaces if not already done so
+ if self.halted:
+ print ("halted - not executing", name)
+ return
+
# TODO, asmregs is from the spec, e.g. add RT,RA,RB
# see http://bugs.libre-riscv.org/show_bug.cgi?id=282
asmop = yield from self.get_assembly_name()
print ("call", name, asmop)
+
+ # check halted condition
+ if name == 'attn':
+ self.halted = True
+ return
+
+ # check illegal instruction
+ illegal = False
if name not in ['mtcrf', 'mtocrf']:
- assert name == asmop, "name %s != %s" % (name, asmop)
+ illegal = name != asmop
+
+ if illegal:
+ print ("name %s != %s - calling ILLEGAL trap" % (name, asmop))
+ self.TRAP(0x700, PI.ILLEG)
+ self.namespace['NIA'] = self.trap_nia
+ self.pc.update(self.namespace)
+ return
info = self.instrs[name]
yield from self.prep_namespace(info.form, info.op_fields)
# preserve order of register names
- input_names = create_args(list(info.read_regs) + list(info.uninit_regs))
+ input_names = create_args(list(info.read_regs) +
+ list(info.uninit_regs))
print(input_names)
# main registers (RT, RA ...)
else:
inputs.append(self.namespace[special])
+ # clear trap (trap) NIA
+ self.trap_nia = None
+
print(inputs)
results = info.func(self, *inputs)
print(results)
+ # "inject" decorator takes namespace from function locals: we need to
+ # overwrite NIA being overwritten (sigh)
+ if self.trap_nia is not None:
+ self.namespace['NIA'] = self.trap_nia
+
+ print ("after func", self.namespace['CIA'], self.namespace['NIA'])
+
# detect if CA/CA32 already in outputs (sra*, basically)
already_done = 0
if info.write_regs:
already_done |= 2
print ("carry already done?", bin(already_done))
- carry_en = yield self.dec2.e.output_carry
+ carry_en = yield self.dec2.e.do.output_carry
if carry_en:
yield from self.handle_carry_(inputs, results, already_done)
- ov_en = yield self.dec2.e.oe.oe
- ov_ok = yield self.dec2.e.oe.ok
+
+ # detect if overflow was in return result
+ overflow = None
+ if info.write_regs:
+ for name, output in zip(output_names, results):
+ if name == 'overflow':
+ overflow = output
+
+ ov_en = yield self.dec2.e.do.oe.oe
+ ov_ok = yield self.dec2.e.do.oe.ok
+ print ("internal overflow", overflow)
if ov_en & ov_ok:
- yield from self.handle_overflow(inputs, results)
- rc_en = yield self.dec2.e.rc.data
+ yield from self.handle_overflow(inputs, results, overflow)
+
+ rc_en = yield self.dec2.e.do.rc.data
if rc_en:
self.handle_comparison(results)
# any modified return results?
if info.write_regs:
for name, output in zip(output_names, results):
+ if name == 'overflow': # ignore, done already (above)
+ continue
if isinstance(output, int):
output = SelectableInt(output, 256)
if name in ['CA', 'CA32']:
self.spr[name] = output
else:
self.namespace[name].eq(output)
+ if name == 'MSR':
+ print ('msr written', hex(self.msr.value))
else:
regnum = yield getattr(self.decoder, name)
print('writing reg %d %s' % (regnum, str(output)))
output = SelectableInt(output.value, 64)
self.gpr[regnum] = output
- # update program counter
+ print ("end of call", self.namespace['CIA'], self.namespace['NIA'])
+ # UPDATE program counter
self.pc.update(self.namespace)
saved_values = func_globals.copy() # Shallow copy of dict.
func_globals.update(context)
result = func(*args, **kwargs)
+ print ("globals after", func_globals['CIA'], func_globals['NIA'])
+ print ("args[0]", args[0].namespace['CIA'],
+ args[0].namespace['NIA'])
args[0].namespace = func_globals
#exec (func.__code__, func_globals)