From 93ae79f9dc5a9504c124e1b9c8b589e2901e4f58 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Fri, 23 Apr 2021 15:38:16 +0100 Subject: [PATCH] more openpower import conversion --- src/soc/decoder/isa/caller.py | 1223 +------------------------ src/soc/decoder/power_pseudo.py | 350 ------- src/soc/decoder/power_svp64.py | 176 ---- src/soc/decoder/power_svp64_extra.py | 167 ---- src/soc/decoder/power_svp64_prefix.py | 62 -- src/soc/decoder/power_svp64_rm.py | 155 ---- 6 files changed, 4 insertions(+), 2129 deletions(-) delete mode 100644 src/soc/decoder/power_pseudo.py delete mode 100644 src/soc/decoder/power_svp64.py delete mode 100644 src/soc/decoder/power_svp64_extra.py delete mode 100644 src/soc/decoder/power_svp64_prefix.py delete mode 100644 src/soc/decoder/power_svp64_rm.py diff --git a/src/soc/decoder/isa/caller.py b/src/soc/decoder/isa/caller.py index f19769f3..837d54c0 100644 --- a/src/soc/decoder/isa/caller.py +++ b/src/soc/decoder/isa/caller.py @@ -1,1220 +1,5 @@ -# SPDX-License-Identifier: LGPLv3+ -# Copyright (C) 2020, 2021 Luke Kenneth Casson Leighton -# Copyright (C) 2020 Michael Nolan -# Funded by NLnet http://nlnet.nl -"""core of the python-based POWER9 simulator - -this is part of a cycle-accurate POWER9 simulator. its primary purpose is -not speed, it is for both learning and educational purposes, as well as -a method of verifying the HDL. - -related bugs: - -* https://bugs.libre-soc.org/show_bug.cgi?id=424 -""" - -from nmigen.back.pysim import Settle -from functools import wraps -from copy import copy -from openpower.decoder.orderedset import OrderedSet -from openpower.decoder.selectable_int import (FieldSelectableInt, SelectableInt, - selectconcat) -from openpower.decoder.power_enums import (spr_dict, spr_byname, XER_bits, - insns, MicrOp, In1Sel, In2Sel, In3Sel, - OutSel, CROutSel, - SVP64RMMode, SVP64PredMode, - SVP64PredInt, SVP64PredCR) - -from openpower.decoder.power_enums import SVPtype - -from openpower.decoder.helpers import exts, gtu, ltu, undefined -from soc.consts import PIb, MSRb # big-endian (PowerISA versions) -from soc.consts import SVP64CROffs -from openpower.decoder.power_svp64 import SVP64RM, decode_extra - -from openpower.decoder.isa.radixmmu import RADIX -from openpower.decoder.isa.mem import Mem, swap_order - -from collections import namedtuple -import math -import sys - -instruction_info = namedtuple('instruction_info', - 'func read_regs uninit_regs write_regs ' + - 'special_regs op_fields form asmregs') - -special_sprs = { - 'LR': 8, - 'CTR': 9, - 'TAR': 815, - 'XER': 1, - 'VRSAVE': 256} - - -REG_SORT_ORDER = { - # TODO (lkcl): adjust other registers that should be in a particular order - # probably CA, CA32, and CR - "RT": 0, - "RA": 0, - "RB": 0, - "RS": 0, - "CR": 0, - "LR": 0, - "CTR": 0, - "TAR": 0, - "CA": 0, - "CA32": 0, - "MSR": 0, - "SVSTATE": 0, - - "overflow": 1, -} - - -def create_args(reglist, extra=None): - retval = list(OrderedSet(reglist)) - retval.sort(key=lambda reg: REG_SORT_ORDER[reg]) - if extra is not None: - return [extra] + retval - return retval - - - -class GPR(dict): - def __init__(self, decoder, isacaller, svstate, regfile): - dict.__init__(self) - self.sd = decoder - self.isacaller = isacaller - self.svstate = svstate - for i in range(32): - self[i] = SelectableInt(regfile[i], 64) - - def __call__(self, ridx): - return self[ridx] - - def set_form(self, form): - self.form = form - - def getz(self, rnum): - # rnum = rnum.value # only SelectableInt allowed - print("GPR getzero?", rnum) - if rnum == 0: - return SelectableInt(0, 64) - return self[rnum] - - def _get_regnum(self, attr): - getform = self.sd.sigforms[self.form] - rnum = getattr(getform, attr) - return rnum - - def ___getitem__(self, attr): - """ XXX currently not used - """ - rnum = self._get_regnum(attr) - offs = self.svstate.srcstep - print("GPR getitem", attr, rnum, "srcoffs", offs) - return self.regfile[rnum] - - def dump(self): - for i in range(0, len(self), 8): - s = [] - for j in range(8): - s.append("%08x" % self[i+j].value) - s = ' '.join(s) - print("reg", "%2d" % i, s) - - -class SPR(dict): - def __init__(self, dec2, initial_sprs={}): - self.sd = dec2 - dict.__init__(self) - for key, v in initial_sprs.items(): - if isinstance(key, SelectableInt): - key = key.value - key = special_sprs.get(key, key) - if isinstance(key, int): - info = spr_dict[key] - else: - info = spr_byname[key] - if not isinstance(v, SelectableInt): - v = SelectableInt(v, info.length) - self[key] = v - - def __getitem__(self, key): - print("get spr", key) - print("dict", self.items()) - # if key in special_sprs get the special spr, otherwise return key - if isinstance(key, SelectableInt): - key = key.value - if isinstance(key, int): - key = spr_dict[key].SPR - key = special_sprs.get(key, key) - if key == 'HSRR0': # HACK! - key = 'SRR0' - if key == 'HSRR1': # HACK! - key = 'SRR1' - if key in self: - res = dict.__getitem__(self, key) - else: - if isinstance(key, int): - info = spr_dict[key] - else: - info = spr_byname[key] - dict.__setitem__(self, key, SelectableInt(0, info.length)) - res = dict.__getitem__(self, key) - print("spr returning", key, res) - return res - - def __setitem__(self, key, value): - if isinstance(key, SelectableInt): - key = key.value - if isinstance(key, int): - key = spr_dict[key].SPR - print("spr key", key) - key = special_sprs.get(key, key) - if key == 'HSRR0': # HACK! - self.__setitem__('SRR0', value) - if key == 'HSRR1': # HACK! - self.__setitem__('SRR1', value) - print("setting spr", key, value) - dict.__setitem__(self, key, value) - - def __call__(self, ridx): - return self[ridx] - - -class PC: - def __init__(self, pc_init=0): - self.CIA = SelectableInt(pc_init, 64) - self.NIA = self.CIA + SelectableInt(4, 64) # only true for v3.0B! - - def update_nia(self, is_svp64): - increment = 8 if is_svp64 else 4 - self.NIA = self.CIA + SelectableInt(increment, 64) - - def update(self, namespace, is_svp64): - """updates the program counter (PC) by 4 if v3.0B mode or 8 if SVP64 - """ - self.CIA = namespace['NIA'].narrow(64) - self.update_nia(is_svp64) - namespace['CIA'] = self.CIA - namespace['NIA'] = self.NIA - - -# Simple-V: see https://libre-soc.org/openpower/sv -class SVP64State: - def __init__(self, init=0): - self.spr = SelectableInt(init, 32) - # fields of SVSTATE, see https://libre-soc.org/openpower/sv/sprs/ - self.maxvl = FieldSelectableInt(self.spr, tuple(range(0,7))) - self.vl = FieldSelectableInt(self.spr, tuple(range(7,14))) - self.srcstep = FieldSelectableInt(self.spr, tuple(range(14,21))) - self.dststep = FieldSelectableInt(self.spr, tuple(range(21,28))) - self.subvl = FieldSelectableInt(self.spr, tuple(range(28,30))) - self.svstep = FieldSelectableInt(self.spr, tuple(range(30,32))) - - -# SVP64 ReMap field -class SVP64RMFields: - def __init__(self, init=0): - self.spr = SelectableInt(init, 24) - # SVP64 RM fields: see https://libre-soc.org/openpower/sv/svp64/ - self.mmode = FieldSelectableInt(self.spr, [0]) - self.mask = FieldSelectableInt(self.spr, tuple(range(1,4))) - self.elwidth = FieldSelectableInt(self.spr, tuple(range(4,6))) - self.ewsrc = FieldSelectableInt(self.spr, tuple(range(6,8))) - self.subvl = FieldSelectableInt(self.spr, tuple(range(8,10))) - self.extra = FieldSelectableInt(self.spr, tuple(range(10,19))) - self.mode = FieldSelectableInt(self.spr, tuple(range(19,24))) - # these cover the same extra field, split into parts as EXTRA2 - self.extra2 = list(range(4)) - self.extra2[0] = FieldSelectableInt(self.spr, tuple(range(10,12))) - self.extra2[1] = FieldSelectableInt(self.spr, tuple(range(12,14))) - self.extra2[2] = FieldSelectableInt(self.spr, tuple(range(14,16))) - self.extra2[3] = FieldSelectableInt(self.spr, tuple(range(16,18))) - self.smask = FieldSelectableInt(self.spr, tuple(range(16,19))) - # and here as well, but EXTRA3 - self.extra3 = list(range(3)) - self.extra3[0] = FieldSelectableInt(self.spr, tuple(range(10,13))) - self.extra3[1] = FieldSelectableInt(self.spr, tuple(range(13,16))) - self.extra3[2] = FieldSelectableInt(self.spr, tuple(range(16,19))) - - -SVP64RM_MMODE_SIZE = len(SVP64RMFields().mmode.br) -SVP64RM_MASK_SIZE = len(SVP64RMFields().mask.br) -SVP64RM_ELWIDTH_SIZE = len(SVP64RMFields().elwidth.br) -SVP64RM_EWSRC_SIZE = len(SVP64RMFields().ewsrc.br) -SVP64RM_SUBVL_SIZE = len(SVP64RMFields().subvl.br) -SVP64RM_EXTRA2_SPEC_SIZE = len(SVP64RMFields().extra2[0].br) -SVP64RM_EXTRA3_SPEC_SIZE = len(SVP64RMFields().extra3[0].br) -SVP64RM_SMASK_SIZE = len(SVP64RMFields().smask.br) -SVP64RM_MODE_SIZE = len(SVP64RMFields().mode.br) - - -# SVP64 Prefix fields: see https://libre-soc.org/openpower/sv/svp64/ -class SVP64PrefixFields: - def __init__(self): - self.insn = SelectableInt(0, 32) - # 6 bit major opcode EXT001, 2 bits "identifying" (7, 9), 24 SV ReMap - self.major = FieldSelectableInt(self.insn, tuple(range(0,6))) - self.pid = FieldSelectableInt(self.insn, (7, 9)) # must be 0b11 - rmfields = [6, 8] + list(range(10,32)) # SVP64 24-bit RM (ReMap) - self.rm = FieldSelectableInt(self.insn, rmfields) - - -SV64P_MAJOR_SIZE = len(SVP64PrefixFields().major.br) -SV64P_PID_SIZE = len(SVP64PrefixFields().pid.br) -SV64P_RM_SIZE = len(SVP64PrefixFields().rm.br) - -# decode SVP64 predicate integer to reg number and invert -def get_predint(gpr, mask): - r10 = gpr(10) - r30 = gpr(30) - print ("get_predint", mask, SVP64PredInt.ALWAYS.value) - if mask == SVP64PredInt.ALWAYS.value: - return 0xffff_ffff_ffff_ffff - if mask == SVP64PredInt.R3_UNARY.value: - return 1 << (gpr(3).value & 0b111111) - if mask == SVP64PredInt.R3.value: - return gpr(3).value - if mask == SVP64PredInt.R3_N.value: - return ~gpr(3).value - if mask == SVP64PredInt.R10.value: - return gpr(10).value - if mask == SVP64PredInt.R10_N.value: - return ~gpr(10).value - if mask == SVP64PredInt.R30.value: - return gpr(30).value - if mask == SVP64PredInt.R30_N.value: - return ~gpr(30).value - -# decode SVP64 predicate CR to reg number and invert status -def _get_predcr(mask): - if mask == SVP64PredCR.LT.value: - return 0, 1 - if mask == SVP64PredCR.GE.value: - return 0, 0 - if mask == SVP64PredCR.GT.value: - return 1, 1 - if mask == SVP64PredCR.LE.value: - return 1, 0 - if mask == SVP64PredCR.EQ.value: - return 2, 1 - if mask == SVP64PredCR.NE.value: - return 2, 0 - if mask == SVP64PredCR.SO.value: - return 3, 1 - if mask == SVP64PredCR.NS.value: - return 3, 0 - -# read individual CR fields (0..VL-1), extract the required bit -# and construct the mask -def get_predcr(crl, mask, vl): - idx, noninv = _get_predcr(mask) - mask = 0 - for i in range(vl): - cr = crl[i+SVP64CROffs.CRPred] - if cr[idx].value == noninv: - mask |= (1<= 1 - print("outputs", repr(outputs)) - if isinstance(outputs, list) or isinstance(outputs, tuple): - output = outputs[0] - else: - output = outputs - gts = [] - for x in inputs: - print("gt input", x, output) - gt = (gtu(x, output)) - gts.append(gt) - print(gts) - cy = 1 if any(gts) else 0 - print("CA", cy, gts) - if not (1 & already_done): - self.spr['XER'][XER_bits['CA']] = cy - - print("inputs", already_done, inputs) - # 32 bit carry - # ARGH... different for OP_ADD... *sigh*... - op = yield self.dec2.e.do.insn_type - if op == MicrOp.OP_ADD.value: - res32 = (output.value & (1 << 32)) != 0 - a32 = (inputs[0].value & (1 << 32)) != 0 - if len(inputs) >= 2: - b32 = (inputs[1].value & (1 << 32)) != 0 - else: - b32 = False - cy32 = res32 ^ a32 ^ b32 - print("CA32 ADD", cy32) - else: - gts = [] - for x in inputs: - print("input", x, output) - print(" x[32:64]", x, x[32:64]) - print(" o[32:64]", output, output[32:64]) - gt = (gtu(x[32:64], output[32:64])) == SelectableInt(1, 1) - gts.append(gt) - cy32 = 1 if any(gts) else 0 - print("CA32", cy32, gts) - if not (2 & already_done): - self.spr['XER'][XER_bits['CA32']] = cy32 - - def handle_overflow(self, inputs, outputs, div_overflow): - if hasattr(self.dec2.e.do, "invert_in"): - inv_a = yield self.dec2.e.do.invert_in - if inv_a: - inputs[0] = ~inputs[0] - - imm_ok = yield self.dec2.e.do.imm_data.ok - if imm_ok: - imm = yield self.dec2.e.do.imm_data.data - inputs.append(SelectableInt(imm, 64)) - assert len(outputs) >= 1 - print("handle_overflow", inputs, outputs, div_overflow) - if len(inputs) < 2 and div_overflow is None: - return - - # div overflow is different: it's returned by the pseudo-code - # because it's more complex than can be done by analysing the output - if div_overflow is not None: - ov, ov32 = div_overflow, div_overflow - # arithmetic overflow can be done by analysing the input and output - elif len(inputs) >= 2: - output = outputs[0] - - # OV (64-bit) - input_sgn = [exts(x.value, x.bits) < 0 for x in inputs] - output_sgn = exts(output.value, output.bits) < 0 - ov = 1 if input_sgn[0] == input_sgn[1] and \ - output_sgn != input_sgn[0] else 0 - - # OV (32-bit) - input32_sgn = [exts(x.value, 32) < 0 for x in inputs] - output32_sgn = exts(output.value, 32) < 0 - ov32 = 1 if input32_sgn[0] == input32_sgn[1] and \ - output32_sgn != input32_sgn[0] else 0 - - self.spr['XER'][XER_bits['OV']] = ov - self.spr['XER'][XER_bits['OV32']] = ov32 - so = self.spr['XER'][XER_bits['SO']] - so = so | ov - self.spr['XER'][XER_bits['SO']] = so - - def handle_comparison(self, outputs, cr_idx=0): - out = outputs[0] - assert isinstance(out, SelectableInt), \ - "out zero not a SelectableInt %s" % repr(outputs) - print("handle_comparison", out.bits, hex(out.value)) - # TODO - XXX *processor* in 32-bit mode - # https://bugs.libre-soc.org/show_bug.cgi?id=424 - # if is_32bit: - # o32 = exts(out.value, 32) - # print ("handle_comparison exts 32 bit", hex(o32)) - out = exts(out.value, out.bits) - print("handle_comparison exts", hex(out)) - zero = SelectableInt(out == 0, 1) - positive = SelectableInt(out > 0, 1) - negative = SelectableInt(out < 0, 1) - SO = self.spr['XER'][XER_bits['SO']] - print("handle_comparison SO", SO) - cr_field = selectconcat(negative, positive, zero, SO) - self.crl[cr_idx].eq(cr_field) - - def set_pc(self, pc_val): - self.namespace['NIA'] = SelectableInt(pc_val, 64) - self.pc.update(self.namespace, self.is_svp64_mode) - - def setup_one(self): - """set up one instruction - """ - if self.respect_pc: - pc = self.pc.CIA.value - else: - pc = self.fake_pc - self._pc = pc - ins = self.imem.ld(pc, 4, False, True, instr_fetch=True) - if ins is None: - raise KeyError("no instruction at 0x%x" % pc) - print("setup: 0x%x 0x%x %s" % (pc, ins & 0xffffffff, bin(ins))) - print("CIA NIA", self.respect_pc, self.pc.CIA.value, self.pc.NIA.value) - - yield self.dec2.sv_rm.eq(0) - yield self.dec2.dec.raw_opcode_in.eq(ins & 0xffffffff) - yield self.dec2.dec.bigendian.eq(self.bigendian) - yield self.dec2.state.msr.eq(self.msr.value) - yield self.dec2.state.pc.eq(pc) - if self.svstate is not None: - yield self.dec2.state.svstate.eq(self.svstate.spr.value) - - # SVP64. first, check if the opcode is EXT001, and SVP64 id bits set - yield Settle() - opcode = yield self.dec2.dec.opcode_in - pfx = SVP64PrefixFields() # TODO should probably use SVP64PrefixDecoder - pfx.insn.value = opcode - major = pfx.major.asint(msb0=True) # MSB0 inversion - print ("prefix test: opcode:", major, bin(major), - pfx.insn[7] == 0b1, pfx.insn[9] == 0b1) - self.is_svp64_mode = ((major == 0b000001) and - pfx.insn[7].value == 0b1 and - pfx.insn[9].value == 0b1) - self.pc.update_nia(self.is_svp64_mode) - self.namespace['NIA'] = self.pc.NIA - self.namespace['SVSTATE'] = self.svstate.spr - if not self.is_svp64_mode: - return - - # in SVP64 mode. decode/print out svp64 prefix, get v3.0B instruction - print ("svp64.rm", bin(pfx.rm.asint(msb0=True))) - print (" svstate.vl", self.svstate.vl.asint(msb0=True)) - print (" svstate.mvl", self.svstate.maxvl.asint(msb0=True)) - sv_rm = pfx.rm.asint(msb0=True) - ins = self.imem.ld(pc+4, 4, False, True, instr_fetch=True) - print(" svsetup: 0x%x 0x%x %s" % (pc+4, ins & 0xffffffff, bin(ins))) - yield self.dec2.dec.raw_opcode_in.eq(ins & 0xffffffff) # v3.0B suffix - yield self.dec2.sv_rm.eq(sv_rm) # svp64 prefix - yield Settle() - - def execute_one(self): - """execute one instruction - """ - # get the disassembly code for this instruction - if self.is_svp64_mode: - code = self.disassembly[self._pc+4] - print(" svp64 sim-execute", hex(self._pc), code) - else: - code = self.disassembly[self._pc] - print("sim-execute", hex(self._pc), code) - opname = code.split(' ')[0] - yield from self.call(opname) - - # don't use this except in special circumstances - if not self.respect_pc: - self.fake_pc += 4 - - print("execute one, CIA NIA", self.pc.CIA.value, self.pc.NIA.value) - - def get_assembly_name(self): - # TODO, asmregs is from the spec, e.g. add RT,RA,RB - # see http://bugs.libre-riscv.org/show_bug.cgi?id=282 - dec_insn = yield self.dec2.e.do.insn - asmcode = yield self.dec2.dec.op.asmcode - print("get assembly name asmcode", asmcode, hex(dec_insn)) - asmop = insns.get(asmcode, None) - int_op = yield self.dec2.dec.op.internal_op - - # sigh reconstruct the assembly instruction name - if hasattr(self.dec2.e.do, "oe"): - ov_en = yield self.dec2.e.do.oe.oe - ov_ok = yield self.dec2.e.do.oe.ok - else: - ov_en = False - ov_ok = False - if hasattr(self.dec2.e.do, "rc"): - rc_en = yield self.dec2.e.do.rc.rc - rc_ok = yield self.dec2.e.do.rc.ok - else: - rc_en = False - rc_ok = False - # grrrr have to special-case MUL op (see DecodeOE) - print("ov %d en %d rc %d en %d op %d" % - (ov_ok, ov_en, rc_ok, rc_en, int_op)) - if int_op in [MicrOp.OP_MUL_H64.value, MicrOp.OP_MUL_H32.value]: - print("mul op") - if rc_en & rc_ok: - asmop += "." - else: - if not asmop.endswith("."): # don't add "." to "andis." - if rc_en & rc_ok: - asmop += "." - if hasattr(self.dec2.e.do, "lk"): - lk = yield self.dec2.e.do.lk - if lk: - asmop += "l" - print("int_op", int_op) - if int_op in [MicrOp.OP_B.value, MicrOp.OP_BC.value]: - AA = yield self.dec2.dec.fields.FormI.AA[0:-1] - print("AA", AA) - if AA: - asmop += "a" - spr_msb = yield from self.get_spr_msb() - if int_op == MicrOp.OP_MFCR.value: - if spr_msb: - asmop = 'mfocrf' - else: - asmop = 'mfcr' - # XXX TODO: for whatever weird reason this doesn't work - # https://bugs.libre-soc.org/show_bug.cgi?id=390 - if int_op == MicrOp.OP_MTCRF.value: - if spr_msb: - asmop = 'mtocrf' - else: - asmop = 'mtcrf' - return asmop - - def get_spr_msb(self): - dec_insn = yield self.dec2.e.do.insn - return dec_insn & (1 << 20) != 0 # sigh - XFF.spr[-1]? - - def call(self, name): - """call(opcode) - the primary execution point for instructions - """ - name = name.strip() # remove spaces if not already done so - if self.halted: - print("halted - not executing", name) - return - - # TODO, asmregs is from the spec, e.g. add RT,RA,RB - # see http://bugs.libre-riscv.org/show_bug.cgi?id=282 - asmop = yield from self.get_assembly_name() - print("call", name, asmop) - - # check privileged - int_op = yield self.dec2.dec.op.internal_op - spr_msb = yield from self.get_spr_msb() - - instr_is_privileged = False - if int_op in [MicrOp.OP_ATTN.value, - MicrOp.OP_MFMSR.value, - MicrOp.OP_MTMSR.value, - MicrOp.OP_MTMSRD.value, - # TODO: OP_TLBIE - MicrOp.OP_RFID.value]: - instr_is_privileged = True - if int_op in [MicrOp.OP_MFSPR.value, - MicrOp.OP_MTSPR.value] and spr_msb: - instr_is_privileged = True - - print("is priv", instr_is_privileged, hex(self.msr.value), - self.msr[MSRb.PR]) - # check MSR priv bit and whether op is privileged: if so, throw trap - if instr_is_privileged and self.msr[MSRb.PR] == 1: - self.TRAP(0x700, PIb.PRIV) - self.namespace['NIA'] = self.trap_nia - self.pc.update(self.namespace, self.is_svp64_mode) - return - - # check halted condition - if name == 'attn': - self.halted = True - return - - # check illegal instruction - illegal = False - if name not in ['mtcrf', 'mtocrf']: - illegal = name != asmop - - # sigh deal with setvl not being supported by binutils (.long) - if asmop.startswith('setvl'): - illegal = False - name = 'setvl' - - if illegal: - print("illegal", name, asmop) - self.TRAP(0x700, PIb.ILLEG) - self.namespace['NIA'] = self.trap_nia - self.pc.update(self.namespace, self.is_svp64_mode) - print("name %s != %s - calling ILLEGAL trap, PC: %x" % - (name, asmop, self.pc.CIA.value)) - return - - info = self.instrs[name] - yield from self.prep_namespace(info.form, info.op_fields) - - # preserve order of register names - input_names = create_args(list(info.read_regs) + - list(info.uninit_regs)) - print(input_names) - - # get SVP64 entry for the current instruction - sv_rm = self.svp64rm.instrs.get(name) - if sv_rm is not None: - dest_cr, src_cr, src_byname, dest_byname = decode_extra(sv_rm) - else: - dest_cr, src_cr, src_byname, dest_byname = False, False, {}, {} - print ("sv rm", sv_rm, dest_cr, src_cr, src_byname, dest_byname) - - # get SVSTATE VL (oh and print out some debug stuff) - if self.is_svp64_mode: - vl = self.svstate.vl.asint(msb0=True) - srcstep = self.svstate.srcstep.asint(msb0=True) - dststep = self.svstate.dststep.asint(msb0=True) - sv_a_nz = yield self.dec2.sv_a_nz - in1 = yield self.dec2.e.read_reg1.data - print ("SVP64: VL, srcstep, dststep, sv_a_nz, in1", - vl, srcstep, dststep, sv_a_nz, in1) - - # get predicate mask - srcmask = dstmask = 0xffff_ffff_ffff_ffff - if self.is_svp64_mode: - pmode = yield self.dec2.rm_dec.predmode - sv_ptype = yield self.dec2.dec.op.SV_Ptype - srcpred = yield self.dec2.rm_dec.srcpred - dstpred = yield self.dec2.rm_dec.dstpred - pred_src_zero = yield self.dec2.rm_dec.pred_sz - pred_dst_zero = yield self.dec2.rm_dec.pred_dz - if pmode == SVP64PredMode.INT.value: - srcmask = dstmask = get_predint(self.gpr, dstpred) - if sv_ptype == SVPtype.P2.value: - srcmask = get_predint(self.gpr, srcpred) - elif pmode == SVP64PredMode.CR.value: - srcmask = dstmask = get_predcr(self.crl, dstpred, vl) - if sv_ptype == SVPtype.P2.value: - srcmask = get_predcr(self.crl, srcpred, vl) - print (" pmode", pmode) - print (" ptype", sv_ptype) - print (" srcpred", bin(srcpred)) - print (" dstpred", bin(dstpred)) - print (" srcmask", bin(srcmask)) - print (" dstmask", bin(dstmask)) - print (" pred_sz", bin(pred_src_zero)) - print (" pred_dz", bin(pred_dst_zero)) - - # okaaay, so here we simply advance srcstep (TODO dststep) - # until the predicate mask has a "1" bit... or we run out of VL - # let srcstep==VL be the indicator to move to next instruction - if not pred_src_zero: - while (((1< 64: - output = SelectableInt(output.value, 64) - self.gpr[regnum] = output - - # check if it is the SVSTATE.src/dest step that needs incrementing - # this is our Sub-Program-Counter loop from 0 to VL-1 - if self.is_svp64_mode: - # XXX twin predication TODO - vl = self.svstate.vl.asint(msb0=True) - mvl = self.svstate.maxvl.asint(msb0=True) - srcstep = self.svstate.srcstep.asint(msb0=True) - dststep = self.svstate.dststep.asint(msb0=True) - sv_ptype = yield self.dec2.dec.op.SV_Ptype - no_out_vec = not (yield self.dec2.no_out_vec) - no_in_vec = not (yield self.dec2.no_in_vec) - print (" svstate.vl", vl) - print (" svstate.mvl", mvl) - print (" svstate.srcstep", srcstep) - print (" svstate.dststep", dststep) - print (" no_out_vec", no_out_vec) - print (" no_in_vec", no_in_vec) - print (" sv_ptype", sv_ptype, sv_ptype == SVPtype.P2.value) - # check if srcstep needs incrementing by one, stop PC advancing - # svp64 loop can end early if the dest is scalar for single-pred - # but for 2-pred both src/dest have to be checked. - # XXX this might not be true! it may just be LD/ST - if sv_ptype == SVPtype.P2.value: - svp64_is_vector = (no_out_vec or no_in_vec) - else: - svp64_is_vector = no_out_vec - if svp64_is_vector and srcstep != vl-1 and dststep != vl-1: - self.svstate.srcstep += SelectableInt(1, 7) - self.svstate.dststep += SelectableInt(1, 7) - self.pc.NIA.value = self.pc.CIA.value - self.namespace['NIA'] = self.pc.NIA - self.namespace['SVSTATE'] = self.svstate.spr - print("end of sub-pc call", self.namespace['CIA'], - self.namespace['NIA']) - return # DO NOT allow PC to update whilst Sub-PC loop running - # reset loop to zero - self.svp64_reset_loop() - - self.update_pc_next() - - def update_pc_next(self): - # UPDATE program counter - self.pc.update(self.namespace, self.is_svp64_mode) - self.svstate.spr = self.namespace['SVSTATE'] - print("end of call", self.namespace['CIA'], - self.namespace['NIA'], - self.namespace['SVSTATE']) - - def svp64_reset_loop(self): - self.svstate.srcstep[0:7] = 0 - self.svstate.dststep[0:7] = 0 - print (" svstate.srcstep loop end (PC to update)") - self.pc.update_nia(self.is_svp64_mode) - self.namespace['NIA'] = self.pc.NIA - self.namespace['SVSTATE'] = self.svstate.spr - -def inject(): - """Decorator factory. - - this decorator will "inject" variables into the function's namespace, - from the *dictionary* in self.namespace. it therefore becomes possible - to make it look like a whole stack of variables which would otherwise - need "self." inserted in front of them (*and* for those variables to be - added to the instance) "appear" in the function. - - "self.namespace['SI']" for example becomes accessible as just "SI" but - *only* inside the function, when decorated. - """ - def variable_injector(func): - @wraps(func) - def decorator(*args, **kwargs): - try: - func_globals = func.__globals__ # Python 2.6+ - except AttributeError: - func_globals = func.func_globals # Earlier versions. - - context = args[0].namespace # variables to be injected - saved_values = func_globals.copy() # Shallow copy of dict. - func_globals.update(context) - result = func(*args, **kwargs) - print("globals after", func_globals['CIA'], func_globals['NIA']) - print("args[0]", args[0].namespace['CIA'], - args[0].namespace['NIA'], - args[0].namespace['SVSTATE']) - args[0].namespace = func_globals - #exec (func.__code__, func_globals) - - # finally: - # func_globals = saved_values # Undo changes. - - return result - - return decorator - - return variable_injector - +# moved to openpower-isa +# https://git.libre-soc.org/?p=openpower-isa.git;a=summary +# wildcard imports here ONLY to support migration +from openpower.decoder.isa.caller import * diff --git a/src/soc/decoder/power_pseudo.py b/src/soc/decoder/power_pseudo.py deleted file mode 100644 index eb87b626..00000000 --- a/src/soc/decoder/power_pseudo.py +++ /dev/null @@ -1,350 +0,0 @@ -# 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 openpower.decoder.power_decoder import create_pdecode -from nmigen.back.pysim import Simulator, Delay -from nmigen import Module, Signal - -from openpower.decoder.pseudo.parser import GardenSnakeCompiler -from openpower.decoder.selectable_int import SelectableInt, selectconcat -from openpower.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 openpower.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/soc/decoder/power_svp64.py b/src/soc/decoder/power_svp64.py deleted file mode 100644 index 3e3332ea..00000000 --- a/src/soc/decoder/power_svp64.py +++ /dev/null @@ -1,176 +0,0 @@ -# SPDX-License-Identifier: LGPLv3+ -# Copyright (C) 2021 Luke Kenneth Casson Leighton -# Funded by NLnet http://nlnet.nl - -from openpower.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/soc/decoder/power_svp64_extra.py b/src/soc/decoder/power_svp64_extra.py deleted file mode 100644 index 1ac3c52b..00000000 --- a/src/soc/decoder/power_svp64_extra.py +++ /dev/null @@ -1,167 +0,0 @@ -"""SVP64 EXTRA field decoder -""" - -from nmigen import Module, Elaboratable, Signal, Mux, Const, Cat -from nmigen.cli import rtlil -from nmutil.util import sel - - -from openpower.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/soc/decoder/power_svp64_prefix.py b/src/soc/decoder/power_svp64_prefix.py deleted file mode 100644 index 13352ebf..00000000 --- a/src/soc/decoder/power_svp64_prefix.py +++ /dev/null @@ -1,62 +0,0 @@ -"""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/soc/decoder/power_svp64_rm.py b/src/soc/decoder/power_svp64_rm.py deleted file mode 100644 index 16b0116a..00000000 --- a/src/soc/decoder/power_svp64_rm.py +++ /dev/null @@ -1,155 +0,0 @@ -# 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 openpower.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 - -- 2.30.2