add first openpower decoder files, copied from soc/decoder
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 23 Apr 2021 12:01:23 +0000 (13:01 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 23 Apr 2021 12:01:23 +0000 (13:01 +0100)
16 files changed:
src/openpower/decoder/__init__.py [new file with mode: 0644]
src/openpower/decoder/decode2execute1.py [new file with mode: 0644]
src/openpower/decoder/helpers.py [new file with mode: 0644]
src/openpower/decoder/orderedset.py [new file with mode: 0644]
src/openpower/decoder/power_decoder.py [new file with mode: 0644]
src/openpower/decoder/power_decoder2.py [new file with mode: 0644]
src/openpower/decoder/power_enums.py [new file with mode: 0644]
src/openpower/decoder/power_fields.py [new file with mode: 0644]
src/openpower/decoder/power_fieldsn.py [new file with mode: 0644]
src/openpower/decoder/power_pseudo.py [new file with mode: 0644]
src/openpower/decoder/power_regspec_map.py [new file with mode: 0644]
src/openpower/decoder/power_svp64.py [new file with mode: 0644]
src/openpower/decoder/power_svp64_extra.py [new file with mode: 0644]
src/openpower/decoder/power_svp64_prefix.py [new file with mode: 0644]
src/openpower/decoder/power_svp64_rm.py [new file with mode: 0644]
src/openpower/decoder/selectable_int.py [new file with mode: 0644]

diff --git a/src/openpower/decoder/__init__.py b/src/openpower/decoder/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/openpower/decoder/decode2execute1.py b/src/openpower/decoder/decode2execute1.py
new file mode 100644 (file)
index 0000000..6333dcd
--- /dev/null
@@ -0,0 +1,130 @@
+"""Decode2ToExecute1Type
+
+based on Anton Blanchard microwatt decode2.vhdl
+
+"""
+from nmigen import Signal, Record
+from nmutil.iocontrol import RecordObject
+from soc.decoder.power_enums import (MicrOp, CryIn, Function,
+                                     SPRfull, SPRreduced, LDSTMode)
+from soc.consts import TT
+from soc.experiment.mem_types import LDSTException
+
+
+class Data(Record):
+
+    def __init__(self, width, name):
+        name_ok = "%s_ok" % name
+        layout = ((name, width), (name_ok, 1))
+        Record.__init__(self, layout)
+        self.data = getattr(self, name) # convenience
+        self.ok = getattr(self, name_ok) # convenience
+        self.data.reset_less = True # grrr
+        self.reset_less = True # grrr
+
+    def ports(self):
+        return [self.data, self.ok]
+
+
+class IssuerDecode2ToOperand(RecordObject):
+    """IssuerDecode2ToOperand
+
+    contains the subset of fields needed for Issuer to decode the instruction
+    and get register rdflags signals set up.  it also doubles up as the
+    "Trap" temporary store, because part of the Decoder's job is to
+    identify whether a trap / interrupt / exception should occur.
+    """
+
+    def __init__(self, name=None):
+
+        RecordObject.__init__(self, name=name)
+
+        # current "state" (TODO: this in its own Record)
+        self.msr = Signal(64, reset_less=True)
+        self.cia = Signal(64, reset_less=True)
+
+        # instruction, type and decoded information
+        self.insn = Signal(32, reset_less=True) # original instruction
+        self.insn_type = Signal(MicrOp, reset_less=True)
+        self.fn_unit = Signal(Function, reset_less=True)
+        self.lk = Signal(reset_less=True)
+        self.rc = Data(1, "rc")
+        self.oe = Data(1, "oe")
+        self.input_carry = Signal(CryIn, reset_less=True)
+        self.traptype  = Signal(TT.size, reset_less=True) # trap main_stage.py
+        self.ldst_exc  = LDSTException("exc")
+        self.trapaddr  = Signal(13, reset_less=True)
+        self.read_cr_whole = Data(8, "cr_rd") # CR full read mask
+        self.write_cr_whole = Data(8, "cr_wr") # CR full write mask
+        self.is_32bit = Signal(reset_less=True)
+
+
+class Decode2ToOperand(IssuerDecode2ToOperand):
+
+    def __init__(self, name=None):
+
+        IssuerDecode2ToOperand.__init__(self, name=name)
+
+        # instruction, type and decoded information
+        self.imm_data = Data(64, name="imm")
+        self.invert_in = Signal(reset_less=True)
+        self.zero_a = Signal(reset_less=True)
+        self.output_carry = Signal(reset_less=True)
+        self.input_cr = Signal(reset_less=True)  # instr. has a CR as input
+        self.output_cr = Signal(reset_less=True) # instr. has a CR as output
+        self.invert_out = Signal(reset_less=True)
+        self.is_32bit = Signal(reset_less=True)
+        self.is_signed = Signal(reset_less=True)
+        self.data_len = Signal(4, reset_less=True) # bytes
+        self.byte_reverse  = Signal(reset_less=True)
+        self.sign_extend  = Signal(reset_less=True)# do we need this?
+        self.ldst_mode  = Signal(LDSTMode, reset_less=True) # LD/ST mode
+        self.write_cr0 = Signal(reset_less=True)
+
+
+class Decode2ToExecute1Type(RecordObject):
+
+    def __init__(self, name=None, asmcode=True, opkls=None, do=None,
+                       regreduce_en=False):
+
+        if regreduce_en:
+            SPR = SPRreduced
+        else:
+            SPR = SPRfull
+
+        if do is None and opkls is None:
+            opkls = Decode2ToOperand
+
+        RecordObject.__init__(self, name=name)
+
+        if asmcode:
+            self.asmcode = Signal(8, reset_less=True) # only for simulator
+        self.write_reg = Data(7, name="rego")
+        self.write_ea = Data(7, name="ea") # for LD/ST in update mode
+        self.read_reg1 = Data(7, name="reg1")
+        self.read_reg2 = Data(7, name="reg2")
+        self.read_reg3 = Data(7, name="reg3")
+        self.write_spr = Data(SPR, name="spro")
+        self.read_spr1 = Data(SPR, name="spr1")
+        #self.read_spr2 = Data(SPR, name="spr2") # only one needed
+
+        self.xer_in = Signal(3, reset_less=True)   # xer might be read
+        self.xer_out = Signal(reset_less=True)  # xer might be written
+
+        self.read_fast1 = Data(3, name="fast1")
+        self.read_fast2 = Data(3, name="fast2")
+        self.write_fast1 = Data(3, name="fasto1")
+        self.write_fast2 = Data(3, name="fasto2")
+
+        self.read_cr1 = Data(7, name="cr_in1")
+        self.read_cr2 = Data(7, name="cr_in2")
+        self.read_cr3 = Data(7, name="cr_in2")
+        self.write_cr = Data(7, name="cr_out")
+
+        # decode operand data
+        print ("decode2execute init", name, opkls, do)
+        #assert name is not None, str(opkls)
+        if do is not None:
+            self.do = do
+        else:
+            self.do = opkls(name)
diff --git a/src/openpower/decoder/helpers.py b/src/openpower/decoder/helpers.py
new file mode 100644 (file)
index 0000000..6f63cc3
--- /dev/null
@@ -0,0 +1,222 @@
+import unittest
+from soc.decoder.selectable_int import SelectableInt, onebit
+from nmutil.divmod import trunc_divs, trunc_rems
+from operator import floordiv, mod
+from soc.decoder.selectable_int import selectltu as ltu
+from soc.decoder.selectable_int import selectgtu as gtu
+from soc.decoder.selectable_int import check_extsign
+
+trunc_div = floordiv
+trunc_rem = mod
+DIVS = trunc_divs
+MODS = trunc_rems
+
+"""
+Links:
+* https://bugs.libre-soc.org/show_bug.cgi?id=324 - add trunc_div and trunc_rem
+"""
+
+
+def exts(value, bits):
+    sign = 1 << (bits - 1)
+    return (value & (sign - 1)) - (value & sign)
+
+
+def EXTS(value):
+    """ extends sign bit out from current MSB to all 256 bits
+    """
+    assert isinstance(value, SelectableInt)
+    return SelectableInt(exts(value.value, value.bits) & ((1 << 256)-1), 256)
+
+
+def EXTS64(value):
+    """ extends sign bit out from current MSB to 64 bits
+    """
+    assert isinstance(value, SelectableInt)
+    return SelectableInt(exts(value.value, value.bits) & ((1 << 64)-1), 64)
+
+
+def EXTS128(value):
+    """ extends sign bit out from current MSB to 128 bits
+    """
+    assert isinstance(value, SelectableInt)
+    return SelectableInt(exts(value.value, value.bits) & ((1 << 128)-1), 128)
+
+
+# signed version of MUL
+def MULS(a, b):
+    if isinstance(b, int):
+        b = SelectableInt(b, self.bits)
+    b = check_extsign(a, b)
+    a_s = a.value & (1 << (a.bits-1)) != 0
+    b_s = b.value & (1 << (b.bits-1)) != 0
+    result = abs(a) * abs(b)
+    print("MULS", result, a_s, b_s)
+    if a_s == b_s:
+        return result
+    return -result
+
+
+# XXX should this explicitly extend from 32 to 64?
+def EXTZ64(value):
+    if isinstance(value, SelectableInt):
+        value = value.value
+    return SelectableInt(value & ((1 << 32)-1), 64)
+
+
+def rotl(value, bits, wordlen):
+    if isinstance(bits, SelectableInt):
+        bits = bits.value
+    mask = (1 << wordlen) - 1
+    bits = bits & (wordlen - 1)
+    return ((value << bits) | (value >> (wordlen-bits))) & mask
+
+
+def ROTL64(value, bits):
+    return rotl(value, bits, 64)
+
+
+def ROTL32(value, bits):
+    if isinstance(value, SelectableInt):
+        value = SelectableInt(value.value, 64)
+    return rotl(value | (value << 32), bits, 64)
+
+
+def MASK(x, y):
+    if isinstance(x, SelectableInt):
+        x = x.value
+    if isinstance(y, SelectableInt):
+        y = y.value
+    if x < y:
+        x = 64-x
+        y = 63-y
+        mask_a = ((1 << x) - 1) & ((1 << 64) - 1)
+        mask_b = ((1 << y) - 1) & ((1 << 64) - 1)
+    elif x == y:
+        return 1 << (63-x)
+    else:
+        x = 64-x
+        y = 63-y
+        mask_a = ((1 << x) - 1) & ((1 << 64) - 1)
+        mask_b = (~((1 << y) - 1)) & ((1 << 64) - 1)
+    return mask_a ^ mask_b
+
+
+def ne(a, b):
+    return onebit(a != b)
+
+
+def eq(a, b):
+    return onebit(a == b)
+
+
+def gt(a, b):
+    return onebit(a > b)
+
+
+def ge(a, b):
+    return onebit(a >= b)
+
+
+def lt(a, b):
+    return onebit(a < b)
+
+
+def le(a, b):
+    return onebit(a <= b)
+
+
+def length(a):
+    return len(a)
+
+
+def undefined(v):
+    """ function that, for Power spec purposes, returns undefined bits of
+        the same shape as the input bits.  however, for purposes of matching
+        POWER9's behavior returns the input bits unchanged.  this effectively
+        "marks" (tags) locations in the v3.0B spec that need to be submitted
+        for clarification.
+    """
+    return v
+
+# For these tests I tried to find power instructions that would let me
+# isolate each of these helper operations. So for instance, when I was
+# testing the MASK() function, I chose rlwinm and rldicl because if I
+# set the shift equal to 0 and passed in a value of all ones, the
+# result I got would be exactly the same as the output of MASK()
+
+
+class HelperTests(unittest.TestCase):
+    def test_MASK(self):
+        # Verified using rlwinm, rldicl, rldicr in qemu
+        # li 1, -1
+        # rlwinm reg, 1, 0, 5, 15
+        self.assertHex(MASK(5+32, 15+32), 0x7ff0000)
+        # rlwinm reg, 1, 0, 15, 5
+        self.assertHex(MASK(15+32, 5+32), 0xfffffffffc01ffff)
+        self.assertHex(MASK(30+32, 2+32), 0xffffffffe0000003)
+        # rldicl reg, 1, 0, 37
+        self.assertHex(MASK(37, 63), 0x7ffffff)
+        self.assertHex(MASK(10, 63), 0x3fffffffffffff)
+        self.assertHex(MASK(58, 63), 0x3f)
+        # rldicr reg, 1, 0, 37
+        self.assertHex(MASK(0, 37), 0xfffffffffc000000)
+        self.assertHex(MASK(0, 10), 0xffe0000000000000)
+        self.assertHex(MASK(0, 58), 0xffffffffffffffe0)
+
+        # li 2, 5
+        # slw 1, 1, 2
+        self.assertHex(MASK(32, 63-5), 0xffffffe0)
+
+        self.assertHex(MASK(32, 33), 0xc0000000)
+        self.assertHex(MASK(32, 32), 0x80000000)
+        self.assertHex(MASK(33, 33), 0x40000000)
+
+    def test_ROTL64(self):
+        # r1 = 0xdeadbeef12345678
+        value = 0xdeadbeef12345678
+
+        # rldicl reg, 1, 10, 0
+        self.assertHex(ROTL64(value, 10), 0xb6fbbc48d159e37a)
+        # rldicl reg, 1, 35, 0
+        self.assertHex(ROTL64(value, 35), 0x91a2b3c6f56df778)
+        self.assertHex(ROTL64(value, 58), 0xe37ab6fbbc48d159)
+        self.assertHex(ROTL64(value, 22), 0xbbc48d159e37ab6f)
+
+    def test_ROTL32(self):
+        # r1 = 0xdeadbeef
+        value = 0xdeadbeef
+
+        # rlwinm reg, 1, 10, 0, 31
+        self.assertHex(ROTL32(value, 10), 0xb6fbbf7a)
+        # rlwinm reg, 1, 17, 0, 31
+        self.assertHex(ROTL32(value, 17), 0x7ddfbd5b)
+        self.assertHex(ROTL32(value, 25), 0xdfbd5b7d)
+        self.assertHex(ROTL32(value, 30), 0xf7ab6fbb)
+
+    def test_EXTS64(self):
+        value_a = SelectableInt(0xdeadbeef, 32)  # r1
+        value_b = SelectableInt(0x73123456, 32)  # r2
+        value_c = SelectableInt(0x80000000, 32)  # r3
+
+        # extswsli reg, 1, 0
+        self.assertHex(EXTS64(value_a), 0xffffffffdeadbeef)
+        # extswsli reg, 2, 0
+        self.assertHex(EXTS64(value_b), SelectableInt(value_b.value, 64))
+        # extswsli reg, 3, 0
+        self.assertHex(EXTS64(value_c), 0xffffffff80000000)
+
+    def assertHex(self, a, b):
+        a_val = a
+        if isinstance(a, SelectableInt):
+            a_val = a.value
+        b_val = b
+        if isinstance(b, SelectableInt):
+            b_val = b.value
+        msg = "{:x} != {:x}".format(a_val, b_val)
+        return self.assertEqual(a, b, msg)
+
+
+if __name__ == '__main__':
+    print(SelectableInt.__bases__)
+    unittest.main()
diff --git a/src/openpower/decoder/orderedset.py b/src/openpower/decoder/orderedset.py
new file mode 100644 (file)
index 0000000..d5f0b41
--- /dev/null
@@ -0,0 +1,51 @@
+# Originally from http://code.activestate.com/recipes/576694/
+# cut down to minimum
+
+import collections
+
+class OrderedSet(collections.MutableSet):
+
+    def __init__(self, iterable=None):
+        self.end = end = [] 
+        end += [None, end, end]         # sentinel node for doubly linked list
+        self.map = {}                   # key --> [key, prev, next]
+        if iterable is not None:
+            self |= iterable
+
+    def __len__(self):
+        return len(self.map)
+
+    def __contains__(self, key):
+        return key in self.map
+
+    def add(self, key):
+        if key in self.map:
+            return
+        end = self.end
+        curr = end[1]
+        curr[2] = end[1] = self.map[key] = [key, curr, end]
+
+    def discard(self, key):
+        if key in self.map:        
+            key, prev, next = self.map.pop(key)
+            prev[2] = next
+            next[1] = prev
+
+    def __iter__(self):
+        end = self.end
+        curr = end[2]
+        while curr is not end:
+            yield curr[0]
+            curr = curr[2]
+
+    def __repr__(self):
+        if not self:
+            return '%s()' % (self.__class__.__name__,)
+        return '%s(%r)' % (self.__class__.__name__, list(self))
+
+if __name__ == '__main__':
+    s = OrderedSet('abracadaba')
+    t = OrderedSet('simsalabim')
+    print(s | t)
+    print(s & t)
+    print(s - t)
diff --git a/src/openpower/decoder/power_decoder.py b/src/openpower/decoder/power_decoder.py
new file mode 100644 (file)
index 0000000..eb6df3c
--- /dev/null
@@ -0,0 +1,607 @@
+"""Cascading Power ISA Decoder
+
+License: LGPLv3+
+
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+
+This module uses CSV tables in a hierarchical/peer cascading fashion,
+to create a multi-level instruction decoder by recognising appropriate
+patterns.  The output is a wide, flattened (1-level) series of bitfields,
+suitable for a simple RISC engine.
+
+This is based on Anton Blanchard's excellent microwatt work:
+https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
+
+The basic principle is that the python code does the heavy lifting
+(reading the CSV files, constructing the hierarchy), creating the HDL
+AST with for-loops generating switch-case statements.
+
+Where "normal" HDL would do this, in laborious excruciating detail:
+
+    switch (opcode & major_mask_bits):
+        case opcode_2: decode_opcode_2()
+        case opcode_19:
+                switch (opcode & minor_19_mask_bits)
+                    case minor_opcode_19_operation_X:
+                    case minor_opcode_19_operation_y:
+
+we take *full* advantage of the decoupling between python and the
+nmigen AST data structure, to do this:
+
+    with m.Switch(opcode & self.mask):
+        for case_bitmask in subcases:
+            with m.If(opcode & case_bitmask): {do_something}
+
+this includes specifying the information sufficient to perform subdecoding.
+
+create_pdecode()
+
+    the full hierarchical tree for decoding POWER9 is specified here
+    subsetting is possible by specifying col_subset (row_subset TODO)
+
+PowerDecoder
+
+    takes a *list* of CSV files with an associated bit-range that it
+    is requested to match against the "opcode" row of the CSV file.
+    This pattern can be either an integer, a binary number, *or* a
+    wildcard nmigen Case pattern of the form "001--1-100".
+
+Subdecoders
+
+    these are *additional* cases with further decoding.  The "pattern"
+    argument is specified as one of the Case statements (a peer of the
+    opcode row in the CSV file), and thus further fields of the opcode
+    may be decoded giving increasing levels of detail.
+
+Top Level:
+
+    [ (extra.csv: bit-fields entire 32-bit range
+        opcode                           -> matches
+        000000---------------01000000000 -> ILLEGAL instruction
+        01100000000000000000000000000000 -> SIM_CONFIG instruction
+        ................................ ->
+      ),
+      (major.csv: first 6 bits ONLY
+        opcode                           -> matches
+        001100                           -> ALU,OP_ADD (add)
+        001101                           -> ALU,OP_ADD (another type of add)
+        ......                           -> ...
+        ......                           -> ...
+        subdecoders:
+        001011 this must match *MAJOR*.CSV
+            [ (minor_19.csv: bits 21 through 30 inclusive:
+                opcode                  -> matches
+                0b0000000000            -> ALU,OP_MCRF
+                ............            -> ....
+              ),
+              (minor_19_00000.csv: bits 21 through 25 inclusive:
+                opcode                  -> matches
+                0b00010                 -> ALU,add_pcis
+              )
+            ]
+      ),
+    ]
+
+
+"""
+
+import gc
+from collections import namedtuple
+from nmigen import Module, Elaboratable, Signal, Cat, Mux
+from nmigen.cli import rtlil
+from soc.decoder.power_enums import (Function, Form, MicrOp,
+                                     In1Sel, In2Sel, In3Sel, OutSel,
+                                     SVEXTRA, SVEtype, SVPtype,  # Simple-V
+                                     RC, LdstLen, LDSTMode, CryIn,
+                                     single_bit_flags, CRInSel,
+                                     CROutSel, get_signal_name,
+                                     default_values, insns, asmidx)
+from soc.decoder.power_fields import DecodeFields
+from soc.decoder.power_fieldsn import SigDecode, SignalBitRange
+from soc.decoder.power_svp64 import SVP64RM
+
+# key data structure in which the POWER decoder is specified,
+# in a hierarchical fashion
+Subdecoder = namedtuple(  # fix autoformatter
+    "Subdecoder",
+    ["pattern",    # the major pattern to search for (e.g. major opcode)
+     "opcodes",    # a dictionary of minor patterns to find
+     "opint",      # true => the pattern must not be in "10----11" format
+     # the bits (as a range) against which "pattern" matches
+     "bitsel",
+     "suffix",     # shift the opcode down before decoding
+     "subdecoders"  # list of further subdecoders for *additional* matches,
+     # *ONLY* after "pattern" has *ALSO* been matched against.
+     ])
+
+power_op_types = {'function_unit': Function,
+                  'internal_op': MicrOp,
+                  'form': Form,
+                  'asmcode': 8,
+                  'SV_Etype': SVEtype,
+                  'SV_Ptype': SVPtype,
+                  'in1_sel': In1Sel,
+                  'in2_sel': In2Sel,
+                  'in3_sel': In3Sel,
+                  'out_sel': OutSel,
+                  'cr_in': CRInSel,
+                  'cr_out': CROutSel,
+                  'sv_in1': SVEXTRA,
+                  'sv_in2': SVEXTRA,
+                  'sv_in3': SVEXTRA,
+                  'sv_out': SVEXTRA,
+                  'sv_out2': SVEXTRA,
+                  'sv_cr_in': SVEXTRA,
+                  'sv_cr_out': SVEXTRA,
+                  'ldst_len': LdstLen,
+                  'upd': LDSTMode,
+                  'rc_sel': RC,
+                  'cry_in': CryIn
+                  }
+
+power_op_csvmap = {'function_unit': 'unit',
+                   'form': 'form',
+                   'internal_op': 'internal op',
+                   'in1_sel': 'in1',
+                   'in2_sel': 'in2',
+                   'in3_sel': 'in3',
+                   'out_sel': 'out',
+                   'sv_in1': 'sv_in1',
+                   'sv_in2': 'sv_in2',
+                   'sv_in3': 'sv_in3',
+                   'sv_out': 'sv_out',
+                   'sv_out2': 'sv_out2',
+                   'sv_cr_in': 'sv_cr_in',
+                   'sv_cr_out': 'sv_cr_out',
+                   'SV_Etype': 'SV_Etype',
+                   'SV_Ptype': 'SV_Ptype',
+                   'cr_in': 'CR in',
+                   'cr_out': 'CR out',
+                   'ldst_len': 'ldst len',
+                   'upd': 'upd',
+                   'rc_sel': 'rc',
+                   'cry_in': 'cry in',
+                   }
+
+
+def get_pname(field, pname):
+    if pname is None:
+        return field
+    return "%s_%s" % (pname, field)
+
+
+class PowerOp:
+    """PowerOp - a dynamic class that stores (subsets of) CSV rows of data
+    about a PowerISA instruction.  this is a "micro-code" expanded format
+    which generates an awful lot of wires, hence the subsetting
+    """
+
+    def __init__(self, incl_asm=True, name=None, subset=None):
+        self.subset = subset
+        debug_report = set()
+        fields = set()
+        for field, ptype in power_op_types.items():
+            fields.add(field)
+            if subset and field not in subset:
+                continue
+            fname = get_pname(field, name)
+            setattr(self, field, Signal(ptype, reset_less=True, name=fname))
+            debug_report.add(field)
+        for bit in single_bit_flags:
+            field = get_signal_name(bit)
+            fields.add(field)
+            if subset and field not in subset:
+                continue
+            debug_report.add(field)
+            fname = get_pname(field, name)
+            setattr(self, field, Signal(reset_less=True, name=fname))
+        print("PowerOp debug", name, debug_report)
+        print("        fields", fields)
+
+    def _eq(self, row=None):
+        if row is None:
+            row = default_values
+        # TODO: this conversion process from a dict to an object
+        # should really be done using e.g. namedtuple and then
+        # call eq not _eq
+        if False:  # debugging
+            if row['CR in'] == '1':
+                import pdb
+                pdb.set_trace()
+                print(row)
+            if row['CR out'] == '0':
+                import pdb
+                pdb.set_trace()
+                print(row)
+            print(row)
+        ldst_mode = row['upd']
+        if ldst_mode.isdigit():
+            row['upd'] = int(ldst_mode)
+        res = []
+        for field, ptype in power_op_types.items():
+            if not hasattr(self, field):
+                continue
+            if field not in power_op_csvmap:
+                continue
+            csvname = power_op_csvmap[field]
+            print(field, ptype, csvname, row)
+            val = row[csvname]
+            if csvname == 'upd' and isinstance(val, int):  # LDSTMode different
+                val = ptype(val)
+            else:
+                val = ptype[val]
+            res.append(getattr(self, field).eq(val))
+        if False:
+            print(row.keys())
+        asmcode = row['comment']
+        if hasattr(self, "asmcode") and asmcode in asmidx:
+            res.append(self.asmcode.eq(asmidx[asmcode]))
+        for bit in single_bit_flags:
+            field = get_signal_name(bit)
+            if not hasattr(self, field):
+                continue
+            sig = getattr(self, field)
+            res.append(sig.eq(int(row.get(bit, 0))))
+        return res
+
+    def _get_eq(self, res, field, otherop):
+        copyfrom = getattr(otherop, field, None)
+        copyto = getattr(self, field, None)
+        if copyfrom is not None and copyto is not None:
+            res.append(copyto.eq(copyfrom))
+
+    def eq(self, otherop):
+        res = []
+        for field in power_op_types.keys():
+            self._get_eq(res, field, otherop)
+        for bit in single_bit_flags:
+            self._get_eq(res, get_signal_name(bit), otherop)
+        return res
+
+    def ports(self):
+        res = []
+        for field in power_op_types.keys():
+            if hasattr(self, field):
+                res.append(getattr(self, field))
+        if hasattr(self, "asmcode"):
+            res.append(self.asmcode)
+        for field in single_bit_flags:
+            field = get_signal_name(field)
+            if hasattr(self, field):
+                res.append(getattr(self, field))
+        return res
+
+
+class PowerDecoder(Elaboratable):
+    """PowerDecoder - decodes an incoming opcode into the type of operation
+
+    this is a recursive algorithm, creating Switch statements that can
+    have further match-and-decode on other parts of the opcode field before
+    finally landing at a "this CSV entry details gets returned" thing.
+
+    the complicating factor is the row and col subsetting.  column subsetting
+    dynamically chooses only the CSV columns requested, whilst row subsetting
+    allows a function to be called on the row to determine if the Case
+    statement is to be generated for that row.  this not only generates
+    completely different Decoders, it also means that some sub-decoders
+    will turn up blank (empty switch statements).  if that happens we do
+    not want the parent to include a Mux for an entirely blank switch statement
+    so we have to store the switch/case statements in a tree, and
+    post-analyse it.
+
+    the reason for the tree is because elaborate can only be called *after*
+    the constructor is called.  all quite messy.
+    """
+
+    def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
+        self.actually_does_something = False
+        self.pname = name
+        self.col_subset = col_subset
+        self.row_subsetfn = row_subset
+        if not isinstance(dec, list):
+            dec = [dec]
+        self.dec = dec
+        self.opcode_in = Signal(width, reset_less=True)
+
+        self.op = PowerOp(name=name, subset=col_subset)
+        for d in dec:
+            if d.suffix is not None and d.suffix >= width:
+                d.suffix = None
+        self.width = width
+
+    def suffix_mask(self, d):
+        return ((1 << d.suffix) - 1)
+
+    def divide_opcodes(self, d):
+        divided = {}
+        mask = self.suffix_mask(d)
+        print("mask", hex(mask))
+        for row in d.opcodes:
+            opcode = row['opcode']
+            if d.opint and '-' not in opcode:
+                opcode = int(opcode, 0)
+            key = opcode & mask
+            opcode = opcode >> d.suffix
+            if key not in divided:
+                divided[key] = []
+            r = row.copy()
+            r['opcode'] = opcode
+            divided[key].append(r)
+        return divided
+
+    def tree_analyse(self):
+        self.decs = decs = []
+        self.submodules = submodules = {}
+        self.eqs = eqs = []
+
+        # go through the list of CSV decoders first
+        for d in self.dec:
+            cases = []
+            opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
+                                   reset_less=True)
+            eq = []
+            case_does_something = False
+            eq.append(opcode_switch.eq(
+                self.opcode_in[d.bitsel[0]:d.bitsel[1]]))
+            if d.suffix:
+                opcodes = self.divide_opcodes(d)
+                opc_in = Signal(d.suffix, reset_less=True)
+                eq.append(opc_in.eq(opcode_switch[:d.suffix]))
+                # begin the dynamic Switch statement here
+                switch_case = {}
+                cases.append([opc_in, switch_case])
+                sub_eqs = []
+                for key, row in opcodes.items():
+                    bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
+                    sd = Subdecoder(pattern=None, opcodes=row,
+                                    bitsel=bitsel, suffix=None,
+                                    opint=False, subdecoders=[])
+                    mname = get_pname("dec_sub%d" % key, self.pname)
+                    subdecoder = PowerDecoder(width=32, dec=sd,
+                                              name=mname,
+                                              col_subset=self.col_subset,
+                                              row_subset=self.row_subsetfn)
+                    if not subdecoder.tree_analyse():
+                        del subdecoder
+                        continue
+                    submodules[mname] = subdecoder
+                    sub_eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
+                    # add in the dynamic Case statement here
+                    switch_case[key] = self.op.eq(subdecoder.op)
+                    self.actually_does_something = True
+                    case_does_something = True
+                if case_does_something:
+                    eq += sub_eqs
+            else:
+                # TODO: arguments, here (all of them) need to be a list.
+                # a for-loop around the *list* of decoder args.
+                switch_case = {}
+                cases.append([opcode_switch, switch_case])
+                seqs = self.handle_subdecoders(switch_case, submodules, d)
+                if seqs:
+                    case_does_something = True
+                eq += seqs
+                for row in d.opcodes:
+                    opcode = row['opcode']
+                    if d.opint and '-' not in opcode:
+                        opcode = int(opcode, 0)
+                    if not row['unit']:
+                        continue
+                    if self.row_subsetfn:
+                        if not self.row_subsetfn(opcode, row):
+                            continue
+                    # add in the dynamic Case statement here
+                    switch_case[opcode] = self.op._eq(row)
+                    self.actually_does_something = True
+                    case_does_something = True
+
+            if cases:
+                decs.append(cases)
+            if case_does_something:
+                eqs += eq
+                print("submodule eqs", self.pname, eq)
+
+        print("submodules", self.pname, submodules)
+
+        gc.collect()
+        return self.actually_does_something
+
+    def handle_subdecoders(self, switch_case, submodules, d):
+        eqs = []
+        for dec in d.subdecoders:
+            if isinstance(dec, list):  # XXX HACK: take first pattern
+                dec = dec[0]
+            print("subdec", dec.pattern, self.pname)
+            mname = get_pname("dec%d" % dec.pattern, self.pname)
+            subdecoder = PowerDecoder(self.width, dec,
+                                      name=mname,
+                                      col_subset=self.col_subset,
+                                      row_subset=self.row_subsetfn)
+            if not subdecoder.tree_analyse():  # doesn't do anything
+                del subdecoder
+                continue                      # skip
+            submodules[mname] = subdecoder
+            eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
+            switch_case[dec.pattern] = self.op.eq(subdecoder.op)
+            self.actually_does_something = True
+
+        return eqs
+
+    def elaborate(self, platform):
+        print("decoder elaborate", self.pname, self.submodules)
+        m = Module()
+        comb = m.d.comb
+
+        comb += self.eqs
+
+        for mname, subdecoder in self.submodules.items():
+            setattr(m.submodules, mname, subdecoder)
+
+        for switch_case in self.decs:
+            for (switch, cases) in switch_case:
+                with m.Switch(switch):
+                    for key, eqs in cases.items():
+                        with m.Case(key):
+                            comb += eqs
+        return m
+
+    def ports(self):
+        return [self.opcode_in] + self.op.ports()
+
+
+class TopPowerDecoder(PowerDecoder):
+    """TopPowerDecoder
+
+    top-level hierarchical decoder for POWER ISA
+    bigendian dynamically switches between big and little endian decoding
+    (reverses byte order).  See V3.0B p44 1.11.2
+    """
+
+    def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
+        PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset)
+        self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
+        self.fields.create_specs()
+        self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
+        self.bigendian = Signal(reset_less=True)
+
+        for fname, value in self.fields.common_fields.items():
+            signame = get_pname(fname, name)
+            sig = Signal(value[0:-1].shape(), reset_less=True, name=signame)
+            setattr(self, fname, sig)
+
+        # create signals for all field forms
+        forms = self.form_names
+        self.sigforms = {}
+        for form in forms:
+            fields = self.fields.instrs[form]
+            fk = fields.keys()
+            Fields = namedtuple("Fields", fk)
+            sf = {}
+            for k, value in fields.items():
+                fname = "%s_%s" % (form, k)
+                sig = Signal(value[0:-1].shape(), reset_less=True, name=fname)
+                sf[k] = sig
+            instr = Fields(**sf)
+            setattr(self, "Form%s" % form, instr)
+            self.sigforms[form] = instr
+
+        self.tree_analyse()
+
+    @property
+    def form_names(self):
+        return self.fields.instrs.keys()
+
+    def elaborate(self, platform):
+        m = PowerDecoder.elaborate(self, platform)
+        comb = m.d.comb
+        # sigh duplicated in SVP64PowerDecoder
+        # raw opcode in assumed to be in LE order: byte-reverse it to get BE
+        raw_le = self.raw_opcode_in
+        l = []
+        for i in range(0, self.width, 8):
+            l.append(raw_le[i:i+8])
+        l.reverse()
+        raw_be = Cat(*l)
+        comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
+
+        # add all signal from commonly-used fields
+        for fname, value in self.fields.common_fields.items():
+            sig = getattr(self, fname)
+            comb += sig.eq(value[0:-1])
+
+        # link signals for all field forms
+        forms = self.form_names
+        for form in forms:
+            sf = self.sigforms[form]
+            fields = self.fields.instrs[form]
+            for k, value in fields.items():
+                sig = getattr(sf, k)
+                comb += sig.eq(value[0:-1])
+
+        return m
+
+    def ports(self):
+        return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
+
+
+####################################################
+# PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
+
+def create_pdecode(name=None, col_subset=None, row_subset=None):
+    """create_pdecode - creates a cascading hierarchical POWER ISA decoder
+
+    subsetting of the PowerOp decoding is possible by setting col_subset
+    """
+
+    # some alteration to the CSV files is required for SV so we use
+    # a class to do it
+    isa = SVP64RM()
+    get_csv = isa.get_svp64_csv
+
+    # minor 19 has extra patterns
+    m19 = []
+    m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
+                          opint=True, bitsel=(1, 11), suffix=None,
+                          subdecoders=[]))
+    m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
+                          opint=True, bitsel=(1, 6), suffix=None,
+                          subdecoders=[]))
+
+    # minor opcodes.
+    pminor = [
+        m19,
+        Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
+                   opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
+        Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
+                   opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
+        Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
+                   opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
+        Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
+                   opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
+        Subdecoder(pattern=22, opcodes=get_csv("minor_22.csv"),
+                   opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
+    ]
+
+    # top level: extra merged with major
+    dec = []
+    opcodes = get_csv("major.csv")
+    dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
+                          bitsel=(26, 32), suffix=None, subdecoders=pminor))
+    opcodes = get_csv("extra.csv")
+    dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
+                          bitsel=(0, 32), suffix=None, subdecoders=[]))
+
+    return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
+                           row_subset=row_subset)
+
+
+if __name__ == '__main__':
+
+    if True:
+        # row subset
+
+        def rowsubsetfn(opcode, row):
+            print("row_subset", opcode, row)
+            return row['unit'] == 'ALU'
+
+        pdecode = create_pdecode(name="rowsub",
+                                 col_subset={'function_unit', 'in1_sel'},
+                                 row_subset=rowsubsetfn)
+        vl = rtlil.convert(pdecode, ports=pdecode.ports())
+        with open("row_subset_decoder.il", "w") as f:
+            f.write(vl)
+
+        # col subset
+
+        pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'})
+        vl = rtlil.convert(pdecode, ports=pdecode.ports())
+        with open("col_subset_decoder.il", "w") as f:
+            f.write(vl)
+
+    # full decoder
+
+    pdecode = create_pdecode()
+    vl = rtlil.convert(pdecode, ports=pdecode.ports())
+    with open("decoder.il", "w") as f:
+        f.write(vl)
diff --git a/src/openpower/decoder/power_decoder2.py b/src/openpower/decoder/power_decoder2.py
new file mode 100644 (file)
index 0000000..c0d523d
--- /dev/null
@@ -0,0 +1,1358 @@
+"""Power ISA Decoder second stage
+
+based on Anton Blanchard microwatt decode2.vhdl
+
+Note: OP_TRAP is used for exceptions and interrupts (micro-code style) by
+over-riding the internal opcode when an exception is needed.
+"""
+
+from nmigen import Module, Elaboratable, Signal, Mux, Const, Cat, Repl, Record
+from nmigen.cli import rtlil
+from nmutil.util import sel
+
+from soc.regfile.regfiles import XERRegs
+
+from nmutil.picker import PriorityPicker
+from nmutil.iocontrol import RecordObject
+from nmutil.extend import exts
+
+from soc.experiment.mem_types import LDSTException
+
+from soc.decoder.power_svp64_prefix import SVP64PrefixDecoder
+from soc.decoder.power_svp64_extra import SVP64CRExtra, SVP64RegExtra
+from soc.decoder.power_svp64_rm import SVP64RMModeDecode
+from soc.decoder.power_regspec_map import regspec_decode_read
+from soc.decoder.power_regspec_map import regspec_decode_write
+from soc.decoder.power_decoder import create_pdecode
+from soc.decoder.power_enums import (MicrOp, CryIn, Function,
+                                     CRInSel, CROutSel,
+                                     LdstLen, In1Sel, In2Sel, In3Sel,
+                                     OutSel, SPRfull, SPRreduced,
+                                     RC, LDSTMode,
+                                     SVEXTRA, SVEtype, SVPtype)
+from soc.decoder.decode2execute1 import (Decode2ToExecute1Type, Data,
+                                         Decode2ToOperand)
+from soc.sv.svp64 import SVP64Rec
+from soc.consts import (MSR, SPEC, EXTRA2, EXTRA3, SVP64P, field,
+                        SPEC_SIZE, SPECb, SPEC_AUG_SIZE, SVP64CROffs)
+
+from soc.regfile.regfiles import FastRegs
+from soc.consts import TT
+from soc.config.state import CoreState
+from soc.regfile.util import spr_to_fast
+
+
+def decode_spr_num(spr):
+    return Cat(spr[5:10], spr[0:5])
+
+
+def instr_is_priv(m, op, insn):
+    """determines if the instruction is privileged or not
+    """
+    comb = m.d.comb
+    is_priv_insn = Signal(reset_less=True)
+    with m.Switch(op):
+        with m.Case(MicrOp.OP_ATTN, MicrOp.OP_MFMSR, MicrOp.OP_MTMSRD,
+                    MicrOp.OP_MTMSR, MicrOp.OP_RFID):
+            comb += is_priv_insn.eq(1)
+        with m.Case(MicrOp.OP_TLBIE) : comb += is_priv_insn.eq(1)
+        with m.Case(MicrOp.OP_MFSPR, MicrOp.OP_MTSPR):
+            with m.If(insn[20]):  # field XFX.spr[-1] i think
+                comb += is_priv_insn.eq(1)
+    return is_priv_insn
+
+
+class SPRMap(Elaboratable):
+    """SPRMap: maps POWER9 SPR numbers to internal enum values, fast and slow
+    """
+
+    def __init__(self, regreduce_en):
+        self.regreduce_en = regreduce_en
+        if regreduce_en:
+            SPR = SPRreduced
+        else:
+            SPR = SPRfull
+
+        self.spr_i = Signal(10, reset_less=True)
+        self.spr_o = Data(SPR, name="spr_o")
+        self.fast_o = Data(3, name="fast_o")
+
+    def elaborate(self, platform):
+        m = Module()
+        if self.regreduce_en:
+            SPR = SPRreduced
+        else:
+            SPR = SPRfull
+        with m.Switch(self.spr_i):
+            for i, x in enumerate(SPR):
+                with m.Case(x.value):
+                    m.d.comb += self.spr_o.data.eq(i)
+                    m.d.comb += self.spr_o.ok.eq(1)
+            for x, v in spr_to_fast.items():
+                with m.Case(x.value):
+                    m.d.comb += self.fast_o.data.eq(v)
+                    m.d.comb += self.fast_o.ok.eq(1)
+        return m
+
+
+class DecodeA(Elaboratable):
+    """DecodeA from instruction
+
+    decodes register RA, implicit and explicit CSRs
+    """
+
+    def __init__(self, dec, regreduce_en):
+        self.regreduce_en = regreduce_en
+        if self.regreduce_en:
+            SPR = SPRreduced
+        else:
+            SPR = SPRfull
+        self.dec = dec
+        self.sel_in = Signal(In1Sel, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.reg_out = Data(5, name="reg_a")
+        self.spr_out = Data(SPR, "spr_a")
+        self.fast_out = Data(3, "fast_a")
+        self.sv_nz = Signal(1)
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+        reg = self.reg_out
+        m.submodules.sprmap = sprmap = SPRMap(self.regreduce_en)
+
+        # select Register A field, if *full 7 bits* are zero (2 more from SVP64)
+        ra = Signal(5, reset_less=True)
+        comb += ra.eq(self.dec.RA)
+        with m.If((self.sel_in == In1Sel.RA) |
+                  ((self.sel_in == In1Sel.RA_OR_ZERO) &
+                   ((ra != Const(0, 5)) | (self.sv_nz != Const(0, 1))))):
+            comb += reg.data.eq(ra)
+            comb += reg.ok.eq(1)
+
+        # some Logic/ALU ops have RS as the 3rd arg, but no "RA".
+        # moved it to 1st position (in1_sel)... because
+        rs = Signal(5, reset_less=True)
+        comb += rs.eq(self.dec.RS)
+        with m.If(self.sel_in == In1Sel.RS):
+            comb += reg.data.eq(rs)
+            comb += reg.ok.eq(1)
+
+        # decode Fast-SPR based on instruction type
+        with m.Switch(op.internal_op):
+
+            # BC or BCREG: implicit register (CTR) NOTE: same in DecodeOut
+            with m.Case(MicrOp.OP_BC):
+                with m.If(~self.dec.BO[2]):  # 3.0B p38 BO2=0, use CTR reg
+                    # constant: CTR
+                    comb += self.fast_out.data.eq(FastRegs.CTR)
+                    comb += self.fast_out.ok.eq(1)
+            with m.Case(MicrOp.OP_BCREG):
+                xo9 = self.dec.FormXL.XO[9]  # 3.0B p38 top bit of XO
+                xo5 = self.dec.FormXL.XO[5]  # 3.0B p38
+                with m.If(xo9 & ~xo5):
+                    # constant: CTR
+                    comb += self.fast_out.data.eq(FastRegs.CTR)
+                    comb += self.fast_out.ok.eq(1)
+
+            # MFSPR move from SPRs
+            with m.Case(MicrOp.OP_MFSPR):
+                spr = Signal(10, reset_less=True)
+                comb += spr.eq(decode_spr_num(self.dec.SPR))  # from XFX
+                comb += sprmap.spr_i.eq(spr)
+                comb += self.spr_out.eq(sprmap.spr_o)
+                comb += self.fast_out.eq(sprmap.fast_o)
+
+        return m
+
+
+class DecodeAImm(Elaboratable):
+    """DecodeA immediate from instruction
+
+    decodes register RA, whether immediate-zero, implicit and
+    explicit CSRs.  SVP64 mode requires 2 extra bits
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(In1Sel, reset_less=True)
+        self.immz_out = Signal(reset_less=True)
+        self.sv_nz = Signal(1) # EXTRA bits from SVP64
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+
+        # zero immediate requested
+        ra = Signal(5, reset_less=True)
+        comb += ra.eq(self.dec.RA)
+        with m.If((self.sel_in == In1Sel.RA_OR_ZERO) &
+                    (ra == Const(0, 5)) &
+                    (self.sv_nz == Const(0, 1))):
+            comb += self.immz_out.eq(1)
+
+        return m
+
+
+class DecodeB(Elaboratable):
+    """DecodeB from instruction
+
+    decodes register RB, different forms of immediate (signed, unsigned),
+    and implicit SPRs.  register B is basically "lane 2" into the CompUnits.
+    by industry-standard convention, "lane 2" is where fully-decoded
+    immediates are muxed in.
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(In2Sel, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.reg_out = Data(7, "reg_b")
+        self.reg_isvec = Signal(1, name="reg_b_isvec") # TODO: in reg_out
+        self.fast_out = Data(3, "fast_b")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+        reg = self.reg_out
+
+        # select Register B field
+        with m.Switch(self.sel_in):
+            with m.Case(In2Sel.RB):
+                comb += reg.data.eq(self.dec.RB)
+                comb += reg.ok.eq(1)
+            with m.Case(In2Sel.RS):
+                # for M-Form shiftrot
+                comb += reg.data.eq(self.dec.RS)
+                comb += reg.ok.eq(1)
+
+        # decode SPR2 based on instruction type
+        # BCREG implicitly uses LR or TAR for 2nd reg
+        # CTR however is already in fast_spr1 *not* 2.
+        with m.If(op.internal_op == MicrOp.OP_BCREG):
+            xo9 = self.dec.FormXL.XO[9]  # 3.0B p38 top bit of XO
+            xo5 = self.dec.FormXL.XO[5]  # 3.0B p38
+            with m.If(~xo9):
+                comb += self.fast_out.data.eq(FastRegs.LR)
+                comb += self.fast_out.ok.eq(1)
+            with m.Elif(xo5):
+                comb += self.fast_out.data.eq(FastRegs.TAR)
+                comb += self.fast_out.ok.eq(1)
+
+        return m
+
+
+class DecodeBImm(Elaboratable):
+    """DecodeB immediate from instruction
+    """
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(In2Sel, reset_less=True)
+        self.imm_out = Data(64, "imm_b")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+
+        # select Register B Immediate
+        with m.Switch(self.sel_in):
+            with m.Case(In2Sel.CONST_UI): # unsigned
+                comb += self.imm_out.data.eq(self.dec.UI)
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_SI):  # sign-extended 16-bit
+                si = Signal(16, reset_less=True)
+                comb += si.eq(self.dec.SI)
+                comb += self.imm_out.data.eq(exts(si, 16, 64))
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_SI_HI):  # sign-extended 16+16=32 bit
+                si_hi = Signal(32, reset_less=True)
+                comb += si_hi.eq(self.dec.SI << 16)
+                comb += self.imm_out.data.eq(exts(si_hi, 32, 64))
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_UI_HI): # unsigned
+                ui = Signal(16, reset_less=True)
+                comb += ui.eq(self.dec.UI)
+                comb += self.imm_out.data.eq(ui << 16)
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_LI): # sign-extend 24+2=26 bit
+                li = Signal(26, reset_less=True)
+                comb += li.eq(self.dec.LI << 2)
+                comb += self.imm_out.data.eq(exts(li, 26, 64))
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_BD): # sign-extend (14+2)=16 bit
+                bd = Signal(16, reset_less=True)
+                comb += bd.eq(self.dec.BD << 2)
+                comb += self.imm_out.data.eq(exts(bd, 16, 64))
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_DS): # sign-extended (14+2=16) bit
+                ds = Signal(16, reset_less=True)
+                comb += ds.eq(self.dec.DS << 2)
+                comb += self.imm_out.data.eq(exts(ds, 16, 64))
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_M1): # signed (-1)
+                comb += self.imm_out.data.eq(~Const(0, 64))  # all 1s
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_SH): # unsigned - for shift
+                comb += self.imm_out.data.eq(self.dec.sh)
+                comb += self.imm_out.ok.eq(1)
+            with m.Case(In2Sel.CONST_SH32): # unsigned - for shift
+                comb += self.imm_out.data.eq(self.dec.SH32)
+                comb += self.imm_out.ok.eq(1)
+
+        return m
+
+
+class DecodeC(Elaboratable):
+    """DecodeC from instruction
+
+    decodes register RC.  this is "lane 3" into some CompUnits (not many)
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(In3Sel, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.reg_out = Data(5, "reg_c")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+        reg = self.reg_out
+
+        # select Register C field
+        with m.Switch(self.sel_in):
+            with m.Case(In3Sel.RB):
+                # for M-Form shiftrot
+                comb += reg.data.eq(self.dec.RB)
+                comb += reg.ok.eq(1)
+            with m.Case(In3Sel.RS):
+                comb += reg.data.eq(self.dec.RS)
+                comb += reg.ok.eq(1)
+
+        return m
+
+
+class DecodeOut(Elaboratable):
+    """DecodeOut from instruction
+
+    decodes output register RA, RT or SPR
+    """
+
+    def __init__(self, dec, regreduce_en):
+        self.regreduce_en = regreduce_en
+        if self.regreduce_en:
+            SPR = SPRreduced
+        else:
+            SPR = SPRfull
+        self.dec = dec
+        self.sel_in = Signal(OutSel, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.reg_out = Data(5, "reg_o")
+        self.spr_out = Data(SPR, "spr_o")
+        self.fast_out = Data(3, "fast_o")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        m.submodules.sprmap = sprmap = SPRMap(self.regreduce_en)
+        op = self.dec.op
+        reg = self.reg_out
+
+        # select Register out field
+        with m.Switch(self.sel_in):
+            with m.Case(OutSel.RT):
+                comb += reg.data.eq(self.dec.RT)
+                comb += reg.ok.eq(1)
+            with m.Case(OutSel.RA):
+                comb += reg.data.eq(self.dec.RA)
+                comb += reg.ok.eq(1)
+            with m.Case(OutSel.SPR):
+                spr = Signal(10, reset_less=True)
+                comb += spr.eq(decode_spr_num(self.dec.SPR))  # from XFX
+                # MFSPR move to SPRs - needs mapping
+                with m.If(op.internal_op == MicrOp.OP_MTSPR):
+                    comb += sprmap.spr_i.eq(spr)
+                    comb += self.spr_out.eq(sprmap.spr_o)
+                    comb += self.fast_out.eq(sprmap.fast_o)
+
+        # determine Fast Reg
+        with m.Switch(op.internal_op):
+
+            # BC or BCREG: implicit register (CTR) NOTE: same in DecodeA
+            with m.Case(MicrOp.OP_BC, MicrOp.OP_BCREG):
+                with m.If(~self.dec.BO[2]):  # 3.0B p38 BO2=0, use CTR reg
+                    # constant: CTR
+                    comb += self.fast_out.data.eq(FastRegs.CTR)
+                    comb += self.fast_out.ok.eq(1)
+
+            # RFID 1st spr (fast)
+            with m.Case(MicrOp.OP_RFID):
+                comb += self.fast_out.data.eq(FastRegs.SRR0)  # constant: SRR0
+                comb += self.fast_out.ok.eq(1)
+
+        return m
+
+
+class DecodeOut2(Elaboratable):
+    """DecodeOut2 from instruction
+
+    decodes output registers (2nd one).  note that RA is *implicit* below,
+    which now causes problems with SVP64
+
+    TODO: SVP64 is a little more complex, here.  svp64 allows extending
+    by one more destination by having one more EXTRA field.  RA-as-src
+    is not the same as RA-as-dest.  limited in that it's the same first
+    5 bits (from the v3.0B opcode), but still kinda cool.  mostly used
+    for operations that have src-as-dest: mostly this is LD/ST-with-update
+    but there are others.
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(OutSel, reset_less=True)
+        self.lk = Signal(reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.reg_out = Data(5, "reg_o2")
+        self.fast_out = Data(3, "fast_o2")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+        #m.submodules.svdec = svdec = SVP64RegExtra()
+
+        # get the 5-bit reg data before svp64-munging it into 7-bit plus isvec
+        #reg = Signal(5, reset_less=True)
+
+        if hasattr(self.dec.op, "upd"):
+            # update mode LD/ST uses read-reg A also as an output
+            with m.If(self.dec.op.upd == LDSTMode.update):
+                comb += self.reg_out.data.eq(self.dec.RA)
+                comb += self.reg_out.ok.eq(1)
+
+        # B, BC or BCREG: potential implicit register (LR) output
+        # these give bl, bcl, bclrl, etc.
+        with m.Switch(op.internal_op):
+
+            # BC* implicit register (LR)
+            with m.Case(MicrOp.OP_BC, MicrOp.OP_B, MicrOp.OP_BCREG):
+                with m.If(self.lk):  # "link" mode
+                    comb += self.fast_out.data.eq(FastRegs.LR)  # constant: LR
+                    comb += self.fast_out.ok.eq(1)
+
+            # RFID 2nd spr (fast)
+            with m.Case(MicrOp.OP_RFID):
+                comb += self.fast_out.data.eq(FastRegs.SRR1)  # constant: SRR1
+                comb += self.fast_out.ok.eq(1)
+
+        return m
+
+
+class DecodeRC(Elaboratable):
+    """DecodeRc from instruction
+
+    decodes Record bit Rc
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(RC, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.rc_out = Data(1, "rc")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+
+        # select Record bit out field
+        with m.Switch(self.sel_in):
+            with m.Case(RC.RC):
+                comb += self.rc_out.data.eq(self.dec.Rc)
+                comb += self.rc_out.ok.eq(1)
+            with m.Case(RC.ONE):
+                comb += self.rc_out.data.eq(1)
+                comb += self.rc_out.ok.eq(1)
+            with m.Case(RC.NONE):
+                comb += self.rc_out.data.eq(0)
+                comb += self.rc_out.ok.eq(1)
+
+        return m
+
+
+class DecodeOE(Elaboratable):
+    """DecodeOE from instruction
+
+    decodes OE field: uses RC decode detection which might not be good
+
+    -- For now, use "rc" in the decode table to decide whether oe exists.
+    -- This is not entirely correct architecturally: For mulhd and
+    -- mulhdu, the OE field is reserved. It remains to be seen what an
+    -- actual POWER9 does if we set it on those instructions, for now we
+    -- test that further down when assigning to the multiplier oe input.
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(RC, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.oe_out = Data(1, "oe")
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+
+        with m.Switch(op.internal_op):
+
+            # mulhw, mulhwu, mulhd, mulhdu - these *ignore* OE
+            # also rotate
+            # XXX ARGH! ignoring OE causes incompatibility with microwatt
+            # http://lists.libre-soc.org/pipermail/libre-soc-dev/2020-August/000302.html
+            with m.Case(MicrOp.OP_MUL_H64, MicrOp.OP_MUL_H32,
+                        MicrOp.OP_EXTS, MicrOp.OP_CNTZ,
+                        MicrOp.OP_SHL, MicrOp.OP_SHR, MicrOp.OP_RLC,
+                        MicrOp.OP_LOAD, MicrOp.OP_STORE,
+                        MicrOp.OP_RLCL, MicrOp.OP_RLCR,
+                        MicrOp.OP_EXTSWSLI):
+                pass
+
+            # all other ops decode OE field
+            with m.Default():
+                # select OE bit out field
+                with m.Switch(self.sel_in):
+                    with m.Case(RC.RC):
+                        comb += self.oe_out.data.eq(self.dec.OE)
+                        comb += self.oe_out.ok.eq(1)
+
+        return m
+
+
+class DecodeCRIn(Elaboratable):
+    """Decodes input CR from instruction
+
+    CR indices - insn fields - (not the data *in* the CR) require only 3
+    bits because they refer to CR0-CR7
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.sel_in = Signal(CRInSel, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.cr_bitfield = Data(3, "cr_bitfield")
+        self.cr_bitfield_b = Data(3, "cr_bitfield_b")
+        self.cr_bitfield_o = Data(3, "cr_bitfield_o")
+        self.whole_reg = Data(8,  "cr_fxm")
+        self.sv_override = Signal(2, reset_less=True) # do not do EXTRA spec
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+        m.submodules.ppick = ppick = PriorityPicker(8, reverse_i=True,
+                                                       reverse_o=True)
+
+        # zero-initialisation
+        comb += self.cr_bitfield.ok.eq(0)
+        comb += self.cr_bitfield_b.ok.eq(0)
+        comb += self.cr_bitfield_o.ok.eq(0)
+        comb += self.whole_reg.ok.eq(0)
+        comb += self.sv_override.eq(0)
+
+        # select the relevant CR bitfields
+        with m.Switch(self.sel_in):
+            with m.Case(CRInSel.NONE):
+                pass  # No bitfield activated
+            with m.Case(CRInSel.CR0):
+                comb += self.cr_bitfield.data.eq(0) # CR0 (MSB0 numbering)
+                comb += self.cr_bitfield.ok.eq(1)
+                comb += self.sv_override.eq(1)
+            with m.Case(CRInSel.CR1):
+                comb += self.cr_bitfield.data.eq(1) # CR1 (MSB0 numbering)
+                comb += self.cr_bitfield.ok.eq(1)
+                comb += self.sv_override.eq(2)
+            with m.Case(CRInSel.BI):
+                comb += self.cr_bitfield.data.eq(self.dec.BI[2:5])
+                comb += self.cr_bitfield.ok.eq(1)
+            with m.Case(CRInSel.BFA):
+                comb += self.cr_bitfield.data.eq(self.dec.FormX.BFA)
+                comb += self.cr_bitfield.ok.eq(1)
+            with m.Case(CRInSel.BA_BB):
+                comb += self.cr_bitfield.data.eq(self.dec.BA[2:5])
+                comb += self.cr_bitfield.ok.eq(1)
+                comb += self.cr_bitfield_b.data.eq(self.dec.BB[2:5])
+                comb += self.cr_bitfield_b.ok.eq(1)
+                comb += self.cr_bitfield_o.data.eq(self.dec.BT[2:5])
+                comb += self.cr_bitfield_o.ok.eq(1)
+            with m.Case(CRInSel.BC):
+                comb += self.cr_bitfield.data.eq(self.dec.BC[2:5])
+                comb += self.cr_bitfield.ok.eq(1)
+            with m.Case(CRInSel.WHOLE_REG):
+                comb += self.whole_reg.ok.eq(1)
+                move_one = Signal(reset_less=True)
+                comb += move_one.eq(self.insn_in[20]) # MSB0 bit 11
+                with m.If((op.internal_op == MicrOp.OP_MFCR) & move_one):
+                    # must one-hot the FXM field
+                    comb += ppick.i.eq(self.dec.FXM)
+                    comb += self.whole_reg.data.eq(ppick.o)
+                with m.Else():
+                    # otherwise use all of it
+                    comb += self.whole_reg.data.eq(0xff)
+
+        return m
+
+
+class DecodeCROut(Elaboratable):
+    """Decodes input CR from instruction
+
+    CR indices - insn fields - (not the data *in* the CR) require only 3
+    bits because they refer to CR0-CR7
+    """
+
+    def __init__(self, dec):
+        self.dec = dec
+        self.rc_in = Signal(reset_less=True)
+        self.sel_in = Signal(CROutSel, reset_less=True)
+        self.insn_in = Signal(32, reset_less=True)
+        self.cr_bitfield = Data(3, "cr_bitfield")
+        self.whole_reg = Data(8,  "cr_fxm")
+        self.sv_override = Signal(2, reset_less=True) # do not do EXTRA spec
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        op = self.dec.op
+        m.submodules.ppick = ppick = PriorityPicker(8, reverse_i=True,
+                                                       reverse_o=True)
+
+        comb += self.cr_bitfield.ok.eq(0)
+        comb += self.whole_reg.ok.eq(0)
+        comb += self.sv_override.eq(0)
+
+        # please note these MUST match (setting of cr_bitfield.ok) exactly
+        # with write_cr0 below in PowerDecoder2.  the reason it's separated
+        # is to avoid having duplicate copies of DecodeCROut in multiple
+        # PowerDecoderSubsets.  register decoding should be a one-off in
+        # PowerDecoder2.  see https://bugs.libre-soc.org/show_bug.cgi?id=606
+
+        with m.Switch(self.sel_in):
+            with m.Case(CROutSel.NONE):
+                pass  # No bitfield activated
+            with m.Case(CROutSel.CR0):
+                comb += self.cr_bitfield.data.eq(0) # CR0 (MSB0 numbering)
+                comb += self.cr_bitfield.ok.eq(self.rc_in)  # only when RC=1
+                comb += self.sv_override.eq(1)
+            with m.Case(CROutSel.CR1):
+                comb += self.cr_bitfield.data.eq(1) # CR1 (MSB0 numbering)
+                comb += self.cr_bitfield.ok.eq(self.rc_in)  # only when RC=1
+                comb += self.sv_override.eq(2)
+            with m.Case(CROutSel.BF):
+                comb += self.cr_bitfield.data.eq(self.dec.FormX.BF)
+                comb += self.cr_bitfield.ok.eq(1)
+            with m.Case(CROutSel.BT):
+                comb += self.cr_bitfield.data.eq(self.dec.FormXL.BT[2:5])
+                comb += self.cr_bitfield.ok.eq(1)
+            with m.Case(CROutSel.WHOLE_REG):
+                comb += self.whole_reg.ok.eq(1)
+                move_one = Signal(reset_less=True)
+                comb += move_one.eq(self.insn_in[20])
+                with m.If((op.internal_op == MicrOp.OP_MTCRF)):
+                    with m.If(move_one):
+                        # must one-hot the FXM field
+                        comb += ppick.i.eq(self.dec.FXM)
+                        with m.If(ppick.en_o):
+                            comb += self.whole_reg.data.eq(ppick.o)
+                        with m.Else():
+                            comb += self.whole_reg.data.eq(0b00000001) # CR7
+                    with m.Else():
+                        comb += self.whole_reg.data.eq(self.dec.FXM)
+                with m.Else():
+                    # otherwise use all of it
+                    comb += self.whole_reg.data.eq(0xff)
+
+        return m
+
+# dictionary of Input Record field names that, if they exist,
+# will need a corresponding CSV Decoder file column (actually, PowerOp)
+# to be decoded (this includes the single bit names)
+record_names = {'insn_type': 'internal_op',
+                'fn_unit': 'function_unit',
+                'rc': 'rc_sel',
+                'oe': 'rc_sel',
+                'zero_a': 'in1_sel',
+                'imm_data': 'in2_sel',
+                'invert_in': 'inv_a',
+                'invert_out': 'inv_out',
+                'rc': 'cr_out',
+                'oe': 'cr_in',
+                'output_carry': 'cry_out',
+                'input_carry': 'cry_in',
+                'is_32bit': 'is_32b',
+                'is_signed': 'sgn',
+                'lk': 'lk',
+                'data_len': 'ldst_len',
+                'byte_reverse': 'br',
+                'sign_extend': 'sgn_ext',
+                'ldst_mode': 'upd',
+                }
+
+
+class PowerDecodeSubset(Elaboratable):
+    """PowerDecodeSubset: dynamic subset decoder
+
+    only fields actually requested are copied over. hence, "subset" (duh).
+    """
+    def __init__(self, dec, opkls=None, fn_name=None, final=False, state=None,
+                            svp64_en=True, regreduce_en=False):
+
+        self.svp64_en = svp64_en
+        self.regreduce_en = regreduce_en
+        if svp64_en:
+            self.sv_rm = SVP64Rec(name="dec_svp64") # SVP64 RM field
+        self.sv_a_nz = Signal(1)
+        self.final = final
+        self.opkls = opkls
+        self.fn_name = fn_name
+        if opkls is None:
+            opkls = Decode2ToOperand
+        self.do = opkls(fn_name)
+        col_subset = self.get_col_subset(self.do)
+
+        # only needed for "main" PowerDecode2
+        if not self.final:
+            self.e = Decode2ToExecute1Type(name=self.fn_name, do=self.do,
+                                           regreduce_en=regreduce_en)
+
+        # create decoder if one not already given
+        if dec is None:
+            dec = create_pdecode(name=fn_name, col_subset=col_subset,
+                                      row_subset=self.rowsubsetfn)
+        self.dec = dec
+
+        # state information needed by the Decoder
+        if state is None:
+            state = CoreState("dec2")
+        self.state = state
+
+    def get_col_subset(self, do):
+        subset = { 'cr_in', 'cr_out', 'rc_sel'} # needed, non-optional
+        for k, v in record_names.items():
+            if hasattr(do, k):
+                subset.add(v)
+        print ("get_col_subset", self.fn_name, do.fields, subset)
+        return subset
+
+    def rowsubsetfn(self, opcode, row):
+        """select per-Function-Unit subset of opcodes to be processed
+
+        normally this just looks at the "unit" column.  MMU is different
+        in that it processes specific SPR set/get operations that the SPR
+        pipeline should not.
+        """
+        return (row['unit'] == self.fn_name or
+                # sigh a dreadful hack: MTSPR and MFSPR need to be processed
+                # by the MMU pipeline so we direct those opcodes to MMU **AND**
+                # SPR pipelines, then selectively weed out the SPRs that should
+                # or should not not go to each pipeline, further down.
+                # really this should be done by modifying the CSV syntax
+                # to support multiple tasks (unit column multiple entries)
+                # see https://bugs.libre-soc.org/show_bug.cgi?id=310
+               (self.fn_name == 'MMU' and row['unit'] == 'SPR' and
+                row['internal op'] in ['OP_MTSPR', 'OP_MFSPR'])
+                )
+
+    def ports(self):
+        ports = self.dec.ports() + self.e.ports()
+        if self.svp64_en:
+            ports += self.sv_rm.ports()
+        return ports
+
+    def needs_field(self, field, op_field):
+        if self.final:
+            do = self.do
+        else:
+            do = self.e_tmp.do
+        return hasattr(do, field) and self.op_get(op_field) is not None
+
+    def do_copy(self, field, val, final=False):
+        if final or self.final:
+            do = self.do
+        else:
+            do = self.e_tmp.do
+        if hasattr(do, field) and val is not None:
+            return getattr(do, field).eq(val)
+        return []
+
+    def op_get(self, op_field):
+        return getattr(self.dec.op, op_field, None)
+
+    def elaborate(self, platform):
+        if self.regreduce_en:
+            SPR = SPRreduced
+        else:
+            SPR = SPRfull
+        m = Module()
+        comb = m.d.comb
+        state = self.state
+        op, do = self.dec.op, self.do
+        msr, cia = state.msr, state.pc
+        # fill in for a normal instruction (not an exception)
+        # copy over if non-exception, non-privileged etc. is detected
+        if not self.final:
+            if self.fn_name is None:
+                name = "tmp"
+            else:
+                name = self.fn_name + "tmp"
+            self.e_tmp = Decode2ToExecute1Type(name=name, opkls=self.opkls,
+                                           regreduce_en=self.regreduce_en)
+
+        # set up submodule decoders
+        m.submodules.dec = self.dec
+        m.submodules.dec_rc = self.dec_rc = dec_rc = DecodeRC(self.dec)
+        m.submodules.dec_oe = dec_oe = DecodeOE(self.dec)
+
+        # copy instruction through...
+        for i in [do.insn, dec_rc.insn_in, dec_oe.insn_in, ]:
+            comb += i.eq(self.dec.opcode_in)
+
+        # ...and subdecoders' input fields
+        comb += dec_rc.sel_in.eq(op.rc_sel)
+        comb += dec_oe.sel_in.eq(op.rc_sel)  # XXX should be OE sel
+
+        # copy "state" over
+        comb += self.do_copy("msr", msr)
+        comb += self.do_copy("cia", cia)
+
+        # set up instruction type
+        # no op: defaults to OP_ILLEGAL
+        internal_op = self.op_get("internal_op")
+        comb += self.do_copy("insn_type", internal_op)
+
+        # function unit for decoded instruction: requires minor redirect
+        # for SPR set/get
+        fn = self.op_get("function_unit")
+        spr = Signal(10, reset_less=True)
+        comb += spr.eq(decode_spr_num(self.dec.SPR)) # from XFX
+
+        # Microwatt doesn't implement the partition table
+        # instead has PRTBL register (SPR) to point to process table
+        is_spr_mv = Signal()
+        is_mmu_spr = Signal()
+        comb += is_spr_mv.eq((internal_op == MicrOp.OP_MTSPR) |
+                             (internal_op == MicrOp.OP_MFSPR))
+        comb += is_mmu_spr.eq((spr == SPR.DSISR.value) |
+                              (spr == SPR.DAR.value) |
+                              (spr == SPR.PRTBL.value) |
+                              (spr == SPR.PIDR.value))
+        # MMU must receive MMU SPRs
+        with m.If(is_spr_mv & (fn == Function.SPR) & is_mmu_spr):
+            comb += self.do_copy("fn_unit", Function.NONE)
+            comb += self.do_copy("insn_type", MicrOp.OP_ILLEGAL)
+        # SPR pipe must *not* receive MMU SPRs
+        with m.Elif(is_spr_mv & (fn == Function.MMU) & ~is_mmu_spr):
+            comb += self.do_copy("fn_unit", Function.NONE)
+            comb += self.do_copy("insn_type", MicrOp.OP_ILLEGAL)
+        # all others ok
+        with m.Else():
+            comb += self.do_copy("fn_unit", fn)
+
+        # immediates
+        if self.needs_field("zero_a", "in1_sel"):
+            m.submodules.dec_ai = dec_ai = DecodeAImm(self.dec)
+            comb += dec_ai.sv_nz.eq(self.sv_a_nz)
+            comb += dec_ai.sel_in.eq(op.in1_sel)
+            comb += self.do_copy("zero_a", dec_ai.immz_out)  # RA==0 detected
+        if self.needs_field("imm_data", "in2_sel"):
+            m.submodules.dec_bi = dec_bi = DecodeBImm(self.dec)
+            comb += dec_bi.sel_in.eq(op.in2_sel)
+            comb += self.do_copy("imm_data", dec_bi.imm_out) # imm in RB
+
+        # rc and oe out
+        comb += self.do_copy("rc", dec_rc.rc_out)
+        comb += self.do_copy("oe", dec_oe.oe_out)
+
+        # CR in/out - note: these MUST match with what happens in
+        # DecodeCROut!
+        rc_out = self.dec_rc.rc_out.data
+        with m.Switch(op.cr_out):
+            with m.Case(CROutSel.CR0, CROutSel.CR1):
+                comb += self.do_copy("write_cr0", rc_out) # only when RC=1
+            with m.Case(CROutSel.BF, CROutSel.BT):
+                comb += self.do_copy("write_cr0", 1)
+
+        comb += self.do_copy("input_cr", self.op_get("cr_in"))   # CR in
+        comb += self.do_copy("output_cr", self.op_get("cr_out"))  # CR out
+
+        # decoded/selected instruction flags
+        comb += self.do_copy("data_len", self.op_get("ldst_len"))
+        comb += self.do_copy("invert_in", self.op_get("inv_a"))
+        comb += self.do_copy("invert_out", self.op_get("inv_out"))
+        comb += self.do_copy("input_carry", self.op_get("cry_in"))
+        comb += self.do_copy("output_carry", self.op_get("cry_out"))
+        comb += self.do_copy("is_32bit", self.op_get("is_32b"))
+        comb += self.do_copy("is_signed", self.op_get("sgn"))
+        lk = self.op_get("lk")
+        if lk is not None:
+            with m.If(lk):
+                comb += self.do_copy("lk", self.dec.LK)  # XXX TODO: accessor
+
+        comb += self.do_copy("byte_reverse", self.op_get("br"))
+        comb += self.do_copy("sign_extend", self.op_get("sgn_ext"))
+        comb += self.do_copy("ldst_mode", self.op_get("upd"))  # LD/ST mode
+
+        return m
+
+
+class PowerDecode2(PowerDecodeSubset):
+    """PowerDecode2: the main instruction decoder.
+
+    whilst PowerDecode is responsible for decoding the actual opcode, this
+    module encapsulates further specialist, sparse information and
+    expansion of fields that is inconvenient to have in the CSV files.
+    for example: the encoding of the immediates, which are detected
+    and expanded out to their full value from an annotated (enum)
+    representation.
+
+    implicit register usage is also set up, here.  for example: OP_BC
+    requires implicitly reading CTR, OP_RFID requires implicitly writing
+    to SRR1 and so on.
+
+    in addition, PowerDecoder2 is responsible for detecting whether
+    instructions are illegal (or privileged) or not, and instead of
+    just leaving at that, *replacing* the instruction to execute with
+    a suitable alternative (trap).
+
+    LDSTExceptions are done the cycle _after_ they're detected (after
+    they come out of LDSTCompUnit).  basically despite the instruction
+    being decoded, the results of the decode are completely ignored
+    and "exception.happened" used to set the "actual" instruction to
+    "OP_TRAP".  the LDSTException data structure gets filled in,
+    in the CompTrapOpSubset and that's what it fills in SRR.
+
+    to make this work, TestIssuer must notice "exception.happened"
+    after the (failed) LD/ST and copies the LDSTException info from
+    the output, into here (PowerDecoder2).  without incrementing PC.
+    """
+
+    def __init__(self, dec, opkls=None, fn_name=None, final=False,
+                            state=None, svp64_en=True, regreduce_en=False):
+        super().__init__(dec, opkls, fn_name, final, state, svp64_en,
+                         regreduce_en=False)
+        self.exc = LDSTException("dec2_exc")
+
+        if self.svp64_en:
+            self.cr_out_isvec = Signal(1, name="cr_out_isvec")
+            self.cr_in_isvec = Signal(1, name="cr_in_isvec")
+            self.cr_in_b_isvec = Signal(1, name="cr_in_b_isvec")
+            self.cr_in_o_isvec = Signal(1, name="cr_in_o_isvec")
+            self.in1_isvec = Signal(1, name="reg_a_isvec")
+            self.in2_isvec = Signal(1, name="reg_b_isvec")
+            self.in3_isvec = Signal(1, name="reg_c_isvec")
+            self.o_isvec = Signal(1, name="reg_o_isvec")
+            self.o2_isvec = Signal(1, name="reg_o2_isvec")
+            self.no_in_vec = Signal(1, name="no_in_vec") # no inputs vector
+            self.no_out_vec = Signal(1, name="no_out_vec") # no outputs vector
+            self.loop_continue = Signal(1, name="loop_continue")
+            self.rm_dec = SVP64RMModeDecode("svp64_rm_dec")
+        else:
+            self.no_in_vec = Const(1, 1)
+            self.no_out_vec = Const(1, 1)
+            self.loop_continue = Const(0, 1)
+
+    def get_col_subset(self, opkls):
+        subset = super().get_col_subset(opkls)
+        subset.add("asmcode")
+        subset.add("in1_sel")
+        subset.add("in2_sel")
+        subset.add("in3_sel")
+        subset.add("out_sel")
+        if self.svp64_en:
+            subset.add("sv_in1")
+            subset.add("sv_in2")
+            subset.add("sv_in3")
+            subset.add("sv_out")
+            subset.add("sv_out2")
+            subset.add("sv_cr_in")
+            subset.add("sv_cr_out")
+            subset.add("SV_Etype")
+            subset.add("SV_Ptype")
+        subset.add("lk")
+        subset.add("internal_op")
+        subset.add("form")
+        return subset
+
+    def elaborate(self, platform):
+        m = super().elaborate(platform)
+        comb = m.d.comb
+        state = self.state
+        e_out, op, do_out = self.e, self.dec.op, self.e.do
+        dec_spr, msr, cia, ext_irq = state.dec, state.msr, state.pc, state.eint
+        rc_out = self.dec_rc.rc_out.data
+        e = self.e_tmp
+        do = e.do
+
+        # fill in for a normal instruction (not an exception)
+        # copy over if non-exception, non-privileged etc. is detected
+
+        # set up submodule decoders
+        m.submodules.dec_a = dec_a = DecodeA(self.dec, self.regreduce_en)
+        m.submodules.dec_b = dec_b = DecodeB(self.dec)
+        m.submodules.dec_c = dec_c = DecodeC(self.dec)
+        m.submodules.dec_o = dec_o = DecodeOut(self.dec, self.regreduce_en)
+        m.submodules.dec_o2 = dec_o2 = DecodeOut2(self.dec)
+        m.submodules.dec_cr_in = self.dec_cr_in = DecodeCRIn(self.dec)
+        m.submodules.dec_cr_out = self.dec_cr_out = DecodeCROut(self.dec)
+        comb += dec_a.sv_nz.eq(self.sv_a_nz)
+
+        if self.svp64_en:
+            # and SVP64 Extra decoders
+            m.submodules.crout_svdec = crout_svdec = SVP64CRExtra()
+            m.submodules.crin_svdec = crin_svdec = SVP64CRExtra()
+            m.submodules.crin_svdec_b = crin_svdec_b = SVP64CRExtra()
+            m.submodules.crin_svdec_o = crin_svdec_o = SVP64CRExtra()
+            m.submodules.in1_svdec = in1_svdec = SVP64RegExtra()
+            m.submodules.in2_svdec = in2_svdec = SVP64RegExtra()
+            m.submodules.in3_svdec = in3_svdec = SVP64RegExtra()
+            m.submodules.o_svdec = o_svdec = SVP64RegExtra()
+            m.submodules.o2_svdec = o2_svdec = SVP64RegExtra()
+
+            # debug access to crout_svdec (used in get_pdecode_cr_out)
+            self.crout_svdec = crout_svdec
+
+            # and SVP64 RM mode decoder
+            m.submodules.sv_rm_dec = rm_dec = self.rm_dec
+
+        # get the 5-bit reg data before svp64-munging it into 7-bit plus isvec
+        reg = Signal(5, reset_less=True)
+
+        # copy instruction through...
+        for i in [do.insn, dec_a.insn_in, dec_b.insn_in,
+                  self.dec_cr_in.insn_in, self.dec_cr_out.insn_in,
+                  dec_c.insn_in, dec_o.insn_in, dec_o2.insn_in]:
+            comb += i.eq(self.dec.opcode_in)
+
+        # CR setup
+        comb += self.dec_cr_in.sel_in.eq(op.cr_in)
+        comb += self.dec_cr_out.sel_in.eq(op.cr_out)
+        comb += self.dec_cr_out.rc_in.eq(rc_out)
+
+        # CR register info
+        comb += self.do_copy("read_cr_whole", self.dec_cr_in.whole_reg)
+        comb += self.do_copy("write_cr_whole", self.dec_cr_out.whole_reg)
+
+        # ...and subdecoders' input fields
+        comb += dec_a.sel_in.eq(op.in1_sel)
+        comb += dec_b.sel_in.eq(op.in2_sel)
+        comb += dec_c.sel_in.eq(op.in3_sel)
+        comb += dec_o.sel_in.eq(op.out_sel)
+        comb += dec_o2.sel_in.eq(op.out_sel)
+        if hasattr(do, "lk"):
+            comb += dec_o2.lk.eq(do.lk)
+
+        if self.svp64_en:
+            # now do the SVP64 munging.  op.SV_Etype and op.sv_in1 comes from
+            # PowerDecoder which in turn comes from LDST-RM*.csv and RM-*.csv
+            # which in turn were auto-generated by sv_analysis.py
+            extra = self.sv_rm.extra            # SVP64 extra bits 10:18
+
+            #######
+            # CR out
+            comb += crout_svdec.idx.eq(op.sv_cr_out)  # SVP64 CR out
+            comb += self.cr_out_isvec.eq(crout_svdec.isvec)
+
+            #######
+            # CR in - selection slightly different due to shared CR field sigh
+            cr_a_idx = Signal(SVEXTRA)
+            cr_b_idx = Signal(SVEXTRA)
+
+            # these change slightly, when decoding BA/BB.  really should have
+            # their own separate CSV column: sv_cr_in1 and sv_cr_in2, but hey
+            comb += cr_a_idx.eq(op.sv_cr_in)
+            comb += cr_b_idx.eq(SVEXTRA.NONE)
+            with m.If(op.sv_cr_in == SVEXTRA.Idx_1_2.value):
+                comb += cr_a_idx.eq(SVEXTRA.Idx1)
+                comb += cr_b_idx.eq(SVEXTRA.Idx2)
+
+            comb += self.cr_in_isvec.eq(crin_svdec.isvec)
+            comb += self.cr_in_b_isvec.eq(crin_svdec_b.isvec)
+            comb += self.cr_in_o_isvec.eq(crin_svdec_o.isvec)
+
+            # indices are slightly different, BA/BB mess sorted above
+            comb += crin_svdec.idx.eq(cr_a_idx)       # SVP64 CR in A
+            comb += crin_svdec_b.idx.eq(cr_b_idx)     # SVP64 CR in B
+            comb += crin_svdec_o.idx.eq(op.sv_cr_out) # SVP64 CR out
+
+            # get SVSTATE srcstep (TODO: elwidth etc.) needed below
+            srcstep = Signal.like(self.state.svstate.srcstep)
+            dststep = Signal.like(self.state.svstate.dststep)
+            comb += srcstep.eq(self.state.svstate.srcstep)
+            comb += dststep.eq(self.state.svstate.dststep)
+
+            # registers a, b, c and out and out2 (LD/ST EA)
+            for to_reg, fromreg, svdec, out in (
+                (e.read_reg1, dec_a.reg_out, in1_svdec, False),
+                (e.read_reg2, dec_b.reg_out, in2_svdec, False),
+                (e.read_reg3, dec_c.reg_out, in3_svdec, False),
+                (e.write_reg, dec_o.reg_out, o_svdec, True),
+                (e.write_ea, dec_o2.reg_out, o2_svdec, True)):
+                comb += svdec.extra.eq(extra)        # EXTRA field of SVP64 RM
+                comb += svdec.etype.eq(op.SV_Etype)  # EXTRA2/3 for this insn
+                comb += svdec.reg_in.eq(fromreg.data) # 3-bit (CR0/BC/BFA)
+                comb += to_reg.ok.eq(fromreg.ok)
+                # detect if Vectorised: add srcstep/dststep if yes.
+                # to_reg is 7-bits, outs get dststep added, ins get srcstep
+                with m.If(svdec.isvec):
+                    step = dststep if out else srcstep
+                    comb += to_reg.data.eq(step+svdec.reg_out)
+                with m.Else():
+                    comb += to_reg.data.eq(svdec.reg_out)
+
+            comb += in1_svdec.idx.eq(op.sv_in1)  # SVP64 reg #1 (in1_sel)
+            comb += in2_svdec.idx.eq(op.sv_in2)  # SVP64 reg #2 (in2_sel)
+            comb += in3_svdec.idx.eq(op.sv_in3)  # SVP64 reg #3 (in3_sel)
+            comb += o_svdec.idx.eq(op.sv_out)    # SVP64 output (out_sel)
+            comb += o2_svdec.idx.eq(op.sv_out2)  # SVP64 output (implicit)
+            # XXX TODO - work out where this should come from.  the problem is
+            # that LD-with-update is implied (computed from "is instruction in
+            # "update mode" rather than specified cleanly as its own CSV column
+
+            # output reg-is-vectorised (and when no in/out is vectorised)
+            comb += self.in1_isvec.eq(in1_svdec.isvec)
+            comb += self.in2_isvec.eq(in2_svdec.isvec)
+            comb += self.in3_isvec.eq(in3_svdec.isvec)
+            comb += self.o_isvec.eq(o_svdec.isvec)
+            comb += self.o2_isvec.eq(o2_svdec.isvec)
+            # TODO add SPRs here.  must be True when *all* are scalar
+            l = map(lambda svdec: svdec.isvec, [in1_svdec, in2_svdec, in3_svdec,
+                                        crin_svdec, crin_svdec_b, crin_svdec_o])
+            comb += self.no_in_vec.eq(~Cat(*l).bool()) # all input scalar
+            l = map(lambda svdec: svdec.isvec, [o2_svdec, o_svdec, crout_svdec])
+            comb += self.no_out_vec.eq(~Cat(*l).bool()) # all output scalar
+            # now create a general-purpose "test" as to whether looping
+            # should continue.  this doesn't include predication bit-tests
+            loop = self.loop_continue
+            with m.Switch(op.SV_Ptype):
+                with m.Case(SVPtype.P2.value):
+                    # twin-predication
+                    # TODO: *and cache-inhibited LD/ST!*
+                    comb += loop.eq(~(self.no_in_vec | self.no_out_vec))
+                with m.Case(SVPtype.P1.value):
+                    # single-predication, test relies on dest only
+                    comb += loop.eq(~self.no_out_vec)
+                with m.Default():
+                    # not an SV operation, no looping
+                    comb += loop.eq(0)
+
+            # condition registers (CR)
+            for to_reg, cr, name, svdec, out in (
+                (e.read_cr1, self.dec_cr_in, "cr_bitfield", crin_svdec, 0),
+                (e.read_cr2, self.dec_cr_in, "cr_bitfield_b", crin_svdec_b, 0),
+                (e.read_cr3, self.dec_cr_in, "cr_bitfield_o", crin_svdec_o, 0),
+                (e.write_cr, self.dec_cr_out, "cr_bitfield", crout_svdec, 1)):
+                fromreg = getattr(cr, name)
+                comb += svdec.extra.eq(extra)        # EXTRA field of SVP64 RM
+                comb += svdec.etype.eq(op.SV_Etype)  # EXTRA2/3 for this insn
+                comb += svdec.cr_in.eq(fromreg.data) # 3-bit (CR0/BC/BFA)
+                with m.If(svdec.isvec):
+                    # check if this is CR0 or CR1: treated differently
+                    # (does not "listen" to EXTRA2/3 spec for a start)
+                    # also: the CRs start from completely different locations
+                    step = dststep if out else srcstep
+                    with m.If(cr.sv_override == 1): # CR0
+                        offs = SVP64CROffs.CR0
+                        comb += to_reg.data.eq(step+offs)
+                    with m.Elif(cr.sv_override == 2): # CR1
+                        offs = SVP64CROffs.CR1
+                        comb += to_reg.data.eq(step+1)
+                    with m.Else():
+                        comb += to_reg.data.eq(step+svdec.cr_out) # 7-bit out
+                with m.Else():
+                    comb += to_reg.data.eq(svdec.cr_out) # 7-bit output
+                comb += to_reg.ok.eq(fromreg.ok)
+
+            # sigh must determine if RA is nonzero (7 bit)
+            comb += self.sv_a_nz.eq(e.read_reg1.data != Const(0, 7))
+        else:
+            # connect up to/from read/write GPRs
+            for to_reg, fromreg in ((e.read_reg1, dec_a.reg_out),
+                                    (e.read_reg2, dec_b.reg_out),
+                                    (e.read_reg3, dec_c.reg_out),
+                                    (e.write_reg, dec_o.reg_out),
+                                    (e.write_ea, dec_o2.reg_out)):
+                comb += to_reg.data.eq(fromreg.data)
+                comb += to_reg.ok.eq(fromreg.ok)
+
+            # connect up to/from read/write CRs
+            for to_reg, cr, name in (
+                        (e.read_cr1, self.dec_cr_in, "cr_bitfield", ),
+                        (e.read_cr2, self.dec_cr_in, "cr_bitfield_b", ),
+                        (e.read_cr3, self.dec_cr_in, "cr_bitfield_o", ),
+                        (e.write_cr, self.dec_cr_out, "cr_bitfield", )):
+                fromreg = getattr(cr, name)
+                comb += to_reg.data.eq(fromreg.data)
+                comb += to_reg.ok.eq(fromreg.ok)
+
+        # SPRs out
+        comb += e.read_spr1.eq(dec_a.spr_out)
+        comb += e.write_spr.eq(dec_o.spr_out)
+
+        # Fast regs out
+        comb += e.read_fast1.eq(dec_a.fast_out)
+        comb += e.read_fast2.eq(dec_b.fast_out)
+        comb += e.write_fast1.eq(dec_o.fast_out)
+        comb += e.write_fast2.eq(dec_o2.fast_out)
+
+        if self.svp64_en:
+            # connect up SVP64 RM Mode decoding
+            fn = self.op_get("function_unit")
+            comb += rm_dec.fn_in.eq(fn) # decode needs to know if LD/ST type
+            comb += rm_dec.ptype_in.eq(op.SV_Ptype) # Single/Twin predicated
+            comb += rm_dec.rc_in.eq(rc_out) # Rc=1
+            comb += rm_dec.rm_in.eq(self.sv_rm) # SVP64 RM mode
+
+        # sigh this is exactly the sort of thing for which the
+        # decoder is designed to not need.  MTSPR, MFSPR and others need
+        # access to the XER bits.  however setting e.oe is not appropriate
+        with m.If(op.internal_op == MicrOp.OP_MFSPR):
+            comb += e.xer_in.eq(0b111) # SO, CA, OV
+        with m.If(op.internal_op == MicrOp.OP_CMP):
+            comb += e.xer_in.eq(1<<XERRegs.SO) # SO
+        with m.If(op.internal_op == MicrOp.OP_MTSPR):
+            comb += e.xer_out.eq(1)
+
+        # set the trapaddr to 0x700 for a td/tw/tdi/twi operation
+        with m.If(op.internal_op == MicrOp.OP_TRAP):
+            # *DO NOT* call self.trap here.  that would reset absolutely
+            # everything including destroying read of RA and RB.
+            comb += self.do_copy("trapaddr", 0x70) # strip first nibble
+
+        ####################
+        # ok so the instruction's been decoded, blah blah, however
+        # now we need to determine if it's actually going to go ahead...
+        # *or* if in fact it's a privileged operation, whether there's
+        # an external interrupt, etc. etc.  this is a simple priority
+        # if-elif-elif sequence.  decrement takes highest priority,
+        # EINT next highest, privileged operation third.
+
+        # check if instruction is privileged
+        is_priv_insn = instr_is_priv(m, op.internal_op, e.do.insn)
+
+        # different IRQ conditions
+        ext_irq_ok = Signal()
+        dec_irq_ok = Signal()
+        priv_ok = Signal()
+        illeg_ok = Signal()
+        exc = self.exc
+
+        comb += ext_irq_ok.eq(ext_irq & msr[MSR.EE]) # v3.0B p944 (MSR.EE)
+        comb += dec_irq_ok.eq(dec_spr[63] & msr[MSR.EE]) # 6.5.11 p1076
+        comb += priv_ok.eq(is_priv_insn & msr[MSR.PR])
+        comb += illeg_ok.eq(op.internal_op == MicrOp.OP_ILLEGAL)
+
+        # LD/ST exceptions.  TestIssuer copies the exception info at us
+        # after a failed LD/ST.
+        with m.If(exc.happened):
+            with m.If(exc.alignment):
+                self.trap(m, TT.PRIV, 0x600)
+            with m.Elif(exc.instr_fault):
+                with m.If(exc.segment_fault):
+                    self.trap(m, TT.PRIV, 0x480)
+                with m.Else():
+                    # pass exception info to trap to create SRR1
+                    self.trap(m, TT.MEMEXC, 0x400, exc)
+            with m.Else():
+                with m.If(exc.segment_fault):
+                    self.trap(m, TT.PRIV, 0x380)
+                with m.Else():
+                    self.trap(m, TT.PRIV, 0x300)
+
+        # decrement counter (v3.0B p1099): TODO 32-bit version (MSR.LPCR)
+        with m.Elif(dec_irq_ok):
+            self.trap(m, TT.DEC, 0x900)   # v3.0B 6.5 p1065
+
+        # external interrupt? only if MSR.EE set
+        with m.Elif(ext_irq_ok):
+            self.trap(m, TT.EINT, 0x500)
+
+        # privileged instruction trap
+        with m.Elif(priv_ok):
+            self.trap(m, TT.PRIV, 0x700)
+
+        # illegal instruction must redirect to trap. this is done by
+        # *overwriting* the decoded instruction and starting again.
+        # (note: the same goes for interrupts and for privileged operations,
+        # just with different trapaddr and traptype)
+        with m.Elif(illeg_ok):
+            # illegal instruction trap
+            self.trap(m, TT.ILLEG, 0x700)
+
+        # no exception, just copy things to the output
+        with m.Else():
+            comb += e_out.eq(e)
+
+        ####################
+        # follow-up after trap/irq to set up SRR0/1
+
+        # trap: (note e.insn_type so this includes OP_ILLEGAL) set up fast regs
+        # Note: OP_SC could actually be modified to just be a trap
+        with m.If((do_out.insn_type == MicrOp.OP_TRAP) |
+                  (do_out.insn_type == MicrOp.OP_SC)):
+            # TRAP write fast1 = SRR0
+            comb += e_out.write_fast1.data.eq(FastRegs.SRR0)  # constant: SRR0
+            comb += e_out.write_fast1.ok.eq(1)
+            # TRAP write fast2 = SRR1
+            comb += e_out.write_fast2.data.eq(FastRegs.SRR1)  # constant: SRR1
+            comb += e_out.write_fast2.ok.eq(1)
+
+        # RFID: needs to read SRR0/1
+        with m.If(do_out.insn_type == MicrOp.OP_RFID):
+            # TRAP read fast1 = SRR0
+            comb += e_out.read_fast1.data.eq(FastRegs.SRR0)  # constant: SRR0
+            comb += e_out.read_fast1.ok.eq(1)
+            # TRAP read fast2 = SRR1
+            comb += e_out.read_fast2.data.eq(FastRegs.SRR1)  # constant: SRR1
+            comb += e_out.read_fast2.ok.eq(1)
+
+        # annoying simulator bug
+        if hasattr(e_out, "asmcode") and hasattr(self.dec.op, "asmcode"):
+            comb += e_out.asmcode.eq(self.dec.op.asmcode)
+
+        return m
+
+    def trap(self, m, traptype, trapaddr, exc=None):
+        """trap: this basically "rewrites" the decoded instruction as a trap
+        """
+        comb = m.d.comb
+        op, e = self.dec.op, self.e
+        comb += e.eq(0)  # reset eeeeeverything
+
+        # start again
+        comb += self.do_copy("insn", self.dec.opcode_in, True)
+        comb += self.do_copy("insn_type", MicrOp.OP_TRAP, True)
+        comb += self.do_copy("fn_unit", Function.TRAP, True)
+        comb += self.do_copy("trapaddr", trapaddr >> 4, True) # bottom 4 bits
+        comb += self.do_copy("traptype", traptype, True)  # request type
+        comb += self.do_copy("ldst_exc", exc, True)  # request type
+        comb += self.do_copy("msr", self.state.msr, True) # copy of MSR "state"
+        comb += self.do_copy("cia", self.state.pc, True)  # copy of PC "state"
+
+
+
+def get_rdflags(e, cu):
+    rdl = []
+    for idx in range(cu.n_src):
+        regfile, regname, _ = cu.get_in_spec(idx)
+        rdflag, read = regspec_decode_read(e, regfile, regname)
+        rdl.append(rdflag)
+    print("rdflags", rdl)
+    return Cat(*rdl)
+
+
+if __name__ == '__main__':
+    pdecode = create_pdecode()
+    dec2 = PowerDecode2(pdecode)
+    vl = rtlil.convert(dec2, ports=dec2.ports() + pdecode.ports())
+    with open("dec2.il", "w") as f:
+        f.write(vl)
diff --git a/src/openpower/decoder/power_enums.py b/src/openpower/decoder/power_enums.py
new file mode 100644 (file)
index 0000000..3353d20
--- /dev/null
@@ -0,0 +1,486 @@
+# SPDX-License: LGPLv3+
+# Copyright (C) 2020, 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2020, Michael Nolan
+
+"""Enums used in OpenPOWER ISA decoding
+
+Note: for SV, from v3.1B p12:
+
+    The designated SPR sandbox consists of non-privileged SPRs 704-719 and
+    privileged SPRs 720-735.
+
+Note: the option exists to select a much shorter list of SPRs, to reduce
+regfile size in HDL.  this is SPRreduced and the supported list is in
+get_spr_enum
+"""
+
+from enum import Enum, unique
+import csv
+import os
+from os.path import dirname, join
+from collections import namedtuple
+
+
+def find_wiki_dir():
+    filedir = os.path.dirname(os.path.abspath(__file__))
+    basedir = dirname(dirname(dirname(filedir)))
+    tabledir = join(basedir, 'libreriscv')
+    tabledir = join(tabledir, 'openpower')
+    return join(tabledir, 'isatables')
+
+
+def find_wiki_file(name):
+    filedir = os.path.dirname(os.path.abspath(__file__))
+    basedir = dirname(dirname(dirname(filedir)))
+    tabledir = join(basedir, 'libreriscv')
+    tabledir = join(tabledir, 'openpower')
+    tabledir = join(tabledir, 'isatables')
+
+    return join(find_wiki_dir(), name)
+
+
+def get_csv(name):
+    file_path = find_wiki_file(name)
+    with open(file_path, 'r') as csvfile:
+        reader = csv.DictReader(csvfile)
+        return list(reader)
+
+
+# names of the fields in the tables that don't correspond to an enum
+single_bit_flags = ['inv A', 'inv out',
+                    'cry out', 'BR', 'sgn ext', 'rsrv', '32b',
+                    'sgn', 'lk', 'sgl pipe']
+
+# default values for fields in the table
+default_values = {'unit': "NONE", 'internal op': "OP_ILLEGAL",
+                  'in1': "RA", 'in2': 'NONE', 'in3': 'NONE', 'out': 'NONE',
+                  'CR in': 'NONE',
+                  'ldst len': 'NONE',
+                  'upd': '0',
+                  'rc': 'NONE', 'cry in': 'ZERO', 'form': 'NONE'}
+
+
+def get_signal_name(name):
+    if name[0].isdigit():
+        name = "is_" + name
+    return name.lower().replace(' ', '_')
+
+# this corresponds to which Function Unit (pipeline-with-Reservation-Stations)
+# is to process and guard the operation.  they are roughly divided by having
+# the same register input/output signature (X-Form, etc.)
+
+
+@unique
+class Function(Enum):
+    NONE = 0
+    ALU = 1 << 1
+    LDST = 1 << 2
+    SHIFT_ROT = 1 << 3
+    LOGICAL = 1 << 4
+    BRANCH = 1 << 5
+    CR = 1 << 6
+    TRAP = 1 << 7
+    MUL = 1 << 8
+    DIV = 1 << 9
+    SPR = 1 << 10
+    MMU = 1 << 11
+    SV = 1 << 12  # Simple-V https://libre-soc.org/openpower/sv
+    VL = 1 << 13  # setvl
+
+
+@unique
+class Form(Enum):
+    NONE = 0
+    I = 1
+    B = 2
+    SC = 3
+    D = 4
+    DS = 5
+    DQ = 6
+    DX = 7
+    X = 8
+    XL = 9
+    XFX = 10
+    XFL = 11
+    XX1 = 12
+    XX2 = 13
+    XX3 = 14
+    XX4 = 15
+    XS = 16
+    XO = 17
+    A = 18
+    M = 19
+    MD = 20
+    MDS = 21
+    VA = 22
+    VC = 23
+    VX = 24
+    EVX = 25
+    EVS = 26
+    Z22 = 27
+    Z23 = 28
+    SVL = 29  # Simple-V for setvl instruction
+
+# Simple-V svp64 fields https://libre-soc.org/openpower/sv/svp64/
+
+
+@unique
+class SVPtype(Enum):
+    NONE = 0
+    P1 = 1
+    P2 = 2
+
+
+@unique
+class SVEtype(Enum):
+    NONE = 0
+    EXTRA2 = 1
+    EXTRA3 = 2
+
+
+@unique
+class SVEXTRA(Enum):
+    NONE = 0
+    Idx0 = 1
+    Idx1 = 2
+    Idx2 = 3
+    Idx3 = 4
+    Idx_1_2 = 5  # due to weird BA/BB for crops
+
+
+@unique
+class SVP64PredMode(Enum):
+    ALWAYS = 0
+    INT = 1
+    CR = 2
+
+
+@unique
+class SVP64PredInt(Enum):
+    ALWAYS = 0
+    R3_UNARY = 1
+    R3 = 2
+    R3_N = 3
+    R10 = 4
+    R10_N = 5
+    R30 = 6
+    R30_N = 7
+
+
+@unique
+class SVP64PredCR(Enum):
+    LT = 0
+    GE = 1
+    GT = 2
+    LE = 3
+    EQ = 4
+    NE = 5
+    SO = 6
+    NS = 7
+
+
+@unique
+class SVP64RMMode(Enum):
+    NORMAL = 0
+    MAPREDUCE = 1
+    FFIRST = 2
+    SATURATE = 3
+    PREDRES = 4
+
+
+@unique
+class SVP64width(Enum):
+    DEFAULT = 0
+    EW_32 = 1
+    EW_16 = 2
+    EW_8 = 3
+
+
+@unique
+class SVP64subvl(Enum):
+    VEC1 = 0
+    VEC2 = 1
+    VEC3 = 2
+    VEC4 = 3
+
+
+@unique
+class SVP64sat(Enum):
+    NONE = 0
+    SIGNED = 1
+    UNSIGNED = 2
+
+
+# supported instructions: make sure to keep up-to-date with CSV files
+# just like everything else
+_insns = [
+    "NONE", "add", "addc", "addco", "adde", "addeo", "addi", "addic", "addic.",
+    "addis", "addme", "addmeo", "addo", "addze", "addzeo", "and", "andc",
+    "andi.", "andis.", "attn", "b", "bc", "bcctr", "bclr", "bctar",
+    "bpermd", "cmp", "cmpb", "cmpeqb", "cmpi", "cmpl", "cmpli", "cmprb",
+    "cntlzd", "cntlzw", "cnttzd", "cnttzw", "crand", "crandc", "creqv",
+    "crnand", "crnor", "cror", "crorc", "crxor", "darn", "dcbf", "dcbst",
+    "dcbt", "dcbtst", "dcbz", "divd", "divde", "divdeo", "divdeu",
+    "divdeuo", "divdo", "divdu", "divduo", "divw", "divwe", "divweo",
+    "divweu", "divweuo", "divwo", "divwu", "divwuo", "eqv", "extsb",
+    "extsh", "extsw", "extswsli", "hrfid", "icbi", "icbt", "isel", "isync",
+    "lbarx", "lbz", "lbzu", "lbzux", "lbzx", "ld", "ldarx", "ldbrx",
+    "ldu", "ldux", "ldx", "lha", "lharx", "lhau", "lhaux", "lhax",
+    "lhbrx", "lhz", "lhzu", "lhzux", "lhzx", "lwa", "lwarx", "lwaux",
+    "lwax", "lwbrx", "lwz", "lwzcix", "lwzu", "lwzux", "lwzx", "mcrf", "mcrxr",
+    "mcrxrx", "mfcr/mfocrf", "mfmsr", "mfspr", "modsd", "modsw", "modud",
+    "moduw", "mtcrf/mtocrf", "mtmsr", "mtmsrd", "mtspr", "mulhd", "mulhdu",
+    "mulhw", "mulhwu", "mulld", "mulldo", "mulli", "mullw", "mullwo",
+    "nand", "neg", "nego", "nop", "nor", "or", "orc", "ori", "oris",
+    "popcntb", "popcntd", "popcntw", "prtyd", "prtyw", "rfid", "rldcl",
+    "rldcr", "rldic", "rldicl", "rldicr", "rldimi", "rlwimi", "rlwinm",
+    "rlwnm", "setb",
+    "setvl",  # https://libre-soc.org/openpower/sv/setvl
+    "sim_cfg", "slbia", "sld", "slw", "srad", "sradi", "sraw",
+    "srawi", "srd", "srw", "stb", "stbcix", "stbcx", "stbu", "stbux", "stbx",
+    "std", "stdbrx", "stdcx", "stdu", "stdux", "stdx", "sth", "sthbrx", "sthcx",
+    "sthu", "sthux", "sthx", "stw", "stwbrx", "stwcx", "stwu", "stwux",
+    "stwx", "subf", "subfc", "subfco", "subfe", "subfeo", "subfic",
+    "subfme", "subfmeo", "subfo", "subfze", "subfzeo", "sync", "td",
+    "tdi", "tlbie", "tlbiel", "tw", "twi", "xor", "xori", "xoris",
+]
+
+# two-way lookup of instruction-to-index and vice-versa
+insns = {}
+asmidx = {}
+for i, insn in enumerate(_insns):
+    insns[i] = insn
+    asmidx[insn] = i
+
+# Internal Operation numbering.  Add new opcodes here (FPADD, FPMUL etc.)
+
+
+@unique
+class MicrOp(Enum):
+    OP_ILLEGAL = 0     # important that this is zero (see power_decoder.py)
+    OP_NOP = 1
+    OP_ADD = 2
+    OP_ADDPCIS = 3
+    OP_AND = 4
+    OP_ATTN = 5
+    OP_B = 6
+    OP_BC = 7
+    OP_BCREG = 8
+    OP_BPERM = 9
+    OP_CMP = 10
+    OP_CMPB = 11
+    OP_CMPEQB = 12
+    OP_CMPRB = 13
+    OP_CNTZ = 14
+    OP_CRAND = 15
+    OP_CRANDC = 16
+    OP_CREQV = 17
+    OP_CRNAND = 18
+    OP_CRNOR = 19
+    OP_CROR = 20
+    OP_CRORC = 21
+    OP_CRXOR = 22
+    OP_DARN = 23
+    OP_DCBF = 24
+    OP_DCBST = 25
+    OP_DCBT = 26
+    OP_DCBTST = 27
+    OP_DCBZ = 28
+    OP_DIV = 29
+    OP_DIVE = 30
+    OP_EXTS = 31
+    OP_EXTSWSLI = 32
+    OP_ICBI = 33
+    OP_ICBT = 34
+    OP_ISEL = 35
+    OP_ISYNC = 36
+    OP_LOAD = 37
+    OP_STORE = 38
+    OP_MADDHD = 39
+    OP_MADDHDU = 40
+    OP_MADDLD = 41
+    OP_MCRF = 42
+    OP_MCRXR = 43
+    OP_MCRXRX = 44
+    OP_MFCR = 45
+    OP_MFSPR = 46
+    OP_MOD = 47
+    OP_MTCRF = 48
+    OP_MTSPR = 49
+    OP_MUL_L64 = 50
+    OP_MUL_H64 = 51
+    OP_MUL_H32 = 52
+    OP_OR = 53
+    OP_POPCNT = 54
+    OP_PRTY = 55
+    OP_RLC = 56
+    OP_RLCL = 57
+    OP_RLCR = 58
+    OP_SETB = 59
+    OP_SHL = 60
+    OP_SHR = 61
+    OP_SYNC = 62
+    OP_TRAP = 63
+    OP_XOR = 67
+    OP_SIM_CONFIG = 68
+    OP_CROP = 69
+    OP_RFID = 70
+    OP_MFMSR = 71
+    OP_MTMSRD = 72
+    OP_SC = 73
+    OP_MTMSR = 74
+    OP_TLBIE = 75
+    OP_SETVL = 76
+
+
+@unique
+class In1Sel(Enum):
+    NONE = 0
+    RA = 1
+    RA_OR_ZERO = 2
+    SPR = 3
+    RS = 4  # for some ALU/Logical operations
+
+
+@unique
+class In2Sel(Enum):
+    NONE = 0
+    RB = 1
+    CONST_UI = 2
+    CONST_SI = 3
+    CONST_UI_HI = 4
+    CONST_SI_HI = 5
+    CONST_LI = 6
+    CONST_BD = 7
+    CONST_DS = 8
+    CONST_M1 = 9
+    CONST_SH = 10
+    CONST_SH32 = 11
+    SPR = 12
+    RS = 13  # for shiftrot (M-Form)
+
+
+@unique
+class In3Sel(Enum):
+    NONE = 0
+    RS = 1
+    RB = 2  # for shiftrot (M-Form)
+
+
+@unique
+class OutSel(Enum):
+    NONE = 0
+    RT = 1
+    RA = 2
+    SPR = 3
+    RT_OR_ZERO = 4
+
+
+@unique
+class LdstLen(Enum):
+    NONE = 0
+    is1B = 1
+    is2B = 2
+    is4B = 4
+    is8B = 8
+
+
+@unique
+class LDSTMode(Enum):
+    NONE = 0
+    update = 1
+    cix = 2
+    cx = 3
+
+
+@unique
+class RC(Enum):
+    NONE = 0
+    ONE = 1
+    RC = 2
+
+
+@unique
+class CryIn(Enum):
+    ZERO = 0
+    ONE = 1
+    CA = 2
+    # TODO OV = 3
+
+
+@unique
+class CRInSel(Enum):
+    NONE = 0
+    CR0 = 1
+    BI = 2
+    BFA = 3
+    BA_BB = 4
+    BC = 5
+    WHOLE_REG = 6
+    CR1 = 7
+
+
+@unique
+class CROutSel(Enum):
+    NONE = 0
+    CR0 = 1
+    BF = 2
+    BT = 3
+    WHOLE_REG = 4
+    CR1 = 5
+
+
+# SPRs - Special-Purpose Registers.  See V3.0B Figure 18 p971 and
+# http://libre-riscv.org/openpower/isatables/sprs.csv
+# http://bugs.libre-riscv.org/show_bug.cgi?id=261
+
+def get_spr_enum(full_file):
+    """get_spr_enum - creates an Enum of SPRs, dynamically
+    has the option to reduce the enum to a much shorter list.
+    this saves drastically on the size of the regfile
+    """
+    short_list = {'PIDR', 'DAR', 'PRTBL', 'DSISR', 'SVSRR0', 'SVSTATE',
+                  'SPRG0_priv', 'SPRG1_priv', 'SPRG2_priv', 'SPRG3_priv',
+                  'SPRG3'
+                 }
+    spr_csv = []
+    for row in get_csv("sprs.csv"):
+        if full_file or row['SPR'] in short_list:
+            spr_csv.append(row)
+
+    spr_info = namedtuple('spr_info', 'SPR priv_mtspr priv_mfspr length idx')
+    spr_dict = {}
+    spr_byname = {}
+    for row in spr_csv:
+        info = spr_info(SPR=row['SPR'], priv_mtspr=row['priv_mtspr'],
+                        priv_mfspr=row['priv_mfspr'], length=int(row['len']),
+                        idx=int(row['Idx']))
+        spr_dict[int(row['Idx'])] = info
+        spr_byname[row['SPR']] = info
+    fields = [(row['SPR'], int(row['Idx'])) for row in spr_csv]
+    SPR = Enum('SPR', fields)
+    return SPR, spr_dict, spr_byname
+
+SPRfull, spr_dict, spr_byname = get_spr_enum(full_file=True)
+SPRreduced, _, _ = get_spr_enum(full_file=False)
+
+XER_bits = {
+    'SO': 32,
+    'OV': 33,
+    'CA': 34,
+    'OV32': 44,
+    'CA32': 45
+}
+
+if __name__ == '__main__':
+    # find out what the heck is in SPR enum :)
+    print("sprs full", len(SPRfull))
+    print(dir(SPRfull))
+    print("sprs reduced", len(SPRreduced))
+    print(dir(SPRreduced))
+    print(dir(Enum))
+    print(SPRfull.__members__['TAR'])
+    for x in SPRfull:
+        print(x, x.value, str(x), x.name)
+
+    print("function", Function.ALU.name)
diff --git a/src/openpower/decoder/power_fields.py b/src/openpower/decoder/power_fields.py
new file mode 100644 (file)
index 0000000..d0fe85c
--- /dev/null
@@ -0,0 +1,255 @@
+from collections import OrderedDict, namedtuple
+from soc.decoder.power_enums import find_wiki_file
+
+
+class BitRange(OrderedDict):
+    """BitRange: remaps from straight indices (0,1,2..) to bit numbers
+    """
+
+    def __getitem__(self, subscript):
+        if isinstance(subscript, slice):
+            return list(self.values())[subscript]
+        else:
+            return OrderedDict.__getitem__(self, subscript)
+
+
+def decode_instructions(form):
+    res = {}
+    accum = []
+    for l in form:
+        if l.strip().startswith("Formats"):
+            l = l.strip().split(":")[-1]
+            l = l.replace(" ", "")
+            l = l.split(",")
+            for fmt in l:
+                if fmt not in res:
+                    res[fmt] = [accum[0]]
+                else:
+                    res[fmt].append(accum[0])
+            accum = []
+        else:
+            accum.append(l.strip())
+    return res
+
+
+def decode_form_header(hdr):
+    res = {}
+    count = 0
+    hdr = hdr.strip()
+    for f in hdr.split("|"):
+        if not f:
+            continue
+        if f[0].isdigit():
+            idx = int(f.strip().split(' ')[0])
+            res[count] = idx
+        count += len(f) + 1
+    return res
+
+
+def find_unique(d, key):
+    if key not in d:
+        return key
+    idx = 1
+    while "%s_%d" % (key, idx) in d:
+        idx += 1
+    return "%s_%d" % (key, idx)
+
+
+def decode_line(header, line):
+    line = line.strip()
+    res = {}
+    count = 0
+    prev_fieldname = None
+    for f in line.split("|"):
+        if not f:
+            continue
+        end = count + len(f) + 1
+        fieldname = f.strip()
+        if not fieldname or fieldname.startswith('/'):
+            if prev_fieldname is not None:
+                res[prev_fieldname] = (res[prev_fieldname], header[count])
+                prev_fieldname = None
+            count = end
+            continue
+        bitstart = header[count]
+        if prev_fieldname is not None:
+            res[prev_fieldname] = (res[prev_fieldname], bitstart)
+        res[fieldname] = bitstart
+        count = end
+        prev_fieldname = fieldname
+    res[prev_fieldname] = (bitstart, 32)
+    return res
+
+
+def decode_form(form):
+    header = decode_form_header(form[0])
+    res = []
+    for line in form[1:]:
+        dec = decode_line(header, line)
+        if dec:
+            res.append(dec)
+    fields = {}
+    falternate = {}
+    for l in res:
+        for k, (start, end) in l.items():
+            if k in fields:
+                if (start, end) == fields[k]:
+                    continue  # already in and matching for this Form
+                if k in falternate:
+                    alternate = "%s_%d" % (k, falternate[k])
+                    if (start, end) == fields[alternate]:
+                        continue
+                falternate[k] = fidx = falternate.get(k, 0) + 1
+                fields["%s_%d" % (k, fidx)] = (start, end)
+            else:
+                fields[k] = (start, end)
+    return fields
+
+
+class DecodeFields:
+
+    def __init__(self, bitkls=BitRange, bitargs=(), fname=None,
+                 name_on_wiki=None):
+        self.bitkls = bitkls
+        self.bitargs = bitargs
+        if fname is None:
+            assert name_on_wiki is None
+            fname = "fields.txt"
+            name_on_wiki = "fields.text"
+        self.fname = find_wiki_file(name_on_wiki)
+
+    @property
+    def form_names(self):
+        return self.instrs.keys()
+
+    def create_specs(self):
+        self.forms, self.instrs = self.decode_fields()
+        forms = self.form_names
+        #print ("specs", self.forms, forms)
+        for form in forms:
+            fields = self.instrs[form]
+            fk = fields.keys()
+            Fields = namedtuple("Fields", fk)
+            instr = Fields(**fields)
+            setattr(self, "Form%s" % form, instr)
+        # now add in some commonly-used fields (should be done automatically)
+        # note that these should only be ones which are the same on all Forms
+        # note: these are from microwatt insn_helpers.vhdl
+        self.common_fields = {
+            "PO": self.Formall.PO,
+            "RS": self.FormX.RS,
+            "RT": self.FormX.RT,
+            "RA": self.FormX.RA,
+            "RB": self.FormX.RB,
+            "SI": self.FormD.SI,
+            "UI": self.FormD.UI,
+            "L": self.FormD.L,
+            "SH32": self.FormM.SH,
+            "sh": self.FormMD.sh,
+            "MB32": self.FormM.MB,
+            "ME32": self.FormM.ME,
+            "LI": self.FormI.LI,
+            "LK": self.FormI.LK,
+            "AA": self.FormB.AA,
+            "Rc": self.FormX.Rc,
+            "OE": self.FormXO.OE,
+            "BD": self.FormB.BD,
+            "BF": self.FormX.BF,
+            "CR": self.FormXL.XO,
+            "BB": self.FormXL.BB,
+            "BA": self.FormXL.BA,
+            "BT": self.FormXL.BT,
+            "FXM": self.FormXFX.FXM,
+            "BO": self.FormXL.BO,
+            "BI": self.FormXL.BI,
+            "BH": self.FormXL.BH,
+            "D": self.FormD.D,
+            "DS": self.FormDS.DS,
+            "TO": self.FormX.TO,
+            "BC": self.FormA.BC,
+            "SH": self.FormX.SH,
+            "ME": self.FormM.ME,
+            "MB": self.FormM.MB,
+            "SPR": self.FormXFX.SPR}
+        for k, v in self.common_fields.items():
+            setattr(self, k, v)
+
+    def decode_fields(self):
+        with open(self.fname) as f:
+            txt = f.readlines()
+        #print ("decode", txt)
+        forms = {}
+        reading_data = False
+        for l in txt:
+            l = l.strip()
+            if len(l) == 0:
+                continue
+            if reading_data:
+                if l[0] == '#':
+                    reading_data = False
+                else:
+                    forms[heading].append(l)
+            if not reading_data:
+                assert l[0] == '#'
+                heading = l[1:].strip()
+                # if heading.startswith('1.6.28'): # skip instr fields for now
+                #     break
+                heading = heading.split(' ')[-1]
+                reading_data = True
+                forms[heading] = []
+
+        res = {}
+        inst = {}
+
+        for hdr, form in forms.items():
+            if heading == 'Fields':
+                i = decode_instructions(form)
+                for form, field in i.items():
+                    inst[form] = self.decode_instruction_fields(field)
+            # else:
+            #     res[hdr] = decode_form(form)
+        return res, inst
+
+    def decode_instruction_fields(self, fields):
+        res = {}
+        for field in fields:
+            f, spec = field.strip().split(" ")
+            ss = spec[1:-1].split(",")
+            fs = f.split(",")
+            if len(fs) > 1:
+                individualfields = []
+                for f0, s0 in zip(fs, ss):
+                    txt = "%s (%s)" % (f0, s0)
+                    individualfields.append(txt)
+                if len(fs) > 1:
+                    res.update(self.decode_instruction_fields(
+                        individualfields))
+            d = self.bitkls(*self.bitargs)
+            idx = 0
+            for s in ss:
+                s = s.split(':')
+                if len(s) == 1:
+                    d[idx] = int(s[0])
+                    idx += 1
+                else:
+                    start = int(s[0])
+                    end = int(s[1])
+                    while start <= end:
+                        d[idx] = start
+                        idx += 1
+                        start += 1
+            f = f.replace(",", "_")
+            unique = find_unique(res, f)
+            res[unique] = d
+
+        return res
+
+
+if __name__ == '__main__':
+    dec = DecodeFields()
+    dec.create_specs()
+    forms, instrs = dec.forms, dec.instrs
+    for form, fields in instrs.items():
+        print("Form", form)
+        for field, bits in fields.items():
+            print("\tfield", field, bits)
diff --git a/src/openpower/decoder/power_fieldsn.py b/src/openpower/decoder/power_fieldsn.py
new file mode 100644 (file)
index 0000000..852dd15
--- /dev/null
@@ -0,0 +1,79 @@
+from collections import OrderedDict
+from soc.decoder.power_fields import DecodeFields, BitRange
+from nmigen import Module, Elaboratable, Signal, Cat
+from nmigen.cli import rtlil
+from copy import deepcopy
+
+
+class SignalBitRange(BitRange):
+    def __init__(self, signal):
+        BitRange.__init__(self)
+        self.signal = signal
+
+    def __deepcopy__(self, memo):
+        signal = deepcopy(self.signal, memo)
+        retval = SignalBitRange(signal=signal)
+        for k, v in self.items():
+            k = deepcopy(k, memo)
+            v = deepcopy(v, memo)
+            retval[k] = v
+        return retval
+
+    def _rev(self, k):
+        width = self.signal.width
+        return width-1-k
+
+    def __getitem__(self, subs):
+        # *sigh* field numberings are bit-inverted.  PowerISA 3.0B section 1.3.2
+        if isinstance(subs, slice):
+            res = []
+            start, stop, step = subs.start, subs.stop, subs.step
+            if step is None:
+                step = 1
+            if start is None:
+                start = 0
+            if stop is None:
+                stop = -1
+            if start < 0:
+                start = len(self) + start + 1
+            if stop < 0:
+                stop = len(self) + stop + 1
+            for t in range(start, stop, step):
+                t = len(self) - 1 - t  # invert field back
+                k = OrderedDict.__getitem__(self, t)
+                res.append(self.signal[self._rev(k)])  # reverse-order here
+            return Cat(*res)
+        else:
+            if subs < 0:
+                subs = len(self) + subs
+            subs = len(self) - 1 - subs  # invert field back
+            k = OrderedDict.__getitem__(self, subs)
+            return self.signal[self._rev(k)]  # reverse-order here
+
+
+class SigDecode(Elaboratable):
+
+    def __init__(self, width):
+        self.opcode_in = Signal(width, reset_less=False)
+        self.df = DecodeFields(SignalBitRange, [self.opcode_in])
+        self.df.create_specs()
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        return m
+
+    def ports(self):
+        return [self.opcode_in]
+
+
+def create_sigdecode():
+    s = SigDecode(32)
+    return s
+
+
+if __name__ == '__main__':
+    sigdecode = create_sigdecode()
+    vl = rtlil.convert(sigdecode, ports=sigdecode.ports())
+    with open("decoder.il", "w") as f:
+        f.write(vl)
diff --git a/src/openpower/decoder/power_pseudo.py b/src/openpower/decoder/power_pseudo.py
new file mode 100644 (file)
index 0000000..3e02cb7
--- /dev/null
@@ -0,0 +1,350 @@
+# 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
+import sys
+from pprint import pprint
+from copy import copy
+from ply import lex, yacc
+import astor
+import ast
+
+from soc.decoder.power_decoder import create_pdecode
+from nmigen.back.pysim import Simulator, Delay
+from nmigen import Module, Signal
+
+from soc.decoder.pseudo.parser import GardenSnakeCompiler
+from soc.decoder.selectable_int import SelectableInt, selectconcat
+from soc.decoder.isa.caller import GPR, Mem
+
+
+####### Test code #######
+
+bpermd = r"""
+perm <- [0] * 8
+if index < 64:
+    index <- (RS)[8*i:8*i+7]
+RA <- [0]*56 || perm[0:7]
+print (RA)
+"""
+
+bpermd = r"""
+if index < 64 then index <- 0
+else index <- 5
+do while index < 5
+    index <- 0
+    leave
+for i = 0 to 7
+    index <- 0
+"""
+
+_bpermd = r"""
+for i = 0 to 7
+   index <- (RS)[8*i:8*i+7]
+   if index < 64 then
+        permi <- (RB)[index]
+   else
+        permi <- 0
+RA <- [0]*56|| perm[0:7]
+"""
+
+cnttzd = """
+n  <- 0
+do while n < 64
+    print (n)
+    if (RS)[63-n] = 0b1 then
+        leave
+    n  <- n + 1
+RA <- EXTZ64(n)
+print (RA)
+"""
+
+cmpi = """
+if      a < EXTS(SI) then
+    c <- 0b100
+else if a > EXTS(SI) then
+    c <- 0b010
+"""
+
+cmpi = """
+RA[0:1] <- 0b11
+"""
+
+cmpi = """
+in_range <-  ((x | y) &
+              (a | b))
+in_range <-  (x + y) - (a + b)
+"""
+
+cmpi = """
+(RA)[0:1] <- 1
+src1    <- EXTZ((RA)[56:63])
+CR[4*BF+32] <- 0b0
+in_range <- src21lo  <= src1 & src1 <=  src21hi 
+"""
+
+cmpeqb = """
+src1 <- GPR[RA]
+src1 <- src1[0:56]
+"""
+
+addpcis = """
+D <- d0||d1||d2
+"""
+
+testmul = """
+x <- [0] * 16
+RT <- (RA) + EXTS(SI || [0]*16)
+"""
+
+testgetzero = """
+RS <- (RA|0)
+RS <- RS + 1
+print(RS)
+"""
+
+testcat = """
+RT <- (load_data[56:63] || load_data[48:55]
+    || load_data[40:47] || load_data[32:39]
+    || load_data[24:31] || load_data[16:23]
+    || load_data[8:15]  || load_data[0:7])
+"""
+
+testgpr = """
+GPR(5) <- x
+"""
+testmem = """
+a <- (RA|0)
+b <- (RB|0)
+RA <- MEM(RB, 2)
+EA <- a + 1
+MEM(EA, 1) <- (RS)[56:63]
+RB <- RA
+RA <- EA
+"""
+
+testgprslice = """
+MEM(EA, 4) <- GPR(r)[32:63]
+#x <- x[0][32:63]
+"""
+
+testdo = r"""
+do i = 0 to 7
+    print(i)
+"""
+
+testcond = """
+ctr_ok <- BO[2] | ((CTR[M:63] != 0) ^ BO[3])
+cond_ok <- BO[0] | Â¬(CR[BI+32] ^  BO[1])
+"""
+
+lswx = """
+if RA = 0 then EA <- 0
+else           EA <- (RA)
+if NB = 0 then n <-  32
+else           n <-  NB
+r <- RT - 1
+i <- 32
+do while n > 0
+    if i = 32 then
+        r <- (r + 1) % 32
+        GPR(r) <- 0
+    GPR(r)[i:i+7] <- MEM(EA, 1)
+    i <- i + 8
+    if i = 64 then i <- 32
+    EA <- EA + 1
+    n <- n - 1
+"""
+
+_lswx = """
+GPR(r)[x] <- 1
+"""
+
+switchtest = """
+switch (n)
+    case(1): x <- 5
+    case(2): fallthrough
+    case(3):
+        x <- 3
+    case(4): fallthrough
+    default:
+        x <- 9
+"""
+
+hextest = """
+RT <- 0x0001_a000_0000_0000
+"""
+
+code = hextest
+#code = lswx
+#code = testcond
+#code = testdo
+#code = _bpermd
+#code = testmul
+#code = testgetzero
+#code = testcat
+#code = testgpr
+#code = testmem
+#code = testgprslice
+#code = testreg
+#code = cnttzd
+#code = cmpi
+#code = cmpeqb
+#code = addpcis
+#code = bpermd
+
+
+def tolist(num):
+    l = []
+    for i in range(64):
+        l.append(1 if (num & (1 << i)) else 0)
+    l.reverse()
+    return l
+
+
+def get_reg_hex(reg):
+    return hex(reg.value)
+
+
+def convert_to_python(pcode, form, incl_carry):
+
+    print("form", form)
+    gsc = GardenSnakeCompiler(form=form, incl_carry=incl_carry)
+
+    tree = gsc.compile(pcode, mode="exec", filename="string")
+    tree = ast.fix_missing_locations(tree)
+    regsused = {'read_regs': gsc.parser.read_regs,
+                'write_regs': gsc.parser.write_regs,
+                'uninit_regs': gsc.parser.uninit_regs,
+                'special_regs': gsc.parser.special_regs,
+                'op_fields': gsc.parser.op_fields}
+    return astor.to_source(tree), regsused
+
+
+def test():
+
+    gsc = GardenSnakeCompiler(debug=True)
+
+    gsc.regfile = {}
+    for i in range(32):
+        gsc.regfile[i] = i
+    gsc.gpr = GPR(gsc.parser.sd, gsc.regfile)
+    gsc.mem = Mem()
+
+    _compile = gsc.compile
+
+    tree = _compile(code, mode="single", filename="string")
+    tree = ast.fix_missing_locations(tree)
+    print(ast.dump(tree))
+
+    print("astor dump")
+    print(astor.dump_tree(tree))
+    print("to source")
+    source = astor.to_source(tree)
+    print(source)
+
+    # sys.exit(0)
+
+    # Set up the GardenSnake run-time environment
+    def print_(*args):
+        print("args", args)
+        print("-->", " ".join(map(str, args)))
+
+    from soc.decoder.helpers import (EXTS64, EXTZ64, ROTL64, ROTL32, MASK,
+                                     trunc_div, trunc_rem)
+
+    d = {}
+    d["print"] = print_
+    d["EXTS64"] = EXTS64
+    d["EXTZ64"] = EXTZ64
+    d["trunc_div"] = trunc_div
+    d["trunc_rem"] = trunc_rem
+    d["SelectableInt"] = SelectableInt
+    d["concat"] = selectconcat
+    d["GPR"] = gsc.gpr
+    d["MEM"] = gsc.mem
+    d["memassign"] = gsc.mem.memassign
+
+    form = 'X'
+    gsc.gpr.set_form(form)
+    getform = gsc.parser.sd.sigforms[form]._asdict()
+    #print ("getform", form)
+    # for k, f in getform.items():
+    #print (k, f)
+    #d[k] = getform[k]
+
+    compiled_code = compile(source, mode="exec", filename="<string>")
+
+    m = Module()
+    comb = m.d.comb
+    instruction = Signal(32)
+
+    m.submodules.decode = decode = gsc.parser.sd
+    comb += decode.raw_opcode_in.eq(instruction)
+    sim = Simulator(m)
+
+    instr = [0x11111117]
+
+    def process():
+        for ins in instr:
+            print("0x{:X}".format(ins & 0xffffffff))
+
+            # ask the decoder to decode this binary data (endian'd)
+            yield decode.bigendian.eq(0)  # little / big?
+            yield instruction.eq(ins)          # raw binary instr.
+            yield Delay(1e-6)
+
+            # uninitialised regs, drop them into dict for function
+            for rname in gsc.parser.uninit_regs:
+                d[rname] = SelectableInt(0, 64)  # uninitialised (to zero)
+                print("uninitialised", rname, hex(d[rname].value))
+
+            # read regs, drop them into dict for function
+            for rname in gsc.parser.read_regs:
+                regidx = yield getattr(decode.sigforms['X'], rname)
+                d[rname] = gsc.gpr[regidx]  # contents of regfile
+                d["_%s" % rname] = regidx  # actual register value
+                print("read reg", rname, regidx, hex(d[rname].value))
+
+            exec(compiled_code, d)  # code gets executed here in dict "d"
+            print("Done")
+
+            print(d.keys())  # shows the variables that may have been created
+
+            print(decode.sigforms['X'])
+            x = yield decode.sigforms['X'].RS
+            ra = yield decode.sigforms['X'].RA
+            rb = yield decode.sigforms['X'].RB
+            print("RA", ra, d['RA'])
+            print("RB", rb, d['RB'])
+            print("RS", x)
+
+            for wname in gsc.parser.write_regs:
+                reg = getform[wname]
+                regidx = yield reg
+                print("write regs", regidx, wname, d[wname], reg)
+                gsc.gpr[regidx] = d[wname]
+
+    sim.add_process(process)
+    with sim.write_vcd("simulator.vcd", "simulator.gtkw",
+                       traces=decode.ports()):
+        sim.run()
+
+    gsc.gpr.dump()
+
+    for i in range(0, len(gsc.mem.mem), 16):
+        hexstr = []
+        for j in range(16):
+            hexstr.append("%02x" % gsc.mem.mem[i+j])
+        hexstr = ' '.join(hexstr)
+        print("mem %4x" % i, hexstr)
+
+
+if __name__ == '__main__':
+    test()
diff --git a/src/openpower/decoder/power_regspec_map.py b/src/openpower/decoder/power_regspec_map.py
new file mode 100644 (file)
index 0000000..05ff481
--- /dev/null
@@ -0,0 +1,184 @@
+"""regspec_decode
+
+functions for the relationship between regspecs and Decode2Execute1Type
+
+these functions encodes the understanding (relationship) between
+Regfiles, Computation Units, and the Power ISA Decoder (PowerDecoder2).
+
+based on the regspec, which contains the register file name and register
+name, return a tuple of:
+
+* how the decoder should determine whether the Function Unit needs
+  access to a given Regport or not
+* which Regfile number on that port should be read to get that data
+* when it comes to writing: likewise, which Regfile num should be written
+
+Note that some of the port numbering encoding is *unary*.  in the case
+of "Full Condition Register", it's a full 8-bit mask of read/write-enables.
+This actually matches directly with the XFX field in MTCR, and at
+some point that 8-bit mask from the instruction could actually be passed
+directly through to full_cr (TODO).
+
+For the INT and CR numbering, these are expressed in binary in the
+instruction and need to be converted to unary (1<<read_reg1.data).
+Note however that XFX in MTCR is unary-masked!
+
+XER regs are implicitly-encoded (hard-coded) based on whether the
+operation has carry or overflow.
+
+FAST regfile is, again, implicitly encoded, back in PowerDecode2, based
+on the type of operation (see DecodeB for an example, where fast_out
+is set, then carried into read_fast2 in PowerDecode2).
+
+The SPR regfile on the other hand is *binary*-encoded, and, furthermore,
+has to be "remapped" to internal SPR Enum indices (see SPRMap in PowerDecode2)
+see https://libre-soc.org/3d_gpu/architecture/regfile/ section on regspecs
+"""
+from nmigen import Const
+from soc.regfile.regfiles import XERRegs, FastRegs, StateRegs
+from soc.decoder.power_enums import CryIn
+
+
+def regspec_decode_read(e, regfile, name):
+    """regspec_decode_read
+    """
+
+    # INT regfile
+
+    if regfile == 'INT':
+        # Int register numbering is *unary* encoded
+        if name == 'ra': # RA
+            return e.read_reg1.ok, e.read_reg1.data
+        if name == 'rb': # RB
+            return e.read_reg2.ok, e.read_reg2.data
+        if name == 'rc': # RS
+            return e.read_reg3.ok, e.read_reg3.data
+
+    # CR regfile
+
+    if regfile == 'CR':
+        # CRRegs register numbering is *unary* encoded
+        if name == 'full_cr': # full CR (from FXM field)
+            return e.do.read_cr_whole.ok, e.do.read_cr_whole.data
+        if name == 'cr_a': # CR A
+            return e.read_cr1.ok, 1<<(7-e.read_cr1.data)
+        if name == 'cr_b': # CR B
+            return e.read_cr2.ok, 1<<(7-e.read_cr2.data)
+        if name == 'cr_c': # CR C
+            return e.read_cr3.ok, 1<<(7-e.read_cr3.data)
+
+    # XER regfile
+
+    if regfile == 'XER':
+        # XERRegs register numbering is *unary* encoded
+        SO = 1<<XERRegs.SO
+        CA = 1<<XERRegs.CA
+        OV = 1<<XERRegs.OV
+        if name == 'xer_so':
+            # SO needs to be read for overflow *and* for creation
+            # of CR0 and also for MFSPR
+            return ((e.do.oe.oe[0] & e.do.oe.ok) | (e.xer_in & SO == SO)|
+                     (e.do.rc.rc & e.do.rc.ok)), SO
+        if name == 'xer_ov':
+            return ((e.do.oe.oe[0] & e.do.oe.ok) |
+                    (e.xer_in & CA == CA)), OV
+        if name == 'xer_ca':
+            return ((e.do.input_carry == CryIn.CA.value) |
+                    (e.xer_in & OV == OV)), CA
+
+    # STATE regfile
+
+    if regfile == 'STATE':
+        # STATE register numbering is *unary* encoded
+        PC = 1<<StateRegs.PC
+        MSR = 1<<StateRegs.MSR
+        if name in ['cia', 'nia']:
+            return Const(1), PC # TODO: detect read-conditions
+        if name == 'msr':
+            return Const(1), MSR # TODO: detect read-conditions
+
+    # FAST regfile
+
+    if regfile == 'FAST':
+        # FAST register numbering is *unary* encoded
+        if name == 'fast1':
+            return e.read_fast1.ok, e.read_fast1.data
+        if name == 'fast2':
+            return e.read_fast2.ok, e.read_fast2.data
+
+    # SPR regfile
+
+    if regfile == 'SPR':
+        # SPR register numbering is *binary* encoded
+        if name == 'spr1':
+            return e.read_spr1.ok, e.read_spr1.data
+
+    assert False, "regspec not found %s %s" % (regfile, name)
+
+
+def regspec_decode_write(e, regfile, name):
+    """regspec_decode_write
+    """
+
+    # INT regfile
+
+    if regfile == 'INT':
+        # Int register numbering is *unary* encoded
+        if name == 'o': # RT
+            return e.write_reg, e.write_reg.data
+        if name == 'o1': # RA (update mode: LD/ST EA)
+            return e.write_ea, e.write_ea.data
+
+    # CR regfile
+
+    if regfile == 'CR':
+        # CRRegs register numbering is *unary* encoded
+        # *sigh*.  numbering inverted on part-CRs.  because POWER.
+        if name == 'full_cr': # full CR (from FXM field)
+            return e.do.write_cr_whole.ok, e.do.write_cr_whole.data
+        if name == 'cr_a': # CR A
+            return e.write_cr, 1<<(7-e.write_cr.data)
+
+    # XER regfile
+
+    if regfile == 'XER':
+        # XERRegs register numbering is *unary* encoded
+        SO = 1<<XERRegs.SO
+        CA = 1<<XERRegs.CA
+        OV = 1<<XERRegs.OV
+        if name == 'xer_so':
+            return e.xer_out, SO # hmmm
+        if name == 'xer_ov':
+            return e.xer_out, OV # hmmm
+        if name == 'xer_ca':
+            return e.xer_out, CA # hmmm
+
+    # STATE regfile
+
+    if regfile == 'STATE':
+        # STATE register numbering is *unary* encoded
+        PC = 1<<StateRegs.PC
+        MSR = 1<<StateRegs.MSR
+        if name in ['cia', 'nia']:
+            return None, PC # hmmm
+        if name == 'msr':
+            return None, MSR # hmmm
+
+    # FAST regfile
+
+    if regfile == 'FAST':
+        # FAST register numbering is *unary* encoded
+        if name == 'fast1':
+            return e.write_fast1, e.write_fast1.data
+        if name == 'fast2':
+            return e.write_fast2, e.write_fast2.data
+
+    # SPR regfile
+
+    if regfile == 'SPR':
+        # SPR register numbering is *binary* encoded
+        if name == 'spr1': # SPR1
+            return e.write_spr, e.write_spr.data
+
+    assert False, "regspec not found %s %s" % (regfile, name)
+
diff --git a/src/openpower/decoder/power_svp64.py b/src/openpower/decoder/power_svp64.py
new file mode 100644 (file)
index 0000000..167053c
--- /dev/null
@@ -0,0 +1,176 @@
+# SPDX-License-Identifier: LGPLv3+
+# Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Funded by NLnet http://nlnet.nl
+
+from soc.decoder.power_enums import get_csv, find_wiki_dir
+import os
+
+# identifies register by type
+def is_CR_3bit(regname):
+    return regname in ['BF', 'BFA']
+
+def is_CR_5bit(regname):
+    return regname in ['BA', 'BB', 'BC', 'BI', 'BT']
+
+def is_GPR(regname):
+    return regname in ['RA', 'RB', 'RC', 'RS', 'RT']
+
+def get_regtype(regname):
+    if is_CR_3bit(regname):
+        return "CR_3bit"
+    if is_CR_5bit(regname):
+        return "CR_5bit"
+    if is_GPR(regname):
+        return "GPR"
+
+
+def decode_extra(rm, prefix=''):
+    # first turn the svp64 rm into a "by name" dict, recording
+    # which position in the RM EXTRA it goes into
+    # also: record if the src or dest was a CR, for sanity-checking
+    # (elwidth overrides on CRs are banned)
+    dest_reg_cr, src_reg_cr = False, False
+    svp64_srcreg_byname = {}
+    svp64_destreg_byname = {}
+    for i in range(4):
+        print (rm)
+        rfield = rm[prefix+str(i)]
+        if not rfield or rfield == '0':
+            continue
+        print ("EXTRA field", i, rfield)
+        rfield = rfield.split(";") # s:RA;d:CR1 etc.
+        for r in rfield:
+            rtype = r[0]
+            # TODO: ignoring s/d makes it impossible to do
+            # LD/ST-with-update.
+            r = r[2:] # ignore s: and d:
+            if rtype == 'd':
+                svp64_destreg_byname[r] = i # dest reg in EXTRA position 0-3
+            else:
+                svp64_srcreg_byname[r] = i # src reg in EXTRA position 0-3
+            # check the regtype (if CR, record that)
+            regtype = get_regtype(r)
+            if regtype in ['CR_3bit', 'CR_5bit']:
+                if rtype == 'd':
+                    dest_reg_cr = True
+                if rtype == 's':
+                    src_reg_cr = True
+
+    return dest_reg_cr, src_reg_cr, svp64_srcreg_byname, svp64_destreg_byname
+
+
+# gets SVP64 ReMap information
+class SVP64RM:
+    def __init__(self, microwatt_format=False):
+        """SVP64RM: gets micro-opcode information
+
+        microwatt_format: moves RS to in1 (to match decode1.vhdl)
+        """
+        self.instrs = {}
+        self.svp64_instrs = {}
+        pth = find_wiki_dir()
+        for fname in os.listdir(pth):
+            if fname.startswith("RM") or fname.startswith("LDSTRM"):
+                for entry in get_csv(fname):
+                    if microwatt_format:
+                        # move RS from position 1 to position 3, to match
+                        # microwatt decode1.vhdl format
+                        if entry['in1'] == 'RS' and entry['in3'] == 'NONE':
+                            entry['in1'] = 'NONE'
+                            entry['in3'] = 'RS'
+                    self.instrs[entry['insn']] = entry
+
+
+    def get_svp64_csv(self, fname):
+        # first get the v3.0B entries
+        v30b = get_csv(fname)
+
+        # now add the RM fields (for each instruction)
+        for entry in v30b:
+            # *sigh* create extra field "out2" based on LD/ST update
+            # KEEP TRACK HERE https://bugs.libre-soc.org/show_bug.cgi?id=619
+            entry['out2'] = 'NONE'
+            if entry['upd'] == '1':
+                entry['out2'] = 'RA'
+
+            # dummy (blank) fields, first
+            entry.update({'EXTRA0': '0', 'EXTRA1': '0', 'EXTRA2': '0',
+                          'EXTRA3': '0',
+                          'SV_Ptype': 'NONE', 'SV_Etype': 'NONE',
+                          'sv_cr_in': 'NONE', 'sv_cr_out': 'NONE'})
+            for fname in ['in1', 'in2', 'in3', 'out', 'out2']:
+                entry['sv_%s' % fname] = 'NONE'
+
+            # is this SVP64-augmented?
+            asmcode = entry['comment']
+            if asmcode not in self.instrs:
+                continue
+
+            # start updating the fields, merge relevant info
+            svp64 = self.instrs[asmcode]
+            for k, v in {'EXTRA0': '0', 'EXTRA1': '1', 'EXTRA2': '2',
+                          'EXTRA3': '3',
+                          'SV_Ptype': 'Ptype', 'SV_Etype': 'Etype'}.items():
+                entry[k] = svp64[v]
+
+            # hmm, we need something more useful: a cross-association
+            # of the in1/2/3 and CR in/out with the EXTRA0-3 fields
+            decode = decode_extra(entry, "EXTRA")
+            dest_reg_cr, src_reg_cr, svp64_src, svp64_dest = decode
+
+            # now examine in1/2/3/out, create sv_in1/2/3/out
+            for fname in ['in1', 'in2', 'in3', 'out', 'out2']:
+                regfield = entry[fname]
+                extra_index = None
+                if regfield == 'RA_OR_ZERO':
+                    regfield = 'RA'
+                print (asmcode, regfield, fname, svp64_dest, svp64_src)
+                # find the reg in the SVP64 extra map
+                if (fname in ['out', 'out2'] and regfield in svp64_dest):
+                    extra_index = svp64_dest[regfield]
+                if (fname not in ['out', 'out2'] and regfield in svp64_src):
+                    extra_index = svp64_src[regfield]
+                # ta-daa, we know in1/2/3/out's bit-offset
+                if extra_index is not None:
+                    entry['sv_%s' % fname] = "Idx"+str(extra_index)
+
+            # TODO: CRs a little tricky, the power_enums.CRInSel is a bit odd.
+            # ignore WHOLE_REG for now
+            cr_in = entry['CR in']
+            extra_index = 'NONE'
+            if cr_in in svp64_src:
+                entry['sv_cr_in'] = "Idx"+str(svp64_src[cr_in])
+            elif cr_in == 'BA_BB':
+                index1 = svp64_src.get('BA', None)
+                index2 = svp64_src.get('BB', None)
+                entry['sv_cr_in'] = "Idx_%d_%d" % (index1, index2)
+
+            # CRout a lot easier.  ignore WHOLE_REG for now
+            cr_out = entry['CR out']
+            extra_index = svp64_dest.get(cr_out, None)
+            if extra_index is not None:
+                entry['sv_cr_out'] = 'Idx%d' % extra_index
+
+            # more enum-friendly Ptype names.  should have done this in
+            # sv_analysis.py, oh well
+            if entry['SV_Ptype'] == '1P':
+                entry['SV_Ptype'] = 'P1'
+            if entry['SV_Ptype'] == '2P':
+                entry['SV_Ptype'] = 'P2'
+            self.svp64_instrs[asmcode] = entry
+
+        return v30b
+
+if __name__ == '__main__':
+    isa = SVP64RM()
+    minor_31 = isa.get_svp64_csv("minor_31.csv")
+    for entry in minor_31:
+        if entry['comment'].startswith('ldu'):
+            print ("entry", entry)
+    minor_19 = isa.get_svp64_csv("minor_19.csv")
+    for entry in minor_19:
+        if entry['comment'].startswith('cr'):
+            print (entry)
+    minor_31 = isa.get_svp64_csv("minor_31.csv")
+    for entry in minor_31:
+        print (entry)
diff --git a/src/openpower/decoder/power_svp64_extra.py b/src/openpower/decoder/power_svp64_extra.py
new file mode 100644 (file)
index 0000000..02c0bfd
--- /dev/null
@@ -0,0 +1,167 @@
+"""SVP64 EXTRA field decoder
+"""
+
+from nmigen import Module, Elaboratable, Signal, Mux, Const, Cat
+from nmigen.cli import rtlil
+from nmutil.util import sel
+
+
+from soc.decoder.power_enums import (SVEXTRA, SVEtype)
+from soc.consts import (SPEC, EXTRA2, EXTRA3, SVP64P, field,
+                        SPEC_SIZE, SPECb, SPEC_AUG_SIZE, SVP64CROffs)
+
+
+class SVP64ExtraSpec(Elaboratable):
+    """SVP64ExtraSpec - decodes SVP64 Extra specification.
+
+    selects the required EXTRA2/3 field.
+
+    see https://libre-soc.org/openpower/sv/svp64/
+    """
+    def __init__(self):
+        self.extra   = Signal(9, reset_less=True)
+        self.etype   = Signal(SVEtype, reset_less=True) # 2 or 3 bits
+        self.idx     = Signal(SVEXTRA, reset_less=True) # which part of extra
+        self.spec  = Signal(3) # EXTRA spec for the register
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        spec = self.spec
+        extra = self.extra
+
+        # back in the LDSTRM-* and RM-* files generated by sv_analysis.py
+        # we marked every op with an Etype: EXTRA2 or EXTRA3, and also said
+        # which of the 4 (or 3 for EXTRA3) sub-fields of bits 10:18 contain
+        # the register-extension information.  extract those now
+        with m.Switch(self.etype):
+            # 2-bit index selection mode
+            with m.Case(SVEtype.EXTRA2):
+                with m.Switch(self.idx):
+                    with m.Case(SVEXTRA.Idx0):  # 1st 2 bits [0:1]
+                        comb += spec[SPEC.VEC].eq(extra[EXTRA2.IDX0_VEC])
+                        comb += spec[SPEC.MSB].eq(extra[EXTRA2.IDX0_MSB])
+                    with m.Case(SVEXTRA.Idx1):  # 2nd 2 bits [2:3]
+                        comb += spec[SPEC.VEC].eq(extra[EXTRA2.IDX1_VEC])
+                        comb += spec[SPEC.MSB].eq(extra[EXTRA2.IDX1_MSB])
+                    with m.Case(SVEXTRA.Idx2):  # 3rd 2 bits [4:5]
+                        comb += spec[SPEC.VEC].eq(extra[EXTRA2.IDX2_VEC])
+                        comb += spec[SPEC.MSB].eq(extra[EXTRA2.IDX2_MSB])
+                    with m.Case(SVEXTRA.Idx3):  # 4th 2 bits [6:7]
+                        comb += spec[SPEC.VEC].eq(extra[EXTRA2.IDX3_VEC])
+                        comb += spec[SPEC.MSB].eq(extra[EXTRA2.IDX3_MSB])
+            # 3-bit index selection mode
+            with m.Case(SVEtype.EXTRA3):
+                with m.Switch(self.idx):
+                    with m.Case(SVEXTRA.Idx0):  # 1st 3 bits [0:2]
+                        extra3_idx0 = sel(m, extra, EXTRA3.IDX0)
+                        comb += spec.eq(extra3_idx0)
+                    with m.Case(SVEXTRA.Idx1):  # 2nd 3 bits [3:5]
+                        extra3_idx1 = sel(m, extra, EXTRA3.IDX1)
+                        comb += spec.eq(extra3_idx1)
+                    with m.Case(SVEXTRA.Idx2):  # 3rd 3 bits [6:8]
+                        extra3_idx2 = sel(m, extra, EXTRA3.IDX2)
+                        comb += spec.eq(extra3_idx2)
+                    # cannot fit more than 9 bits so there is no 4th thing
+
+        return m
+
+
+class SVP64RegExtra(SVP64ExtraSpec):
+    """SVP64RegExtra - decodes SVP64 Extra fields to determine reg extension
+
+    incoming 5-bit GPR/FP is turned into a 7-bit and marked as scalar/vector
+    depending on info in one of the positions in the EXTRA field.
+
+    designed so that "no change" to the 5-bit register number occurs if
+    SV either does not apply or the relevant EXTRA2/3 field bits are zero.
+
+    see https://libre-soc.org/openpower/sv/svp64/
+    """
+    def __init__(self):
+        SVP64ExtraSpec.__init__(self)
+        self.reg_in  = Signal(5) # incoming reg number (5 bits, RA, RB)
+        self.reg_out = Signal(7) # extra-augmented output (7 bits)
+        self.isvec   = Signal(1) # reg is marked as vector if true
+
+    def elaborate(self, platform):
+        m = super().elaborate(platform) # select required EXTRA2/3
+        comb = m.d.comb
+
+        # first get the spec.  if not changed it's "scalar identity behaviour"
+        # which is zero which is ok.
+        spec = self.spec
+
+        # now decode it. bit 0 is "scalar/vector".  note that spec could be zero
+        #  from above, which (by design) has the effect of "no change", below.
+
+        # simple: isvec is top bit of spec
+        comb += self.isvec.eq(spec[SPEC.VEC])
+        # extra bits for register number augmentation
+        spec_aug = Signal(SPEC_AUG_SIZE)
+        comb += spec_aug.eq(field(spec, SPECb.MSB, SPECb.LSB, SPEC_SIZE))
+
+        # decode vector differently from scalar
+        with m.If(self.isvec):
+            # Vector: shifted up, extra in LSBs (RA << 2) | spec[1:2]
+            comb += self.reg_out.eq(Cat(spec_aug, self.reg_in))
+        with m.Else():
+            # Scalar: not shifted up, extra in MSBs RA | (spec[1:2] << 5)
+            comb += self.reg_out.eq(Cat(self.reg_in, spec_aug))
+
+        return m
+
+
+class SVP64CRExtra(SVP64ExtraSpec):
+    """SVP64CRExtra - decodes SVP64 Extra fields to determine CR extension
+
+    incoming 3-bit CR is turned into a 7-bit and marked as scalar/vector
+    depending on info in one of the positions in the EXTRA field.
+
+    yes, really, 128 CRs.  INT is 128, FP is 128, therefore CRs are 128.
+
+    designed so that "no change" to the 3-bit CR register number occurs if
+    SV either does not apply or the relevant EXTRA2/3 field bits are zero.
+
+    see https://libre-soc.org/openpower/sv/svp64/appendix
+    """
+    def __init__(self):
+        SVP64ExtraSpec.__init__(self)
+        self.cr_in  = Signal(3) # incoming CR number (3 bits, BA[0:2], BFA)
+        self.cr_out = Signal(7) # extra-augmented CR output (7 bits)
+        self.isvec  = Signal(1) # reg is marked as vector if true
+
+    def elaborate(self, platform):
+        m = super().elaborate(platform) # select required EXTRA2/3
+        comb = m.d.comb
+
+        # first get the spec.  if not changed it's "scalar identity behaviour"
+        # which is zero which is ok.
+        spec = self.spec
+
+        # now decode it. bit 0 is "scalar/vector".  note that spec could be zero
+        #  from above, which (by design) has the effect of "no change", below.
+
+        # simple: isvec is top bit of spec
+        comb += self.isvec.eq(spec[SPEC.VEC])
+        # extra bits for register number augmentation
+        spec_aug = Signal(SPEC_AUG_SIZE)
+        comb += spec_aug.eq(field(spec, SPECb.MSB, SPECb.LSB, SPEC_SIZE))
+
+        # decode vector differently from scalar, insert bits 1 and 2 accordingly
+        with m.If(self.isvec):
+            # Vector: shifted up, extra in LSBs (CR << 4) | (spec[1:2] << 2)
+            comb += self.cr_out.eq(Cat(Const(0, 2), spec_aug, self.cr_in))
+        with m.Else():
+            # Scalar: not shifted up, extra in MSBs CR | (spec[1:2] << 3)
+            comb += self.cr_out.eq(Cat(self.cr_in, spec_aug))
+
+        return m
+
+
+if __name__ == '__main__':
+    pdecode = create_pdecode()
+    dec2 = PowerDecode2(pdecode)
+    vl = rtlil.convert(dec2, ports=dec2.ports() + pdecode.ports())
+    with open("dec2.il", "w") as f:
+        f.write(vl)
diff --git a/src/openpower/decoder/power_svp64_prefix.py b/src/openpower/decoder/power_svp64_prefix.py
new file mode 100644 (file)
index 0000000..13352eb
--- /dev/null
@@ -0,0 +1,62 @@
+"""SVP64 Prefix Decoder 
+
+"""
+
+from nmigen import Module, Elaboratable, Signal, Mux, Const, Cat
+from nmigen.cli import rtlil
+from nmutil.util import sel
+
+from soc.consts import SVP64P
+
+# SVP64 Prefix fields: see https://libre-soc.org/openpower/sv/svp64/
+# identifies if an instruction is a SVP64-encoded prefix, and extracts
+# the 24-bit SVP64 context (RM) if it is
+class SVP64PrefixDecoder(Elaboratable):
+
+    def __init__(self):
+        self.opcode_in = Signal(32, reset_less=True)
+        self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
+        self.is_svp64_mode = Signal(1, reset_less=True)
+        self.svp64_rm = Signal(24, reset_less=True)
+        self.bigendian = Signal(reset_less=True)
+
+    def elaborate(self, platform):
+        m = Module()
+        opcode_in = self.opcode_in
+        comb = m.d.comb
+        # sigh copied this from TopPowerDecoder
+        # raw opcode in assumed to be in LE order: byte-reverse it to get BE
+        raw_le = self.raw_opcode_in
+        l = []
+        for i in range(0, 32, 8):
+            l.append(raw_le[i:i+8])
+        l.reverse()
+        raw_be = Cat(*l)
+        comb += opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
+
+        # start identifying if the incoming opcode is SVP64 prefix)
+        major = sel(m, opcode_in, SVP64P.OPC)
+        ident = sel(m, opcode_in, SVP64P.SVP64_7_9)
+
+        comb += self.is_svp64_mode.eq(
+            (major == Const(1, 6)) &   # EXT01
+            (ident == Const(0b11, 2))  # identifier bits
+        )
+
+        with m.If(self.is_svp64_mode):
+            # now grab the 24-bit ReMap context bits,
+            rm = sel(m, opcode_in, SVP64P.RM)
+            comb += self.svp64_rm.eq(rm)
+
+        return m
+
+    def ports(self):
+        return [self.opcode_in, self.raw_opcode_in, self.is_svp64_mode,
+                self.svp64_rm, self.bigendian]
+
+
+if __name__ == '__main__':
+    svp64 = SVP64PrefixDecoder()
+    vl = rtlil.convert(svp64, ports=svp64.ports())
+    with open("svp64_prefix_dec.il", "w") as f:
+        f.write(vl)
diff --git a/src/openpower/decoder/power_svp64_rm.py b/src/openpower/decoder/power_svp64_rm.py
new file mode 100644 (file)
index 0000000..ac29159
--- /dev/null
@@ -0,0 +1,155 @@
+# SPDX-License-Identifier: LGPLv3+
+# Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Funded by NLnet http://nlnet.nl
+"""SVP64 RM (Remap) Record.
+
+https://libre-soc.org/openpower/sv/svp64/
+
+| Field Name  | Field bits | Description                            |
+|-------------|------------|----------------------------------------|
+| MASKMODE    | `0`        | Execution (predication) Mask Kind      |
+| MASK        | `1:3`      | Execution Mask                         |
+| ELWIDTH     | `4:5`      | Element Width                          |
+| ELWIDTH_SRC | `6:7`      | Element Width for Source               |
+| SUBVL       | `8:9`      | Sub-vector length                      |
+| EXTRA       | `10:18`    | context-dependent extra                |
+| MODE        | `19:23`    | changes Vector behaviour               |
+"""
+
+from nmigen import Elaboratable, Module, Signal, Const
+from soc.decoder.power_enums import (SVP64RMMode, Function, SVPtype,
+                                    SVP64PredMode, SVP64sat)
+from soc.consts import EXTRA3, SVP64MODE
+from soc.sv.svp64 import SVP64Rec
+from nmutil.util import sel
+
+
+"""RM Mode
+there are three Mode variants, two for LD/ST and one for everything else
+https://libre-soc.org/openpower/sv/svp64/
+https://libre-soc.org/openpower/sv/ldst/
+
+LD/ST immed:
+00     str     sz dz   normal mode
+01     inv     CR-bit  Rc=1: ffirst CR sel
+01     inv     els RC1 Rc=0: ffirst z/nonz
+10     N       dz els  sat mode: N=0/1 u/s
+11     inv     CR-bit  Rc=1: pred-result CR sel
+11     inv     els RC1 Rc=0: pred-result z/nonz
+
+LD/ST indexed:
+00     0       sz dz   normal mode
+00     1       rsvd    reserved
+01     inv     CR-bit  Rc=1: ffirst CR sel
+01     inv     dz RC1  Rc=0: ffirst z/nonz
+10     N       sz dz   sat mode: N=0/1 u/s
+11     inv     CR-bit  Rc=1: pred-result CR sel
+11     inv     dz RC1  Rc=0: pred-result z/nonz
+
+Arithmetic:
+00     0       sz dz   normal mode
+00     1       dz CRM  reduce mode (mapreduce), SUBVL=1
+00     1       SVM CRM subvector reduce mode, SUBVL>1
+01     inv     CR-bit  Rc=1: ffirst CR sel
+01     inv     dz RC1  Rc=0: ffirst z/nonz
+10     N       sz dz   sat mode: N=0/1 u/s
+11     inv     CR-bit  Rc=1: pred-result CR sel
+11     inv     dz RC1  Rc=0: pred-result z/nonz
+"""
+
+class SVP64RMModeDecode(Elaboratable):
+    def __init__(self, name=None):
+        self.rm_in = SVP64Rec(name=name)
+        self.fn_in = Signal(Function) # LD/ST is different
+        self.ptype_in = Signal(SVPtype)
+        self.rc_in = Signal()
+        self.ldst_idx = Signal()
+
+        # main mode (normal, reduce, saturate, ffirst, pred-result)
+        self.mode = Signal(SVP64RMMode)
+
+        # predication
+        self.predmode = Signal(SVP64PredMode)
+        self.srcpred = Signal(3) # source predicate
+        self.dstpred = Signal(3) # destination predicate
+        self.pred_sz = Signal(1) # predicate source zeroing
+        self.pred_dz = Signal(1) # predicate dest zeroing
+        
+        self.saturate = Signal(SVP64sat)
+        self.RC1 = Signal()
+        self.cr_sel = Signal(2)
+        self.inv = Signal(1)
+        self.map_evm = Signal(1)
+        self.map_crm = Signal(1)
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        mode = self.rm_in.mode
+
+        # decode pieces of mode
+        is_ldst = Signal()
+        comb += is_ldst.eq(self.fn_in == Function.LDST)
+        mode2 = sel(m, mode, SVP64MODE.MOD2)
+        with m.Switch(mode2):
+            with m.Case(0): # needs further decoding (LDST no mapreduce)
+                with m.If(is_ldst):
+                    comb += self.mode.eq(SVP64RMMode.NORMAL)
+                with m.Elif(mode[SVP64MODE.REDUCE]):
+                    comb += self.mode.eq(SVP64RMMode.MAPREDUCE)
+                with m.Else():
+                    comb += self.mode.eq(SVP64RMMode.NORMAL)
+            with m.Case(1):
+                comb += self.mode.eq(SVP64RMMode.FFIRST) # fail-first
+            with m.Case(2):
+                comb += self.mode.eq(SVP64RMMode.SATURATE) # saturate
+            with m.Case(3):
+                comb += self.mode.eq(SVP64RMMode.PREDRES) # predicate result
+
+        # extract zeroing
+        with m.Switch(mode2):
+            with m.Case(0): # needs further decoding (LDST no mapreduce)
+                with m.If(is_ldst):
+                    comb += self.pred_sz.eq(mode[SVP64MODE.SZ])
+                    comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+                with m.Elif(mode[SVP64MODE.REDUCE]):
+                    with m.If(self.rm_in.subvl == Const(0, 2)): # no SUBVL
+                        comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+                with m.Else():
+                    comb += self.pred_sz.eq(mode[SVP64MODE.SZ])
+                    comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+            with m.Case(1, 3):
+                with m.If(is_ldst):
+                    with m.If(~self.ldst_idx):
+                        comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+                with m.Elif(self.rc_in):
+                    comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+            with m.Case(2):
+                with m.If(is_ldst & ~self.ldst_idx):
+                    comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+                with m.Else():
+                    comb += self.pred_sz.eq(mode[SVP64MODE.SZ])
+                    comb += self.pred_dz.eq(mode[SVP64MODE.DZ])
+
+        # extract src/dest predicate.  use EXTRA3.MASK because EXTRA2.MASK
+        # is in exactly the same bits
+        srcmask = sel(m, self.rm_in.extra, EXTRA3.MASK)
+        dstmask = self.rm_in.mask
+        with m.If(self.ptype_in == SVPtype.P2):
+            comb += self.srcpred.eq(srcmask)
+        with m.Else():
+            comb += self.srcpred.eq(dstmask)
+        comb += self.dstpred.eq(dstmask)
+
+        # identify predicate mode
+        with m.If(self.rm_in.mmode == 1):
+            comb += self.predmode.eq(SVP64PredMode.CR) # CR Predicate
+        with m.Elif((self.srcpred == 0) & (self.dstpred == 0)):
+            comb += self.predmode.eq(SVP64PredMode.ALWAYS) # No predicate
+        with m.Else():
+            comb += self.predmode.eq(SVP64PredMode.INT) # non-zero src: INT
+
+        # TODO: detect zeroing mode, saturation mode, a few more.
+
+        return m
+
diff --git a/src/openpower/decoder/selectable_int.py b/src/openpower/decoder/selectable_int.py
new file mode 100644 (file)
index 0000000..152764c
--- /dev/null
@@ -0,0 +1,560 @@
+import unittest
+from copy import copy
+from soc.decoder.power_fields import BitRange
+from operator import (add, sub, mul, floordiv, truediv, mod, or_, and_, xor,
+                      neg, inv, lshift, rshift)
+
+
+def check_extsign(a, b):
+    if isinstance(b, FieldSelectableInt):
+        b = b.get_range()
+    if isinstance(b, int):
+        return SelectableInt(b, a.bits)
+    if b.bits != 256:
+        return b
+    return SelectableInt(b.value, a.bits)
+
+
+class FieldSelectableInt:
+    """FieldSelectableInt: allows bit-range selection onto another target
+    """
+
+    def __init__(self, si, br):
+        self.si = si  # target selectable int
+        if isinstance(br, list) or isinstance(br, tuple):
+            _br = BitRange()
+            for i, v in enumerate(br):
+                _br[i] = v
+            br = _br
+        self.br = br  # map of indices.
+
+    def eq(self, b):
+        if isinstance(b, SelectableInt):
+            for i in range(b.bits):
+                self[i] = b[i]
+        else:
+            self.si = copy(b.si)
+            self.br = copy(b.br)
+
+    def _op(self, op, b):
+        vi = self.get_range()
+        vi = op(vi, b)
+        return self.merge(vi)
+
+    def _op1(self, op):
+        vi = self.get_range()
+        vi = op(vi)
+        return self.merge(vi)
+
+    def __getitem__(self, key):
+        print("getitem", key, self.br)
+        if isinstance(key, SelectableInt):
+            key = key.value
+        if isinstance(key, int):
+            key = self.br[key]  # don't do POWER 1.3.4 bit-inversion
+            return self.si[key]
+        if isinstance(key, slice):
+            key = self.br[key]
+            return selectconcat(*[self.si[x] for x in key])
+
+    def __setitem__(self, key, value):
+        if isinstance(key, SelectableInt):
+            key = key.value
+        key = self.br[key]  # don't do POWER 1.3.4 bit-inversion
+        if isinstance(key, int):
+            return self.si.__setitem__(key, value)
+        else:
+            if not isinstance(value, SelectableInt):
+                value = SelectableInt(value, bits=len(key))
+            for i, k in enumerate(key):
+                self.si[k] = value[i]
+
+    def __negate__(self):
+        return self._op1(negate)
+
+    def __invert__(self):
+        return self._op1(inv)
+
+    def __add__(self, b):
+        return self._op(add, b)
+
+    def __sub__(self, b):
+        return self._op(sub, b)
+
+    def __mul__(self, b):
+        return self._op(mul, b)
+
+    def __div__(self, b):
+        return self._op(truediv, b)
+
+    def __mod__(self, b):
+        return self._op(mod, b)
+
+    def __and__(self, b):
+        return self._op(and_, b)
+
+    def __or__(self, b):
+        return self._op(or_, b)
+
+    def __xor__(self, b):
+        return self._op(xor, b)
+
+    def get_range(self):
+        vi = SelectableInt(0, len(self.br))
+        for k, v in self.br.items():
+            vi[k] = self.si[v]
+        return vi
+
+    def merge(self, vi):
+        fi = copy(self)
+        for i, v in fi.br.items():
+            fi.si[v] = vi[i]
+        return fi
+
+    def __repr__(self):
+        return "FieldSelectableInt(si=%s, br=%s)" % (self.si, self.br)
+
+    def asint(self, msb0=False):
+        res = 0
+        brlen = len(self.br)
+        for i, key in self.br.items():
+            bit = self.si[key].value
+            #print("asint", i, key, bit)
+            res |= bit << ((brlen-i-1) if msb0 else i)
+        return res
+
+
+class FieldSelectableIntTestCase(unittest.TestCase):
+    def test_arith(self):
+        a = SelectableInt(0b10101, 5)
+        b = SelectableInt(0b011, 3)
+        br = BitRange()
+        br[0] = 0
+        br[1] = 2
+        br[2] = 3
+        fs = FieldSelectableInt(a, br)
+        c = fs + b
+        print(c)
+        #self.assertEqual(c.value, a.value + b.value)
+
+    def test_select(self):
+        a = SelectableInt(0b00001111, 8)
+        br = BitRange()
+        br[0] = 0
+        br[1] = 1
+        br[2] = 4
+        br[3] = 5
+        fs = FieldSelectableInt(a, br)
+
+        self.assertEqual(fs.get_range(), 0b0011)
+
+    def test_select_range(self):
+        a = SelectableInt(0b00001111, 8)
+        br = BitRange()
+        br[0] = 0
+        br[1] = 1
+        br[2] = 4
+        br[3] = 5
+        fs = FieldSelectableInt(a, br)
+
+        self.assertEqual(fs[2:4], 0b11)
+
+        fs[0:2] = 0b10
+        self.assertEqual(fs.get_range(), 0b1011)
+
+class SelectableInt:
+    """SelectableInt - a class that behaves exactly like python int
+
+    this class is designed to mirror precisely the behaviour of python int.
+    the only difference is that it must contain the context of the bitwidth
+    (number of bits) associated with that integer.
+
+    FieldSelectableInt can then operate on partial bits, and because there
+    is a bit width associated with SelectableInt, slices operate correctly
+    including negative start/end points.
+    """
+
+    def __init__(self, value, bits):
+        if isinstance(value, SelectableInt):
+            value = value.value
+        mask = (1 << bits) - 1
+        self.value = value & mask
+        self.bits = bits
+        self.overflow = (value & ~mask) != 0
+
+    def eq(self, b):
+        self.value = b.value
+        self.bits = b.bits
+
+    def to_signed_int(self):
+        print ("to signed?", self.value & (1<<(self.bits-1)), self.value)
+        if self.value & (1<<(self.bits-1)) != 0: # negative
+            res = self.value - (1<<self.bits)
+            print ("    val -ve:", self.bits, res)
+        else:
+            res = self.value
+            print ("    val +ve:", res)
+        return res
+
+    def _op(self, op, b):
+        if isinstance(b, int):
+            b = SelectableInt(b, self.bits)
+        b = check_extsign(self, b)
+        assert b.bits == self.bits
+        return SelectableInt(op(self.value, b.value), self.bits)
+
+    def __add__(self, b):
+        return self._op(add, b)
+
+    def __sub__(self, b):
+        return self._op(sub, b)
+
+    def __mul__(self, b):
+        # different case: mul result needs to fit the total bitsize
+        if isinstance(b, int):
+            b = SelectableInt(b, self.bits)
+        print("SelectableInt mul", hex(self.value), hex(b.value),
+              self.bits, b.bits)
+        return SelectableInt(self.value * b.value, self.bits + b.bits)
+
+    def __floordiv__(self, b):
+        return self._op(floordiv, b)
+
+    def __truediv__(self, b):
+        return self._op(truediv, b)
+
+    def __mod__(self, b):
+        return self._op(mod, b)
+
+    def __and__(self, b):
+        return self._op(and_, b)
+
+    def __or__(self, b):
+        return self._op(or_, b)
+
+    def __xor__(self, b):
+        return self._op(xor, b)
+
+    def __abs__(self):
+        print("abs", self.value & (1 << (self.bits-1)))
+        if self.value & (1 << (self.bits-1)) != 0:
+            return -self
+        return self
+
+    def __rsub__(self, b):
+        if isinstance(b, int):
+            b = SelectableInt(b, self.bits)
+        b = check_extsign(self, b)
+        assert b.bits == self.bits
+        return SelectableInt(b.value - self.value, self.bits)
+
+    def __radd__(self, b):
+        if isinstance(b, int):
+            b = SelectableInt(b, self.bits)
+        b = check_extsign(self, b)
+        assert b.bits == self.bits
+        return SelectableInt(b.value + self.value, self.bits)
+
+    def __rxor__(self, b):
+        b = check_extsign(self, b)
+        assert b.bits == self.bits
+        return SelectableInt(self.value ^ b.value, self.bits)
+
+    def __invert__(self):
+        return SelectableInt(~self.value, self.bits)
+
+    def __neg__(self):
+        res = SelectableInt((~self.value) + 1, self.bits)
+        print ("neg", hex(self.value), hex(res.value))
+        return res
+
+    def __lshift__(self, b):
+        b = check_extsign(self, b)
+        return SelectableInt(self.value << b.value, self.bits)
+
+    def __rshift__(self, b):
+        b = check_extsign(self, b)
+        return SelectableInt(self.value >> b.value, self.bits)
+
+    def __getitem__(self, key):
+        if isinstance(key, SelectableInt):
+            key = key.value
+        if isinstance(key, int):
+            assert key < self.bits, "key %d accessing %d" % (key, self.bits)
+            assert key >= 0
+            # NOTE: POWER 3.0B annotation order!  see p4 1.3.2
+            # MSB is indexed **LOWEST** (sigh)
+            key = self.bits - (key + 1)
+
+            value = (self.value >> key) & 1
+            print("getitem", key, self.bits, hex(self.value), value)
+            return SelectableInt(value, 1)
+        elif isinstance(key, slice):
+            assert key.step is None or key.step == 1
+            assert key.start < key.stop
+            assert key.start >= 0
+            assert key.stop <= self.bits
+
+            stop = self.bits - key.start
+            start = self.bits - key.stop
+
+            bits = stop - start
+            #print ("__getitem__ slice num bits", start, stop, bits)
+            mask = (1 << bits) - 1
+            value = (self.value >> start) & mask
+            print("getitem", stop, start, self.bits, hex(self.value), value)
+            return SelectableInt(value, bits)
+
+    def __setitem__(self, key, value):
+        if isinstance(key, SelectableInt):
+            key = key.value
+        print("setitem", key, self.bits, hex(self.value))
+        if isinstance(key, int):
+            assert key < self.bits
+            assert key >= 0
+            key = self.bits - (key + 1)
+            if isinstance(value, SelectableInt):
+                assert value.bits == 1
+                value = value.value
+
+            value = value << key
+            mask = 1 << key
+            self.value = (self.value & ~mask) | (value & mask)
+        elif isinstance(key, slice):
+            assert key.step is None or key.step == 1
+            assert key.start < key.stop
+            assert key.start >= 0
+            assert key.stop <= self.bits
+
+            stop = self.bits - key.start
+            start = self.bits - key.stop
+
+            bits = stop - start
+            #print ("__setitem__ slice num bits", bits)
+            if isinstance(value, SelectableInt):
+                assert value.bits == bits, "%d into %d" % (value.bits, bits)
+                value = value.value
+            mask = ((1 << bits) - 1) << start
+            value = value << start
+            self.value = (self.value & ~mask) | (value & mask)
+
+    def __ge__(self, other):
+        if isinstance(other, FieldSelectableInt):
+            other = other.get_range()
+        if isinstance(other, SelectableInt):
+            other = check_extsign(self, other)
+            assert other.bits == self.bits
+            other = other.to_signed_int()
+        if isinstance(other, int):
+            return onebit(self.to_signed_int() >= other)
+        assert False
+
+    def __le__(self, other):
+        if isinstance(other, FieldSelectableInt):
+            other = other.get_range()
+        if isinstance(other, SelectableInt):
+            other = check_extsign(self, other)
+            assert other.bits == self.bits
+            other = other.to_signed_int()
+        if isinstance(other, int):
+            return onebit(self.to_signed_int() <= other)
+        assert False
+
+    def __gt__(self, other):
+        if isinstance(other, FieldSelectableInt):
+            other = other.get_range()
+        if isinstance(other, SelectableInt):
+            other = check_extsign(self, other)
+            assert other.bits == self.bits
+            other = other.to_signed_int()
+        if isinstance(other, int):
+            return onebit(self.to_signed_int() > other)
+        assert False
+
+    def __lt__(self, other):
+        print ("SelectableInt lt", self, other)
+        if isinstance(other, FieldSelectableInt):
+            other = other.get_range()
+        if isinstance(other, SelectableInt):
+            other = check_extsign(self, other)
+            assert other.bits == self.bits
+            other = other.to_signed_int()
+        if isinstance(other, int):
+            a = self.to_signed_int()
+            res = onebit(a  < other)
+            print ("    a < b", a, other, res)
+            return res
+        assert False
+
+    def __eq__(self, other):
+        print("__eq__", self, other)
+        if isinstance(other, FieldSelectableInt):
+            other = other.get_range()
+        if isinstance(other, SelectableInt):
+            other = check_extsign(self, other)
+            assert other.bits == self.bits
+            other = other.value
+        print ("    eq", other, self.value, other == self.value)
+        if isinstance(other, int):
+            return onebit(other == self.value)
+        assert False
+
+    def narrow(self, bits):
+        assert bits <= self.bits
+        return SelectableInt(self.value, bits)
+
+    def __bool__(self):
+        return self.value != 0
+
+    def __repr__(self):
+        return "SelectableInt(value=0x{:x}, bits={})".format(self.value,
+                                                             self.bits)
+
+    def __len__(self):
+        return self.bits
+
+    def asint(self):
+        return self.value
+
+
+def onebit(bit):
+    return SelectableInt(1 if bit else 0, 1)
+
+
+def selectltu(lhs, rhs):
+    """ less-than (unsigned)
+    """
+    if isinstance(rhs, SelectableInt):
+        rhs = rhs.value
+    return onebit(lhs.value < rhs)
+
+
+def selectgtu(lhs, rhs):
+    """ greater-than (unsigned)
+    """
+    if isinstance(rhs, SelectableInt):
+        rhs = rhs.value
+    return onebit(lhs.value > rhs)
+
+
+# XXX this probably isn't needed...
+def selectassign(lhs, idx, rhs):
+    if isinstance(idx, tuple):
+        if len(idx) == 2:
+            lower, upper = idx
+            step = None
+        else:
+            lower, upper, step = idx
+        toidx = range(lower, upper, step)
+        fromidx = range(0, upper-lower, step)  # XXX eurgh...
+    else:
+        toidx = [idx]
+        fromidx = [0]
+    for t, f in zip(toidx, fromidx):
+        lhs[t] = rhs[f]
+
+
+def selectconcat(*args, repeat=1):
+    if repeat != 1 and len(args) == 1 and isinstance(args[0], int):
+        args = [SelectableInt(args[0], 1)]
+    if repeat != 1:  # multiplies the incoming arguments
+        tmp = []
+        for i in range(repeat):
+            tmp += args
+        args = tmp
+    res = copy(args[0])
+    for i in args[1:]:
+        if isinstance(i, FieldSelectableInt):
+            i = i.si
+        assert isinstance(i, SelectableInt), "can only concat SIs, sorry"
+        res.bits += i.bits
+        res.value = (res.value << i.bits) | i.value
+    print("concat", repeat, res)
+    return res
+
+
+class SelectableIntTestCase(unittest.TestCase):
+    def test_arith(self):
+        a = SelectableInt(5, 8)
+        b = SelectableInt(9, 8)
+        c = a + b
+        d = a - b
+        e = a * b
+        f = -a
+        g = abs(f)
+        h = abs(a)
+        self.assertEqual(c.value, a.value + b.value)
+        self.assertEqual(d.value, (a.value - b.value) & 0xFF)
+        self.assertEqual(e.value, (a.value * b.value) & 0xFF)
+        self.assertEqual(f.value, (-a.value) & 0xFF)
+        self.assertEqual(c.bits, a.bits)
+        self.assertEqual(d.bits, a.bits)
+        self.assertEqual(e.bits, a.bits)
+        self.assertEqual(a.bits, f.bits)
+        self.assertEqual(a.bits, h.bits)
+
+    def test_logic(self):
+        a = SelectableInt(0x0F, 8)
+        b = SelectableInt(0xA5, 8)
+        c = a & b
+        d = a | b
+        e = a ^ b
+        f = ~a
+        self.assertEqual(c.value, a.value & b.value)
+        self.assertEqual(d.value, a.value | b.value)
+        self.assertEqual(e.value, a.value ^ b.value)
+        self.assertEqual(f.value, 0xF0)
+
+    def test_get(self):
+        a = SelectableInt(0xa2, 8)
+        # These should be big endian
+        self.assertEqual(a[7], 0)
+        self.assertEqual(a[0:4], 10)
+        self.assertEqual(a[4:8], 2)
+
+    def test_set(self):
+        a = SelectableInt(0x5, 8)
+        a[7] = SelectableInt(0, 1)
+        self.assertEqual(a, 4)
+        a[4:8] = 9
+        self.assertEqual(a, 9)
+        a[0:4] = 3
+        self.assertEqual(a, 0x39)
+        a[0:4] = a[4:8]
+        self.assertEqual(a, 0x99)
+
+    def test_concat(self):
+        a = SelectableInt(0x1, 1)
+        c = selectconcat(a, repeat=8)
+        self.assertEqual(c, 0xff)
+        self.assertEqual(c.bits, 8)
+        a = SelectableInt(0x0, 1)
+        c = selectconcat(a, repeat=8)
+        self.assertEqual(c, 0x00)
+        self.assertEqual(c.bits, 8)
+
+    def test_repr(self):
+        for i in range(65536):
+            a = SelectableInt(i, 16)
+            b = eval(repr(a))
+            self.assertEqual(a, b)
+
+    def test_cmp(self):
+        a = SelectableInt(10, bits=8)
+        b = SelectableInt(5, bits=8)
+        self.assertTrue(a > b)
+        self.assertFalse(a < b)
+        self.assertTrue(a != b)
+        self.assertFalse(a == b)
+
+    def test_unsigned(self):
+        a = SelectableInt(0x80, bits=8)
+        b = SelectableInt(0x7f, bits=8)
+        self.assertTrue(a > b)
+        self.assertFalse(a < b)
+        self.assertTrue(a != b)
+        self.assertFalse(a == b)
+
+
+if __name__ == "__main__":
+    unittest.main()