From ac90cacafac7c372ae23f989356aa7ef283c071b Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Fri, 23 Apr 2021 13:38:17 +0100 Subject: [PATCH] add SV records --- src/openpower/sv/svp64.py | 35 ++ src/openpower/sv/svstate.py | 31 ++ src/openpower/sv/trans/svp64.py | 672 ++++++++++++++++++++++++++++++++ 3 files changed, 738 insertions(+) create mode 100644 src/openpower/sv/svp64.py create mode 100644 src/openpower/sv/svstate.py create mode 100644 src/openpower/sv/trans/svp64.py diff --git a/src/openpower/sv/svp64.py b/src/openpower/sv/svp64.py new file mode 100644 index 00000000..b57ce780 --- /dev/null +++ b/src/openpower/sv/svp64.py @@ -0,0 +1,35 @@ +# 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 Record + +# in nMigen, Record begins at the LSB and fills upwards +class SVP64Rec(Record): + def __init__(self, name=None): + Record.__init__(self, layout=[("mode" , 5), + ("extra" , 9), + ("subvl" , 2), + ("ewsrc" , 2), + ("elwidth" , 2), + ("mask" , 3), + ("mmode" , 1)], name=name) + + def ports(self): + return [self.mmode, self.mask, self.elwidth, self.ewsrc, + self.extra, self.mode] + diff --git a/src/openpower/sv/svstate.py b/src/openpower/sv/svstate.py new file mode 100644 index 00000000..6052a8f1 --- /dev/null +++ b/src/openpower/sv/svstate.py @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: LGPLv3+ +# Copyright (C) 2021 Luke Kenneth Casson Leighton +# Funded by NLnet http://nlnet.nl +"""SVSATE SPR Record. actually a peer of PC (CIA/NIA) and MSR + +https://libre-soc.org/openpower/sv/sprs/ + +| Field | Name | Description | +| ----- | -------- | --------------------- | +| 0:6 | maxvl | Max Vector Length | +| 7:13 | vl | Vector Length | +| 14:20 | srcstep | for srcstep = 0..VL-1 | +| 21:27 | dststep | for dststep = 0..VL-1 | +| 28:29 | subvl | Sub-vector length | +| 30:31 | svstep | for svstep = 0..SUBVL-1 | +""" + +from nmutil.iocontrol import RecordObject +from nmigen import Signal + + +# In nMigen, Record order is from LSB to MSB +class SVSTATERec(RecordObject): + def __init__(self, name=None): + super().__init__(name=name) + self.svstep = Signal(2) + self.subvl = Signal(2) + self.dststep = Signal(7) + self.srcstep = Signal(7) + self.vl = Signal(7) + self.maxvl = Signal(7) diff --git a/src/openpower/sv/trans/svp64.py b/src/openpower/sv/trans/svp64.py new file mode 100644 index 00000000..5df66ce1 --- /dev/null +++ b/src/openpower/sv/trans/svp64.py @@ -0,0 +1,672 @@ +# SPDX-License-Identifier: LGPLv3+ +# Copyright (C) 2021 Luke Kenneth Casson Leighton +# Funded by NLnet http://nlnet.nl + +"""SVP64 OpenPOWER v3.0B assembly translator + +This class takes raw svp64 assembly mnemonics (aliases excluded) and +creates an EXT001-encoded "svp64 prefix" followed by a v3.0B opcode. + +It is very simple and straightforward, the only weirdness being the +extraction of the register information and conversion to v3.0B numbering. + +Encoding format of svp64: https://libre-soc.org/openpower/sv/svp64/ +Bugtracker: https://bugs.libre-soc.org/show_bug.cgi?id=578 +""" + +import os, sys +from collections import OrderedDict + +from soc.decoder.isa.caller import (SVP64PrefixFields, SV64P_MAJOR_SIZE, + SV64P_PID_SIZE, SVP64RMFields, + SVP64RM_EXTRA2_SPEC_SIZE, + SVP64RM_EXTRA3_SPEC_SIZE, + SVP64RM_MODE_SIZE, SVP64RM_SMASK_SIZE, + SVP64RM_MMODE_SIZE, SVP64RM_MASK_SIZE, + SVP64RM_SUBVL_SIZE, SVP64RM_EWSRC_SIZE, + SVP64RM_ELWIDTH_SIZE) +from soc.decoder.pseudo.pagereader import ISA +from soc.decoder.power_svp64 import SVP64RM, get_regtype, decode_extra +from soc.decoder.selectable_int import SelectableInt +from soc.consts import SVP64MODE + + +# decode GPR into sv extra +def get_extra_gpr(etype, regmode, field): + if regmode == 'scalar': + # cut into 2-bits 5-bits SS FFFFF + sv_extra = field >> 5 + field = field & 0b11111 + else: + # cut into 5-bits 2-bits FFFFF SS + sv_extra = field & 0b11 + field = field >> 2 + return sv_extra, field + + +# decode 3-bit CR into sv extra +def get_extra_cr_3bit(etype, regmode, field): + if regmode == 'scalar': + # cut into 2-bits 3-bits SS FFF + sv_extra = field >> 3 + field = field & 0b111 + else: + # cut into 3-bits 4-bits FFF SSSS but will cut 2 zeros off later + sv_extra = field & 0b1111 + field = field >> 4 + return sv_extra, field + + +# decodes SUBVL +def decode_subvl(encoding): + pmap = {'2': 0b01, '3': 0b10, '4': 0b11} + assert encoding in pmap, \ + "encoding %s for SUBVL not recognised" % encoding + return pmap[encoding] + + +# decodes elwidth +def decode_elwidth(encoding): + pmap = {'8': 0b11, '16': 0b10, '32': 0b01} + assert encoding in pmap, \ + "encoding %s for elwidth not recognised" % encoding + return pmap[encoding] + + +# decodes predicate register encoding +def decode_predicate(encoding): + pmap = { # integer + '1<> 1) == 0, \ + "scalar GPR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as scalar + sv_extra = sv_extra & 0b01 + else: + # range is r0-r127 in increments of 4 + assert sv_extra & 0b01 == 0, \ + "vector field %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as vector (bit 2 set) + sv_extra = 0b10 | (sv_extra >> 1) + elif regmode == 'vector': + # EXTRA3 vector bit needs marking + sv_extra |= 0b100 + + # encode SV-CR 3-bit field into extra, v3.0field + elif rtype == 'CR_3bit': + sv_extra, field = get_extra_cr_3bit(etype, regmode, field) + # now sanity-check (and shrink afterwards) + if etype == 'EXTRA2': + if regmode == 'scalar': + # range is CR0-CR15 in increments of 1 + assert (sv_extra >> 1) == 0, \ + "scalar CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as scalar + sv_extra = sv_extra & 0b01 + else: + # range is CR0-CR127 in increments of 16 + assert sv_extra & 0b111 == 0, \ + "vector CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as vector (bit 2 set) + sv_extra = 0b10 | (sv_extra >> 3) + else: + if regmode == 'scalar': + # range is CR0-CR31 in increments of 1 + assert (sv_extra >> 2) == 0, \ + "scalar CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as scalar + sv_extra = sv_extra & 0b11 + else: + # range is CR0-CR127 in increments of 8 + assert sv_extra & 0b11 == 0, \ + "vector CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as vector (bit 3 set) + sv_extra = 0b100 | (sv_extra >> 2) + + # encode SV-CR 5-bit field into extra, v3.0field + # *sigh* this is the same as 3-bit except the 2 LSBs are + # passed through + elif rtype == 'CR_5bit': + cr_subfield = field & 0b11 + field = field >> 2 # strip bottom 2 bits + sv_extra, field = get_extra_cr_3bit(etype, regmode, field) + # now sanity-check (and shrink afterwards) + if etype == 'EXTRA2': + if regmode == 'scalar': + # range is CR0-CR15 in increments of 1 + assert (sv_extra >> 1) == 0, \ + "scalar CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as scalar + sv_extra = sv_extra & 0b01 + else: + # range is CR0-CR127 in increments of 16 + assert sv_extra & 0b111 == 0, \ + "vector CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as vector (bit 2 set) + sv_extra = 0b10 | (sv_extra >> 3) + else: + if regmode == 'scalar': + # range is CR0-CR31 in increments of 1 + assert (sv_extra >> 2) == 0, \ + "scalar CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as scalar + sv_extra = sv_extra & 0b11 + else: + # range is CR0-CR127 in increments of 8 + assert sv_extra & 0b11 == 0, \ + "vector CR %s cannot fit into EXTRA2 %s" % \ + (rname, str(extras[extra_idx])) + # all good: encode as vector (bit 3 set) + sv_extra = 0b100 | (sv_extra >> 2) + + # reconstruct the actual 5-bit CR field + field = (field << 2) | cr_subfield + + # capture the extra field info + print ("=>", "%5s" % bin(sv_extra), field) + extras[extra_idx] = sv_extra + + # append altered field value to v3.0b, differs for LDST + if ldst_imm: + v30b_newfields.append(("%s(%s)" % (immed, str(field)))) + else: + v30b_newfields.append(str(field)) + + print ("new v3.0B fields", v30b_op, v30b_newfields) + print ("extras", extras) + + # rright. now we have all the info. start creating SVP64 RM + svp64_rm = SVP64RMFields() + + # begin with EXTRA fields + for idx, sv_extra in extras.items(): + if idx is None: continue + if etype == 'EXTRA2': + svp64_rm.extra2[idx].eq( + SelectableInt(sv_extra, SVP64RM_EXTRA2_SPEC_SIZE)) + else: + svp64_rm.extra3[idx].eq( + SelectableInt(sv_extra, SVP64RM_EXTRA3_SPEC_SIZE)) + + # parts of svp64_rm + mmode = 0 # bit 0 + pmask = 0 # bits 1-3 + destwid = 0 # bits 4-5 + srcwid = 0 # bits 6-7 + subvl = 0 # bits 8-9 + smask = 0 # bits 16-18 but only for twin-predication + mode = 0 # bits 19-23 + + mask_m_specified = False + has_pmask = False + has_smask = False + + saturation = None + src_zero = 0 + dst_zero = 0 + sv_mode = None + + mapreduce = False + mapreduce_crm = False + mapreduce_svm = False + + predresult = False + failfirst = False + + # ok let's start identifying opcode augmentation fields + for encmode in opmodes: + # predicate mask (src and dest) + if encmode.startswith("m="): + pme = encmode + pmmode, pmask = decode_predicate(encmode[2:]) + smmode, smask = pmmode, pmask + mmode = pmmode + mask_m_specified = True + # predicate mask (dest) + elif encmode.startswith("dm="): + pme = encmode + pmmode, pmask = decode_predicate(encmode[3:]) + mmode = pmmode + has_pmask = True + # predicate mask (src, twin-pred) + elif encmode.startswith("sm="): + sme = encmode + smmode, smask = decode_predicate(encmode[3:]) + mmode = smmode + has_smask = True + # vec2/3/4 + elif encmode.startswith("vec"): + subvl = decode_subvl(encmode[3:]) + # elwidth + elif encmode.startswith("ew="): + destwid = decode_elwidth(encmode[3:]) + elif encmode.startswith("sw="): + srcwid = decode_elwidth(encmode[3:]) + # saturation + elif encmode == 'sats': + assert sv_mode is None + saturation = 1 + sv_mode = 0b10 + elif encmode == 'satu': + assert sv_mode is None + sv_mode = 0b10 + saturation = 0 + # predicate zeroing + elif encmode == 'sz': + src_zero = 1 + elif encmode == 'dz': + dst_zero = 1 + # failfirst + elif encmode.startswith("ff="): + assert sv_mode is None + sv_mode = 0b01 + failfirst = decode_ffirst(encmode[3:]) + # predicate-result, interestingly same as fail-first + elif encmode.startswith("pr="): + assert sv_mode is None + sv_mode = 0b11 + predresult = decode_ffirst(encmode[3:]) + # map-reduce mode + elif encmode == 'mr': + assert sv_mode is None + sv_mode = 0b00 + mapreduce = True + elif encmode == 'crm': # CR on map-reduce + assert sv_mode is None + sv_mode = 0b00 + mapreduce_crm = True + elif encmode == 'svm': # sub-vector mode + mapreduce_svm = True + else: + raise AssertionError("unknown encmode %s" % encmode) + + if ptype == '2P': + # since m=xx takes precedence (overrides) sm=xx and dm=xx, + # treat them as mutually exclusive + if mask_m_specified: + assert not has_smask,\ + "cannot have both source-mask and predicate mask" + assert not has_pmask,\ + "cannot have both dest-mask and predicate mask" + # since the default is INT predication (ALWAYS), if you + # specify one CR mask, you must specify both, to avoid + # mixing INT and CR reg types + if has_pmask and pmmode == 1: + assert has_smask, \ + "need explicit source-mask in CR twin predication" + if has_smask and smmode == 1: + assert has_pmask, \ + "need explicit dest-mask in CR twin predication" + # sanity-check that 2Pred mask is same mode + if has_pmask and has_smask: + assert smmode == pmmode, \ + "predicate masks %s and %s must be same reg type" % \ + (pme, sme) + + # sanity-check that twin-predication mask only specified in 2P mode + if ptype == '1P': + assert not has_smask, \ + "source-mask can only be specified on Twin-predicate ops" + assert not has_pmask, \ + "dest-mask can only be specified on Twin-predicate ops" + + # construct the mode field, doing sanity-checking along the way + + if mapreduce_svm: + assert sv_mode == 0b00, "sub-vector mode in mapreduce only" + assert subvl != 0, "sub-vector mode not possible on SUBVL=1" + + if src_zero: + assert has_smask or mask_m_specified, \ + "src zeroing requires a source predicate" + if dst_zero: + assert has_pmask or mask_m_specified, \ + "dest zeroing requires a dest predicate" + + # "normal" mode + if sv_mode is None: + mode |= src_zero << SVP64MODE.SZ # predicate zeroing + mode |= dst_zero << SVP64MODE.DZ # predicate zeroing + sv_mode = 0b00 + + # "mapreduce" modes + elif sv_mode == 0b00: + mode |= (0b1<