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