--- /dev/null
+/all.py
+/bcd.py
+/branch.py
+/comparefixed.py
+/condition.py
+/fixedarith.py
+/fixedldstcache.py
+/fixedload.py
+/fixedlogical.py
+/fixedshift.py
+/fixedstore.py
+/fixedtrap.py
+/sprset.py
+/stringldst.py
+/system.py
+/simplev.py
+*.orig
+*.rej
--- /dev/null
+# SPDX-License-Identifier: LGPLv3+
+# Copyright (C) 2020, 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2020 Michael Nolan
+# Funded by NLnet http://nlnet.nl
+"""core of the python-based POWER9 simulator
+
+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 nmigen.back.pysim import Settle
+from functools import wraps
+from copy import copy
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.selectable_int import (FieldSelectableInt, SelectableInt,
+ selectconcat)
+from soc.decoder.power_enums import (spr_dict, spr_byname, XER_bits,
+ insns, MicrOp, In1Sel, In2Sel, In3Sel,
+ OutSel, CROutSel,
+ SVP64RMMode, SVP64PredMode,
+ SVP64PredInt, SVP64PredCR)
+
+from soc.decoder.power_enums import SVPtype
+
+from soc.decoder.helpers import exts, gtu, ltu, undefined
+from soc.consts import PIb, MSRb # big-endian (PowerISA versions)
+from soc.consts import SVP64CROffs
+from soc.decoder.power_svp64 import SVP64RM, decode_extra
+
+from soc.decoder.isa.radixmmu import RADIX
+from soc.decoder.isa.mem import Mem, swap_order
+
+from collections import namedtuple
+import math
+import sys
+
+instruction_info = namedtuple('instruction_info',
+ 'func read_regs uninit_regs write_regs ' +
+ 'special_regs op_fields form asmregs')
+
+special_sprs = {
+ 'LR': 8,
+ 'CTR': 9,
+ 'TAR': 815,
+ 'XER': 1,
+ 'VRSAVE': 256}
+
+
+REG_SORT_ORDER = {
+ # TODO (lkcl): adjust other registers that should be in a particular order
+ # probably CA, CA32, and CR
+ "RT": 0,
+ "RA": 0,
+ "RB": 0,
+ "RS": 0,
+ "CR": 0,
+ "LR": 0,
+ "CTR": 0,
+ "TAR": 0,
+ "CA": 0,
+ "CA32": 0,
+ "MSR": 0,
+ "SVSTATE": 0,
+
+ "overflow": 1,
+}
+
+
+def create_args(reglist, extra=None):
+ retval = list(OrderedSet(reglist))
+ retval.sort(key=lambda reg: REG_SORT_ORDER[reg])
+ if extra is not None:
+ return [extra] + retval
+ return retval
+
+
+
+class GPR(dict):
+ def __init__(self, decoder, isacaller, svstate, regfile):
+ dict.__init__(self)
+ self.sd = decoder
+ self.isacaller = isacaller
+ self.svstate = svstate
+ for i in range(32):
+ self[i] = SelectableInt(regfile[i], 64)
+
+ def __call__(self, ridx):
+ return self[ridx]
+
+ def set_form(self, form):
+ self.form = form
+
+ def getz(self, rnum):
+ # rnum = rnum.value # only SelectableInt allowed
+ print("GPR getzero?", rnum)
+ if rnum == 0:
+ return SelectableInt(0, 64)
+ return self[rnum]
+
+ def _get_regnum(self, attr):
+ getform = self.sd.sigforms[self.form]
+ rnum = getattr(getform, attr)
+ return rnum
+
+ def ___getitem__(self, attr):
+ """ XXX currently not used
+ """
+ rnum = self._get_regnum(attr)
+ offs = self.svstate.srcstep
+ print("GPR getitem", attr, rnum, "srcoffs", offs)
+ return self.regfile[rnum]
+
+ def dump(self):
+ for i in range(0, len(self), 8):
+ s = []
+ for j in range(8):
+ s.append("%08x" % self[i+j].value)
+ s = ' '.join(s)
+ print("reg", "%2d" % i, s)
+
+
+class SPR(dict):
+ def __init__(self, dec2, initial_sprs={}):
+ self.sd = dec2
+ dict.__init__(self)
+ 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 == 'HSRR0': # HACK!
+ key = 'SRR0'
+ if key == 'HSRR1': # HACK!
+ key = 'SRR1'
+ if key in self:
+ res = dict.__getitem__(self, key)
+ else:
+ if isinstance(key, int):
+ info = spr_dict[key]
+ else:
+ info = spr_byname[key]
+ dict.__setitem__(self, key, SelectableInt(0, info.length))
+ 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)
+ if key == 'HSRR0': # HACK!
+ self.__setitem__('SRR0', value)
+ if key == 'HSRR1': # HACK!
+ self.__setitem__('SRR1', value)
+ print("setting spr", key, value)
+ dict.__setitem__(self, key, value)
+
+ def __call__(self, ridx):
+ return self[ridx]
+
+
+class PC:
+ def __init__(self, pc_init=0):
+ self.CIA = SelectableInt(pc_init, 64)
+ self.NIA = self.CIA + SelectableInt(4, 64) # only true for v3.0B!
+
+ def update_nia(self, is_svp64):
+ increment = 8 if is_svp64 else 4
+ self.NIA = self.CIA + SelectableInt(increment, 64)
+
+ def update(self, namespace, is_svp64):
+ """updates the program counter (PC) by 4 if v3.0B mode or 8 if SVP64
+ """
+ self.CIA = namespace['NIA'].narrow(64)
+ self.update_nia(is_svp64)
+ namespace['CIA'] = self.CIA
+ namespace['NIA'] = self.NIA
+
+
+# Simple-V: see https://libre-soc.org/openpower/sv
+class SVP64State:
+ def __init__(self, init=0):
+ self.spr = SelectableInt(init, 32)
+ # fields of SVSTATE, see https://libre-soc.org/openpower/sv/sprs/
+ self.maxvl = FieldSelectableInt(self.spr, tuple(range(0,7)))
+ self.vl = FieldSelectableInt(self.spr, tuple(range(7,14)))
+ self.srcstep = FieldSelectableInt(self.spr, tuple(range(14,21)))
+ self.dststep = FieldSelectableInt(self.spr, tuple(range(21,28)))
+ self.subvl = FieldSelectableInt(self.spr, tuple(range(28,30)))
+ self.svstep = FieldSelectableInt(self.spr, tuple(range(30,32)))
+
+
+# SVP64 ReMap field
+class SVP64RMFields:
+ def __init__(self, init=0):
+ self.spr = SelectableInt(init, 24)
+ # SVP64 RM fields: see https://libre-soc.org/openpower/sv/svp64/
+ self.mmode = FieldSelectableInt(self.spr, [0])
+ self.mask = FieldSelectableInt(self.spr, tuple(range(1,4)))
+ self.elwidth = FieldSelectableInt(self.spr, tuple(range(4,6)))
+ self.ewsrc = FieldSelectableInt(self.spr, tuple(range(6,8)))
+ self.subvl = FieldSelectableInt(self.spr, tuple(range(8,10)))
+ self.extra = FieldSelectableInt(self.spr, tuple(range(10,19)))
+ self.mode = FieldSelectableInt(self.spr, tuple(range(19,24)))
+ # these cover the same extra field, split into parts as EXTRA2
+ self.extra2 = list(range(4))
+ self.extra2[0] = FieldSelectableInt(self.spr, tuple(range(10,12)))
+ self.extra2[1] = FieldSelectableInt(self.spr, tuple(range(12,14)))
+ self.extra2[2] = FieldSelectableInt(self.spr, tuple(range(14,16)))
+ self.extra2[3] = FieldSelectableInt(self.spr, tuple(range(16,18)))
+ self.smask = FieldSelectableInt(self.spr, tuple(range(16,19)))
+ # and here as well, but EXTRA3
+ self.extra3 = list(range(3))
+ self.extra3[0] = FieldSelectableInt(self.spr, tuple(range(10,13)))
+ self.extra3[1] = FieldSelectableInt(self.spr, tuple(range(13,16)))
+ self.extra3[2] = FieldSelectableInt(self.spr, tuple(range(16,19)))
+
+
+SVP64RM_MMODE_SIZE = len(SVP64RMFields().mmode.br)
+SVP64RM_MASK_SIZE = len(SVP64RMFields().mask.br)
+SVP64RM_ELWIDTH_SIZE = len(SVP64RMFields().elwidth.br)
+SVP64RM_EWSRC_SIZE = len(SVP64RMFields().ewsrc.br)
+SVP64RM_SUBVL_SIZE = len(SVP64RMFields().subvl.br)
+SVP64RM_EXTRA2_SPEC_SIZE = len(SVP64RMFields().extra2[0].br)
+SVP64RM_EXTRA3_SPEC_SIZE = len(SVP64RMFields().extra3[0].br)
+SVP64RM_SMASK_SIZE = len(SVP64RMFields().smask.br)
+SVP64RM_MODE_SIZE = len(SVP64RMFields().mode.br)
+
+
+# SVP64 Prefix fields: see https://libre-soc.org/openpower/sv/svp64/
+class SVP64PrefixFields:
+ def __init__(self):
+ self.insn = SelectableInt(0, 32)
+ # 6 bit major opcode EXT001, 2 bits "identifying" (7, 9), 24 SV ReMap
+ self.major = FieldSelectableInt(self.insn, tuple(range(0,6)))
+ self.pid = FieldSelectableInt(self.insn, (7, 9)) # must be 0b11
+ rmfields = [6, 8] + list(range(10,32)) # SVP64 24-bit RM (ReMap)
+ self.rm = FieldSelectableInt(self.insn, rmfields)
+
+
+SV64P_MAJOR_SIZE = len(SVP64PrefixFields().major.br)
+SV64P_PID_SIZE = len(SVP64PrefixFields().pid.br)
+SV64P_RM_SIZE = len(SVP64PrefixFields().rm.br)
+
+# decode SVP64 predicate integer to reg number and invert
+def get_predint(gpr, mask):
+ r10 = gpr(10)
+ r30 = gpr(30)
+ print ("get_predint", mask, SVP64PredInt.ALWAYS.value)
+ if mask == SVP64PredInt.ALWAYS.value:
+ return 0xffff_ffff_ffff_ffff
+ if mask == SVP64PredInt.R3_UNARY.value:
+ return 1 << (gpr(3).value & 0b111111)
+ if mask == SVP64PredInt.R3.value:
+ return gpr(3).value
+ if mask == SVP64PredInt.R3_N.value:
+ return ~gpr(3).value
+ if mask == SVP64PredInt.R10.value:
+ return gpr(10).value
+ if mask == SVP64PredInt.R10_N.value:
+ return ~gpr(10).value
+ if mask == SVP64PredInt.R30.value:
+ return gpr(30).value
+ if mask == SVP64PredInt.R30_N.value:
+ return ~gpr(30).value
+
+# decode SVP64 predicate CR to reg number and invert status
+def _get_predcr(mask):
+ if mask == SVP64PredCR.LT.value:
+ return 0, 1
+ if mask == SVP64PredCR.GE.value:
+ return 0, 0
+ if mask == SVP64PredCR.GT.value:
+ return 1, 1
+ if mask == SVP64PredCR.LE.value:
+ return 1, 0
+ if mask == SVP64PredCR.EQ.value:
+ return 2, 1
+ if mask == SVP64PredCR.NE.value:
+ return 2, 0
+ if mask == SVP64PredCR.SO.value:
+ return 3, 1
+ if mask == SVP64PredCR.NS.value:
+ return 3, 0
+
+# read individual CR fields (0..VL-1), extract the required bit
+# and construct the mask
+def get_predcr(crl, mask, vl):
+ idx, noninv = _get_predcr(mask)
+ mask = 0
+ for i in range(vl):
+ cr = crl[i+SVP64CROffs.CRPred]
+ if cr[idx].value == noninv:
+ mask |= (1<<i)
+ return mask
+
+
+def get_pdecode_idx_in(dec2, name):
+ op = dec2.dec.op
+ in1_sel = yield op.in1_sel
+ in2_sel = yield op.in2_sel
+ in3_sel = yield op.in3_sel
+ # get the IN1/2/3 from the decoder (includes SVP64 remap and isvec)
+ in1 = yield dec2.e.read_reg1.data
+ in2 = yield dec2.e.read_reg2.data
+ in3 = yield dec2.e.read_reg3.data
+ in1_isvec = yield dec2.in1_isvec
+ in2_isvec = yield dec2.in2_isvec
+ in3_isvec = yield dec2.in3_isvec
+ print ("get_pdecode_idx_in in1", name, in1_sel, In1Sel.RA.value,
+ in1, in1_isvec)
+ print ("get_pdecode_idx_in in2", name, in2_sel, In2Sel.RB.value,
+ in2, in2_isvec)
+ print ("get_pdecode_idx_in in3", name, in3_sel, In3Sel.RS.value,
+ in3, in3_isvec)
+ # identify which regnames map to in1/2/3
+ if name == 'RA':
+ if (in1_sel == In1Sel.RA.value or
+ (in1_sel == In1Sel.RA_OR_ZERO.value and in1 != 0)):
+ return in1, in1_isvec
+ if in1_sel == In1Sel.RA_OR_ZERO.value:
+ return in1, in1_isvec
+ elif name == 'RB':
+ if in2_sel == In2Sel.RB.value:
+ return in2, in2_isvec
+ if in3_sel == In3Sel.RB.value:
+ return in3, in3_isvec
+ # XXX TODO, RC doesn't exist yet!
+ elif name == 'RC':
+ assert False, "RC does not exist yet"
+ elif name == 'RS':
+ if in1_sel == In1Sel.RS.value:
+ return in1, in1_isvec
+ if in2_sel == In2Sel.RS.value:
+ return in2, in2_isvec
+ if in3_sel == In3Sel.RS.value:
+ return in3, in3_isvec
+ return None, False
+
+
+def get_pdecode_cr_out(dec2, name):
+ op = dec2.dec.op
+ out_sel = yield op.cr_out
+ out_bitfield = yield dec2.dec_cr_out.cr_bitfield.data
+ sv_cr_out = yield op.sv_cr_out
+ spec = yield dec2.crout_svdec.spec
+ sv_override = yield dec2.dec_cr_out.sv_override
+ # get the IN1/2/3 from the decoder (includes SVP64 remap and isvec)
+ out = yield dec2.e.write_cr.data
+ o_isvec = yield dec2.o_isvec
+ print ("get_pdecode_cr_out", out_sel, CROutSel.CR0.value, out, o_isvec)
+ print (" sv_cr_out", sv_cr_out)
+ print (" cr_bf", out_bitfield)
+ print (" spec", spec)
+ print (" override", sv_override)
+ # identify which regnames map to out / o2
+ if name == 'CR0':
+ if out_sel == CROutSel.CR0.value:
+ return out, o_isvec
+ print ("get_pdecode_idx_out not found", name)
+ return None, False
+
+
+def get_pdecode_idx_out(dec2, name):
+ op = dec2.dec.op
+ out_sel = yield op.out_sel
+ # get the IN1/2/3 from the decoder (includes SVP64 remap and isvec)
+ out = yield dec2.e.write_reg.data
+ o_isvec = yield dec2.o_isvec
+ # identify which regnames map to out / o2
+ if name == 'RA':
+ print ("get_pdecode_idx_out", out_sel, OutSel.RA.value, out, o_isvec)
+ if out_sel == OutSel.RA.value:
+ return out, o_isvec
+ elif name == 'RT':
+ print ("get_pdecode_idx_out", out_sel, OutSel.RT.value,
+ OutSel.RT_OR_ZERO.value, out, o_isvec)
+ if out_sel == OutSel.RT.value:
+ return out, o_isvec
+ print ("get_pdecode_idx_out not found", name)
+ return None, False
+
+
+# XXX TODO
+def get_pdecode_idx_out2(dec2, name):
+ op = dec2.dec.op
+ print ("TODO: get_pdecode_idx_out2", name)
+ return None, False
+
+
+class ISACaller:
+ # decoder2 - an instance of power_decoder2
+ # regfile - a list of initial values for the registers
+ # initial_{etc} - initial values for SPRs, Condition Register, Mem, MSR
+ # respect_pc - tracks the program counter. requires initial_insns
+ def __init__(self, decoder2, regfile, initial_sprs=None, initial_cr=0,
+ initial_mem=None, initial_msr=0,
+ initial_svstate=0,
+ initial_insns=None, respect_pc=False,
+ disassembly=None,
+ initial_pc=0,
+ bigendian=False,
+ mmu=False,
+ icachemmu=False):
+
+ self.bigendian = bigendian
+ self.halted = False
+ self.is_svp64_mode = False
+ self.respect_pc = respect_pc
+ if initial_sprs is None:
+ initial_sprs = {}
+ if initial_mem is None:
+ initial_mem = {}
+ if initial_insns is None:
+ initial_insns = {}
+ assert self.respect_pc == False, "instructions required to honor pc"
+
+ print("ISACaller insns", respect_pc, initial_insns, disassembly)
+ print("ISACaller initial_msr", initial_msr)
+
+ # "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 + disasm_start] = code
+
+ # set up registers, instruction memory, data memory, PC, SPRs, MSR
+ self.svp64rm = SVP64RM()
+ if initial_svstate is None:
+ initial_svstate = 0
+ if isinstance(initial_svstate, int):
+ initial_svstate = SVP64State(initial_svstate)
+ self.svstate = initial_svstate
+ self.gpr = GPR(decoder2, self, self.svstate, regfile)
+ self.spr = SPR(decoder2, initial_sprs) # initialise SPRs before MMU
+ self.mem = Mem(row_bytes=8, initial_mem=initial_mem)
+ self.imem = Mem(row_bytes=4, initial_mem=initial_insns)
+ # MMU mode, redirect underlying Mem through RADIX
+ self.msr = SelectableInt(initial_msr, 64) # underlying reg
+ if mmu:
+ self.mem = RADIX(self.mem, self)
+ if icachemmu:
+ self.imem = RADIX(self.imem, self)
+ self.pc = PC()
+
+ # TODO, needed here:
+ # FPR (same as GPR except for FP nums)
+ # 4.2.2 p124 FPSCR (definitely "separate" - not in SPR)
+ # note that mffs, mcrfs, mtfsf "manage" this FPSCR
+ # 2.3.1 CR (and sub-fields CR0..CR6 - CR0 SO comes from XER.SO)
+ # note that mfocrf, mfcr, mtcr, mtocrf, mcrxrx "manage" CRs
+ # -- Done
+ # 2.3.2 LR (actually SPR #8) -- Done
+ # 2.3.3 CTR (actually SPR #9) -- Done
+ # 2.3.4 TAR (actually SPR #815)
+ # 3.2.2 p45 XER (actually SPR #1) -- Done
+ # 3.2.3 p46 p232 VRSAVE (actually SPR #256)
+
+ # create CR then allow portions of it to be "selectable" (below)
+ #rev_cr = int('{:016b}'.format(initial_cr)[::-1], 2)
+ self.cr = SelectableInt(initial_cr, 64) # underlying reg
+ #self.cr = FieldSelectableInt(self._cr, list(range(32, 64)))
+
+ # "undefined", just set to variable-bit-width int (use exts "max")
+ #self.undefined = SelectableInt(0, 256) # TODO, not hard-code 256!
+
+ self.namespace = {}
+ self.namespace.update(self.spr)
+ self.namespace.update({'GPR': self.gpr,
+ 'MEM': self.mem,
+ 'SPR': self.spr,
+ 'memassign': self.memassign,
+ 'NIA': self.pc.NIA,
+ 'CIA': self.pc.CIA,
+ 'SVSTATE': self.svstate.spr,
+ 'CR': self.cr,
+ 'MSR': self.msr,
+ 'undefined': 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 = []
+ for i in range(8):
+ bits = tuple(range(i*4+32, (i+1)*4+32)) # errr... maybe?
+ _cr = FieldSelectableInt(self.cr, bits)
+ self.crl.append(_cr)
+ self.namespace["CR%d" % i] = _cr
+
+ self.decoder = decoder2.dec
+ self.dec2 = decoder2
+
+ def TRAP(self, trap_addr=0x700, trap_bit=PIb.TRAP):
+ print("TRAP:", hex(trap_addr), hex(self.namespace['MSR'].value))
+ # 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'].value = self.pc.CIA.value
+ self.spr['SRR1'].value = self.namespace['MSR'].value
+ self.trap_nia = SelectableInt(trap_addr, 64)
+ self.spr['SRR1'][trap_bit] = 1 # change *copy* of MSR in SRR1
+
+ # set exception bits. TODO: this should, based on the address
+ # in figure 66 p1065 V3.0B and the table figure 65 p1063 set these
+ # bits appropriately. however it turns out that *for now* in all
+ # cases (all trap_addrs) the exact same thing is needed.
+ self.msr[MSRb.IR] = 0
+ self.msr[MSRb.DR] = 0
+ self.msr[MSRb.FE0] = 0
+ self.msr[MSRb.FE1] = 0
+ self.msr[MSRb.EE] = 0
+ self.msr[MSRb.RI] = 0
+ self.msr[MSRb.SF] = 1
+ self.msr[MSRb.TM] = 0
+ self.msr[MSRb.VEC] = 0
+ self.msr[MSRb.VSX] = 0
+ self.msr[MSRb.PR] = 0
+ self.msr[MSRb.FP] = 0
+ self.msr[MSRb.PMM] = 0
+ self.msr[MSRb.TEs] = 0
+ self.msr[MSRb.TEe] = 0
+ self.msr[MSRb.UND] = 0
+ self.msr[MSRb.LE] = 1
+
+ def memassign(self, ea, sz, val):
+ self.mem.memassign(ea, sz, val)
+
+ def prep_namespace(self, formname, op_fields):
+ # TODO: get field names from form in decoder*1* (not decoder2)
+ # decoder2 is hand-created, and decoder1.sigform is auto-generated
+ # from spec
+ # then "yield" fields only from op_fields rather than hard-coded
+ # list, here.
+ fields = self.decoder.sigforms[formname]
+ for name in op_fields:
+ if name == 'spr':
+ sig = getattr(fields, name.upper())
+ else:
+ sig = getattr(fields, name)
+ val = yield sig
+ # these are all opcode fields involved in index-selection of CR,
+ # and need to do "standard" arithmetic. CR[BA+32] for example
+ # would, if using SelectableInt, only be 5-bit.
+ if name in ['BF', 'BFA', 'BC', 'BA', 'BB', 'BT', 'BI']:
+ self.namespace[name] = val
+ else:
+ self.namespace[name] = SelectableInt(val, sig.width)
+
+ self.namespace['XER'] = self.spr['XER']
+ self.namespace['CA'] = self.spr['XER'][XER_bits['CA']].value
+ self.namespace['CA32'] = self.spr['XER'][XER_bits['CA32']].value
+
+ def handle_carry_(self, inputs, outputs, already_done):
+ inv_a = yield self.dec2.e.do.invert_in
+ if inv_a:
+ inputs[0] = ~inputs[0]
+
+ imm_ok = yield self.dec2.e.do.imm_data.ok
+ if imm_ok:
+ imm = yield self.dec2.e.do.imm_data.data
+ inputs.append(SelectableInt(imm, 64))
+ assert len(outputs) >= 1
+ print("outputs", repr(outputs))
+ if isinstance(outputs, list) or isinstance(outputs, tuple):
+ output = outputs[0]
+ else:
+ output = outputs
+ gts = []
+ for x in inputs:
+ print("gt input", x, output)
+ gt = (gtu(x, output))
+ gts.append(gt)
+ print(gts)
+ cy = 1 if any(gts) else 0
+ print("CA", cy, gts)
+ if not (1 & already_done):
+ self.spr['XER'][XER_bits['CA']] = cy
+
+ print("inputs", already_done, inputs)
+ # 32 bit carry
+ # ARGH... different for OP_ADD... *sigh*...
+ op = yield self.dec2.e.do.insn_type
+ if op == MicrOp.OP_ADD.value:
+ res32 = (output.value & (1 << 32)) != 0
+ a32 = (inputs[0].value & (1 << 32)) != 0
+ if len(inputs) >= 2:
+ b32 = (inputs[1].value & (1 << 32)) != 0
+ else:
+ b32 = False
+ cy32 = res32 ^ a32 ^ b32
+ print("CA32 ADD", cy32)
+ else:
+ gts = []
+ for x in inputs:
+ print("input", x, output)
+ print(" x[32:64]", x, x[32:64])
+ print(" o[32:64]", output, output[32:64])
+ gt = (gtu(x[32:64], output[32:64])) == SelectableInt(1, 1)
+ gts.append(gt)
+ cy32 = 1 if any(gts) else 0
+ print("CA32", cy32, gts)
+ if not (2 & already_done):
+ self.spr['XER'][XER_bits['CA32']] = cy32
+
+ def handle_overflow(self, inputs, outputs, div_overflow):
+ if hasattr(self.dec2.e.do, "invert_in"):
+ inv_a = yield self.dec2.e.do.invert_in
+ if inv_a:
+ inputs[0] = ~inputs[0]
+
+ imm_ok = yield self.dec2.e.do.imm_data.ok
+ if imm_ok:
+ imm = yield self.dec2.e.do.imm_data.data
+ inputs.append(SelectableInt(imm, 64))
+ assert len(outputs) >= 1
+ 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)
+ input_sgn = [exts(x.value, x.bits) < 0 for x in inputs]
+ output_sgn = exts(output.value, output.bits) < 0
+ ov = 1 if input_sgn[0] == input_sgn[1] and \
+ output_sgn != input_sgn[0] else 0
+
+ # OV (32-bit)
+ input32_sgn = [exts(x.value, 32) < 0 for x in inputs]
+ output32_sgn = exts(output.value, 32) < 0
+ 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
+
+ def handle_comparison(self, outputs, cr_idx=0):
+ out = outputs[0]
+ assert isinstance(out, SelectableInt), \
+ "out zero not a SelectableInt %s" % repr(outputs)
+ 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[cr_idx].eq(cr_field)
+
+ def set_pc(self, pc_val):
+ self.namespace['NIA'] = SelectableInt(pc_val, 64)
+ self.pc.update(self.namespace, self.is_svp64_mode)
+
+ def setup_one(self):
+ """set up one instruction
+ """
+ if self.respect_pc:
+ pc = self.pc.CIA.value
+ else:
+ pc = self.fake_pc
+ self._pc = pc
+ ins = self.imem.ld(pc, 4, False, True, instr_fetch=True)
+ 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("CIA NIA", self.respect_pc, self.pc.CIA.value, self.pc.NIA.value)
+
+ yield self.dec2.sv_rm.eq(0)
+ yield self.dec2.dec.raw_opcode_in.eq(ins & 0xffffffff)
+ yield self.dec2.dec.bigendian.eq(self.bigendian)
+ yield self.dec2.state.msr.eq(self.msr.value)
+ yield self.dec2.state.pc.eq(pc)
+ if self.svstate is not None:
+ yield self.dec2.state.svstate.eq(self.svstate.spr.value)
+
+ # SVP64. first, check if the opcode is EXT001, and SVP64 id bits set
+ yield Settle()
+ opcode = yield self.dec2.dec.opcode_in
+ pfx = SVP64PrefixFields() # TODO should probably use SVP64PrefixDecoder
+ pfx.insn.value = opcode
+ major = pfx.major.asint(msb0=True) # MSB0 inversion
+ print ("prefix test: opcode:", major, bin(major),
+ pfx.insn[7] == 0b1, pfx.insn[9] == 0b1)
+ self.is_svp64_mode = ((major == 0b000001) and
+ pfx.insn[7].value == 0b1 and
+ pfx.insn[9].value == 0b1)
+ self.pc.update_nia(self.is_svp64_mode)
+ self.namespace['NIA'] = self.pc.NIA
+ self.namespace['SVSTATE'] = self.svstate.spr
+ if not self.is_svp64_mode:
+ return
+
+ # in SVP64 mode. decode/print out svp64 prefix, get v3.0B instruction
+ print ("svp64.rm", bin(pfx.rm.asint(msb0=True)))
+ print (" svstate.vl", self.svstate.vl.asint(msb0=True))
+ print (" svstate.mvl", self.svstate.maxvl.asint(msb0=True))
+ sv_rm = pfx.rm.asint(msb0=True)
+ ins = self.imem.ld(pc+4, 4, False, True, instr_fetch=True)
+ print(" svsetup: 0x%x 0x%x %s" % (pc+4, ins & 0xffffffff, bin(ins)))
+ yield self.dec2.dec.raw_opcode_in.eq(ins & 0xffffffff) # v3.0B suffix
+ yield self.dec2.sv_rm.eq(sv_rm) # svp64 prefix
+ yield Settle()
+
+ def execute_one(self):
+ """execute one instruction
+ """
+ # get the disassembly code for this instruction
+ if self.is_svp64_mode:
+ code = self.disassembly[self._pc+4]
+ print(" svp64 sim-execute", hex(self._pc), code)
+ else:
+ code = self.disassembly[self._pc]
+ print("sim-execute", hex(self._pc), code)
+ opname = code.split(' ')[0]
+ yield from self.call(opname)
+
+ # don't use this except in special circumstances
+ if not self.respect_pc:
+ self.fake_pc += 4
+
+ 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
+ dec_insn = yield self.dec2.e.do.insn
+ asmcode = yield self.dec2.dec.op.asmcode
+ print("get assembly name asmcode", asmcode, hex(dec_insn))
+ asmop = insns.get(asmcode, None)
+ int_op = yield self.dec2.dec.op.internal_op
+
+ # sigh reconstruct the assembly instruction name
+ if hasattr(self.dec2.e.do, "oe"):
+ ov_en = yield self.dec2.e.do.oe.oe
+ ov_ok = yield self.dec2.e.do.oe.ok
+ else:
+ ov_en = False
+ ov_ok = False
+ if hasattr(self.dec2.e.do, "rc"):
+ rc_en = yield self.dec2.e.do.rc.rc
+ rc_ok = yield self.dec2.e.do.rc.ok
+ else:
+ rc_en = False
+ rc_ok = False
+ # grrrr have to special-case MUL op (see DecodeOE)
+ print("ov %d en %d rc %d en %d op %d" %
+ (ov_ok, ov_en, rc_ok, rc_en, int_op))
+ if int_op in [MicrOp.OP_MUL_H64.value, MicrOp.OP_MUL_H32.value]:
+ print("mul op")
+ if rc_en & rc_ok:
+ asmop += "."
+ else:
+ if not asmop.endswith("."): # don't add "." to "andis."
+ if rc_en & rc_ok:
+ asmop += "."
+ if hasattr(self.dec2.e.do, "lk"):
+ lk = yield self.dec2.e.do.lk
+ if lk:
+ asmop += "l"
+ print("int_op", int_op)
+ if int_op in [MicrOp.OP_B.value, MicrOp.OP_BC.value]:
+ AA = yield self.dec2.dec.fields.FormI.AA[0:-1]
+ print("AA", AA)
+ if AA:
+ asmop += "a"
+ spr_msb = yield from self.get_spr_msb()
+ if int_op == MicrOp.OP_MFCR.value:
+ if spr_msb:
+ asmop = 'mfocrf'
+ else:
+ asmop = 'mfcr'
+ # XXX TODO: for whatever weird reason this doesn't work
+ # https://bugs.libre-soc.org/show_bug.cgi?id=390
+ if int_op == MicrOp.OP_MTCRF.value:
+ if spr_msb:
+ asmop = 'mtocrf'
+ else:
+ asmop = 'mtcrf'
+ return asmop
+
+ def get_spr_msb(self):
+ dec_insn = yield self.dec2.e.do.insn
+ return dec_insn & (1 << 20) != 0 # sigh - XFF.spr[-1]?
+
+ def call(self, name):
+ """call(opcode) - the primary execution point for instructions
+ """
+ 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 privileged
+ int_op = yield self.dec2.dec.op.internal_op
+ spr_msb = yield from self.get_spr_msb()
+
+ instr_is_privileged = False
+ if int_op in [MicrOp.OP_ATTN.value,
+ MicrOp.OP_MFMSR.value,
+ MicrOp.OP_MTMSR.value,
+ MicrOp.OP_MTMSRD.value,
+ # TODO: OP_TLBIE
+ MicrOp.OP_RFID.value]:
+ instr_is_privileged = True
+ if int_op in [MicrOp.OP_MFSPR.value,
+ MicrOp.OP_MTSPR.value] and spr_msb:
+ instr_is_privileged = True
+
+ print("is priv", instr_is_privileged, hex(self.msr.value),
+ self.msr[MSRb.PR])
+ # check MSR priv bit and whether op is privileged: if so, throw trap
+ if instr_is_privileged and self.msr[MSRb.PR] == 1:
+ self.TRAP(0x700, PIb.PRIV)
+ self.namespace['NIA'] = self.trap_nia
+ self.pc.update(self.namespace, self.is_svp64_mode)
+ return
+
+ # check halted condition
+ if name == 'attn':
+ self.halted = True
+ return
+
+ # check illegal instruction
+ illegal = False
+ if name not in ['mtcrf', 'mtocrf']:
+ illegal = name != asmop
+
+ # sigh deal with setvl not being supported by binutils (.long)
+ if asmop.startswith('setvl'):
+ illegal = False
+ name = 'setvl'
+
+ if illegal:
+ print("illegal", name, asmop)
+ self.TRAP(0x700, PIb.ILLEG)
+ self.namespace['NIA'] = self.trap_nia
+ self.pc.update(self.namespace, self.is_svp64_mode)
+ print("name %s != %s - calling ILLEGAL trap, PC: %x" %
+ (name, asmop, self.pc.CIA.value))
+ 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))
+ print(input_names)
+
+ # get SVP64 entry for the current instruction
+ sv_rm = self.svp64rm.instrs.get(name)
+ if sv_rm is not None:
+ dest_cr, src_cr, src_byname, dest_byname = decode_extra(sv_rm)
+ else:
+ dest_cr, src_cr, src_byname, dest_byname = False, False, {}, {}
+ print ("sv rm", sv_rm, dest_cr, src_cr, src_byname, dest_byname)
+
+ # get SVSTATE VL (oh and print out some debug stuff)
+ if self.is_svp64_mode:
+ vl = self.svstate.vl.asint(msb0=True)
+ srcstep = self.svstate.srcstep.asint(msb0=True)
+ dststep = self.svstate.dststep.asint(msb0=True)
+ sv_a_nz = yield self.dec2.sv_a_nz
+ in1 = yield self.dec2.e.read_reg1.data
+ print ("SVP64: VL, srcstep, dststep, sv_a_nz, in1",
+ vl, srcstep, dststep, sv_a_nz, in1)
+
+ # get predicate mask
+ srcmask = dstmask = 0xffff_ffff_ffff_ffff
+ if self.is_svp64_mode:
+ pmode = yield self.dec2.rm_dec.predmode
+ sv_ptype = yield self.dec2.dec.op.SV_Ptype
+ srcpred = yield self.dec2.rm_dec.srcpred
+ dstpred = yield self.dec2.rm_dec.dstpred
+ pred_src_zero = yield self.dec2.rm_dec.pred_sz
+ pred_dst_zero = yield self.dec2.rm_dec.pred_dz
+ if pmode == SVP64PredMode.INT.value:
+ srcmask = dstmask = get_predint(self.gpr, dstpred)
+ if sv_ptype == SVPtype.P2.value:
+ srcmask = get_predint(self.gpr, srcpred)
+ elif pmode == SVP64PredMode.CR.value:
+ srcmask = dstmask = get_predcr(self.crl, dstpred, vl)
+ if sv_ptype == SVPtype.P2.value:
+ srcmask = get_predcr(self.crl, srcpred, vl)
+ print (" pmode", pmode)
+ print (" ptype", sv_ptype)
+ print (" srcpred", bin(srcpred))
+ print (" dstpred", bin(dstpred))
+ print (" srcmask", bin(srcmask))
+ print (" dstmask", bin(dstmask))
+ print (" pred_sz", bin(pred_src_zero))
+ print (" pred_dz", bin(pred_dst_zero))
+
+ # okaaay, so here we simply advance srcstep (TODO dststep)
+ # until the predicate mask has a "1" bit... or we run out of VL
+ # let srcstep==VL be the indicator to move to next instruction
+ if not pred_src_zero:
+ while (((1<<srcstep) & srcmask) == 0) and (srcstep != vl):
+ print (" skip", bin(1<<srcstep))
+ srcstep += 1
+ # same for dststep
+ if not pred_dst_zero:
+ while (((1<<dststep) & dstmask) == 0) and (dststep != vl):
+ print (" skip", bin(1<<dststep))
+ dststep += 1
+
+ # now work out if the relevant mask bits require zeroing
+ if pred_dst_zero:
+ pred_dst_zero = ((1<<dststep) & dstmask) == 0
+ if pred_src_zero:
+ pred_src_zero = ((1<<srcstep) & srcmask) == 0
+
+ # update SVSTATE with new srcstep
+ self.svstate.srcstep[0:7] = srcstep
+ self.svstate.dststep[0:7] = dststep
+ self.namespace['SVSTATE'] = self.svstate.spr
+ yield self.dec2.state.svstate.eq(self.svstate.spr.value)
+ yield Settle() # let decoder update
+ srcstep = self.svstate.srcstep.asint(msb0=True)
+ dststep = self.svstate.dststep.asint(msb0=True)
+ print (" srcstep", srcstep)
+ print (" dststep", dststep)
+
+ # check if end reached (we let srcstep overrun, above)
+ # nothing needs doing (TODO zeroing): just do next instruction
+ if srcstep == vl or dststep == vl:
+ self.svp64_reset_loop()
+ self.update_pc_next()
+ return
+
+ # VL=0 in SVP64 mode means "do nothing: skip instruction"
+ if self.is_svp64_mode and vl == 0:
+ self.pc.update(self.namespace, self.is_svp64_mode)
+ print("SVP64: VL=0, end of call", self.namespace['CIA'],
+ self.namespace['NIA'])
+ return
+
+ # main input registers (RT, RA ...)
+ inputs = []
+ for name in input_names:
+ # using PowerDecoder2, first, find the decoder index.
+ # (mapping name RA RB RC RS to in1, in2, in3)
+ regnum, is_vec = yield from get_pdecode_idx_in(self.dec2, name)
+ if regnum is None:
+ # doing this is not part of svp64, it's because output
+ # registers, to be modified, need to be in the namespace.
+ regnum, is_vec = yield from get_pdecode_idx_out(self.dec2, name)
+
+ # in case getting the register number is needed, _RA, _RB
+ regname = "_" + name
+ self.namespace[regname] = regnum
+ if not self.is_svp64_mode or not pred_src_zero:
+ print('reading reg %s %s' % (name, str(regnum)), is_vec)
+ reg_val = self.gpr(regnum)
+ else:
+ print('zero input reg %s %s' % (name, str(regnum)), is_vec)
+ reg_val = 0
+ inputs.append(reg_val)
+
+ # "special" registers
+ for special in info.special_regs:
+ if special in special_sprs:
+ inputs.append(self.spr[special])
+ else:
+ inputs.append(self.namespace[special])
+
+ # clear trap (trap) NIA
+ self.trap_nia = None
+
+ # execute actual instruction here
+ print("inputs", inputs)
+ results = info.func(self, *inputs)
+ print("results", 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:
+ output_names = create_args(info.write_regs)
+ for name in output_names:
+ if name == 'CA':
+ already_done |= 1
+ if name == 'CA32':
+ already_done |= 2
+
+ print("carry already done?", bin(already_done))
+ if hasattr(self.dec2.e.do, "output_carry"):
+ carry_en = yield self.dec2.e.do.output_carry
+ else:
+ carry_en = False
+ if carry_en:
+ yield from self.handle_carry_(inputs, results, already_done)
+
+ if not self.is_svp64_mode: # yeah just no. not in parallel processing
+ # 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
+
+ if hasattr(self.dec2.e.do, "oe"):
+ ov_en = yield self.dec2.e.do.oe.oe
+ ov_ok = yield self.dec2.e.do.oe.ok
+ else:
+ ov_en = False
+ ov_ok = False
+ print("internal overflow", overflow, ov_en, ov_ok)
+ if ov_en & ov_ok:
+ yield from self.handle_overflow(inputs, results, overflow)
+
+ # only do SVP64 dest predicated Rc=1 if dest-pred is not enabled
+ rc_en = False
+ if not self.is_svp64_mode or not pred_dst_zero:
+ if hasattr(self.dec2.e.do, "rc"):
+ rc_en = yield self.dec2.e.do.rc.rc
+ if rc_en:
+ regnum, is_vec = yield from get_pdecode_cr_out(self.dec2, "CR0")
+ self.handle_comparison(results, regnum)
+
+ # 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']:
+ if carry_en:
+ print("writing %s to XER" % name, output)
+ self.spr['XER'][XER_bits[name]] = output.value
+ else:
+ print("NOT writing %s to XER" % name, output)
+ elif name in info.special_regs:
+ print('writing special %s' % name, output, special_sprs)
+ if name in special_sprs:
+ self.spr[name] = output
+ else:
+ self.namespace[name].eq(output)
+ if name == 'MSR':
+ print('msr written', hex(self.msr.value))
+ else:
+ regnum, is_vec = yield from get_pdecode_idx_out(self.dec2,
+ name)
+ if regnum is None:
+ # temporary hack for not having 2nd output
+ regnum = yield getattr(self.decoder, name)
+ is_vec = False
+ if self.is_svp64_mode and pred_dst_zero:
+ print('zeroing reg %d %s' % (regnum, str(output)),
+ is_vec)
+ output = SelectableInt(0, 256)
+ else:
+ print('writing reg %d %s' % (regnum, str(output)),
+ is_vec)
+ if output.bits > 64:
+ output = SelectableInt(output.value, 64)
+ self.gpr[regnum] = output
+
+ # check if it is the SVSTATE.src/dest step that needs incrementing
+ # this is our Sub-Program-Counter loop from 0 to VL-1
+ if self.is_svp64_mode:
+ # XXX twin predication TODO
+ vl = self.svstate.vl.asint(msb0=True)
+ mvl = self.svstate.maxvl.asint(msb0=True)
+ srcstep = self.svstate.srcstep.asint(msb0=True)
+ dststep = self.svstate.dststep.asint(msb0=True)
+ sv_ptype = yield self.dec2.dec.op.SV_Ptype
+ no_out_vec = not (yield self.dec2.no_out_vec)
+ no_in_vec = not (yield self.dec2.no_in_vec)
+ print (" svstate.vl", vl)
+ print (" svstate.mvl", mvl)
+ print (" svstate.srcstep", srcstep)
+ print (" svstate.dststep", dststep)
+ print (" no_out_vec", no_out_vec)
+ print (" no_in_vec", no_in_vec)
+ print (" sv_ptype", sv_ptype, sv_ptype == SVPtype.P2.value)
+ # check if srcstep needs incrementing by one, stop PC advancing
+ # svp64 loop can end early if the dest is scalar for single-pred
+ # but for 2-pred both src/dest have to be checked.
+ # XXX this might not be true! it may just be LD/ST
+ if sv_ptype == SVPtype.P2.value:
+ svp64_is_vector = (no_out_vec or no_in_vec)
+ else:
+ svp64_is_vector = no_out_vec
+ if svp64_is_vector and srcstep != vl-1 and dststep != vl-1:
+ self.svstate.srcstep += SelectableInt(1, 7)
+ self.svstate.dststep += SelectableInt(1, 7)
+ self.pc.NIA.value = self.pc.CIA.value
+ self.namespace['NIA'] = self.pc.NIA
+ self.namespace['SVSTATE'] = self.svstate.spr
+ print("end of sub-pc call", self.namespace['CIA'],
+ self.namespace['NIA'])
+ return # DO NOT allow PC to update whilst Sub-PC loop running
+ # reset loop to zero
+ self.svp64_reset_loop()
+
+ self.update_pc_next()
+
+ def update_pc_next(self):
+ # UPDATE program counter
+ self.pc.update(self.namespace, self.is_svp64_mode)
+ self.svstate.spr = self.namespace['SVSTATE']
+ print("end of call", self.namespace['CIA'],
+ self.namespace['NIA'],
+ self.namespace['SVSTATE'])
+
+ def svp64_reset_loop(self):
+ self.svstate.srcstep[0:7] = 0
+ self.svstate.dststep[0:7] = 0
+ print (" svstate.srcstep loop end (PC to update)")
+ self.pc.update_nia(self.is_svp64_mode)
+ self.namespace['NIA'] = self.pc.NIA
+ self.namespace['SVSTATE'] = self.svstate.spr
+
+def inject():
+ """Decorator factory.
+
+ this decorator will "inject" variables into the function's namespace,
+ from the *dictionary* in self.namespace. it therefore becomes possible
+ to make it look like a whole stack of variables which would otherwise
+ need "self." inserted in front of them (*and* for those variables to be
+ added to the instance) "appear" in the function.
+
+ "self.namespace['SI']" for example becomes accessible as just "SI" but
+ *only* inside the function, when decorated.
+ """
+ def variable_injector(func):
+ @wraps(func)
+ def decorator(*args, **kwargs):
+ try:
+ func_globals = func.__globals__ # Python 2.6+
+ except AttributeError:
+ func_globals = func.func_globals # Earlier versions.
+
+ context = args[0].namespace # variables to be injected
+ 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['SVSTATE'])
+ args[0].namespace = func_globals
+ #exec (func.__code__, func_globals)
+
+ # finally:
+ # func_globals = saved_values # Undo changes.
+
+ return result
+
+ return decorator
+
+ return variable_injector
+
+
--- /dev/null
+# SPDX-License-Identifier: LGPLv3+
+# Copyright (C) 2020, 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Funded by NLnet http://nlnet.nl
+"""core of the python-based POWER9 simulator
+
+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 copy import copy
+from soc.decoder.selectable_int import (FieldSelectableInt, SelectableInt,
+ selectconcat)
+
+from soc.decoder.helpers import exts, gtu, ltu, undefined
+import math
+import sys
+
+
+def swap_order(x, nbytes):
+ x = x.to_bytes(nbytes, byteorder='little')
+ x = int.from_bytes(x, byteorder='big', signed=False)
+ return x
+
+
+
+class Mem:
+
+ def __init__(self, row_bytes=8, initial_mem=None):
+ self.mem = {}
+ self.bytes_per_word = row_bytes
+ self.word_log2 = math.ceil(math.log2(row_bytes))
+ print("Sim-Mem", initial_mem, self.bytes_per_word, self.word_log2)
+ if not initial_mem:
+ return
+
+ # different types of memory data structures recognised (for convenience)
+ if isinstance(initial_mem, list):
+ initial_mem = (0, initial_mem)
+ if isinstance(initial_mem, tuple):
+ startaddr, mem = initial_mem
+ initial_mem = {}
+ for i, val in enumerate(mem):
+ initial_mem[startaddr + row_bytes*i] = (val, row_bytes)
+
+ for addr, val in initial_mem.items():
+ if isinstance(val, tuple):
+ (val, width) = val
+ else:
+ width = row_bytes # assume same width
+ #val = swap_order(val, width)
+ self.st(addr, val, width, swap=False)
+
+ def _get_shifter_mask(self, wid, remainder):
+ shifter = ((self.bytes_per_word - wid) - remainder) * \
+ 8 # bits per byte
+ # XXX https://bugs.libre-soc.org/show_bug.cgi?id=377
+ # BE/LE mode?
+ shifter = remainder * 8
+ mask = (1 << (wid * 8)) - 1
+ print("width,rem,shift,mask", wid, remainder, hex(shifter), hex(mask))
+ return shifter, mask
+
+ # TODO: Implement ld/st of lesser width
+ def ld(self, address, width=8, swap=True, check_in_mem=False,
+ instr_fetch=False):
+ print("ld from addr 0x{:x} width {:d}".format(address, width),
+ swap, check_in_mem, instr_fetch)
+ remainder = address & (self.bytes_per_word - 1)
+ address = address >> self.word_log2
+ assert remainder & (width - 1) == 0, "Unaligned access unsupported!"
+ if address in self.mem:
+ val = self.mem[address]
+ elif check_in_mem:
+ return None
+ else:
+ val = 0
+ print("mem @ 0x{:x} rem {:d} : 0x{:x}".format(address, remainder, val))
+
+ if width != self.bytes_per_word:
+ shifter, mask = self._get_shifter_mask(width, remainder)
+ print("masking", hex(val), hex(mask << shifter), shifter)
+ val = val & (mask << shifter)
+ val >>= shifter
+ if swap:
+ val = swap_order(val, width)
+ print("Read 0x{:x} from addr 0x{:x}".format(val, address))
+ return val
+
+ def st(self, addr, v, width=8, swap=True):
+ staddr = addr
+ remainder = addr & (self.bytes_per_word - 1)
+ addr = addr >> self.word_log2
+ print("Writing 0x{:x} to ST 0x{:x} "
+ "memaddr 0x{:x}/{:x}".format(v, staddr, addr, remainder, swap))
+ assert remainder & (width - 1) == 0, "Unaligned access unsupported!"
+ if swap:
+ v = swap_order(v, width)
+ if width != self.bytes_per_word:
+ if addr in self.mem:
+ val = self.mem[addr]
+ else:
+ val = 0
+ shifter, mask = self._get_shifter_mask(width, remainder)
+ val &= ~(mask << shifter)
+ val |= v << shifter
+ self.mem[addr] = val
+ else:
+ self.mem[addr] = v
+ print("mem @ 0x{:x}: 0x{:x}".format(addr, self.mem[addr]))
+
+ def __call__(self, addr, sz):
+ val = self.ld(addr.value, sz, swap=False)
+ print("memread", addr, sz, val)
+ return SelectableInt(val, sz*8)
+
+ def memassign(self, addr, sz, val):
+ print("memassign", addr, sz, val)
+ self.st(addr.value, val.value, sz, swap=False)
+
+
--- /dev/null
+# SPDX-License-Identifier: LGPLv3+
+# Copyright (C) 2020, 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2021 Tobias Platen
+# Funded by NLnet http://nlnet.nl
+"""core of the python-based POWER9 simulator
+
+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=604
+"""
+
+#from nmigen.back.pysim import Settle
+from copy import copy
+from soc.decoder.selectable_int import (FieldSelectableInt, SelectableInt,
+ selectconcat)
+from soc.decoder.helpers import exts, gtu, ltu, undefined
+from soc.decoder.isa.mem import Mem
+from soc.consts import MSRb # big-endian (PowerISA versions)
+
+import math
+import sys
+import unittest
+
+# very quick, TODO move to SelectableInt utils later
+def genmask(shift, size):
+ res = SelectableInt(0, size)
+ for i in range(size):
+ if i < shift:
+ res[size-1-i] = SelectableInt(1, 1)
+ return res
+
+# NOTE: POWER 3.0B annotation order! see p4 1.3.2
+# MSB is indexed **LOWEST** (sigh)
+# from gem5 radixwalk.hh
+# Bitfield<63> valid; 64 - (63 + 1) = 0
+# Bitfield<62> leaf; 64 - (62 + 1) = 1
+
+def rpte_valid(r):
+ return bool(r[0])
+
+def rpte_leaf(r):
+ return bool(r[1])
+
+## Shift address bits 61--12 right by 0--47 bits and
+## supply the least significant 16 bits of the result.
+def addrshift(addr,shift):
+ print("addrshift")
+ print(addr)
+ print(shift)
+ x = addr.value >> shift.value
+ return SelectableInt(x, 16)
+
+def RTS2(data):
+ return data[56:59]
+
+def RTS1(data):
+ return data[1:3]
+
+def RTS(data):
+ zero = SelectableInt(0, 1)
+ return selectconcat(zero, RTS2(data), RTS1(data))
+
+def NLB(x):
+ """
+ Next Level Base
+ right shifted by 8
+ """
+ return x[4:56] # python numbering end+1
+
+def NLS(x):
+ """
+ Next Level Size (PATS and RPDS in same bits btw)
+ NLS >= 5
+ """
+ return x[59:64] # python numbering end+1
+
+def RPDB(x):
+ """
+ Root Page Directory Base
+ power isa docs says 4:55 investigate
+ """
+ return x[8:56] # python numbering end+1
+
+"""
+ Get Root Page
+
+ //Accessing 2nd double word of partition table (pate1)
+ //Ref: Power ISA Manual v3.0B, Book-III, section 5.7.6.1
+ // PTCR Layout
+ // ====================================================
+ // -----------------------------------------------
+ // | /// | PATB | /// | PATS |
+ // -----------------------------------------------
+ // 0 4 51 52 58 59 63
+ // PATB[4:51] holds the base address of the Partition Table,
+ // right shifted by 12 bits.
+ // This is because the address of the Partition base is
+ // 4k aligned. Hence, the lower 12bits, which are always
+ // 0 are ommitted from the PTCR.
+ //
+ // Thus, The Partition Table Base is obtained by (PATB << 12)
+ //
+ // PATS represents the partition table size right-shifted by 12 bits.
+ // The minimal size of the partition table is 4k.
+ // Thus partition table size = (1 << PATS + 12).
+ //
+ // Partition Table
+ // ====================================================
+ // 0 PATE0 63 PATE1 127
+ // |----------------------|----------------------|
+ // | | |
+ // |----------------------|----------------------|
+ // | | |
+ // |----------------------|----------------------|
+ // | | | <-- effLPID
+ // |----------------------|----------------------|
+ // .
+ // .
+ // .
+ // |----------------------|----------------------|
+ // | | |
+ // |----------------------|----------------------|
+ //
+ // The effective LPID forms the index into the Partition Table.
+ //
+ // Each entry in the partition table contains 2 double words, PATE0, PATE1,
+ // corresponding to that partition.
+ //
+ // In case of Radix, The structure of PATE0 and PATE1 is as follows.
+ //
+ // PATE0 Layout
+ // -----------------------------------------------
+ // |1|RTS1|/| RPDB | RTS2 | RPDS |
+ // -----------------------------------------------
+ // 0 1 2 3 4 55 56 58 59 63
+ //
+ // HR[0] : For Radix Page table, first bit should be 1.
+ // RTS1[1:2] : Gives one fragment of the Radix treesize
+ // RTS2[56:58] : Gives the second fragment of the Radix Tree size.
+ // RTS = (RTS1 << 3 + RTS2) + 31.
+ //
+ // RPDB[4:55] = Root Page Directory Base.
+ // RPDS = Logarithm of Root Page Directory Size right shifted by 3.
+ // Thus, Root page directory size = 1 << (RPDS + 3).
+ // Note: RPDS >= 5.
+ //
+ // PATE1 Layout
+ // -----------------------------------------------
+ // |///| PRTB | // | PRTS |
+ // -----------------------------------------------
+ // 0 3 4 51 52 58 59 63
+ //
+ // PRTB[4:51] = Process Table Base. This is aligned to size.
+ // PRTS[59: 63] = Process Table Size right shifted by 12.
+ // Minimal size of the process table is 4k.
+ // Process Table Size = (1 << PRTS + 12).
+ // Note: PRTS <= 24.
+ //
+ // Computing the size aligned Process Table Base:
+ // table_base = (PRTB & ~((1 << PRTS) - 1)) << 12
+ // Thus, the lower 12+PRTS bits of table_base will
+ // be zero.
+
+
+ //Ref: Power ISA Manual v3.0B, Book-III, section 5.7.6.2
+ //
+ // Process Table
+ // ==========================
+ // 0 PRTE0 63 PRTE1 127
+ // |----------------------|----------------------|
+ // | | |
+ // |----------------------|----------------------|
+ // | | |
+ // |----------------------|----------------------|
+ // | | | <-- effPID
+ // |----------------------|----------------------|
+ // .
+ // .
+ // .
+ // |----------------------|----------------------|
+ // | | |
+ // |----------------------|----------------------|
+ //
+ // The effective Process id (PID) forms the index into the Process Table.
+ //
+ // Each entry in the partition table contains 2 double words, PRTE0, PRTE1,
+ // corresponding to that process
+ //
+ // In case of Radix, The structure of PRTE0 and PRTE1 is as follows.
+ //
+ // PRTE0 Layout
+ // -----------------------------------------------
+ // |/|RTS1|/| RPDB | RTS2 | RPDS |
+ // -----------------------------------------------
+ // 0 1 2 3 4 55 56 58 59 63
+ //
+ // RTS1[1:2] : Gives one fragment of the Radix treesize
+ // RTS2[56:58] : Gives the second fragment of the Radix Tree size.
+ // RTS = (RTS1 << 3 + RTS2) << 31,
+ // since minimal Radix Tree size is 4G.
+ //
+ // RPDB = Root Page Directory Base.
+ // RPDS = Root Page Directory Size right shifted by 3.
+ // Thus, Root page directory size = RPDS << 3.
+ // Note: RPDS >= 5.
+ //
+ // PRTE1 Layout
+ // -----------------------------------------------
+ // | /// |
+ // -----------------------------------------------
+ // 0 63
+ // All bits are reserved.
+
+
+"""
+
+testmem = {
+
+ 0x10000: # PARTITION_TABLE_2 (not implemented yet)
+ # PATB_GR=1 PRTB=0x1000 PRTS=0xb
+ 0x800000000100000b,
+
+ 0x30000: # RADIX_ROOT_PTE
+ # V = 1 L = 0 NLB = 0x400 NLS = 9
+ 0x8000000000040009,
+ 0x40000: # RADIX_SECOND_LEVEL
+ # V = 1 L = 1 SW = 0 RPN = 0
+ # R = 1 C = 1 ATT = 0 EAA 0x7
+ 0xc000000000000187,
+
+ 0x1000000: # PROCESS_TABLE_3
+ # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
+ 0x40000000000300ad,
+ }
+
+# this one has a 2nd level RADIX with a RPN of 0x5000
+testmem2 = {
+
+ 0x10000: # PARTITION_TABLE_2 (not implemented yet)
+ # PATB_GR=1 PRTB=0x1000 PRTS=0xb
+ 0x800000000100000b,
+
+ 0x30000: # RADIX_ROOT_PTE
+ # V = 1 L = 0 NLB = 0x400 NLS = 9
+ 0x8000000000040009,
+ 0x40000: # RADIX_SECOND_LEVEL
+ # V = 1 L = 1 SW = 0 RPN = 0x5000
+ # R = 1 C = 1 ATT = 0 EAA 0x7
+ 0xc000000005000187,
+
+ 0x1000000: # PROCESS_TABLE_3
+ # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
+ 0x40000000000300ad,
+ }
+
+testresult = """
+ prtbl = 1000000
+ DCACHE GET 1000000 PROCESS_TABLE_3
+ DCACHE GET 30000 RADIX_ROOT_PTE V = 1 L = 0
+ DCACHE GET 40000 RADIX_SECOND_LEVEL V = 1 L = 1
+ DCACHE GET 10000 PARTITION_TABLE_2
+translated done 1 err 0 badtree 0 addr 40000 pte 0
+"""
+
+# see qemu/target/ppc/mmu-radix64.c for reference
+class RADIX:
+ def __init__(self, mem, caller):
+ self.mem = mem
+ self.caller = caller
+ if caller is not None:
+ print("caller")
+ print(caller)
+ self.dsisr = self.caller.spr["DSISR"]
+ self.dar = self.caller.spr["DAR"]
+ self.pidr = self.caller.spr["PIDR"]
+ self.prtbl = self.caller.spr["PRTBL"]
+ self.msr = self.caller.msr
+
+ # cached page table stuff
+ self.pgtbl0 = 0
+ self.pt0_valid = False
+ self.pgtbl3 = 0
+ self.pt3_valid = False
+
+ def __call__(self, addr, sz):
+ val = self.ld(addr.value, sz, swap=False)
+ print("RADIX memread", addr, sz, val)
+ return SelectableInt(val, sz*8)
+
+ def ld(self, address, width=8, swap=True, check_in_mem=False,
+ instr_fetch=False):
+ print("RADIX: ld from addr 0x%x width %d" % (address, width))
+
+ priv = ~(self.msr[MSRb.PR].value) # problem-state ==> privileged
+ if instr_fetch:
+ mode = 'EXECUTE'
+ else:
+ mode = 'LOAD'
+ addr = SelectableInt(address, 64)
+ pte = self._walk_tree(addr, mode, priv)
+
+ if type(pte)==str:
+ print("error on load",pte)
+ return 0
+
+ # use pte to load from phys address
+ return self.mem.ld(pte.value, width, swap, check_in_mem)
+
+ # XXX set SPRs on error
+
+ # TODO implement
+ def st(self, address, v, width=8, swap=True):
+ print("RADIX: st to addr 0x%x width %d data %x" % (address, width, v))
+
+ priv = ~(self.msr[MSRb.PR].value) # problem-state ==> privileged
+ mode = 'STORE'
+ addr = SelectableInt(address, 64)
+ pte = self._walk_tree(addr, mode, priv)
+
+ # use pte to store at phys address
+ return self.mem.st(pte.value, v, width, swap)
+
+ # XXX set SPRs on error
+
+ def memassign(self, addr, sz, val):
+ print("memassign", addr, sz, val)
+ self.st(addr.value, val.value, sz, swap=False)
+
+ def _next_level(self, addr, check_in_mem):
+ # implement read access to mmu mem here
+
+ # DO NOT perform byte-swapping: load 8 bytes (that's the entry size)
+ value = self.mem.ld(addr.value, 8, False, check_in_mem)
+ if value is None:
+ return "address lookup %x not found" % addr.value
+ # assert(value is not None, "address lookup %x not found" % addr.value)
+
+ data = SelectableInt(value, 64) # convert to SelectableInt
+ print("addr", hex(addr.value))
+ print("value", hex(value))
+ return data;
+
+ def _walk_tree(self, addr, mode, priv=1):
+ """walk tree
+
+ // vaddr 64 Bit
+ // vaddr |-----------------------------------------------------|
+ // | Unused | Used |
+ // |-----------|-----------------------------------------|
+ // | 0000000 | usefulBits = X bits (typically 52) |
+ // |-----------|-----------------------------------------|
+ // | |<--Cursize---->| |
+ // | | Index | |
+ // | | into Page | |
+ // | | Directory | |
+ // |-----------------------------------------------------|
+ // | |
+ // V |
+ // PDE |---------------------------| |
+ // |V|L|//| NLB |///|NLS| |
+ // |---------------------------| |
+ // PDE = Page Directory Entry |
+ // [0] = V = Valid Bit |
+ // [1] = L = Leaf bit. If 0, then |
+ // [4:55] = NLB = Next Level Base |
+ // right shifted by 8 |
+ // [59:63] = NLS = Next Level Size |
+ // | NLS >= 5 |
+ // | V
+ // | |--------------------------|
+ // | | usfulBits = X-Cursize |
+ // | |--------------------------|
+ // |---------------------><--NLS-->| |
+ // | Index | |
+ // | into | |
+ // | PDE | |
+ // |--------------------------|
+ // |
+ // If the next PDE obtained by |
+ // (NLB << 8 + 8 * index) is a |
+ // nonleaf, then repeat the above. |
+ // |
+ // If the next PDE is a leaf, |
+ // then Leaf PDE structure is as |
+ // follows |
+ // |
+ // |
+ // Leaf PDE |
+ // |------------------------------| |----------------|
+ // |V|L|sw|//|RPN|sw|R|C|/|ATT|EAA| | usefulBits |
+ // |------------------------------| |----------------|
+ // [0] = V = Valid Bit |
+ // [1] = L = Leaf Bit = 1 if leaf |
+ // PDE |
+ // [2] = Sw = Sw bit 0. |
+ // [7:51] = RPN = Real Page Number, V
+ // real_page = RPN << 12 -------------> Logical OR
+ // [52:54] = Sw Bits 1:3 |
+ // [55] = R = Reference |
+ // [56] = C = Change V
+ // [58:59] = Att = Physical Address
+ // 0b00 = Normal Memory
+ // 0b01 = SAO
+ // 0b10 = Non Idenmpotent
+ // 0b11 = Tolerant I/O
+ // [60:63] = Encoded Access
+ // Authority
+ //
+ """
+ # get sprs
+ print("_walk_tree")
+ pidr = self.caller.spr["PIDR"]
+ prtbl = self.caller.spr["PRTBL"]
+ print("PIDR", pidr)
+ print("PRTBL", prtbl)
+ p = addr[55:63]
+ print("last 8 bits ----------")
+ print
+
+ # get address of root entry
+ # need to fetch process table entry
+ # v.shift := unsigned('0' & r.prtbl(4 downto 0));
+ shift = selectconcat(SelectableInt(0, 1), NLS(prtbl))
+ addr_next = self._get_prtable_addr(shift, prtbl, addr, pidr)
+ print("starting with prtable, addr_next", addr_next)
+
+ assert(addr_next.bits == 64)
+ #only for first unit tests assert(addr_next.value == 0x1000000)
+
+ # read an entry from prtable, decode PTRE
+ data = self._next_level(addr_next, check_in_mem=False)
+ print("pr_table", data)
+ pgtbl = data # this is cached in microwatt (as v.pgtbl3 / v.pgtbl0)
+ (rts, mbits, pgbase) = self._decode_prte(pgtbl)
+ print("pgbase", pgbase)
+
+ # WIP
+ if mbits == 0:
+ return "invalid"
+
+ # mask_size := mbits(4 downto 0);
+ mask_size = mbits[0:5]
+ assert(mask_size.bits == 5)
+ print("before segment check ==========")
+ print("mask_size:", bin(mask_size.value))
+ print("mbits:", bin(mbits.value))
+
+ print("calling segment_check")
+
+ shift = self._segment_check(addr, mask_size, shift)
+ print("shift", shift)
+
+ if isinstance(addr, str):
+ return addr
+ if isinstance(shift, str):
+ return shift
+
+ old_shift = shift
+
+ mask = mask_size
+
+ # walk tree
+ while True:
+ addrsh = addrshift(addr, shift)
+ print("addrsh",addrsh)
+
+ print("calling _get_pgtable_addr")
+ print(mask) #SelectableInt(value=0x9, bits=4)
+ print(pgbase) #SelectableInt(value=0x40000, bits=56)
+ print(shift) #SelectableInt(value=0x4, bits=16) #FIXME
+ addr_next = self._get_pgtable_addr(mask, pgbase, addrsh)
+ print("DONE addr_next", addr_next)
+
+ print("nextlevel----------------------------")
+ # read an entry
+ data = self._next_level(addr_next, check_in_mem=False)
+ valid = rpte_valid(data)
+ leaf = rpte_leaf(data)
+
+ print(" valid, leaf", valid, leaf)
+ if not valid:
+ return "invalid" # TODO: return error
+ if leaf:
+ print ("is leaf, checking perms")
+ ok = self._check_perms(data, priv, mode)
+ if ok == True: # data was ok, found phys address, return it?
+ paddr = self._get_pte(addrsh, addr, data)
+ print (" phys addr", hex(paddr.value))
+ return paddr
+ return ok # return the error code
+ else:
+ newlookup = self._new_lookup(data, shift, old_shift)
+ if isinstance(newlookup, str):
+ return newlookup
+ old_shift = shift # store old_shift before updating shift
+ shift, mask, pgbase = newlookup
+ print (" next level", shift, mask, pgbase)
+
+ def _get_pgbase(self, data):
+ """
+ v.pgbase := data(55 downto 8) & x"00"; NLB?
+ """
+ zero8 = SelectableInt(0, 8)
+ ret = selectconcat(data[8:56], zero8)
+ assert(ret.bits==56)
+ return ret
+
+ def _new_lookup(self, data, shift, old_shift):
+ """
+ mbits := unsigned('0' & data(4 downto 0));
+ if mbits < 5 or mbits > 16 or mbits > r.shift then
+ v.state := RADIX_FINISH;
+ v.badtree := '1'; -- throw error
+ else
+ v.shift := v.shift - mbits;
+ v.mask_size := mbits(4 downto 0);
+ v.pgbase := data(55 downto 8) & x"00"; NLB?
+ v.state := RADIX_LOOKUP; --> next level
+ end if;
+ """
+ mbits = selectconcat(SelectableInt(0, 1), NLS(data))
+ print("mbits=", mbits)
+ if mbits < 5 or mbits > 16 or mbits > old_shift:
+ print("badtree")
+ return "badtree"
+ # reduce shift (has to be done at same bitwidth)
+ shift = shift - mbits
+ assert mbits.bits == 6
+ mask_size = mbits[2:6] # get 4 LSBs from 6-bit (using MSB0 numbering)
+ pgbase = self._get_pgbase(data)
+ return shift, mask_size, pgbase
+
+ def _decode_prte(self, data):
+ """PRTE0 Layout
+ -----------------------------------------------
+ |/|RTS1|/| RPDB | RTS2 | RPDS |
+ -----------------------------------------------
+ 0 1 2 3 4 55 56 58 59 63
+ """
+ # note that SelectableInt does big-endian! so the indices
+ # below *directly* match the spec, unlike microwatt which
+ # has to turn them around (to LE)
+ rts, mbits = self._get_rts_nls(data)
+ pgbase = self._get_pgbase(data)
+
+ return (rts, mbits, pgbase)
+
+ def _get_rts_nls(self, data):
+ # rts = shift = unsigned('0' & data(62 downto 61) & data(7 downto 5));
+ # RTS1 RTS2
+ rts = RTS(data)
+ assert(rts.bits == 6) # variable rts : unsigned(5 downto 0);
+ print("shift", rts)
+
+ # mbits := unsigned('0' & data(4 downto 0));
+ mbits = selectconcat(SelectableInt(0, 1), NLS(data))
+ assert(mbits.bits == 6) #variable mbits : unsigned(5 downto 0);
+
+ return rts, mbits
+
+ def _segment_check(self, addr, mask_size, shift):
+ """checks segment valid
+ mbits := '0' & r.mask_size;
+ v.shift := r.shift + (31 - 12) - mbits;
+ nonzero := or(r.addr(61 downto 31) and not finalmask(30 downto 0));
+ if r.addr(63) /= r.addr(62) or nonzero = '1' then
+ v.state := RADIX_FINISH;
+ v.segerror := '1';
+ elsif mbits < 5 or mbits > 16 or mbits > (r.shift + (31 - 12)) then
+ v.state := RADIX_FINISH;
+ v.badtree := '1';
+ else
+ v.state := RADIX_LOOKUP;
+ """
+ # note that SelectableInt does big-endian! so the indices
+ # below *directly* match the spec, unlike microwatt which
+ # has to turn them around (to LE)
+ mbits = selectconcat(SelectableInt(0,1), mask_size)
+ mask = genmask(shift, 44)
+ nonzero = addr[2:33] & mask[13:44] # mask 31 LSBs (BE numbered 13:44)
+ print ("RADIX _segment_check nonzero", bin(nonzero.value))
+ print ("RADIX _segment_check addr[0-1]", addr[0].value, addr[1].value)
+ if addr[0] != addr[1] or nonzero != 0:
+ return "segerror"
+ limit = shift + (31 - 12)
+ if mbits.value < 5 or mbits.value > 16 or mbits.value > limit.value:
+ return "badtree"
+ new_shift = SelectableInt(limit.value - mbits.value, shift.bits)
+ # TODO verify that returned result is correct
+ return new_shift
+
+ def _check_perms(self, data, priv, mode):
+ """check page permissions
+ // Leaf PDE |
+ // |------------------------------| |----------------|
+ // |V|L|sw|//|RPN|sw|R|C|/|ATT|EAA| | usefulBits |
+ // |------------------------------| |----------------|
+ // [0] = V = Valid Bit |
+ // [1] = L = Leaf Bit = 1 if leaf |
+ // PDE |
+ // [2] = Sw = Sw bit 0. |
+ // [7:51] = RPN = Real Page Number, V
+ // real_page = RPN << 12 -------------> Logical OR
+ // [52:54] = Sw Bits 1:3 |
+ // [55] = R = Reference |
+ // [56] = C = Change V
+ // [58:59] = Att = Physical Address
+ // 0b00 = Normal Memory
+ // 0b01 = SAO
+ // 0b10 = Non Idenmpotent
+ // 0b11 = Tolerant I/O
+ // [60:63] = Encoded Access
+ // Authority
+ //
+ -- test leaf bit
+ -- check permissions and RC bits
+ perm_ok := '0';
+ if r.priv = '1' or data(3) = '0' then
+ if r.iside = '0' then
+ perm_ok := data(1) or (data(2) and not r.store);
+ else
+ -- no IAMR, so no KUEP support for now
+ -- deny execute permission if cache inhibited
+ perm_ok := data(0) and not data(5);
+ end if;
+ end if;
+ rc_ok := data(8) and (data(7) or not r.store);
+ if perm_ok = '1' and rc_ok = '1' then
+ v.state := RADIX_LOAD_TLB;
+ else
+ v.state := RADIX_FINISH;
+ v.perm_err := not perm_ok;
+ -- permission error takes precedence over RC error
+ v.rc_error := perm_ok;
+ end if;
+ """
+ # decode mode into something that matches microwatt equivalent code
+ instr_fetch, store = 0, 0
+ if mode == 'STORE':
+ store = 1
+ if mode == 'EXECUTE':
+ inst_fetch = 1
+
+ # check permissions and RC bits
+ perm_ok = 0
+ if priv == 1 or data[60] == 0:
+ if instr_fetch == 0:
+ perm_ok = data[62] | (data[61] & (store == 0))
+ # no IAMR, so no KUEP support for now
+ # deny execute permission if cache inhibited
+ perm_ok = data[63] & ~data[58]
+ rc_ok = data[55] & (data[56] | (store == 0))
+ if perm_ok == 1 and rc_ok == 1:
+ return True
+
+ return "perm_err" if perm_ok == 0 else "rc_err"
+
+ def _get_prtable_addr(self, shift, prtbl, addr, pid):
+ """
+ if r.addr(63) = '1' then
+ effpid := x"00000000";
+ else
+ effpid := r.pid;
+ end if;
+ x"00" & r.prtbl(55 downto 36) &
+ ((r.prtbl(35 downto 12) and not finalmask(23 downto 0)) or
+ (effpid(31 downto 8) and finalmask(23 downto 0))) &
+ effpid(7 downto 0) & "0000";
+ """
+ finalmask = genmask(shift, 44)
+ finalmask24 = finalmask[20:44]
+ print ("_get_prtable_addr", shift, prtbl, addr, pid,
+ bin(finalmask24.value))
+ if addr[0].value == 1:
+ effpid = SelectableInt(0, 32)
+ else:
+ effpid = pid #self.pid # TODO, check on this
+ zero8 = SelectableInt(0, 8)
+ zero4 = SelectableInt(0, 4)
+ res = selectconcat(zero8,
+ prtbl[8:28], #
+ (prtbl[28:52] & ~finalmask24) | #
+ (effpid[0:24] & finalmask24), #
+ effpid[24:32],
+ zero4
+ )
+ return res
+
+ def _get_pgtable_addr(self, mask_size, pgbase, addrsh):
+ """
+ x"00" & r.pgbase(55 downto 19) &
+ ((r.pgbase(18 downto 3) and not mask) or (addrsh and mask)) &
+ "000";
+ """
+ print("pgbase",pgbase)
+ assert(pgbase.bits==56)
+ mask16 = genmask(mask_size+5, 16)
+ zero8 = SelectableInt(0, 8)
+ zero3 = SelectableInt(0, 3)
+ res = selectconcat(zero8,
+ pgbase[0:37],
+ (pgbase[37:53] & ~mask16) |
+ (addrsh & mask16),
+ zero3
+ )
+ return res
+
+ def _get_pte(self, shift, addr, pde):
+ """
+ x"00" &
+ ((r.pde(55 downto 12) and not finalmask) or
+ (r.addr(55 downto 12) and finalmask))
+ & r.pde(11 downto 0);
+ """
+ shift.value = 12
+ finalmask = genmask(shift, 44)
+ zero8 = SelectableInt(0, 8)
+ rpn = pde[8:52] # RPN = Real Page Number
+ abits = addr[8:52] # non-masked address bits
+ print(" get_pte RPN", hex(rpn.value))
+ print(" abits", hex(abits.value))
+ print(" shift", shift.value)
+ print(" finalmask", bin(finalmask.value))
+ res = selectconcat(zero8,
+ (rpn & ~finalmask) | #
+ (abits & finalmask), #
+ addr[52:64],
+ )
+ return res
+
+
+class TestRadixMMU(unittest.TestCase):
+
+ def test_genmask(self):
+ shift = SelectableInt(5, 6)
+ mask = genmask(shift, 43)
+ print (" mask", bin(mask.value))
+
+ self.assertEqual(mask.value, 0b11111, "mask should be 5 1s")
+
+ def test_RPDB(self):
+ inp = SelectableInt(0x40000000000300ad, 64)
+
+ rtdb = RPDB(inp)
+ print("rtdb",rtdb,bin(rtdb.value))
+ self.assertEqual(rtdb.value,0x300,"rtdb should be 0x300")
+
+ result = selectconcat(rtdb,SelectableInt(0,8))
+ print("result",result)
+
+ def test_get_pgtable_addr(self):
+
+ mem = None
+ caller = None
+ dut = RADIX(mem, caller)
+
+ mask_size=4
+ pgbase = SelectableInt(0,56)
+ addrsh = SelectableInt(0,16)
+ ret = dut._get_pgtable_addr(mask_size, pgbase, addrsh)
+ print("ret=", ret)
+ self.assertEqual(ret, 0, "pgtbl_addr should be 0")
+
+ def test_walk_tree_1(self):
+
+ # test address as in
+ # https://github.com/power-gem5/gem5/blob/gem5-experimental/src/arch/power/radix_walk_example.txt#L65
+ testaddr = 0x1000
+ expected = 0x1000
+
+ # starting prtbl
+ prtbl = 0x1000000
+
+ # set up dummy minimal ISACaller
+ spr = {'DSISR': SelectableInt(0, 64),
+ 'DAR': SelectableInt(0, 64),
+ 'PIDR': SelectableInt(0, 64),
+ 'PRTBL': SelectableInt(prtbl, 64)
+ }
+ # set problem state == 0 (other unit tests, set to 1)
+ msr = SelectableInt(0, 64)
+ msr[MSRb.PR] = 0
+ class ISACaller: pass
+ caller = ISACaller()
+ caller.spr = spr
+ caller.msr = msr
+
+ shift = SelectableInt(5, 6)
+ mask = genmask(shift, 43)
+ print (" mask", bin(mask.value))
+
+ mem = Mem(row_bytes=8, initial_mem=testmem)
+ mem = RADIX(mem, caller)
+ # -----------------------------------------------
+ # |/|RTS1|/| RPDB | RTS2 | RPDS |
+ # -----------------------------------------------
+ # |0|1 2|3|4 55|56 58|59 63|
+ data = SelectableInt(0, 64)
+ data[1:3] = 0b01
+ data[56:59] = 0b11
+ data[59:64] = 0b01101 # mask
+ data[55] = 1
+ (rts, mbits, pgbase) = mem._decode_prte(data)
+ print (" rts", bin(rts.value), rts.bits)
+ print (" mbits", bin(mbits.value), mbits.bits)
+ print (" pgbase", hex(pgbase.value), pgbase.bits)
+ addr = SelectableInt(0x1000, 64)
+ check = mem._segment_check(addr, mbits, shift)
+ print (" segment check", check)
+
+ print("walking tree")
+ addr = SelectableInt(testaddr,64)
+ # pgbase = None
+ mode = None
+ #mbits = None
+ shift = rts
+ result = mem._walk_tree(addr, mode)
+ print(" walking tree result", result)
+ print("should be", testresult)
+ self.assertEqual(result.value, expected,
+ "expected 0x%x got 0x%x" % (expected,
+ result.value))
+
+ def test_walk_tree_2(self):
+
+ # test address slightly different
+ testaddr = 0x1101
+ expected = 0x5001101
+
+ # starting prtbl
+ prtbl = 0x1000000
+
+ # set up dummy minimal ISACaller
+ spr = {'DSISR': SelectableInt(0, 64),
+ 'DAR': SelectableInt(0, 64),
+ 'PIDR': SelectableInt(0, 64),
+ 'PRTBL': SelectableInt(prtbl, 64)
+ }
+ # set problem state == 0 (other unit tests, set to 1)
+ msr = SelectableInt(0, 64)
+ msr[MSRb.PR] = 0
+ class ISACaller: pass
+ caller = ISACaller()
+ caller.spr = spr
+ caller.msr = msr
+
+ shift = SelectableInt(5, 6)
+ mask = genmask(shift, 43)
+ print (" mask", bin(mask.value))
+
+ mem = Mem(row_bytes=8, initial_mem=testmem2)
+ mem = RADIX(mem, caller)
+ # -----------------------------------------------
+ # |/|RTS1|/| RPDB | RTS2 | RPDS |
+ # -----------------------------------------------
+ # |0|1 2|3|4 55|56 58|59 63|
+ data = SelectableInt(0, 64)
+ data[1:3] = 0b01
+ data[56:59] = 0b11
+ data[59:64] = 0b01101 # mask
+ data[55] = 1
+ (rts, mbits, pgbase) = mem._decode_prte(data)
+ print (" rts", bin(rts.value), rts.bits)
+ print (" mbits", bin(mbits.value), mbits.bits)
+ print (" pgbase", hex(pgbase.value), pgbase.bits)
+ addr = SelectableInt(0x1000, 64)
+ check = mem._segment_check(addr, mbits, shift)
+ print (" segment check", check)
+
+ print("walking tree")
+ addr = SelectableInt(testaddr,64)
+ # pgbase = None
+ mode = None
+ #mbits = None
+ shift = rts
+ result = mem._walk_tree(addr, mode)
+ print(" walking tree result", result)
+ print("should be", testresult)
+ self.assertEqual(result.value, expected,
+ "expected 0x%x got 0x%x" % (expected,
+ result.value))
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+from nmigen import Module, Signal
+from nmigen.back.pysim import Simulator, Delay, Settle
+from nmutil.formaltest import FHDLTestCase
+import unittest
+from soc.decoder.isa.caller import ISACaller
+from soc.decoder.power_decoder import (create_pdecode)
+from soc.decoder.power_decoder2 import (PowerDecode2)
+from soc.simulator.program import Program
+from soc.decoder.isa.caller import ISACaller, inject
+from soc.decoder.selectable_int import SelectableInt
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.isa.all import ISA
+
+
+class Register:
+ def __init__(self, num):
+ self.num = num
+
+def run_tst(generator, initial_regs, initial_sprs=None, svstate=0, mmu=False,
+ initial_cr=0,mem=None):
+ if initial_sprs is None:
+ initial_sprs = {}
+ m = Module()
+ comb = m.d.comb
+ instruction = Signal(32)
+
+ pdecode = create_pdecode()
+
+ gen = list(generator.generate_instructions())
+ insncode = generator.assembly.splitlines()
+ instructions = list(zip(gen, insncode))
+
+ m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
+ simulator = ISA(pdecode2, initial_regs, initial_sprs, initial_cr,
+ initial_insns=gen, respect_pc=True,
+ initial_svstate=svstate,
+ initial_mem=mem,
+ disassembly=insncode,
+ bigendian=0,
+ mmu=mmu)
+ comb += pdecode2.dec.raw_opcode_in.eq(instruction)
+ sim = Simulator(m)
+
+
+ def process():
+
+ yield pdecode2.dec.bigendian.eq(0) # little / big?
+ pc = simulator.pc.CIA.value
+ index = pc//4
+ while index < len(instructions):
+ print("instr pc", pc)
+ try:
+ yield from simulator.setup_one()
+ except KeyError: # indicates instruction not in imem: stop
+ break
+ yield Settle()
+
+ ins, code = instructions[index]
+ print(" 0x{:X}".format(ins & 0xffffffff))
+ opname = code.split(' ')[0]
+ print(code, opname)
+
+ # ask the decoder to decode this binary data (endian'd)
+ yield from simulator.execute_one()
+ pc = simulator.pc.CIA.value
+ index = pc//4
+
+ sim.add_process(process)
+ with sim.write_vcd("simulator.vcd", "simulator.gtkw",
+ traces=[]):
+ sim.run()
+ return simulator
+
+
+class DecoderTestCase(FHDLTestCase):
+
+ def test_add(self):
+ lst = ["add 1, 3, 2"]
+ initial_regs = [0] * 32
+ initial_regs[3] = 0x1234
+ initial_regs[2] = 0x4321
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(1), SelectableInt(0x5555, 64))
+
+ def test_addi(self):
+ lst = ["addi 3, 0, 0x1234",
+ "addi 2, 0, 0x4321",
+ "add 1, 3, 2"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ print(sim.gpr(1))
+ self.assertEqual(sim.gpr(1), SelectableInt(0x5555, 64))
+
+ def test_load_store(self):
+ lst = ["addi 1, 0, 0x0010",
+ "addi 2, 0, 0x1234",
+ "stw 2, 0(1)",
+ "lwz 3, 0(1)"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ print(sim.gpr(1))
+ self.assertEqual(sim.gpr(3), SelectableInt(0x1234, 64))
+
+ @unittest.skip("broken")
+ def test_addpcis(self):
+ lst = ["addpcis 1, 0x1",
+ "addpcis 2, 0x1",
+ "addpcis 3, 0x1"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ self.assertEqual(sim.gpr(1), SelectableInt(0x10004, 64))
+ self.assertEqual(sim.gpr(2), SelectableInt(0x10008, 64))
+ self.assertEqual(sim.gpr(3), SelectableInt(0x1000c, 64))
+
+ def test_branch(self):
+ lst = ["ba 0xc", # branch to line 4
+ "addi 1, 0, 0x1234", # Should never execute
+ "ba 0x1000", # exit the program
+ "addi 2, 0, 0x1234", # line 4
+ "ba 0x8"] # branch to line 3
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ self.assertEqual(sim.pc.CIA, SelectableInt(0x1000, 64))
+ self.assertEqual(sim.gpr(1), SelectableInt(0x0, 64))
+ self.assertEqual(sim.gpr(2), SelectableInt(0x1234, 64))
+
+ def test_branch_link(self):
+ lst = ["bl 0xc",
+ "addi 2, 1, 0x1234",
+ "ba 0x1000",
+ "addi 1, 0, 0x1234",
+ "bclr 20, 0, 0"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ self.assertEqual(sim.spr['LR'], SelectableInt(0x4, 64))
+
+ def test_branch_ctr(self):
+ lst = ["addi 1, 0, 0x10", # target of jump
+ "mtspr 9, 1", # mtctr 1
+ "bcctr 20, 0, 0", # bctr
+ "addi 2, 0, 0x1", # should never execute
+ "addi 1, 0, 0x1234"] # target of ctr
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ self.assertEqual(sim.spr['CTR'], SelectableInt(0x10, 64))
+ self.assertEqual(sim.gpr(1), SelectableInt(0x1234, 64))
+ self.assertEqual(sim.gpr(2), SelectableInt(0, 64))
+
+ def test_branch_cond(self):
+ for i in [0, 10]:
+ lst = [f"addi 1, 0, {i}", # set r1 to i
+ "cmpi cr0, 1, 1, 10", # compare r1 with 10 and store to cr0
+ "bc 12, 2, 0x8", # beq 0x8 -
+ # branch if r1 equals 10 to the nop below
+ "addi 2, 0, 0x1234", # if r1 == 10 this shouldn't execute
+ "or 0, 0, 0"] # branch target
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ if i == 10:
+ self.assertEqual(sim.gpr(2), SelectableInt(0, 64))
+ else:
+ self.assertEqual(sim.gpr(2), SelectableInt(0x1234, 64))
+
+ def test_branch_loop(self):
+ lst = ["addi 1, 0, 0",
+ "addi 1, 0, 0",
+ "addi 1, 1, 1",
+ "add 2, 2, 1",
+ "cmpi cr0, 1, 1, 10",
+ "bc 12, 0, -0xc"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ # Verified with qemu
+ self.assertEqual(sim.gpr(2), SelectableInt(0x37, 64))
+
+ def test_branch_loop_ctr(self):
+ lst = ["addi 1, 0, 0",
+ "addi 2, 0, 7",
+ "mtspr 9, 2", # set ctr to 7
+ "addi 1, 1, 5",
+ "bc 16, 0, -0x4"] # bdnz to the addi above
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ # Verified with qemu
+ self.assertEqual(sim.gpr(1), SelectableInt(0x23, 64))
+
+
+
+ def test_add_compare(self):
+ lst = ["addis 1, 0, 0xffff",
+ "addis 2, 0, 0xffff",
+ "add. 1, 1, 2",
+ "mfcr 3"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ # Verified with QEMU
+ self.assertEqual(sim.gpr(3), SelectableInt(0x80000000, 64))
+
+ def test_cmp(self):
+ lst = ["addis 1, 0, 0xffff",
+ "addis 2, 0, 0xffff",
+ "cmp cr2, 0, 1, 2",
+ "mfcr 3"]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ self.assertEqual(sim.gpr(3), SelectableInt(0x200000, 64))
+
+ def test_slw(self):
+ lst = ["slw 1, 3, 2"]
+ initial_regs = [0] * 32
+ initial_regs[3] = 0xdeadbeefcafebabe
+ initial_regs[2] = 5
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(1), SelectableInt(0x5fd757c0, 64))
+
+ def test_srw(self):
+ lst = ["srw 1, 3, 2"]
+ initial_regs = [0] * 32
+ initial_regs[3] = 0xdeadbeefcafebabe
+ initial_regs[2] = 5
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(1), SelectableInt(0x657f5d5, 64))
+
+ def test_rlwinm(self):
+ lst = ["rlwinm 3, 1, 5, 20, 6"]
+ initial_regs = [0] * 32
+ initial_regs[1] = -1
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(3), SelectableInt(0xfffffffffe000fff, 64))
+
+ def test_rlwimi(self):
+ lst = ["rlwimi 3, 1, 5, 20, 6"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xffffffffdeadbeef
+ initial_regs[3] = 0x12345678
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(3), SelectableInt(0xd5b7ddfbd4345dfb, 64))
+
+ def test_rldic(self):
+ lst = ["rldic 3, 1, 5, 20"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xdeadbeefcafec0de
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(3), SelectableInt(0xdf95fd81bc0, 64))
+
+ def test_prty(self):
+ lst = ["prtyw 2, 1"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xdeadbeeecaffc0de
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(2), SelectableInt(0x100000001, 64))
+
+ def test_popcnt(self):
+ lst = ["popcntb 2, 1",
+ "popcntw 3, 1",
+ "popcntd 4, 1"
+ ]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xdeadbeefcafec0de
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(2),
+ SelectableInt(0x605060704070206, 64))
+ self.assertEqual(sim.gpr(3),
+ SelectableInt(0x1800000013, 64))
+ self.assertEqual(sim.gpr(4),
+ SelectableInt(0x2b, 64))
+
+ def test_cntlz(self):
+ lst = ["cntlzd 2, 1",
+ "cntlzw 4, 3"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0x0000beeecaffc0de
+ initial_regs[3] = 0x0000000000ffc0de
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.gpr(2), SelectableInt(16, 64))
+ self.assertEqual(sim.gpr(4), SelectableInt(8, 64))
+
+ def test_cmpeqb(self):
+ lst = ["cmpeqb cr0, 2, 1",
+ "cmpeqb cr1, 3, 1"]
+ initial_regs = [0] * 32
+ initial_regs[1] = 0x0102030405060708
+ initial_regs[2] = 0x04
+ initial_regs[3] = 0x10
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self.assertEqual(sim.crl[0].get_range().value,
+ SelectableInt(4, 4))
+ self.assertEqual(sim.crl[1].get_range().value,
+ SelectableInt(0, 4))
+
+
+
+ def test_mtcrf(self):
+ for i in range(4):
+ # 0x76540000 gives expected (3+4) (2+4) (1+4) (0+4) for
+ # i=0, 1, 2, 3
+ # The positions of the CR fields have been verified using
+ # QEMU and 'cmp crx, a, b' instructions
+ lst = ["addis 1, 0, 0x7654",
+ "mtcrf %d, 1" % (1 << (7-i)),
+ ]
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program)
+ print("cr", sim.cr)
+ expected = (7-i)
+ # check CR[0]/1/2/3 as well
+ print("cr%d", sim.crl[i])
+ self.assertTrue(SelectableInt(expected, 4) == sim.crl[i])
+ # check CR itself
+ self.assertEqual(sim.cr, SelectableInt(expected << ((7-i)*4), 32))
+
+ def run_tst_program(self, prog, initial_regs=[0] * 32):
+ simulator = run_tst(prog, initial_regs)
+ simulator.gpr.dump()
+ return simulator
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null
+from nmigen import Module, Signal
+#from nmigen.back.pysim import Simulator, Delay, Settle
+from nmutil.formaltest import FHDLTestCase
+import unittest
+from soc.decoder.isa.caller import ISACaller
+from soc.decoder.power_decoder import (create_pdecode)
+from soc.decoder.power_decoder2 import (PowerDecode2)
+from soc.simulator.program import Program
+from soc.decoder.isa.caller import ISACaller, inject, RADIX
+from soc.decoder.selectable_int import SelectableInt
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.isa.all import ISA
+from soc.decoder.isa.test_caller import run_tst
+
+from copy import deepcopy
+
+testmem = {
+
+ 0x10000: # PARTITION_TABLE_2 (not implemented yet)
+ # PATB_GR=1 PRTB=0x1000 PRTS=0xb
+ 0x800000000100000b,
+
+ 0x30000: # RADIX_ROOT_PTE
+ # V = 1 L = 0 NLB = 0x400 NLS = 9
+ 0x8000000000040009,
+ 0x40000: # RADIX_SECOND_LEVEL
+ # V = 1 L = 1 SW = 0 RPN = 0
+ # R = 1 C = 1 ATT = 0 EAA 0x7
+ 0xc000000000000187,
+
+ 0x30800: # RADIX_ROOT_PTE + 8
+ # V = 1 L = 0 NLB = 0x408 NLS = 9
+ 0x8000000000040809,
+ 0x40800: # RADIX_SECOND_LEVEL
+ # V = 1 L = 1 SW = 0 RPN = 0
+ # R = 1 C = 1 ATT = 0 EAA 0x7
+ 0xc000000000000187,
+
+ 0x1000000: # PROCESS_TABLE_3
+ # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
+ 0x40000000000300ad,
+ 0x1000008: # PROCESS_TABLE_3 + 8
+ # RTS1 = 0x2 RPDB = 0x308 RTS2 = 0x5 RPDS = 13
+ 0x40000000000308ad,
+ }
+
+prtbl = 0x1000000 # matches PROCESS_TABLE_3 above
+
+class DecoderTestCase(FHDLTestCase):
+
+ def test_load(self):
+ lst = [ "lwz 3, 0(1)"
+ ]
+ sprs = {'DSISR': SelectableInt(0, 64),
+ 'DAR': SelectableInt(0, 64),
+ 'PIDR': SelectableInt(0, 64),
+ 'PRTBL': SelectableInt(prtbl, 64)
+ }
+
+ initial_regs=[0] * 32
+ initial_regs[1] = 0x1000
+ initial_regs[2] = 0x1234
+
+ initial_mem = deepcopy(testmem)
+ initial_mem[0x1000] = 0x1337 # data to be read
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs=initial_regs,
+ initial_mem=initial_mem,
+ initial_sprs=sprs)
+ self.assertEqual(sim.gpr(3), SelectableInt(0x1337, 64))
+
+ def test_load_pid_1(self):
+ lst = [ "lwz 3, 0(1)"
+ ]
+ sprs = {'DSISR': SelectableInt(0, 64),
+ 'DAR': SelectableInt(0, 64),
+ 'PIDR': SelectableInt(1, 64),
+ 'PRTBL': SelectableInt(prtbl, 64)
+ }
+
+ initial_regs=[0] * 32
+ initial_regs[1] = 0x1000
+ initial_regs[2] = 0x1234
+
+ initial_mem = deepcopy(testmem)
+ initial_mem[0x1000] = 0x1337 # data to be read
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs=initial_regs,
+ initial_mem=initial_mem,
+ initial_sprs=sprs)
+ self.assertEqual(sim.gpr(3), SelectableInt(0x1337, 64))
+
+ def test_load_store(self):
+ lst = ["addi 1, 0, 0x1000",
+ "addi 2, 0, 0x1234",
+ "stw 2, 0(1)",
+ "lwz 3, 0(1)"
+ ]
+ # set up dummy minimal ISACaller
+ sprs = {'DSISR': SelectableInt(0, 64),
+ 'DAR': SelectableInt(0, 64),
+ 'PIDR': SelectableInt(0, 64),
+ 'PRTBL': SelectableInt(prtbl, 64)
+ }
+
+ initial_regs=[0] * 32
+ initial_regs[1] = 0x1000
+ initial_regs[2] = 0x1234
+ initial_mem = deepcopy(testmem)
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs=initial_regs,
+ initial_mem=initial_mem,
+ initial_sprs=sprs)
+ self.assertEqual(sim.gpr(3), SelectableInt(0x1234, 64))
+
+ def run_tst_program(self, prog, initial_regs=None, initial_mem=None,
+ initial_sprs=None):
+ # DO NOT set complex arguments, it is a "singleton" pattern
+ if initial_regs is None:
+ initial_regs = [0] * 32
+
+ simulator = run_tst(prog, initial_regs, mmu=True, mem=initial_mem,
+ initial_sprs=initial_sprs)
+ simulator.gpr.dump()
+ return simulator
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null
+from nmigen import Module, Signal
+from nmigen.back.pysim import Simulator, Delay, Settle
+from nmutil.formaltest import FHDLTestCase
+import unittest
+from soc.decoder.isa.caller import ISACaller
+from soc.decoder.power_decoder import (create_pdecode)
+from soc.decoder.power_decoder2 import (PowerDecode2)
+from soc.simulator.program import Program
+from soc.decoder.isa.caller import ISACaller, SVP64State
+from soc.decoder.selectable_int import SelectableInt
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.isa.all import ISA
+from soc.decoder.isa.test_caller import Register, run_tst
+from soc.sv.trans.svp64 import SVP64Asm
+from soc.consts import SVP64CROffs
+from copy import deepcopy
+
+class DecoderTestCase(FHDLTestCase):
+
+ def _check_regs(self, sim, expected):
+ for i in range(32):
+ self.assertEqual(sim.gpr(i), SelectableInt(expected[i], 64))
+
+ def test_setvl_1(self):
+ lst = SVP64Asm(["setvl 1, 0, 9, 1, 1",
+ ])
+ lst = list(lst)
+
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, svstate=svstate)
+ print ("SVSTATE after", bin(sim.svstate.spr.asint()))
+ print (" vl", bin(sim.svstate.vl.asint(True)))
+ print (" mvl", bin(sim.svstate.maxvl.asint(True)))
+ self.assertEqual(sim.svstate.vl.asint(True), 10)
+ self.assertEqual(sim.svstate.maxvl.asint(True), 10)
+ self.assertEqual(sim.svstate.maxvl.asint(True), 10)
+ print(" gpr1", sim.gpr(1))
+ self.assertEqual(sim.gpr(1), SelectableInt(10, 64))
+
+
+ def test_sv_add(self):
+ # sets VL=2 then adds:
+ # 1 = 5 + 9 => 0x5555 = 0x4321+0x1234
+ # 2 = 6 + 10 => 0x3334 = 0x2223+0x1111
+ isa = SVP64Asm(["setvl 3, 0, 1, 1, 1",
+ 'sv.add 1.v, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = 0x5555
+ expected_regs[2] = 0x3334
+ expected_regs[3] = 2 # setvl places copy of VL here
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs)
+ self._check_regs(sim, expected_regs)
+
+ def run_tst_program(self, prog, initial_regs=None,
+ svstate=None):
+ if initial_regs is None:
+ initial_regs = [0] * 32
+ simulator = run_tst(prog, initial_regs, svstate=svstate)
+ simulator.gpr.dump()
+ return simulator
+
+
+if __name__ == "__main__":
+ unittest.main()
+
--- /dev/null
+from nmigen import Module, Signal
+from nmigen.back.pysim import Simulator, Delay, Settle
+from nmutil.formaltest import FHDLTestCase
+import unittest
+from soc.decoder.isa.caller import ISACaller
+from soc.decoder.power_decoder import (create_pdecode)
+from soc.decoder.power_decoder2 import (PowerDecode2)
+from soc.simulator.program import Program
+from soc.decoder.isa.caller import ISACaller, SVP64State
+from soc.decoder.selectable_int import SelectableInt
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.isa.all import ISA
+from soc.decoder.isa.test_caller import Register, run_tst
+from soc.sv.trans.svp64 import SVP64Asm
+from soc.consts import SVP64CROffs
+from copy import deepcopy
+
+class DecoderTestCase(FHDLTestCase):
+
+ def _check_regs(self, sim, expected):
+ for i in range(32):
+ self.assertEqual(sim.gpr(i), SelectableInt(expected[i], 64))
+
+ def test_sv_load_store(self):
+ lst = SVP64Asm(["addi 1, 0, 0x0010",
+ "addi 2, 0, 0x0008",
+ "addi 5, 0, 0x1234",
+ "addi 6, 0, 0x1235",
+ "sv.stw 5.v, 0(1.v)",
+ "sv.lwz 9.v, 0(1.v)"])
+ lst = list(lst)
+
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, svstate=svstate)
+ print(sim.gpr(1))
+ self.assertEqual(sim.gpr(9), SelectableInt(0x1234, 64))
+ self.assertEqual(sim.gpr(10), SelectableInt(0x1235, 64))
+
+ def test_sv_add(self):
+ # adds:
+ # 1 = 5 + 9 => 0x5555 = 0x4321+0x1234
+ # 2 = 6 + 10 => 0x3334 = 0x2223+0x1111
+ isa = SVP64Asm(['sv.add 1.v, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[5] = 0x4321
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running, then compute answers
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = initial_regs[5] + initial_regs[9] # 0x5555
+ expected_regs[2] = initial_regs[6] + initial_regs[10] # 0x3334
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_add_2(self):
+ # adds:
+ # 1 = 5 + 9 => 0x5555 = 0x4321+0x1234
+ # r1 is scalar so ENDS EARLY
+ isa = SVP64Asm(['sv.add 1, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = initial_regs[5] + initial_regs[9] # 0x5555
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_add_3(self):
+ # adds:
+ # 1 = 5 + 9 => 0x5555 = 0x4321+0x1234
+ # 2 = 5 + 10 => 0x5432 = 0x4321+0x1111
+ isa = SVP64Asm(['sv.add 1.v, 5, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = initial_regs[5] + initial_regs[9] # 0x5555
+ expected_regs[2] = initial_regs[5] + initial_regs[10] # 0x5432
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_add_vl_0(self):
+ # adds:
+ # none because VL is zer0
+ isa = SVP64Asm(['sv.add 1, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=0)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 0 # VL
+ svstate.maxvl[0:7] = 0 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_add_cr(self):
+ # adds when Rc=1: TODO CRs higher up
+ # 1 = 5 + 9 => 0 = -1+1 CR0=0b100
+ # 2 = 6 + 10 => 0x3334 = 0x2223+0x1111 CR1=0b010
+ isa = SVP64Asm(['sv.add. 1.v, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0xffffffffffffffff
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x1
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = initial_regs[5] + initial_regs[9] # 0x0
+ expected_regs[2] = initial_regs[6] + initial_regs[10] # 0x3334
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ # XXX TODO, these need to move to higher range (offset)
+ cr0_idx = SVP64CROffs.CR0
+ cr1_idx = SVP64CROffs.CR1
+ CR0 = sim.crl[cr0_idx].get_range().value
+ CR1 = sim.crl[cr1_idx].get_range().value
+ print ("CR0", CR0)
+ print ("CR1", CR1)
+ self._check_regs(sim, expected_regs)
+ self.assertEqual(CR0, SelectableInt(2, 4))
+ self.assertEqual(CR1, SelectableInt(4, 4))
+
+ def run_tst_program(self, prog, initial_regs=None,
+ svstate=None):
+ if initial_regs is None:
+ initial_regs = [0] * 32
+ simulator = run_tst(prog, initial_regs, svstate=svstate)
+ simulator.gpr.dump()
+ return simulator
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null
+from nmigen import Module, Signal
+from nmigen.back.pysim import Simulator, Delay, Settle
+from nmutil.formaltest import FHDLTestCase
+import unittest
+from soc.decoder.isa.caller import ISACaller
+from soc.decoder.power_decoder import (create_pdecode)
+from soc.decoder.power_decoder2 import (PowerDecode2)
+from soc.simulator.program import Program
+from soc.decoder.isa.caller import ISACaller, SVP64State
+from soc.decoder.selectable_int import SelectableInt
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.isa.all import ISA
+from soc.decoder.isa.test_caller import Register, run_tst
+from soc.sv.trans.svp64 import SVP64Asm
+from soc.consts import SVP64CROffs
+from copy import deepcopy
+
+class DecoderTestCase(FHDLTestCase):
+
+ def _check_regs(self, sim, expected):
+ for i in range(32):
+ self.assertEqual(sim.gpr(i), SelectableInt(expected[i], 64))
+
+ def tst_sv_load_store(self):
+ lst = SVP64Asm(["addi 1, 0, 0x0010",
+ "addi 2, 0, 0x0008",
+ "addi 5, 0, 0x1234",
+ "addi 6, 0, 0x1235",
+ "sv.stw 5.v, 0(1.v)",
+ "sv.lwz 9.v, 0(1.v)"])
+ lst = list(lst)
+
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, svstate=svstate)
+ print(sim.gpr(1))
+ self.assertEqual(sim.gpr(9), SelectableInt(0x1234, 64))
+ self.assertEqual(sim.gpr(10), SelectableInt(0x1235, 64))
+
+ def test_sv_extsw_intpred(self):
+ # extsb, integer twin-pred mask: source is ~r3 (0b01), dest r3 (0b10)
+ # works as follows, where any zeros indicate "skip element"
+ # - sources are 9 and 10
+ # - dests are 5 and 6
+ # - source mask says "pick first element from source (5)
+ # - dest mask says "pick *second* element from dest (10)
+ #
+ # therefore the operation that's carried out is:
+ # GPR(10) = extsb(GPR(5))
+ #
+ # this is a type of back-to-back VREDUCE and VEXPAND but it applies
+ # to *operations*, not just MVs like in traditional Vector ISAs
+ # ascii graphic:
+ #
+ # reg num 0 1 2 3 4 5 6 7 8 9 10
+ # src ~r3=0b01 Y N
+ # |
+ # +-----+
+ # |
+ # dest r3=0b10 N Y
+
+ isa = SVP64Asm(['sv.extsb/sm=~r3/dm=r3 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 0b10 # predicate mask
+ initial_regs[9] = 0x91 # source ~r3 is 0b01 so this will be used
+ initial_regs[10] = 0x90 # this gets skipped
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0x0 # dest r3 is 0b10: skip
+ expected_regs[6] = 0xffff_ffff_ffff_ff91 # 2nd bit of r3 is 1
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_extsw_intpred_dz(self):
+ # extsb, integer twin-pred mask: dest is r3 (0b01), zeroing on dest
+ isa = SVP64Asm(['sv.extsb/dm=r3/dz 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 0b01 # predicate mask (dest)
+ initial_regs[5] = 0xfeed # going to be overwritten
+ initial_regs[6] = 0xbeef # going to be overwritten (with zero)
+ initial_regs[9] = 0x91 # dest r3 is 0b01 so this will be used
+ initial_regs[10] = 0x90 # this gets read but the output gets zero'd
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0xffff_ffff_ffff_ff91 # dest r3 is 0b01: store
+ expected_regs[6] = 0 # 2nd bit of r3 is 1: zero
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_add_intpred(self):
+ # adds, integer predicated mask r3=0b10
+ # 1 = 5 + 9 => not to be touched (skipped)
+ # 2 = 6 + 10 => 0x3334 = 0x2223+0x1111
+ isa = SVP64Asm(['sv.add/m=r3 1.v, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xbeef # not to be altered
+ initial_regs[3] = 0b10 # predicate mask
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = 0xbeef
+ expected_regs[2] = 0x3334
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_sv_add_cr_pred(self):
+ # adds, CR predicated mask CR4.eq = 1, CR5.eq = 0, invert (ne)
+ # 1 = 5 + 9 => not to be touched (skipped)
+ # 2 = 6 + 10 => 0x3334 = 0x2223+0x1111
+ isa = SVP64Asm(['sv.add/m=ne 1.v, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[1] = 0xbeef # not to be altered
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = 0xbeef
+ expected_regs[2] = 0x3334
+
+ # set up CR predicate - CR4.eq=1 and CR5.eq=0
+ cr = (0b0010) << ((7-4)*4) # CR4.eq (we hope)
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate,
+ initial_cr=cr)
+ self._check_regs(sim, expected_regs)
+
+ def tst_sv_add_2(self):
+ # adds:
+ # 1 = 5 + 9 => 0x5555 = 0x4321+0x1234
+ # r1 is scalar so ENDS EARLY
+ isa = SVP64Asm(['sv.add 1, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = 0x5555
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def tst_sv_add_3(self):
+ # adds:
+ # 1 = 5 + 9 => 0x5555 = 0x4321+0x1234
+ # 2 = 5 + 10 => 0x5432 = 0x4321+0x1111
+ isa = SVP64Asm(['sv.add 1.v, 5, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = 0x5555
+ expected_regs[2] = 0x5432
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def tst_sv_add_vl_0(self):
+ # adds:
+ # none because VL is zer0
+ isa = SVP64Asm(['sv.add 1, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0x1234
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x4321
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=0)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 0 # VL
+ svstate.maxvl[0:7] = 0 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def tst_sv_add_cr(self):
+ # adds when Rc=1: TODO CRs higher up
+ # 1 = 5 + 9 => 0 = -1+1 CR0=0b100
+ # 2 = 6 + 10 => 0x3334 = 0x2223+0x1111 CR1=0b010
+ isa = SVP64Asm(['sv.add. 1.v, 5.v, 9.v'
+ ])
+ lst = list(isa)
+ print ("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[9] = 0xffffffffffffffff
+ initial_regs[10] = 0x1111
+ initial_regs[5] = 0x1
+ initial_regs[6] = 0x2223
+ # SVSTATE (in this case, VL=2)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 2 # VL
+ svstate.maxvl[0:7] = 2 # MAXVL
+ print ("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[1] = 0
+ expected_regs[2] = 0x3334
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ # XXX TODO, these need to move to higher range (offset)
+ cr0_idx = SVP64CROffs.CR0
+ cr1_idx = SVP64CROffs.CR1
+ CR0 = sim.crl[cr0_idx].get_range().value
+ CR1 = sim.crl[cr1_idx].get_range().value
+ print ("CR0", CR0)
+ print ("CR1", CR1)
+ self._check_regs(sim, expected_regs)
+ self.assertEqual(CR0, SelectableInt(2, 4))
+ self.assertEqual(CR1, SelectableInt(4, 4))
+
+ def test_intpred_vcompress(self):
+ # reg num 0 1 2 3 4 5 6 7 8 9 10 11
+ # src r3=0b101 Y N Y
+ # | |
+ # +-------+ |
+ # | +-----------+
+ # | |
+ # dest always Y Y Y
+
+ isa = SVP64Asm(['sv.extsb/sm=r3 5.v, 9.v'])
+ lst = list(isa)
+ print("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 0b101 # predicate mask
+ initial_regs[9] = 0x90 # source r3 is 0b101 so this will be used
+ initial_regs[10] = 0x91 # this gets skipped
+ initial_regs[11] = 0x92 # source r3 is 0b101 so this will be used
+ # SVSTATE (in this case, VL=3)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 3 # VL
+ svstate.maxvl[0:7] = 3 # MAXVL
+ print("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0xffff_ffff_ffff_ff90 # (from r9)
+ expected_regs[6] = 0xffff_ffff_ffff_ff92 # (from r11)
+ expected_regs[7] = 0x0 # (VL loop runs out before we can use it)
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_intpred_vexpand(self):
+ # reg num 0 1 2 3 4 5 6 7 8 9 10 11
+ # src always Y Y Y
+ # | |
+ # +-------+ |
+ # | +------+
+ # | |
+ # dest r3=0b101 Y N Y
+
+ isa = SVP64Asm(['sv.extsb/dm=r3 5.v, 9.v'])
+ lst = list(isa)
+ print("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 0b101 # predicate mask
+ initial_regs[9] = 0x90 # source is "always", so this will be used
+ initial_regs[10] = 0x91 # likewise
+ initial_regs[11] = 0x92 # the VL loop runs out before we can use it
+ # SVSTATE (in this case, VL=3)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 3 # VL
+ svstate.maxvl[0:7] = 3 # MAXVL
+ print("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0xffff_ffff_ffff_ff90 # 1st bit of r3 is 1
+ expected_regs[6] = 0x0 # skip
+ expected_regs[7] = 0xffff_ffff_ffff_ff91 # 3nd bit of r3 is 1
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_intpred_twinpred(self):
+ # reg num 0 1 2 3 4 5 6 7 8 9 10 11
+ # src r3=0b101 Y N Y
+ # |
+ # +-----+
+ # |
+ # dest ~r3=0b010 N Y N
+
+ isa = SVP64Asm(['sv.extsb/sm=r3/dm=~r3 5.v, 9.v'])
+ lst = list(isa)
+ print("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 0b101 # predicate mask
+ initial_regs[9] = 0x90 # source r3 is 0b101 so this will be used
+ initial_regs[10] = 0x91 # this gets skipped
+ initial_regs[11] = 0x92 # VL loop runs out before we can use it
+ # SVSTATE (in this case, VL=3)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 3 # VL
+ svstate.maxvl[0:7] = 3 # MAXVL
+ print("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0x0 # dest ~r3 is 0b010: skip
+ expected_regs[6] = 0xffff_ffff_ffff_ff90 # 2nd bit of ~r3 is 1
+ expected_regs[7] = 0x0 # dest ~r3 is 0b010: skip
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ # checks that we are able to resume in the middle of a VL loop,
+ # after an interrupt, or after the user has updated src/dst step
+ # let's assume the user has prepared src/dst step before running this
+ # vector instruction
+ def test_intpred_reentrant(self):
+ # reg num 0 1 2 3 4 5 6 7 8 9 10 11 12
+ # srcstep=1 v
+ # src r3=0b0101 Y N Y N
+ # : |
+ # + - - + |
+ # : +-------+
+ # : |
+ # dest ~r3=0b1010 N Y N Y
+ # dststep=2 ^
+
+ isa = SVP64Asm(['sv.extsb/sm=r3/dm=~r3 5.v, 9.v'])
+ lst = list(isa)
+ print("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 0b0101 # mask
+ initial_regs[9] = 0x90 # srcstep starts at 2, so this gets skipped
+ initial_regs[10] = 0x91 # skip
+ initial_regs[11] = 0x92 # this will be used
+ initial_regs[12] = 0x93 # skip
+
+ # SVSTATE (in this case, VL=4)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 4 # VL
+ svstate.maxvl[0:7] = 4 # MAXVL
+ # set src/dest step on the middle of the loop
+ svstate.srcstep[0:7] = 1
+ svstate.dststep[0:7] = 2
+ print("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0x0 # skip
+ expected_regs[6] = 0x0 # dststep starts at 3, so this gets skipped
+ expected_regs[7] = 0x0 # skip
+ expected_regs[8] = 0xffff_ffff_ffff_ff92 # this will be used
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_shift_one_by_r3_dest(self):
+ # reg num 0 1 2 3 4 5 6 7 8 9 10 11
+ # src r30=0b100 N N Y
+ # |
+ # +-----------+
+ # |
+ # dest r3=1: 1<<r3=0b010 N Y N
+
+ isa = SVP64Asm(['sv.extsb/dm=1<<r3/sm=r30 5.v, 9.v'])
+ lst = list(isa)
+ print("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 1 # dest mask = 1<<r3 = 0b010
+ initial_regs[30] = 0b100 # source mask
+ initial_regs[9] = 0x90 # skipped
+ initial_regs[10] = 0x91 # skipped
+ initial_regs[11] = 0x92 # 3rd bit of r30 is 1
+ # SVSTATE (in this case, VL=3)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 3 # VL
+ svstate.maxvl[0:7] = 3 # MAXVL
+ print("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0x0 # skip
+ expected_regs[6] = 0xffff_ffff_ffff_ff92 # r3 is 1, so this is used
+ expected_regs[7] = 0x0 # skip
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def test_shift_one_by_r3_source(self):
+ # reg num 0 1 2 3 4 5 6 7 8 9 10 11
+ # src r3=2: 1<<r3=0b100 N N Y
+ # |
+ # +-----------+
+ # |
+ # dest r30=0b010 N Y N
+
+ isa = SVP64Asm(['sv.extsb/sm=1<<r3/dm=r30 5.v, 9.v'])
+ lst = list(isa)
+ print("listing", lst)
+
+ # initial values in GPR regfile
+ initial_regs = [0] * 32
+ initial_regs[3] = 2 # source mask = 1<<r3 = 0b100
+ initial_regs[30] = 0b010 # dest mask
+ initial_regs[9] = 0x90 # skipped
+ initial_regs[10] = 0x91 # skipped
+ initial_regs[11] = 0x92 # r3 is 2, so this will be used
+ # SVSTATE (in this case, VL=3)
+ svstate = SVP64State()
+ svstate.vl[0:7] = 3 # VL
+ svstate.maxvl[0:7] = 3 # MAXVL
+ print("SVSTATE", bin(svstate.spr.asint()))
+ # copy before running
+ expected_regs = deepcopy(initial_regs)
+ expected_regs[5] = 0x0 # skip
+ expected_regs[6] = 0xffff_ffff_ffff_ff92 # 2nd bit of r30 is 1
+ expected_regs[7] = 0x0 # skip
+
+ with Program(lst, bigendian=False) as program:
+ sim = self.run_tst_program(program, initial_regs, svstate)
+ self._check_regs(sim, expected_regs)
+
+ def run_tst_program(self, prog, initial_regs=None,
+ svstate=None,
+ initial_cr=0):
+ if initial_regs is None:
+ initial_regs = [0] * 32
+ simulator = run_tst(prog, initial_regs, svstate=svstate,
+ initial_cr=initial_cr)
+ simulator.gpr.dump()
+ return simulator
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null
+# Based on GardenSnake - a parser generator demonstration program
+# GardenSnake was released into the Public Domain by Andrew Dalke.
+
+# Portions of this work are derived from Python's Grammar definition
+# and may be covered under the Python copyright and license
+#
+# Andrew Dalke / Dalke Scientific Software, LLC
+# 30 August 2006 / Cape Town, South Africa
+
+# Modifications for inclusion in PLY distribution
+from copy import copy
+from ply import lex
+from soc.decoder.selectable_int import SelectableInt
+
+# I implemented INDENT / DEDENT generation as a post-processing filter
+
+# The original lex token stream contains WS and NEWLINE characters.
+# WS will only occur before any other tokens on a line.
+
+# I have three filters. One tags tokens by adding two attributes.
+# "must_indent" is True if the token must be indented from the
+# previous code. The other is "at_line_start" which is True for WS
+# and the first non-WS/non-NEWLINE on a line. It flags the check so
+# see if the new line has changed indication level.
+
+# Python's syntax has three INDENT states
+# 0) no colon hence no need to indent
+# 1) "if 1: go()" - simple statements have a COLON but no need for an indent
+# 2) "if 1:\n go()" - complex statements have a COLON NEWLINE and must indent
+NO_INDENT = 0
+MAY_INDENT = 1
+MUST_INDENT = 2
+
+# turn into python-like colon syntax from pseudo-code syntax.
+# identify tokens which tell us whether a "hidden colon" is needed.
+# this in turn means that track_tokens_filter "works" without needing
+# complex grammar rules
+
+
+def python_colonify(lexer, tokens):
+
+ implied_colon_needed = False
+ for token in tokens:
+ #print ("track colon token", token, token.type)
+
+ if token.type == 'THEN':
+ # turn then into colon
+ token.type = "COLON"
+ yield token
+ elif token.type == 'ELSE':
+ yield token
+ token = copy(token)
+ token.type = "COLON"
+ yield token
+ elif token.type in ['DO', 'WHILE', 'FOR', 'SWITCH']:
+ implied_colon_needed = True
+ yield token
+ elif token.type == 'NEWLINE':
+ if implied_colon_needed:
+ ctok = copy(token)
+ ctok.type = "COLON"
+ yield ctok
+ implied_colon_needed = False
+ yield token
+ else:
+ yield token
+
+
+# only care about whitespace at the start of a line
+def track_tokens_filter(lexer, tokens):
+ oldignore = lexer.lexignore
+ lexer.at_line_start = at_line_start = True
+ indent = NO_INDENT
+ saw_colon = False
+ for token in tokens:
+ #print ("track token", token, token.type)
+ token.at_line_start = at_line_start
+
+ if token.type == "COLON":
+ at_line_start = False
+ indent = MAY_INDENT
+ token.must_indent = False
+
+ elif token.type == "NEWLINE":
+ at_line_start = True
+ if indent == MAY_INDENT:
+ indent = MUST_INDENT
+ token.must_indent = False
+
+ elif token.type == "WS":
+ assert token.at_line_start == True
+ at_line_start = True
+ token.must_indent = False
+
+ else:
+ # A real token; only indent after COLON NEWLINE
+ if indent == MUST_INDENT:
+ token.must_indent = True
+ else:
+ token.must_indent = False
+ at_line_start = False
+ indent = NO_INDENT
+
+ # really bad hack that changes ignore lexer state.
+ # when "must indent" is seen (basically "real tokens" seen)
+ # then ignore whitespace.
+ if token.must_indent:
+ lexer.lexignore = ('ignore', ' ')
+ else:
+ lexer.lexignore = oldignore
+
+ token.indent = indent
+ yield token
+ lexer.at_line_start = at_line_start
+
+
+def _new_token(type, lineno):
+ tok = lex.LexToken()
+ tok.type = type
+ tok.value = None
+ tok.lineno = lineno
+ tok.lexpos = -1
+ return tok
+
+# Synthesize a DEDENT tag
+
+
+def DEDENT(lineno):
+ return _new_token("DEDENT", lineno)
+
+# Synthesize an INDENT tag
+
+
+def INDENT(lineno):
+ return _new_token("INDENT", lineno)
+
+
+def count_spaces(l):
+ for i in range(len(l)):
+ if l[i] != ' ':
+ return i
+ return 0
+
+
+def annoying_case_hack_filter(code):
+ """add annoying "silent keyword" (fallthrough)
+
+ this which tricks the parser into taking the (silent) case statement
+ as a "small expression". it can then be spotted and used to indicate
+ "fall through" to the next case (in the parser)
+
+ also skips blank lines
+
+ bugs: any function that starts with the letters "case" or "default"
+ will be detected erroneously. fixing that involves doing a token
+ lexer which spots the fact that "case" and "default" are words,
+ separating them from space, colon, bracket etc.
+
+ http://bugs.libre-riscv.org/show_bug.cgi?id=280
+ """
+ res = []
+ prev_spc_count = None
+ for l in code.split("\n"):
+ spc_count = count_spaces(l)
+ nwhite = l[spc_count:]
+ if len(nwhite) == 0: # skip blank lines
+ continue
+ if nwhite.startswith("case") or nwhite.startswith("default"):
+ #print ("case/default", nwhite, spc_count, prev_spc_count)
+ if (prev_spc_count is not None and
+ prev_spc_count == spc_count and
+ (res[-1].endswith(":") or res[-1].endswith(": fallthrough"))):
+ res[-1] += " fallthrough" # add to previous line
+ prev_spc_count = spc_count
+ else:
+ #print ("notstarts", spc_count, nwhite)
+ prev_spc_count = None
+ res.append(l)
+ return '\n'.join(res)
+
+
+# Track the indentation level and emit the right INDENT / DEDENT events.
+def indentation_filter(tokens):
+ # A stack of indentation levels; will never pop item 0
+ levels = [0]
+ token = None
+ depth = 0
+ prev_was_ws = False
+ for token in tokens:
+ if 0:
+ print("Process", depth, token.indent, token,)
+ if token.at_line_start:
+ print("at_line_start",)
+ if token.must_indent:
+ print("must_indent",)
+ print
+
+ # WS only occurs at the start of the line
+ # There may be WS followed by NEWLINE so
+ # only track the depth here. Don't indent/dedent
+ # until there's something real.
+ if token.type == "WS":
+ assert depth == 0
+ depth = len(token.value)
+ prev_was_ws = True
+ # WS tokens are never passed to the parser
+ continue
+
+ if token.type == "NEWLINE":
+ depth = 0
+ if prev_was_ws or token.at_line_start:
+ # ignore blank lines
+ continue
+ # pass the other cases on through
+ yield token
+ continue
+
+ # then it must be a real token (not WS, not NEWLINE)
+ # which can affect the indentation level
+
+ prev_was_ws = False
+ if token.must_indent:
+ # The current depth must be larger than the previous level
+ if not (depth > levels[-1]):
+ raise IndentationError("expected an indented block")
+
+ levels.append(depth)
+ yield INDENT(token.lineno)
+
+ elif token.at_line_start:
+ # Must be on the same level or one of the previous levels
+ if depth == levels[-1]:
+ # At the same level
+ pass
+ elif depth > levels[-1]:
+ raise IndentationError("indent increase but not in new block")
+ else:
+ # Back up; but only if it matches a previous level
+ try:
+ i = levels.index(depth)
+ except ValueError:
+ raise IndentationError("inconsistent indentation")
+ for _ in range(i+1, len(levels)):
+ yield DEDENT(token.lineno)
+ levels.pop()
+
+ yield token
+
+ ### Finished processing ###
+
+ # Must dedent any remaining levels
+ if len(levels) > 1:
+ assert token is not None
+ for _ in range(1, len(levels)):
+ yield DEDENT(token.lineno)
+
+
+# The top-level filter adds an ENDMARKER, if requested.
+# Python's grammar uses it.
+def filter(lexer, add_endmarker=True):
+ token = None
+ tokens = iter(lexer.token, None)
+ tokens = python_colonify(lexer, tokens)
+ tokens = track_tokens_filter(lexer, tokens)
+ for token in indentation_filter(tokens):
+ yield token
+
+ if add_endmarker:
+ lineno = 1
+ if token is not None:
+ lineno = token.lineno
+ yield _new_token("ENDMARKER", lineno)
+
+##### Lexer ######
+
+
+class PowerLexer:
+ tokens = (
+ 'DEF',
+ 'IF',
+ 'THEN',
+ 'ELSE',
+ 'FOR',
+ 'TO',
+ 'DO',
+ 'WHILE',
+ 'BREAK',
+ 'NAME',
+ 'HEX', # hex numbers
+ 'NUMBER', # Python decimals
+ 'BINARY', # Python binary
+ 'STRING', # single quoted strings only; syntax of raw strings
+ 'LPAR',
+ 'RPAR',
+ 'LBRACK',
+ 'RBRACK',
+ 'COLON',
+ 'EQ',
+ 'ASSIGNEA',
+ 'ASSIGN',
+ 'LTU',
+ 'GTU',
+ 'NE',
+ 'LE',
+ 'GE',
+ 'LT',
+ 'GT',
+ 'PLUS',
+ 'MINUS',
+ 'MULT',
+ 'DIV',
+ 'MOD',
+ 'INVERT',
+ 'APPEND',
+ 'BITOR',
+ 'BITAND',
+ 'BITXOR',
+ 'RETURN',
+ 'SWITCH',
+ 'CASE',
+ 'DEFAULT',
+ 'WS',
+ 'NEWLINE',
+ 'COMMA',
+ 'SEMICOLON',
+ 'INDENT',
+ 'DEDENT',
+ 'ENDMARKER',
+ )
+
+ # Build the lexer
+ def build(self, **kwargs):
+ self.lexer = lex.lex(module=self, **kwargs)
+
+ def t_HEX(self, t):
+ r"""0x[0-9a-fA-F_]+"""
+ val = t.value.replace("_", "")
+ t.value = SelectableInt(int(val, 16), (len(val)-2)*4) # hex = nibble
+ return t
+
+ def t_BINARY(self, t):
+ r"""0b[01]+"""
+ t.value = SelectableInt(int(t.value, 2), len(t.value)-2)
+ return t
+
+ #t_NUMBER = r'\d+'
+ # taken from decmial.py but without the leading sign
+ def t_NUMBER(self, t):
+ r"""(\d+(\.\d*)?|\.\d+)([eE][-+]? \d+)?"""
+ t.value = int(t.value)
+ return t
+
+ def t_STRING(self, t):
+ r"'([^\\']+|\\'|\\\\)*'" # I think this is right ...
+ print(repr(t.value))
+ t.value = t.value[1:-1]
+ return t
+
+ t_COLON = r':'
+ t_EQ = r'='
+ t_ASSIGNEA = r'<-iea'
+ t_ASSIGN = r'<-'
+ t_LTU = r'<u'
+ t_GTU = r'>u'
+ t_NE = r'!='
+ t_LE = r'<='
+ t_GE = r'>='
+ t_LT = r'<'
+ t_GT = r'>'
+ t_PLUS = r'\+'
+ t_MINUS = r'-'
+ t_MULT = r'\*'
+ t_DIV = r'/'
+ t_MOD = r'%'
+ t_INVERT = r'¬'
+ t_COMMA = r','
+ t_SEMICOLON = r';'
+ t_APPEND = r'\|\|'
+ t_BITOR = r'\|'
+ t_BITAND = r'\&'
+ t_BITXOR = r'\^'
+
+ # Ply nicely documented how to do this.
+
+ RESERVED = {
+ "def": "DEF",
+ "if": "IF",
+ "then": "THEN",
+ "else": "ELSE",
+ "leave": "BREAK",
+ "for": "FOR",
+ "to": "TO",
+ "while": "WHILE",
+ "do": "DO",
+ "return": "RETURN",
+ "switch": "SWITCH",
+ "case": "CASE",
+ "default": "DEFAULT",
+ }
+
+ def t_NAME(self, t):
+ r'[a-zA-Z_][a-zA-Z0-9_]*'
+ t.type = self.RESERVED.get(t.value, "NAME")
+ return t
+
+ # Putting this before t_WS let it consume lines with only comments in
+ # them so the latter code never sees the WS part. Not consuming the
+ # newline. Needed for "if 1: #comment"
+ def t_comment(self, t):
+ r"[ ]*\043[^\n]*" # \043 is '#'
+ pass
+
+ # Whitespace
+
+ def t_WS(self, t):
+ r'[ ]+'
+ if t.lexer.at_line_start and t.lexer.paren_count == 0 and \
+ t.lexer.brack_count == 0:
+ return t
+
+ # Don't generate newline tokens when inside of parenthesis, eg
+ # a = (1,
+ # 2, 3)
+ def t_newline(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+ t.type = "NEWLINE"
+ if t.lexer.paren_count == 0 and t.lexer.brack_count == 0:
+ return t
+
+ def t_LBRACK(self, t):
+ r'\['
+ t.lexer.brack_count += 1
+ return t
+
+ def t_RBRACK(self, t):
+ r'\]'
+ # check for underflow? should be the job of the parser
+ t.lexer.brack_count -= 1
+ return t
+
+ def t_LPAR(self, t):
+ r'\('
+ t.lexer.paren_count += 1
+ return t
+
+ def t_RPAR(self, t):
+ r'\)'
+ # check for underflow? should be the job of the parser
+ t.lexer.paren_count -= 1
+ return t
+
+ #t_ignore = " "
+
+ def t_error(self, t):
+ raise SyntaxError("Unknown symbol %r" % (t.value[0],))
+ print("Skipping", repr(t.value[0]))
+ t.lexer.skip(1)
+
+
+# Combine Ply and my filters into a new lexer
+
+class IndentLexer(PowerLexer):
+ def __init__(self, debug=0, optimize=0, lextab='lextab', reflags=0):
+ self.debug = debug
+ self.build(debug=debug, optimize=optimize,
+ lextab=lextab, reflags=reflags)
+ self.token_stream = None
+
+ def input(self, s, add_endmarker=True):
+ s = annoying_case_hack_filter(s)
+ if self.debug:
+ print(s)
+ s += "\n"
+ self.lexer.paren_count = 0
+ self.lexer.brack_count = 0
+ self.lexer.input(s)
+ self.token_stream = filter(self.lexer, add_endmarker)
+
+ def token(self):
+ try:
+ return next(self.token_stream)
+ except StopIteration:
+ return None
+
+
+switchtest = """
+switch (n)
+ case(1): x <- 5
+ case(3): x <- 2
+ case(2):
+
+ case(4):
+ x <- 3
+ case(9):
+
+ default:
+ x <- 9
+print (5)
+"""
+
+cnttzd = """
+n <- 0
+do while n < 64
+ if (RS)[63-n] = 0b1 then
+ leave
+ n <- n + 1
+RA <- EXTZ64(n)
+print (RA)
+"""
+
+if __name__ == '__main__':
+
+ # quick test/demo
+ #code = cnttzd
+ code = switchtest
+ print(code)
+
+ lexer = IndentLexer(debug=1)
+ # Give the lexer some input
+ print("code")
+ print(code)
+ lexer.input(code)
+
+ tokens = iter(lexer.token, None)
+ for token in tokens:
+ print(token)
--- /dev/null
+# Reads OpenPOWER ISA pages from http://libre-riscv.org/openpower/isa
+"""OpenPOWER ISA page parser
+
+returns an OrderedDict of namedtuple "Ops" containing details of all
+instructions listed in markdown files.
+
+format must be strictly as follows (no optional sections) including whitespace:
+
+# Compare Logical
+
+X-Form
+
+* cmpl BF,L,RA,RB
+
+ if L = 0 then a <- [0]*32 || (RA)[32:63]
+ b <- [0]*32 || (RB)[32:63]
+ else a <- (RA)
+ b <- (RB)
+ if a <u b then c <- 0b100
+ else if a >u b then c <- 0b010
+ else c <- 0b001
+ CR[4*BF+32:4*BF+35] <- c || XER[SO]
+
+Special Registers Altered:
+
+ CR field BF
+ Another field
+
+this translates to:
+
+ # heading
+ blank
+ Some-Form
+ blank
+ * instruction registerlist
+ * instruction registerlist
+ blank
+ 4-space-indented pseudo-code
+ 4-space-indented pseudo-code
+ blank
+ Special Registers Altered:
+ 4-space-indented register description
+ blank
+ blank(s) (optional for convenience at end-of-page)
+"""
+
+from collections import namedtuple, OrderedDict
+from copy import copy
+import os
+
+opfields = ("desc", "form", "opcode", "regs", "pcode", "sregs", "page")
+Ops = namedtuple("Ops", opfields)
+
+
+def get_isa_dir():
+ fdir = os.path.abspath(os.path.dirname(__file__))
+ fdir = os.path.split(fdir)[0]
+ fdir = os.path.split(fdir)[0]
+ fdir = os.path.split(fdir)[0]
+ fdir = os.path.split(fdir)[0]
+ return os.path.join(fdir, "libreriscv", "openpower", "isa")
+
+
+class ISA:
+
+ def __init__(self):
+ self.instr = OrderedDict()
+ self.forms = {}
+ self.page = {}
+ for pth in os.listdir(os.path.join(get_isa_dir())):
+ print(get_isa_dir(), pth)
+ if "swp" in pth:
+ continue
+ assert pth.endswith(".mdwn"), "only %s in isa dir" % pth
+ self.read_file(pth)
+ continue
+ # code which helped add in the keyword "Pseudo-code:" automatically
+ rewrite = self.read_file_for_rewrite(pth)
+ name = os.path.join("/tmp", pth)
+ with open(name, "w") as f:
+ f.write('\n'.join(rewrite) + '\n')
+
+ def read_file_for_rewrite(self, fname):
+ pagename = fname.split('.')[0]
+ fname = os.path.join(get_isa_dir(), fname)
+ with open(fname) as f:
+ lines = f.readlines()
+ rewrite = []
+
+ l = lines.pop(0).rstrip() # get first line
+ rewrite.append(l)
+ while lines:
+ print(l)
+ # look for HTML comment, if starting, skip line.
+ # XXX this is braindead! it doesn't look for the end
+ # so please put ending of comments on one line:
+ # <!-- line 1 comment -->
+ # <!-- line 2 comment -->
+ if l.startswith('<!--'):
+ # print ("skipping comment", l)
+ l = lines.pop(0).rstrip() # get first line
+ continue
+
+ # Ignore blank lines before the first #
+ if len(l.strip()) == 0:
+ continue
+
+ # expect get heading
+ assert l.startswith('#'), ("# not found in line %s" % l)
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ print(repr(l))
+ assert len(l) == 0, ("blank line not found %s" % l)
+ rewrite.append(l)
+
+ # Form expected
+ l = lines.pop(0).strip()
+ assert l.endswith('-Form'), ("line with -Form expected %s" % l)
+ rewrite.append(l)
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ assert len(l) == 0, ("blank line not found %s" % l)
+ rewrite.append(l)
+
+ # get list of opcodes
+ while True:
+ l = lines.pop(0).strip()
+ rewrite.append(l)
+ if len(l) == 0:
+ break
+ assert l.startswith('*'), ("* not found in line %s" % l)
+
+ rewrite.append("Pseudo-code:")
+ rewrite.append("")
+ # get pseudocode
+ while True:
+ l = lines.pop(0).rstrip()
+ rewrite.append(l)
+ if len(l) == 0:
+ break
+ assert l.startswith(' '), ("4spcs not found in line %s" % l)
+
+ # "Special Registers Altered" expected
+ l = lines.pop(0).rstrip()
+ assert l.startswith("Special"), ("special not found %s" % l)
+ rewrite.append(l)
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ assert len(l) == 0, ("blank line not found %s" % l)
+ rewrite.append(l)
+
+ # get special regs
+ while lines:
+ l = lines.pop(0).rstrip()
+ rewrite.append(l)
+ if len(l) == 0:
+ break
+ assert l.startswith(' '), ("4spcs not found in line %s" % l)
+
+ # expect and drop whitespace
+ while lines:
+ l = lines.pop(0).rstrip()
+ rewrite.append(l)
+ if len(l) != 0 and not l.startswith('<!--'):
+ break
+
+ return rewrite
+
+ def read_file(self, fname):
+ pagename = fname.split('.')[0]
+ fname = os.path.join(get_isa_dir(), fname)
+ with open(fname) as f:
+ lines = f.readlines()
+
+ # set up dict with current page name
+ d = {'page': pagename}
+
+ # line-by-line lexer/parser, quite straightforward: pops one
+ # line off the list and checks it. nothing complicated needed,
+ # all sections are mandatory so no need for a full LALR parser.
+
+ l = lines.pop(0).rstrip() # get first line
+ while lines:
+ print(l)
+ # look for HTML comment, if starting, skip line.
+ # XXX this is braindead! it doesn't look for the end
+ # so please put ending of comments on one line:
+ # <!-- line 1 comment -->
+ # <!-- line 2 comment -->
+ if l.startswith('<!--'):
+ # print ("skipping comment", l)
+ l = lines.pop(0).rstrip() # get next line
+ continue
+
+ # Ignore blank lines before the first #
+ if len(l) == 0:
+ l = lines.pop(0).rstrip() # get next line
+ continue
+
+ # expect get heading
+ assert l.startswith('#'), ("# not found in line '%s'" % l)
+ d['desc'] = l[1:].strip()
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ print(repr(l))
+ assert len(l) == 0, ("blank line not found %s" % l)
+
+ # Form expected
+ l = lines.pop(0).strip()
+ assert l.endswith('-Form'), ("line with -Form expected %s" % l)
+ d['form'] = l.split('-')[0]
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ assert len(l) == 0, ("blank line not found %s" % l)
+
+ # get list of opcodes
+ li = []
+ while True:
+ l = lines.pop(0).strip()
+ if len(l) == 0:
+ break
+ assert l.startswith('*'), ("* not found in line %s" % l)
+ l = l[1:].split(' ') # lose star
+ l = filter(lambda x: len(x) != 0, l) # strip blanks
+ li.append(list(l))
+ opcodes = li
+
+ # "Pseudocode" expected
+ l = lines.pop(0).rstrip()
+ assert l.startswith("Pseudo-code:"), ("pseudocode found %s" % l)
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ print(repr(l))
+ assert len(l) == 0, ("blank line not found %s" % l)
+
+ # get pseudocode
+ li = []
+ while True:
+ l = lines.pop(0).rstrip()
+ if len(l) == 0:
+ break
+ assert l.startswith(' '), ("4spcs not found in line %s" % l)
+ l = l[4:] # lose 4 spaces
+ li.append(l)
+ d['pcode'] = li
+
+ # "Special Registers Altered" expected
+ l = lines.pop(0).rstrip()
+ assert l.startswith("Special"), ("special not found %s" % l)
+
+ # whitespace expected
+ l = lines.pop(0).strip()
+ assert len(l) == 0, ("blank line not found %s" % l)
+
+ # get special regs
+ li = []
+ while lines:
+ l = lines.pop(0).rstrip()
+ if len(l) == 0:
+ break
+ assert l.startswith(' '), ("4spcs not found in line %s" % l)
+ l = l[4:] # lose 4 spaces
+ li.append(l)
+ d['sregs'] = li
+
+ # add in opcode
+ for o in opcodes:
+ self.add_op(o, d)
+
+ # expect and drop whitespace
+ while lines:
+ l = lines.pop(0).rstrip()
+ if len(l) != 0 and not l.startswith('<!--'):
+ break
+
+ def add_op(self, o, d):
+ opcode, regs = o[0], o[1:]
+ op = copy(d)
+ op['regs'] = regs
+ if len(regs) != 0:
+ regs[0] = regs[0].split(",")
+ op['opcode'] = opcode
+ self.instr[opcode] = Ops(**op)
+
+ # create list of instructions by form
+ form = op['form']
+ fl = self.forms.get(form, [])
+ self.forms[form] = fl + [opcode]
+
+ # create list of instructions by page
+ page = op['page']
+ pl = self.page.get(page, [])
+ self.page[page] = pl + [opcode]
+
+ def pprint_ops(self):
+ for k, v in self.instr.items():
+ print("# %s %s" % (v.opcode, v.desc))
+ print("Form: %s Regs: %s" % (v.form, v.regs))
+ print('\n'.join(map(lambda x: " %s" % x, v.pcode)))
+ print("Specials")
+ print('\n'.join(map(lambda x: " %s" % x, v.sregs)))
+ print()
+ for k, v in isa.forms.items():
+ print(k, v)
+
+
+if __name__ == '__main__':
+ isa = ISA()
+ isa.pprint_ops()
+ # example on how to access cmp regs:
+ print ("cmp regs:", isa.instr["cmp"].regs)
--- /dev/null
+# Based on GardenSnake - a parser generator demonstration program
+# GardenSnake was released into the Public Domain by Andrew Dalke.
+
+# Portions of this work are derived from Python's Grammar definition
+# and may be covered under the Python copyright and license
+#
+# Andrew Dalke / Dalke Scientific Software, LLC
+# 30 August 2006 / Cape Town, South Africa
+
+# Modifications for inclusion in PLY distribution
+from pprint import pprint
+from ply import lex, yacc
+import astor
+from copy import deepcopy
+
+from soc.decoder.power_decoder import create_pdecode
+from soc.decoder.pseudo.lexer import IndentLexer
+from soc.decoder.orderedset import OrderedSet
+
+# I use the Python AST
+#from compiler import ast
+import ast
+
+# Helper function
+
+
+def Assign(autoassign, assignname, left, right, iea_mode):
+ names = []
+ print("Assign", assignname, left, right)
+ if isinstance(left, ast.Name):
+ # Single assignment on left
+ # XXX when doing IntClass, which will have an "eq" function,
+ # this is how to access it
+ # eq = ast.Attribute(left, "eq") # get eq fn
+ # return ast.Call(eq, [right], []) # now call left.eq(right)
+ return ast.Assign([ast.Name(left.id, ast.Store())], right)
+ elif isinstance(left, ast.Tuple):
+ # List of things - make sure they are Name nodes
+ names = []
+ for child in left.getChildren():
+ if not isinstance(child, ast.Name):
+ raise SyntaxError("that assignment not supported")
+ names.append(child.name)
+ ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
+ return ast.Assign([ast.AssTuple(ass_list)], right)
+ elif isinstance(left, ast.Subscript):
+ ls = left.slice
+ # XXX changing meaning of "undefined" to a function
+ #if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
+ # right.id == 'undefined'):
+ # # undefined needs to be copied the exact same slice
+ # right = ast.Subscript(right, ls, ast.Load())
+ # return ast.Assign([left], right)
+ res = ast.Assign([left], right)
+ if autoassign and isinstance(ls, ast.Slice):
+ # hack to create a variable pre-declared based on a slice.
+ # dividend[0:32] = (RA)[0:32] will create
+ # dividend = [0] * 32
+ # dividend[0:32] = (RA)[0:32]
+ # the declaration makes the slice-assignment "work"
+ lower, upper, step = ls.lower, ls.upper, ls.step
+ print("lower, upper, step", repr(lower), repr(upper), step)
+ if not isinstance(lower, ast.Constant) or \
+ not isinstance(upper, ast.Constant):
+ return res
+ qty = ast.Num(upper.value-lower.value)
+ keywords = [ast.keyword(arg='repeat', value=qty)]
+ l = [ast.Num(0)]
+ right = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
+ declare = ast.Assign([ast.Name(assignname, ast.Store())], right)
+ return [declare, res]
+ return res
+ # XXX HMMM probably not needed...
+ ls = left.slice
+ if isinstance(ls, ast.Slice):
+ lower, upper, step = ls.lower, ls.upper, ls.step
+ print("slice assign", lower, upper, step)
+ if step is None:
+ ls = (lower, upper, None)
+ else:
+ ls = (lower, upper, step)
+ ls = ast.Tuple(ls)
+ return ast.Call(ast.Name("selectassign", ast.Load()),
+ [left.value, ls, right], [])
+ else:
+ print("Assign fail")
+ raise SyntaxError("Can't do that yet")
+
+
+# I implemented INDENT / DEDENT generation as a post-processing filter
+
+# The original lex token stream contains WS and NEWLINE characters.
+# WS will only occur before any other tokens on a line.
+
+# I have three filters. One tags tokens by adding two attributes.
+# "must_indent" is True if the token must be indented from the
+# previous code. The other is "at_line_start" which is True for WS
+# and the first non-WS/non-NEWLINE on a line. It flags the check so
+# see if the new line has changed indication level.
+
+
+# No using Python's approach because Ply supports precedence
+
+# comparison: expr (comp_op expr)*
+# arith_expr: term (('+'|'-') term)*
+# term: factor (('*'|'/'|'%'|'//') factor)*
+# factor: ('+'|'-'|'~') factor | power
+# comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+
+def make_le_compare(arg):
+ (left, right) = arg
+ return ast.Call(ast.Name("le", ast.Load()), (left, right), [])
+
+
+def make_ge_compare(arg):
+ (left, right) = arg
+ return ast.Call(ast.Name("ge", ast.Load()), (left, right), [])
+
+
+def make_lt_compare(arg):
+ (left, right) = arg
+ return ast.Call(ast.Name("lt", ast.Load()), (left, right), [])
+
+
+def make_gt_compare(arg):
+ (left, right) = arg
+ return ast.Call(ast.Name("gt", ast.Load()), (left, right), [])
+
+
+def make_eq_compare(arg):
+ (left, right) = arg
+ return ast.Call(ast.Name("eq", ast.Load()), (left, right), [])
+
+
+def make_ne_compare(arg):
+ (left, right) = arg
+ return ast.Call(ast.Name("ne", ast.Load()), (left, right), [])
+
+
+binary_ops = {
+ "^": ast.BitXor(),
+ "&": ast.BitAnd(),
+ "|": ast.BitOr(),
+ "+": ast.Add(),
+ "-": ast.Sub(),
+ "*": ast.Mult(),
+ "/": ast.FloorDiv(),
+ "%": ast.Mod(),
+ "<=": make_le_compare,
+ ">=": make_ge_compare,
+ "<": make_lt_compare,
+ ">": make_gt_compare,
+ "=": make_eq_compare,
+ "!=": make_ne_compare,
+}
+unary_ops = {
+ "+": ast.UAdd(),
+ "-": ast.USub(),
+ "¬": ast.Invert(),
+}
+
+
+def check_concat(node): # checks if the comparison is already a concat
+ print("check concat", node)
+ if not isinstance(node, ast.Call):
+ return [node]
+ print("func", node.func.id)
+ if node.func.id != 'concat':
+ return [node]
+ if node.keywords: # a repeated list-constant, don't optimise
+ return [node]
+ return node.args
+
+
+# identify SelectableInt pattern [something] * N
+# must return concat(something, repeat=N)
+def identify_sint_mul_pattern(p):
+ if p[2] != '*': # multiply
+ return False
+ if not isinstance(p[3], ast.Constant): # rhs = Num
+ return False
+ if not isinstance(p[1], ast.List): # lhs is a list
+ return False
+ l = p[1].elts
+ if len(l) != 1: # lhs is a list of length 1
+ return False
+ return True # yippee!
+
+
+def apply_trailer(atom, trailer):
+ if trailer[0] == "TLIST":
+ # assume depth of one
+ atom = apply_trailer(atom, trailer[1])
+ trailer = trailer[2]
+ if trailer[0] == "CALL":
+ #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
+ return ast.Call(atom, trailer[1], [])
+ # if p[1].id == 'print':
+ # p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
+ # else:
+ # p[0] = ast.CallFunc(p[1], p[2][1], None, None)
+ else:
+ print("subscript atom", trailer[1])
+ #raise AssertionError("not implemented %s" % p[2][0])
+ subs = trailer[1]
+ if len(subs) == 1:
+ idx = subs[0]
+ else:
+ idx = ast.Slice(subs[0], subs[1], None)
+ # if isinstance(atom, ast.Name) and atom.id == 'CR':
+ # atom.id = 'CR' # bad hack
+ #print ("apply_trailer Subscript", atom.id, idx)
+ return ast.Subscript(atom, idx, ast.Load())
+
+########## Parser (tokens -> AST) ######
+
+# also part of Ply
+#import yacc
+
+# https://www.mathcs.emory.edu/~valerie/courses/fall10/155/resources/op_precedence.html
+# python operator precedence
+# Highest precedence at top, lowest at bottom.
+# Operators in the same box evaluate left to right.
+#
+# Operator Description
+# () Parentheses (grouping)
+# f(args...) Function call
+# x[index:index] Slicing
+# x[index] Subscription
+# x.attribute Attribute reference
+# ** Exponentiation
+# ~x Bitwise not
+# +x, -x Positive, negative
+# *, /, % mul, div, remainder
+# +, - Addition, subtraction
+# <<, >> Bitwise shifts
+# & Bitwise AND
+# ^ Bitwise XOR
+# | Bitwise OR
+# in, not in, is, is not, <, <=, >, >=, <>, !=, == comp, membership, ident
+# not x Boolean NOT
+# and Boolean AND
+# or Boolean OR
+# lambda Lambda expression
+
+
+class PowerParser:
+
+ precedence = (
+ ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
+ ("left", "BITOR"),
+ ("left", "BITXOR"),
+ ("left", "BITAND"),
+ ("left", "PLUS", "MINUS"),
+ ("left", "MULT", "DIV", "MOD"),
+ ("left", "INVERT"),
+ )
+
+ def __init__(self, form, include_carry_in_write=False):
+ self.include_ca_in_write = include_carry_in_write
+ self.gprs = {}
+ form = self.sd.sigforms[form]
+ print(form)
+ formkeys = form._asdict().keys()
+ self.declared_vars = set()
+ for rname in ['RA', 'RB', 'RC', 'RT', 'RS']:
+ self.gprs[rname] = None
+ self.declared_vars.add(rname)
+ self.available_op_fields = set()
+ for k in formkeys:
+ if k not in self.gprs:
+ if k == 'SPR': # sigh, lower-case to not conflict
+ k = k.lower()
+ self.available_op_fields.add(k)
+ self.op_fields = OrderedSet()
+ self.read_regs = OrderedSet()
+ self.uninit_regs = OrderedSet()
+ self.write_regs = OrderedSet()
+ self.special_regs = OrderedSet() # see p_atom_name
+
+ # The grammar comments come from Python's Grammar/Grammar file
+
+ # NB: compound_stmt in single_input is followed by extra NEWLINE!
+ # file_input: (NEWLINE | stmt)* ENDMARKER
+
+ def p_file_input_end(self, p):
+ """file_input_end : file_input ENDMARKER"""
+ print("end", p[1])
+ p[0] = p[1]
+
+ def p_file_input(self, p):
+ """file_input : file_input NEWLINE
+ | file_input stmt
+ | NEWLINE
+ | stmt"""
+ if isinstance(p[len(p)-1], str):
+ if len(p) == 3:
+ p[0] = p[1]
+ else:
+ p[0] = [] # p == 2 --> only a blank line
+ else:
+ if len(p) == 3:
+ p[0] = p[1] + p[2]
+ else:
+ p[0] = p[1]
+
+ # funcdef: [decorators] 'def' NAME parameters ':' suite
+ # ignoring decorators
+
+ def p_funcdef(self, p):
+ "funcdef : DEF NAME parameters COLON suite"
+ p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
+
+ # parameters: '(' [varargslist] ')'
+ def p_parameters(self, p):
+ """parameters : LPAR RPAR
+ | LPAR varargslist RPAR"""
+ if len(p) == 3:
+ args = []
+ else:
+ args = p[2]
+ p[0] = ast.arguments(args=args, vararg=None, kwarg=None, defaults=[])
+
+ # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
+ # '**' NAME) |
+ # highly simplified
+
+ def p_varargslist(self, p):
+ """varargslist : varargslist COMMA NAME
+ | NAME"""
+ if len(p) == 4:
+ p[0] = p[1] + p[3]
+ else:
+ p[0] = [p[1]]
+
+ # stmt: simple_stmt | compound_stmt
+ def p_stmt_simple(self, p):
+ """stmt : simple_stmt"""
+ # simple_stmt is a list
+ p[0] = p[1]
+
+ def p_stmt_compound(self, p):
+ """stmt : compound_stmt"""
+ p[0] = [p[1]]
+
+ # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
+ def p_simple_stmt(self, p):
+ """simple_stmt : small_stmts NEWLINE
+ | small_stmts SEMICOLON NEWLINE"""
+ p[0] = p[1]
+
+ def p_small_stmts(self, p):
+ """small_stmts : small_stmts SEMICOLON small_stmt
+ | small_stmt"""
+ if len(p) == 4:
+ p[0] = p[1] + [p[3]]
+ elif isinstance(p[1], list):
+ p[0] = p[1]
+ else:
+ p[0] = [p[1]]
+
+ # small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
+ # import_stmt | global_stmt | exec_stmt | assert_stmt
+ def p_small_stmt(self, p):
+ """small_stmt : flow_stmt
+ | break_stmt
+ | expr_stmt"""
+ if isinstance(p[1], ast.Call):
+ p[0] = ast.Expr(p[1])
+ elif isinstance(p[1], ast.Name) and p[1].id == 'TRAP':
+ # TRAP needs to actually be a function
+ name = ast.Name("self", ast.Load())
+ name = ast.Attribute(name, "TRAP", ast.Load())
+ p[0] = ast.Call(name, [], [])
+ else:
+ p[0] = p[1]
+
+ # expr_stmt: testlist (augassign (yield_expr|testlist) |
+ # ('=' (yield_expr|testlist))*)
+ # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
+ # '<<=' | '>>=' | '**=' | '//=')
+ def p_expr_stmt(self, p):
+ """expr_stmt : testlist ASSIGNEA testlist
+ | testlist ASSIGN testlist
+ | testlist """
+ print("expr_stmt", p)
+ if len(p) == 2:
+ # a list of expressions
+ #p[0] = ast.Discard(p[1])
+ p[0] = p[1]
+ else:
+ iea_mode = p[2] == '<-iea'
+ name = None
+ autoassign = False
+ if isinstance(p[1], ast.Name):
+ name = p[1].id
+ elif isinstance(p[1], ast.Subscript):
+ if isinstance(p[1].value, ast.Name):
+ name = p[1].value.id
+ if name in self.gprs:
+ # add to list of uninitialised
+ self.uninit_regs.add(name)
+ autoassign = (name not in self.declared_vars and
+ name not in self.special_regs)
+ elif isinstance(p[1], ast.Call) and p[1].func.id in ['GPR', 'SPR']:
+ print(astor.dump_tree(p[1]))
+ # replace GPR(x) with GPR[x]
+ idx = p[1].args[0]
+ p[1] = ast.Subscript(p[1].func, idx, ast.Load())
+ elif isinstance(p[1], ast.Call) and p[1].func.id == 'MEM':
+ print("mem assign")
+ print(astor.dump_tree(p[1]))
+ p[1].func.id = "memassign" # change function name to set
+ p[1].args.append(p[3])
+ p[0] = p[1]
+ print("mem rewrite")
+ print(astor.dump_tree(p[0]))
+ return
+ else:
+ print("help, help")
+ print(astor.dump_tree(p[1]))
+ print("expr assign", name, p[1])
+ if name and name in self.gprs:
+ self.write_regs.add(name) # add to list of regs to write
+ p[0] = Assign(autoassign, name, p[1], p[3], iea_mode)
+ if name:
+ self.declared_vars.add(name)
+
+ def p_flow_stmt(self, p):
+ "flow_stmt : return_stmt"
+ p[0] = p[1]
+
+ # return_stmt: 'return' [testlist]
+ def p_return_stmt(self, p):
+ "return_stmt : RETURN testlist"
+ p[0] = ast.Return(p[2])
+
+ def p_compound_stmt(self, p):
+ """compound_stmt : if_stmt
+ | while_stmt
+ | switch_stmt
+ | for_stmt
+ | funcdef
+ """
+ p[0] = p[1]
+
+ def p_break_stmt(self, p):
+ """break_stmt : BREAK
+ """
+ p[0] = ast.Break()
+
+ def p_for_stmt(self, p):
+ """for_stmt : FOR atom EQ test TO test COLON suite
+ | DO atom EQ test TO test COLON suite
+ """
+ start = p[4]
+ end = p[6]
+ if start.value > end.value: # start greater than end, must go -ve
+ # auto-subtract-one (sigh) due to python range
+ end = ast.BinOp(p[6], ast.Add(), ast.Constant(-1))
+ arange = [start, end, ast.Constant(-1)]
+ else:
+ # auto-add-one (sigh) due to python range
+ end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
+ arange = [start, end]
+ it = ast.Call(ast.Name("range", ast.Load()), arange, [])
+ p[0] = ast.For(p[2], it, p[8], [])
+
+ def p_while_stmt(self, p):
+ """while_stmt : DO WHILE test COLON suite ELSE COLON suite
+ | DO WHILE test COLON suite
+ """
+ if len(p) == 6:
+ p[0] = ast.While(p[3], p[5], [])
+ else:
+ p[0] = ast.While(p[3], p[5], p[8])
+
+ def p_switch_smt(self, p):
+ """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
+ """
+ switchon = p[3]
+ print("switch stmt")
+ print(astor.dump_tree(p[1]))
+
+ cases = []
+ current_cases = [] # for deferral
+ for (case, suite) in p[8]:
+ print("for", case, suite)
+ if suite is None:
+ for c in case:
+ current_cases.append(ast.Num(c))
+ continue
+ if case == 'default': # last
+ break
+ for c in case:
+ current_cases.append(ast.Num(c))
+ print("cases", current_cases)
+ compare = ast.Compare(switchon, [ast.In()],
+ [ast.List(current_cases, ast.Load())])
+ current_cases = []
+ cases.append((compare, suite))
+
+ print("ended", case, current_cases)
+ if case == 'default':
+ if current_cases:
+ compare = ast.Compare(switchon, [ast.In()],
+ [ast.List(current_cases, ast.Load())])
+ cases.append((compare, suite))
+ cases.append((None, suite))
+
+ cases.reverse()
+ res = []
+ for compare, suite in cases:
+ print("after rev", compare, suite)
+ if compare is None:
+ assert len(res) == 0, "last case should be default"
+ res = suite
+ else:
+ if not isinstance(res, list):
+ res = [res]
+ res = ast.If(compare, suite, res)
+ p[0] = res
+
+ def p_switches(self, p):
+ """switches : switch_list switch_default
+ | switch_default
+ """
+ if len(p) == 3:
+ p[0] = p[1] + [p[2]]
+ else:
+ p[0] = [p[1]]
+
+ def p_switch_list(self, p):
+ """switch_list : switch_case switch_list
+ | switch_case
+ """
+ if len(p) == 3:
+ p[0] = [p[1]] + p[2]
+ else:
+ p[0] = [p[1]]
+
+ def p_switch_case(self, p):
+ """switch_case : CASE LPAR atomlist RPAR COLON suite
+ """
+ # XXX bad hack
+ if isinstance(p[6][0], ast.Name) and p[6][0].id == 'fallthrough':
+ p[6] = None
+ p[0] = (p[3], p[6])
+
+ def p_switch_default(self, p):
+ """switch_default : DEFAULT COLON suite
+ """
+ p[0] = ('default', p[3])
+
+ def p_atomlist(self, p):
+ """atomlist : atom COMMA atomlist
+ | atom
+ """
+ assert isinstance(p[1], ast.Constant), "case must be numbers"
+ if len(p) == 4:
+ p[0] = [p[1].value] + p[3]
+ else:
+ p[0] = [p[1].value]
+
+ def p_if_stmt(self, p):
+ """if_stmt : IF test COLON suite ELSE COLON if_stmt
+ | IF test COLON suite ELSE COLON suite
+ | IF test COLON suite
+ """
+ if len(p) == 8 and isinstance(p[7], ast.If):
+ p[0] = ast.If(p[2], p[4], [p[7]])
+ elif len(p) == 5:
+ p[0] = ast.If(p[2], p[4], [])
+ else:
+ p[0] = ast.If(p[2], p[4], p[7])
+
+ def p_suite(self, p):
+ """suite : simple_stmt
+ | NEWLINE INDENT stmts DEDENT"""
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ p[0] = p[3]
+
+ def p_stmts(self, p):
+ """stmts : stmts stmt
+ | stmt"""
+ if len(p) == 3:
+ p[0] = p[1] + p[2]
+ else:
+ p[0] = p[1]
+
+ def p_comparison(self, p):
+ """comparison : comparison PLUS comparison
+ | comparison MINUS comparison
+ | comparison MULT comparison
+ | comparison DIV comparison
+ | comparison MOD comparison
+ | comparison EQ comparison
+ | comparison NE comparison
+ | comparison LE comparison
+ | comparison GE comparison
+ | comparison LTU comparison
+ | comparison GTU comparison
+ | comparison LT comparison
+ | comparison GT comparison
+ | comparison BITOR comparison
+ | comparison BITXOR comparison
+ | comparison BITAND comparison
+ | PLUS comparison
+ | comparison MINUS
+ | INVERT comparison
+ | comparison APPEND comparison
+ | power"""
+ if len(p) == 4:
+ print(list(p))
+ if p[2] == '<u':
+ p[0] = ast.Call(ast.Name("ltu", ast.Load()), (p[1], p[3]), [])
+ elif p[2] == '>u':
+ p[0] = ast.Call(ast.Name("gtu", ast.Load()), (p[1], p[3]), [])
+ elif p[2] == '||':
+ l = check_concat(p[1]) + check_concat(p[3])
+ p[0] = ast.Call(ast.Name("concat", ast.Load()), l, [])
+ elif p[2] in ['/', '%']:
+ # bad hack: if % or / used anywhere other than div/mod ops,
+ # do % or /. however if the argument names are "dividend"
+ # we must call the special trunc_divs and trunc_rems functions
+ l, r = p[1], p[3]
+ # actual call will be "dividend / divisor" - just check
+ # LHS name
+ # XXX DISABLE BAD HACK (False)
+ if False and isinstance(l, ast.Name) and l.id == 'dividend':
+ if p[2] == '/':
+ fn = 'trunc_divs'
+ else:
+ fn = 'trunc_rems'
+ # return "function trunc_xxx(l, r)"
+ p[0] = ast.Call(ast.Name(fn, ast.Load()), (l, r), [])
+ else:
+ # return "l {binop} r"
+ p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
+ elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
+ p[0] = binary_ops[p[2]]((p[1], p[3]))
+ elif identify_sint_mul_pattern(p):
+ keywords = [ast.keyword(arg='repeat', value=p[3])]
+ l = p[1].elts
+ p[0] = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
+ else:
+ p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
+ elif len(p) == 3:
+ if isinstance(p[2], str) and p[2] == '-':
+ p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
+ else:
+ p[0] = ast.UnaryOp(unary_ops[p[1]], p[2])
+ else:
+ p[0] = p[1]
+
+ # power: atom trailer* ['**' factor]
+ # trailers enables function calls (and subscripts).
+ # so this is 'trailerlist'
+ def p_power(self, p):
+ """power : atom
+ | atom trailerlist"""
+ if len(p) == 2:
+ print("power dump atom notrailer")
+ print(astor.dump_tree(p[1]))
+ p[0] = p[1]
+ else:
+ print("power dump atom")
+ print(astor.dump_tree(p[1]))
+ print("power dump trailerlist")
+ print(astor.dump_tree(p[2]))
+ p[0] = apply_trailer(p[1], p[2])
+ if isinstance(p[1], ast.Name):
+ name = p[1].id
+ if name in ['RA', 'RS', 'RB', 'RC', 'RT']:
+ self.read_regs.add(name)
+
+ def p_atom_name(self, p):
+ """atom : NAME"""
+ name = p[1]
+ if name in self.available_op_fields:
+ self.op_fields.add(name)
+ if name == 'overflow':
+ self.write_regs.add(name)
+ if self.include_ca_in_write:
+ if name in ['CA', 'CA32']:
+ self.write_regs.add(name)
+ if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR', 'SVSTATE']:
+ self.special_regs.add(name)
+ self.write_regs.add(name) # and add to list to write
+ p[0] = ast.Name(id=name, ctx=ast.Load())
+
+ def p_atom_number(self, p):
+ """atom : BINARY
+ | NUMBER
+ | HEX
+ | STRING"""
+ p[0] = ast.Constant(p[1])
+
+ # '[' [listmaker] ']' |
+
+ def p_atom_listmaker(self, p):
+ """atom : LBRACK listmaker RBRACK"""
+ p[0] = p[2]
+
+ def p_listmaker(self, p):
+ """listmaker : test COMMA listmaker
+ | test
+ """
+ if len(p) == 2:
+ p[0] = ast.List([p[1]], ast.Load())
+ else:
+ p[0] = ast.List([p[1]] + p[3].nodes, ast.Load())
+
+ def p_atom_tuple(self, p):
+ """atom : LPAR testlist RPAR"""
+ print("tuple", p[2])
+ print("astor dump")
+ print(astor.dump_tree(p[2]))
+
+ if isinstance(p[2], ast.Name):
+ name = p[2].id
+ print("tuple name", name)
+ if name in self.gprs:
+ self.read_regs.add(name) # add to list of regs to read
+ #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
+ # return
+ p[0] = p[2]
+ elif isinstance(p[2], ast.BinOp):
+ if isinstance(p[2].left, ast.Name) and \
+ isinstance(p[2].right, ast.Constant) and \
+ p[2].right.value == 0 and \
+ p[2].left.id in self.gprs:
+ rid = p[2].left.id
+ self.read_regs.add(rid) # add to list of regs to read
+ # create special call to GPR.getz
+ gprz = ast.Name("GPR", ast.Load())
+ # get testzero function
+ gprz = ast.Attribute(gprz, "getz", ast.Load())
+ # *sigh* see class GPR. we need index itself not reg value
+ ridx = ast.Name("_%s" % rid, ast.Load())
+ p[0] = ast.Call(gprz, [ridx], [])
+ print("tree", astor.dump_tree(p[0]))
+ else:
+ p[0] = p[2]
+ else:
+ p[0] = p[2]
+
+ def p_trailerlist(self, p):
+ """trailerlist : trailer trailerlist
+ | trailer
+ """
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ p[0] = ("TLIST", p[1], p[2])
+
+ # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+ def p_trailer(self, p):
+ """trailer : trailer_arglist
+ | trailer_subscript
+ """
+ p[0] = p[1]
+
+ def p_trailer_arglist(self, p):
+ "trailer_arglist : LPAR arglist RPAR"
+ p[0] = ("CALL", p[2])
+
+ def p_trailer_subscript(self, p):
+ "trailer_subscript : LBRACK subscript RBRACK"
+ p[0] = ("SUBS", p[2])
+
+ # subscript: '.' '.' '.' | test | [test] ':' [test]
+
+ def p_subscript(self, p):
+ """subscript : test COLON test
+ | test
+ """
+ if len(p) == 4:
+ # add one to end
+ if isinstance(p[3], ast.Constant):
+ end = ast.Constant(p[3].value+1)
+ else:
+ end = ast.BinOp(p[3], ast.Add(), ast.Constant(1))
+ p[0] = [p[1], end]
+ else:
+ p[0] = [p[1]]
+
+ # testlist: test (',' test)* [',']
+ # Contains shift/reduce error
+
+ def p_testlist(self, p):
+ """testlist : testlist_multi COMMA
+ | testlist_multi """
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ # May need to promote singleton to tuple
+ if isinstance(p[1], list):
+ p[0] = p[1]
+ else:
+ p[0] = [p[1]]
+ # Convert into a tuple?
+ if isinstance(p[0], list):
+ p[0] = ast.Tuple(p[0])
+
+ def p_testlist_multi(self, p):
+ """testlist_multi : testlist_multi COMMA test
+ | test"""
+ if len(p) == 2:
+ # singleton
+ p[0] = p[1]
+ else:
+ if isinstance(p[1], list):
+ p[0] = p[1] + [p[3]]
+ else:
+ # singleton -> tuple
+ p[0] = [p[1], p[3]]
+
+ # test: or_test ['if' or_test 'else' test] | lambdef
+ # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
+
+ def p_test(self, p):
+ "test : comparison"
+ p[0] = p[1]
+
+ # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
+ # | '**' test)
+ # XXX INCOMPLETE: this doesn't allow the trailing comma
+
+ def p_arglist(self, p):
+ """arglist : arglist COMMA argument
+ | argument"""
+ if len(p) == 4:
+ p[0] = p[1] + [p[3]]
+ else:
+ p[0] = [p[1]]
+
+ # argument: test [gen_for] | test '=' test # Really [keyword '='] test
+ def p_argument(self, p):
+ "argument : test"
+ p[0] = p[1]
+
+ def p_error(self, p):
+ # print "Error!", repr(p)
+ raise SyntaxError(p)
+
+
+class GardenSnakeParser(PowerParser):
+ def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
+ self.sd = create_pdecode()
+ PowerParser.__init__(self, form, incl_carry)
+ self.debug = debug
+ if lexer is None:
+ lexer = IndentLexer(debug=0)
+ self.lexer = lexer
+ self.tokens = lexer.tokens
+ self.parser = yacc.yacc(module=self, start="file_input_end",
+ debug=debug, write_tables=False)
+
+ def parse(self, code):
+ # self.lexer.input(code)
+ result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
+ return ast.Module(result)
+
+
+###### Code generation ######
+
+#from compiler import misc, syntax, pycodegen
+
+_CACHED_PARSERS = {}
+_CACHE_PARSERS = True
+
+
+class GardenSnakeCompiler(object):
+ def __init__(self, debug=False, form=None, incl_carry=False):
+ if _CACHE_PARSERS:
+ try:
+ parser = _CACHED_PARSERS[debug, form, incl_carry]
+ except KeyError:
+ parser = GardenSnakeParser(debug=debug, form=form,
+ incl_carry=incl_carry)
+ _CACHED_PARSERS[debug, form, incl_carry] = parser
+
+ self.parser = deepcopy(parser)
+ else:
+ self.parser = GardenSnakeParser(debug=debug, form=form,
+ incl_carry=incl_carry)
+
+ def compile(self, code, mode="exec", filename="<string>"):
+ tree = self.parser.parse(code)
+ print("snake")
+ pprint(tree)
+ return tree
+ #misc.set_filename(filename, tree)
+ return compile(tree, mode="exec", filename="<string>")
+ # syntax.check(tree)
+ gen = pycodegen.ModuleCodeGenerator(tree)
+ code = gen.getCode()
+ return code
--- /dev/null
+# python code-writer for OpenPOWER ISA pseudo-code parsing
+
+import os
+import sys
+import shutil
+import subprocess
+from soc.decoder.pseudo.pagereader import ISA
+from soc.decoder.power_pseudo import convert_to_python
+from soc.decoder.orderedset import OrderedSet
+from soc.decoder.isa.caller import create_args
+
+
+def get_isasrc_dir():
+ fdir = os.path.abspath(os.path.dirname(__file__))
+ fdir = os.path.split(fdir)[0]
+ return os.path.join(fdir, "isa")
+
+
+header = """\
+# auto-generated by pywriter.py, do not edit or commit
+
+from soc.decoder.isa.caller import inject, instruction_info
+from soc.decoder.helpers import (EXTS, EXTS64, EXTZ64, ROTL64, ROTL32, MASK,
+ ne, eq, gt, ge, lt, le, ltu, gtu, length,
+ trunc_divs, trunc_rems, MULS, DIVS, MODS,
+ EXTS128, undefined)
+from soc.decoder.selectable_int import SelectableInt
+from soc.decoder.selectable_int import selectconcat as concat
+from soc.decoder.orderedset import OrderedSet
+
+class %s:
+
+"""
+
+iinfo_template = """instruction_info(func=%s,
+ read_regs=%s,
+ uninit_regs=%s, write_regs=%s,
+ special_regs=%s, op_fields=%s,
+ form='%s',
+ asmregs=%s)"""
+
+
+class PyISAWriter(ISA):
+ def __init__(self):
+ ISA.__init__(self)
+ self.pages_written = []
+
+ def write_pysource(self, pagename):
+ self.pages_written.append(pagename)
+ instrs = isa.page[pagename]
+ isadir = get_isasrc_dir()
+ fname = os.path.join(isadir, "%s.py" % pagename)
+ with open(fname, "w") as f:
+ iinf = ''
+ f.write(header % pagename) # write out header
+ # go through all instructions
+ for page in instrs:
+ d = self.instr[page]
+ print("page", pagename, page, fname, d.opcode)
+ pcode = '\n'.join(d.pcode) + '\n'
+ print(pcode)
+ incl_carry = pagename == 'fixedshift'
+ pycode, rused = convert_to_python(pcode, d.form, incl_carry)
+ # create list of arguments to call
+ regs = list(rused['read_regs']) + list(rused['uninit_regs'])
+ regs += list(rused['special_regs'])
+ args = ', '.join(create_args(regs, 'self'))
+ # create list of arguments to return
+ retargs = ', '.join(create_args(rused['write_regs']))
+ # write out function. pre-pend "op_" because some instrs are
+ # also python keywords (cmp). also replace "." with "_"
+ op_fname = "op_%s" % page.replace(".", "_")
+ f.write(" @inject()\n")
+ f.write(" def %s(%s):\n" % (op_fname, args))
+ if 'NIA' in pycode: # HACK - TODO fix
+ f.write(" global NIA\n")
+ pycode = pycode.split("\n")
+ pycode = '\n'.join(map(lambda x: " %s" % x, pycode))
+ pycode = pycode.rstrip()
+ f.write(pycode + '\n')
+ if retargs:
+ f.write(" return (%s,)\n\n" % retargs)
+ else:
+ f.write("\n")
+ # accumulate the instruction info
+ ops = repr(rused['op_fields'])
+ iinfo = iinfo_template % (op_fname, rused['read_regs'],
+ rused['uninit_regs'],
+ rused['write_regs'],
+ rused['special_regs'],
+ ops, d.form, d.regs)
+ iinf += " %s_instrs['%s'] = %s\n" % (pagename, page, iinfo)
+ # write out initialisation of info, for ISACaller to use
+ f.write(" %s_instrs = {}\n" % pagename)
+ f.write(iinf)
+
+ def patch_if_needed(self, source):
+ isadir = get_isasrc_dir()
+ fname = os.path.join(isadir, "%s.py" % source)
+ patchname = os.path.join(isadir, "%s.patch" % source)
+
+ try:
+ with open(patchname, 'r') as patch:
+ newfname = fname + '.orig'
+ shutil.copyfile(fname, newfname)
+ subprocess.check_call(['patch', fname],
+ stdin=patch)
+ except:
+ pass
+
+ def write_isa_class(self):
+ isadir = get_isasrc_dir()
+ fname = os.path.join(isadir, "all.py")
+
+ with open(fname, "w") as f:
+ f.write('# auto-generated by pywriter.py: do not edit or commit\n')
+ f.write('from soc.decoder.isa.caller import ISACaller\n')
+ for page in self.pages_written:
+ f.write('from soc.decoder.isa.%s import %s\n' % (page, page))
+ f.write('\n')
+
+ classes = ', '.join(['ISACaller'] + self.pages_written)
+ f.write('class ISA(%s):\n' % classes)
+ f.write(' def __init__(self, *args, **kwargs):\n')
+ f.write(' super().__init__(*args, **kwargs)\n')
+ f.write(' self.instrs = {\n')
+ for page in self.pages_written:
+ f.write(' **self.%s_instrs,\n' % page)
+ f.write(' }\n')
+
+
+if __name__ == '__main__':
+ isa = PyISAWriter()
+ write_isa_class = True
+ if len(sys.argv) == 1: # quick way to do it
+ print(dir(isa))
+ sources = isa.page.keys()
+ else:
+ sources = sys.argv[1:]
+ if sources[0] == "noall": # don't rewrite all.py
+ write_isa_class = False
+ sources.pop(0)
+ print ("sources", write_isa_class, sources)
+ for source in sources:
+ isa.write_pysource(source)
+ isa.patch_if_needed(source)
+ if write_isa_class:
+ isa.write_isa_class()