--- /dev/null
+"""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)
--- /dev/null
+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()
--- /dev/null
+# 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)
--- /dev/null
+"""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)
--- /dev/null
+"""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)
--- /dev/null
+# 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)
--- /dev/null
+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)
--- /dev/null
+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)
--- /dev/null
+# Based on GardenSnake - a parser generator demonstration program
+# GardenSnake was released into the Public Domain by Andrew Dalke.
+
+# Portions of this work are derived from Python's Grammar definition
+# and may be covered under the Python copyright and license
+#
+# Andrew Dalke / Dalke Scientific Software, LLC
+# 30 August 2006 / Cape Town, South Africa
+
+# Modifications for inclusion in PLY distribution
+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()
--- /dev/null
+"""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)
+
--- /dev/null
+# 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)
--- /dev/null
+"""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)
--- /dev/null
+"""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)
--- /dev/null
+# 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
+
--- /dev/null
+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()