add pseudo and isa
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 23 Apr 2021 12:35:58 +0000 (13:35 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 23 Apr 2021 12:35:58 +0000 (13:35 +0100)
15 files changed:
src/openpower/decoder/isa/.gitignore [new file with mode: 0644]
src/openpower/decoder/isa/__init__.py [new file with mode: 0644]
src/openpower/decoder/isa/caller.py [new file with mode: 0644]
src/openpower/decoder/isa/mem.py [new file with mode: 0644]
src/openpower/decoder/isa/radixmmu.py [new file with mode: 0644]
src/openpower/decoder/isa/test_caller.py [new file with mode: 0644]
src/openpower/decoder/isa/test_caller_radix.py [new file with mode: 0644]
src/openpower/decoder/isa/test_caller_setvl.py [new file with mode: 0644]
src/openpower/decoder/isa/test_caller_svp64.py [new file with mode: 0644]
src/openpower/decoder/isa/test_caller_svp64_predication.py [new file with mode: 0644]
src/openpower/decoder/pseudo/__init__.py [new file with mode: 0644]
src/openpower/decoder/pseudo/lexer.py [new file with mode: 0644]
src/openpower/decoder/pseudo/pagereader.py [new file with mode: 0644]
src/openpower/decoder/pseudo/parser.py [new file with mode: 0644]
src/openpower/decoder/pseudo/pywriter.py [new file with mode: 0644]

diff --git a/src/openpower/decoder/isa/.gitignore b/src/openpower/decoder/isa/.gitignore
new file mode 100644 (file)
index 0000000..8de32d5
--- /dev/null
@@ -0,0 +1,18 @@
+/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
diff --git a/src/openpower/decoder/isa/__init__.py b/src/openpower/decoder/isa/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/openpower/decoder/isa/caller.py b/src/openpower/decoder/isa/caller.py
new file mode 100644 (file)
index 0000000..315db25
--- /dev/null
@@ -0,0 +1,1220 @@
+# 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
+
+
diff --git a/src/openpower/decoder/isa/mem.py b/src/openpower/decoder/isa/mem.py
new file mode 100644 (file)
index 0000000..15df1cb
--- /dev/null
@@ -0,0 +1,125 @@
+# 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)
+
+
diff --git a/src/openpower/decoder/isa/radixmmu.py b/src/openpower/decoder/isa/radixmmu.py
new file mode 100644 (file)
index 0000000..bd81445
--- /dev/null
@@ -0,0 +1,889 @@
+# 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()
diff --git a/src/openpower/decoder/isa/test_caller.py b/src/openpower/decoder/isa/test_caller.py
new file mode 100644 (file)
index 0000000..77b54c7
--- /dev/null
@@ -0,0 +1,329 @@
+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()
diff --git a/src/openpower/decoder/isa/test_caller_radix.py b/src/openpower/decoder/isa/test_caller_radix.py
new file mode 100644 (file)
index 0000000..26c813d
--- /dev/null
@@ -0,0 +1,132 @@
+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()
diff --git a/src/openpower/decoder/isa/test_caller_setvl.py b/src/openpower/decoder/isa/test_caller_setvl.py
new file mode 100644 (file)
index 0000000..ad7991f
--- /dev/null
@@ -0,0 +1,85 @@
+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()
+
diff --git a/src/openpower/decoder/isa/test_caller_svp64.py b/src/openpower/decoder/isa/test_caller_svp64.py
new file mode 100644 (file)
index 0000000..7cc04f4
--- /dev/null
@@ -0,0 +1,205 @@
+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()
diff --git a/src/openpower/decoder/isa/test_caller_svp64_predication.py b/src/openpower/decoder/isa/test_caller_svp64_predication.py
new file mode 100644 (file)
index 0000000..20b7c27
--- /dev/null
@@ -0,0 +1,532 @@
+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()
diff --git a/src/openpower/decoder/pseudo/__init__.py b/src/openpower/decoder/pseudo/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/openpower/decoder/pseudo/lexer.py b/src/openpower/decoder/pseudo/lexer.py
new file mode 100644 (file)
index 0000000..43aab33
--- /dev/null
@@ -0,0 +1,527 @@
+# 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)
diff --git a/src/openpower/decoder/pseudo/pagereader.py b/src/openpower/decoder/pseudo/pagereader.py
new file mode 100644 (file)
index 0000000..ea1b665
--- /dev/null
@@ -0,0 +1,317 @@
+# 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)
diff --git a/src/openpower/decoder/pseudo/parser.py b/src/openpower/decoder/pseudo/parser.py
new file mode 100644 (file)
index 0000000..54b2635
--- /dev/null
@@ -0,0 +1,901 @@
+# 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
diff --git a/src/openpower/decoder/pseudo/pywriter.py b/src/openpower/decoder/pseudo/pywriter.py
new file mode 100644 (file)
index 0000000..77ff775
--- /dev/null
@@ -0,0 +1,148 @@
+# 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()