X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fopenpower%2Fsv%2Fsv_analysis.py;h=332a0b2db0abb03b54be0608f0c993825a75e5b9;hb=b1bb73777b241c9f77059b6a9e6828b5571757be;hp=d62b587521db0303189f88b57873bb6a725d7ec0;hpb=782d622d51388374db95c98b8a7ee7cfa8c3e30b;p=openpower-isa.git diff --git a/src/openpower/sv/sv_analysis.py b/src/openpower/sv/sv_analysis.py index d62b5875..332a0b2d 100644 --- a/src/openpower/sv/sv_analysis.py +++ b/src/openpower/sv/sv_analysis.py @@ -11,14 +11,49 @@ # It finds .csv files in the directory isatables/ # then goes through the categories and creates svp64 CSV augmentation # tables on a per-opcode basis - +# +# NOTE: this program is effectively part of the Simple-V Specification. +# it encapsulates the relationships of what can be SVP64-encoded and +# holds all of the information on how to encode and decode SVP64. +# By auto-generating tables that go into the Simple-V Specification +# this program *is* the specification. do not be confused just because +# it is in python: if you do not understand please ask questions and +# help create patches with explanatory comments. + +import argparse import csv +import enum import os from os.path import dirname, join from glob import glob +from collections import defaultdict from collections import OrderedDict from openpower.decoder.power_svp64 import SVP64RM from openpower.decoder.power_enums import find_wiki_file, get_csv +from openpower.util import log + + +# Ignore those containing: valid test sprs +def glob_valid_csvs(root): + def check_csv(fname): + _, name = os.path.split(fname) + if '-' in name: + return False + if 'valid' in fname: + return False + if 'test' in fname: + return False + if fname.endswith('insndb.csv'): + return False + if fname.endswith('sprs.csv'): + return False + if fname.endswith('minor_19_valid.csv'): + return False + if 'RM' in fname: + return False + return True + + yield from filter(check_csv, glob(root)) # Write an array of dictionaries to the CSV file name: @@ -62,6 +97,11 @@ tablecols = ['unit', 'in', 'outcnt', 'CR in', 'CR out', 'imm' def create_key(row): + """ create an equivalent of a database key by which it is possible + to easily categorise an instruction. later this category is used + to decide what kind of EXTRA encoding is to be done because the + key contains the total number of input and output registers + """ res = OrderedDict() #print ("row", row) for key in keycolumns: @@ -138,10 +178,13 @@ def dformat(d): def tformat(d): - return ' | '.join(d) + " |" + return "| " + ' | '.join(d) + " |" def keyname(row): + """converts a key into a readable string. anything null or zero + is skipped, shortening the readable string + """ res = [] if row['unit'] != 'OTHER': res.append(row['unit']) @@ -163,7 +206,83 @@ def keyname(row): return '-'.join(res) -def process_csvs(): +class Format(enum.Enum): + BINUTILS = enum.auto() + VHDL = enum.auto() + + @classmethod + def _missing_(cls, value): + return { + "binutils": Format.BINUTILS, + "vhdl": Format.VHDL, + }[value.lower()] + + def __str__(self): + return self.name.lower() + + def declarations(self, values, lens): + def declaration_binutils(value, width): + yield f"/* TODO: implement binutils declaration (value={value!r}, width={width!r}) */" + + def declaration_vhdl(value, width): + yield f" type sv_{value}_rom_array_t is " \ + f"array(0 to {width}) of sv_decode_rom_t;" + + for value in values: + if value not in lens: + todo = [f"TODO {value} (or no SVP64 augmentation)"] + todo = self.wrap_comment(todo) + yield from map(lambda line: f" {line}", todo) + else: + width = lens[value] + yield from { + Format.BINUTILS: declaration_binutils, + Format.VHDL: declaration_vhdl, + }[self](value, width) + + def definitions(self, entries_svp64, fullcols): + def definitions_vhdl(): + for (value, entries) in entries_svp64.items(): + yield "" + yield f" constant sv_{value}_decode_rom_array :" + yield f" sv_{value}_rom_array_t := (" + yield f" -- {' '.join(fullcols)}" + + for (op, insn, row) in entries: + yield f" {op:>13} => ({', '.join(row)}), -- {insn}" + + yield f" {'others':>13} => sv_illegal_inst" + yield " );" + yield "" + + def definitions_binutils(): + yield f"/* TODO: implement binutils definitions */" + + yield from { + Format.BINUTILS: definitions_binutils, + Format.VHDL: definitions_vhdl, + }[self]() + + def wrap_comment(self, lines): + def wrap_comment_binutils(lines): + lines = tuple(lines) + if len(lines) == 1: + yield f"/* {lines[0]} */" + else: + yield "/*" + yield from map(lambda line: f" * {line}", lines) + yield " */" + + def wrap_comment_vhdl(lines): + yield from map(lambda line: f"-- {line}", lines) + + yield from { + Format.BINUTILS: wrap_comment_binutils, + Format.VHDL: wrap_comment_vhdl, + }[self](lines) + + +def read_csvs(): csvs = {} csvs_svp64 = {} bykey = {} @@ -173,31 +292,11 @@ def process_csvs(): insns = {} # dictionary of CSV row, by instruction insn_to_csv = {} - print("# OpenPOWER ISA register 'profile's") - print('') - print("this page is auto-generated, do not edit") - print("created by http://libre-soc.org/openpower/sv_analysis.py") - print('') - # Expand that (all .csv files) pth = find_wiki_file("*.csv") # Ignore those containing: valid test sprs - for fname in glob(pth): - print("sv analysis checking", fname) - _, name = os.path.split(fname) - if '-' in name: - continue - if 'valid' in fname: - continue - if 'test' in fname: - continue - if fname.endswith('sprs.csv'): - continue - if fname.endswith('minor_19_valid.csv'): - continue - if 'RM' in fname: - continue + for fname in glob_valid_csvs(pth): csvname = os.path.split(fname)[1] csvname_ = csvname.split(".")[0] # csvname is something like: minor_59.csv, fname the whole path @@ -207,7 +306,7 @@ def process_csvs(): for row in csv: if blank_key(row): continue - print("row", row) + #print("row", row) insn_name = row['comment'] condition = row['CONDITIONS'] # skip instructions that are not suitable @@ -215,18 +314,26 @@ def process_csvs(): continue # skip pseudo-alias lxxxbr if insn_name in ['mcrxr', 'mcrxrx', 'darn']: continue - if insn_name in ['bctar', 'bcctr']: + if insn_name in ['bctar', 'bcctr']: # for now. TODO continue if 'rfid' in insn_name: continue - if insn_name in ['setvl', ]: # SVP64 opcodes + if 'addpcis' in insn_name: # skip for now continue + # sv.bc is being classified as 2P-2S-1D by mistake due to SPRs + if insn_name.startswith('bc'): + # whoops: remove out reg (SPRs CTR etc) + row['in1'] = 'NONE' + row['in2'] = 'NONE' + row['in3'] = 'NONE' + row['out'] = 'NONE' + insns[(insn_name, condition)] = row # accumulate csv data insn_to_csv[insn_name] = csvname_ # CSV file name by instruction dkey = create_key(row) key = tuple(dkey.values()) - # print("key=", key) + #print("key=", key, dkey) dictkeys[key] = dkey primarykeys.add(key) if key not in bykey: @@ -244,8 +351,306 @@ def process_csvs(): primarykeys = list(primarykeys) primarykeys.sort() + return (csvs, csvs_svp64, primarykeys, bykey, insn_to_csv, insns, + dictkeys, immediates) + + +def regs_profile(insn, res): + """get a more detailed register profile: 1st operand is RA, + 2nd is RB, etc. etc + """ + regs = [] + for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']: + if insn[k].startswith('CONST'): + res[k] = '' + regs.append('') + else: + res[k] = insn[k] + if insn[k] == 'RA_OR_ZERO': + regs.append('RA') + elif insn[k] != 'NONE': + regs.append(insn[k]) + else: + regs.append('') + return regs + + +def extra_classifier(insn_name, value, name, res, regs): + """extra_classifier: creates the SVP64.RM EXTRA2/3 classification. + there is very little space (9 bits) to mark register operands + (RT RA RB, BA BB, BFA, FRS etc.) with the "extra" information + needed to tell if *EACH* operand (of which there can be up to five!) + is Vectorised, and whether its numbering is extended into the + 0..127 range rather than the limited 3/5 bit of Scalar v3.0 Power ISA. + + thus begins the rather tedious but by-rote examination of EVERY + Scalar instruction, working out how best to tell a decoder how to + extend the registers. EXTRA2 can have up to 4 slots (of 2 bit each) + where due to RM.EXTRA being 9 bits, EXTRA3 can have up to 3 slots + (of 3 bit each). the index REGNAME says which slot the register + named REGNAME must read its decoding from. d: means destination, + s: means source. some are *shared slots* especially LDST update. + some Rc=1 ops have the CR0/CR1 as a co-result which is also + obviously Vectorised if the result is Vectorised. + + it is actually quite straightforward but the sheer quantity of + Scalar Power ISA instructions made it prudent to do this in an + intelligent way, almost by-rote, by analysing the register profiles. + """ + # for LD/ST FP, use FRT/FRS not RT/RS, and use CR1 not CR0 + if insn_name.startswith("lf"): + dRT = 'd:FRT' + dCR = 'd:CR1' + else: + dRT = 'd:RT' + dCR = 'd:CR0' + if insn_name.startswith("stf"): + sRS = 's:FRS' + dCR = 'd:CR1' + else: + sRS = 's:RS' + dCR = 'd:CR0' + + # sigh now the fun begins. this isn't the sanest way to do it + # but the patterns are pretty regular. we start with the "profile" + # because that determines how much space is available (total num + # regs to decode) then if necessary begin apecialising either + # by the instruction name or through more detailed register + # profiling. example: + # if regs == ['RA', '', '', 'RT', '', '']: + # is in the order in1 in2 in3 out1 out2 Rc=1 + + # ******** + # start with LD/ST + + if value == 'LDSTRM-2P-1S1D': + res['Etype'] = 'EXTRA3' # RM EXTRA3 type + res['0'] = dRT # RT: Rdest_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + + elif value == 'LDSTRM-2P-1S2D': + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = dRT # RT: Rdest_EXTRA3 + res['1'] = 'd:RA' # RA: Rdest2_EXTRA2 + res['2'] = 's:RA' # RA: Rsrc1_EXTRA2 + + elif value == 'LDSTRM-2P-2S': + # stw, std, sth, stb + res['Etype'] = 'EXTRA3' # RM EXTRA3 type + res['0'] = sRS # RS: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + + elif value == 'LDSTRM-2P-2S1D': + if 'st' in insn_name and 'x' not in insn_name: # stwu/stbu etc + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = 'd:RA' # RA: Rdest1_EXTRA2 + res['1'] = sRS # RS: Rdsrc1_EXTRA2 + res['2'] = 's:RA' # RA: Rsrc2_EXTRA2 + elif 'st' in insn_name and 'x' in insn_name: # stwux + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = 'd:RA' # RA: Rdest1_EXTRA2 + # RS: Rdest2_EXTRA2, RA: Rsrc1_EXTRA2 + res['1'] = "%s;%s" % (sRS, 's:RA') + res['2'] = 's:RB' # RB: Rsrc2_EXTRA2 + elif 'u' in insn_name: # ldux etc. + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = dRT # RT: Rdest1_EXTRA2 + res['1'] = 'd:RA' # RA: Rdest2_EXTRA2 + res['2'] = 's:RB' # RB: Rsrc1_EXTRA2 + else: + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = dRT # RT: Rdest1_EXTRA2 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 + res['2'] = 's:RB' # RB: Rsrc2_EXTRA2 + + elif value == 'LDSTRM-2P-3S': + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + if 'cx' in insn_name: + res['0'] = "%s;%s" % (sRS, dCR) # RS: Rsrc1_EXTRA2 CR0: dest + else: + res['0'] = sRS # RS: Rsrc1_EXTRA2 + res['1'] = 's:RA' # RA: Rsrc2_EXTRA2 + res['2'] = 's:RB' # RA: Rsrc3_EXTRA2 + + # ********** + # now begins,arithmetic + + elif value == 'RM-2P-1S1D': + res['Etype'] = 'EXTRA3' # RM EXTRA3 type + if insn_name == 'mtspr': + res['0'] = 'd:SPR' # SPR: Rdest1_EXTRA3 + res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 + elif insn_name == 'mfspr': + res['0'] = 'd:RS' # RS: Rdest1_EXTRA3 + res['1'] = 's:SPR' # SPR: Rsrc1_EXTRA3 + elif name == 'CRio' and insn_name == 'mcrf': + res['0'] = 'd:BF' # BFA: Rdest1_EXTRA3 + res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3 + elif 'mfcr' in insn_name or 'mfocrf' in insn_name: + res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 + res['1'] = 's:CR' # CR: Rsrc1_EXTRA3 + elif insn_name == 'setb': + res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 + res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3 + elif insn_name.startswith('cmp'): # cmpi + res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + elif regs == ['RA', '', '', 'RT', '', '']: + res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + elif regs == ['RA', '', '', 'RT', '', 'CR0']: + res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + elif (regs == ['RS', '', '', 'RA', '', 'CR0'] or + regs == ['', '', 'RS', 'RA', '', 'CR0']): + res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3 + res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 + elif regs == ['RS', '', '', 'RA', '', '']: + res['0'] = 'd:RA' # RA: Rdest1_EXTRA3 + res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 + elif regs == ['', 'FRB', '', 'FRT', '0', 'CR1']: + res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 + elif regs == ['', 'FRB', '', '', '', 'CR1']: + res['0'] = 'd:CR1' # CR1: Rdest1_EXTRA3 + res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3 + elif regs == ['', 'FRB', '', '', '', 'BF']: + res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 + res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3 + elif regs == ['', 'FRB', '', 'FRT', '', 'CR1']: + res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 + res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3 + elif insn_name == 'fishmv': + # an overwrite instruction + res['0'] = 'd:FRS' # FRS: Rdest1_EXTRA3 + res['1'] = 's:FRS' # FRS: Rsrc1_EXTRA3 + elif insn_name == 'setvl': + res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 + res['1'] = 's:RA' # RS: Rsrc1_EXTRA3 + else: + res['0'] = 'TODO' + print("regs TODO", insn_name, regs) + + elif value == 'RM-1P-2S1D': + res['Etype'] = 'EXTRA3' # RM EXTRA3 type + if insn_name.startswith('cr'): + res['0'] = 'd:BT' # BT: Rdest1_EXTRA3 + res['1'] = 's:BA' # BA: Rsrc1_EXTRA3 + res['2'] = 's:BB' # BB: Rsrc2_EXTRA3 + elif regs == ['FRA', '', 'FRC', 'FRT', '', 'CR1']: + res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 + res['2'] = 's:FRC' # FRC: Rsrc1_EXTRA3 + # should be for fcmp + elif regs == ['FRA', 'FRB', '', '', '', 'BF']: + res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 + res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3 + elif regs == ['FRA', 'FRB', '', 'FRT', '', '']: + res['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 + res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3 + elif regs == ['FRA', 'FRB', '', 'FRT', '', 'CR1']: + res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 + res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3 + elif regs == ['FRA', 'RB', '', 'FRT', '', 'CR1']: + res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 + res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 + elif name == '2R-1W' or insn_name == 'cmpb': # cmpb + if insn_name in ['bpermd', 'cmpb']: + res['0'] = 'd:RA' # RA: Rdest1_EXTRA3 + res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 + else: + res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 + elif insn_name.startswith('cmp'): # cmp + res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 + elif (regs == ['', 'RB', 'RS', 'RA', '', 'CR0'] or + regs == ['RS', 'RB', '', 'RA', '', 'CR0']): + res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3 + res['1'] = 's:RB' # RB: Rsrc1_EXTRA3 + res['2'] = 's:RS' # RS: Rsrc1_EXTRA3 + elif regs == ['RA', 'RB', '', 'RT', '', 'CR0']: + res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 + elif regs == ['RA', '', 'RS', 'RA', '', 'CR0']: + res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + res['2'] = 's:RS' # RS: Rsrc1_EXTRA3 + else: + res['0'] = 'TODO' + + elif value == 'RM-2P-2S1D': + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + if insn_name.startswith('mt'): # mtcrf + res['0'] = 'd:CR' # CR: Rdest1_EXTRA2 + res['1'] = 's:RS' # RS: Rsrc1_EXTRA2 + res['2'] = 's:CR' # CR: Rsrc2_EXTRA2 + else: + res['0'] = 'TODO' + + elif value == 'RM-1P-3S1D': + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + if regs == ['RA', 'RB', 'RC', 'RT', '', '']: # madd* + res['0'] = 'd:RT' # RT,CR0: Rdest1_EXTRA2 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 + res['2'] = 's:RB' # RT: Rsrc2_EXTRA2 + res['3'] = 's:RC' # RT: Rsrc3_EXTRA2 + elif regs == ['RA', 'RB', 'RC', 'RT', '', 'CR0']: # pcdec + res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 + res['2'] = 's:RB' # RT: Rsrc2_EXTRA2 + res['3'] = 's:RC' # RT: Rsrc3_EXTRA2 + elif regs == ['RA', 'RB', 'RT', 'RT', '', 'CR0']: # overwrite 3-in + res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 + res['2'] = 's:RB' # RT: Rsrc2_EXTRA2 + res['3'] = 's:RT' # RT: Rsrc3_EXTRA2 + elif insn_name == 'isel': + res['0'] = 'd:RT' # RT: Rdest1_EXTRA2 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 + res['2'] = 's:RB' # RT: Rsrc2_EXTRA2 + res['3'] = 's:BC' # BC: Rsrc3_EXTRA2 + else: # fmadd* + res['0'] = 'd:FRT;d:CR1' # FRT, CR1: Rdest1_EXTRA2 + res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA2 + res['2'] = 's:FRB' # FRB: Rsrc2_EXTRA2 + res['3'] = 's:FRC' # FRC: Rsrc3_EXTRA2 + + elif value == 'RM-1P-1D': + res['Etype'] = 'EXTRA3' # RM EXTRA3 type + if insn_name == 'svstep': + res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3 + if insn_name == 'fmvis': + res['0'] = 'd:FRS' # FRS: Rdest1_EXTRA3 + + # HACK! thos should be RM-1P-1S butvthere is a bug with sv.bc + elif value == 'RM-2P-1S': + res['Etype'] = 'EXTRA3' # RM EXTRA3 type + if insn_name.startswith('bc'): + res['0'] = 's:BI' # BI: Rsrc1_EXTRA3 + + +def process_csvs(format): + + print("# Draft SVP64 Power ISA register 'profile's") + print('') + print("this page is auto-generated, do not edit") + print("created by http://libre-soc.org/openpower/sv_analysis.py") + print('') + + (csvs, csvs_svp64, primarykeys, bykey, insn_to_csv, insns, + dictkeys, immediates) = read_csvs() + # mapping to old SVPrefix "Forms" mapsto = {'3R-1W-CRo': 'RM-1P-3S1D', + '3R-1W': 'RM-1P-3S1D', '2R-1W-CRio': 'RM-1P-2S1D', '2R-1W-CRi': 'RM-1P-3S1D', '2R-1W-CRo': 'RM-1P-2S1D', @@ -260,13 +665,14 @@ def process_csvs(): '1R-1W': 'RM-2P-1S1D', '1R-1W-imm': 'RM-2P-1S1D', '1R-CRo': 'RM-2P-1S1D', - '1R-imm': 'non-SV', + '1R-imm': 'RM-1P-1S', '1W-CRo': 'RM-1P-1D', '1W': 'non-SV', + '1W-imm': 'RM-1P-1D', '1W-CRi': 'RM-2P-1S1D', 'CRio': 'RM-2P-1S1D', 'CR=2R1W': 'RM-1P-2S1D', - 'CRi': 'non-SV', + 'CRi': 'RM-2P-1S', # HACK, bc here, it should be 1P 'imm': 'non-SV', '': 'non-SV', 'LDST-2R-imm': 'LDSTRM-2P-2S', @@ -281,18 +687,19 @@ def process_csvs(): } print("# map to old SV Prefix") print('') - print('[[!table data="""') + print('|internal key | public name |') + print('|----- | ---------- |') for key in primarykeys: name = keyname(dictkeys[key]) value = mapsto.get(name, "-") print(tformat([name, value + " "])) - print('"""]]') + print('') print('') print("# keys") print('') - print('[[!table data="""') print(tformat(tablecols) + " imms | name |") + print(tformat([" - "] * (len(tablecols)+2))) # print out the keys and the table from which they're derived for key in primarykeys: @@ -303,7 +710,7 @@ def process_csvs(): row += " %s | " % ("/".join(imms)) row += " %s |" % name print(row) - print('"""]]') + print('') print('') # print out, by remap name, all the instructions under that category @@ -312,13 +719,13 @@ def process_csvs(): value = mapsto.get(name, "-") print("## %s (%s)" % (name, value)) print('') - print('[[!table data="""') - print(tformat(['CSV', 'opcode', 'asm', 'form'])) + print(tformat(['CSV', 'opcode', 'asm', 'flags', 'form'])) + print(tformat(['---', '------', '---', '-----', '----'])) rows = bykey[key] rows.sort() for row in rows: print(tformat(row)) - print('"""]]') + print('') print('') # for fname, csv in csvs.items(): @@ -332,7 +739,8 @@ def process_csvs(): # create a CSV file, per category, with SV "augmentation" info # XXX note: 'out2' not added here, needs to be added to CSV files # KEEP TRACK OF THESE https://bugs.libre-soc.org/show_bug.cgi?id=619 - csvcols = ['insn', 'CONDITIONS', 'Ptype', 'Etype', '0', '1', '2', '3'] + csvcols = ['insn', 'mode', 'CONDITIONS', 'Ptype', 'Etype', 'SM'] + csvcols += ['0', '1', '2', '3'] csvcols += ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out'] # temporary for key in primarykeys: # get the decoded key containing row-analysis, and name/value @@ -357,10 +765,11 @@ def process_csvs(): # if row[idx] == 'NONE': # row[idx] = '' # get the instruction - print(key, row) + #print(key, row) insn_name = row[2] condition = row[3] insn = insns[(insn_name, condition)] + # start constructing svp64 CSV row res = OrderedDict() res['insn'] = insn_name @@ -377,223 +786,38 @@ def process_csvs(): if insn['upd'] == '1': # LD/ST with update has RA as out2 res['out2'] = 'RA' - # temporary useful info - regs = [] - for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']: - if insn[k].startswith('CONST'): - res[k] = '' - regs.append('') + # set the SVP64 mode to NORMAL, LDST, BRANCH or CR + crops = ['mfcr', 'mfocrf', 'mtcrf', 'mtocrf', + ] + mode = 'NORMAL' + if value.startswith('LDST'): + if 'x' in insn_name: # Indexed detection + mode = 'LDST_IDX' else: - res[k] = insn[k] - if insn[k] == 'RA_OR_ZERO': - regs.append('RA') - elif insn[k] != 'NONE': - regs.append(insn[k]) - else: - regs.append('') - - print("regs", insn_name, regs) - - # for LD/ST FP, use FRT/FRS not RT/RS, and use CR1 not CR0 - if insn_name.startswith("lf"): - dRT = 'd:FRT' - dCR = 'd:CR1' + mode = 'LDST_IMM' + elif insn_name.startswith('bc'): + mode = 'BRANCH' + elif insn_name.startswith('cmp') or insn_name.startswith('cr') or insn_name in crops: + mode = 'CROP' + res['mode'] = mode + + # create a register profile list (update res row as well) + regs = regs_profile(insn, res) + + #print("regs", insn_name, regs) + extra_classifier(insn_name, value, name, res, regs) + + # source-mask is hard to detect, it's part of RM-nn-nn. + # to make disassembler easier, create a yes/no decision here + # see https://libre-soc.org/openpower/sv/svp64/#extra_remap + # MASK_SRC + vstripped = value.replace("LDST", "") + if vstripped in ['RM-2P-1S1D', 'RM-2P-2S', + 'RM-2P-2S1D', 'RM-2P-1S2D', 'RM-2P-3S', + ]: + res['SM'] = 'EN' else: - dRT = 'd:RT' - dCR = 'd:CR0' - if insn_name.startswith("stf"): - sRS = 's:FRS' - dCR = 'd:CR1' - else: - sRS = 's:RS' - dCR = 'd:CR0' - - # sigh now the fun begins. this isn't the sanest way to do it - # but the patterns are pretty regular. - - if value == 'LDSTRM-2P-1S1D': - res['Etype'] = 'EXTRA3' # RM EXTRA3 type - res['0'] = dRT # RT: Rdest_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - - elif value == 'LDSTRM-2P-1S2D': - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - res['0'] = dRT # RT: Rdest_EXTRA3 - res['1'] = 'd:RA' # RA: Rdest2_EXTRA2 - res['2'] = 's:RA' # RA: Rsrc1_EXTRA2 - - elif value == 'LDSTRM-2P-2S': - # stw, std, sth, stb - res['Etype'] = 'EXTRA3' # RM EXTRA2 type - res['0'] = sRS # RS: Rdest1_EXTRA2 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 - - elif value == 'LDSTRM-2P-2S1D': - if 'st' in insn_name and 'x' not in insn_name: # stwu/stbu etc - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - res['0'] = 'd:RA' # RA: Rdest1_EXTRA2 - res['1'] = sRS # RS: Rdsrc1_EXTRA2 - res['2'] = 's:RA' # RA: Rsrc2_EXTRA2 - elif 'st' in insn_name and 'x' in insn_name: # stwux - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - res['0'] = 'd:RA' # RA: Rdest1_EXTRA2 - # RS: Rdest2_EXTRA2, RA: Rsrc1_EXTRA2 - res['1'] = sRS+'s:RA' - res['2'] = 's:RB' # RB: Rsrc2_EXTRA2 - elif 'u' in insn_name: # ldux etc. - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - res['0'] = dRT # RT: Rdest1_EXTRA2 - res['1'] = 'd:RA' # RA: Rdest2_EXTRA2 - res['2'] = 's:RB' # RB: Rsrc1_EXTRA2 - else: - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - res['0'] = dRT # RT: Rdest1_EXTRA2 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 - res['2'] = 's:RB' # RB: Rsrc2_EXTRA2 - - elif value == 'LDSTRM-2P-3S': - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - if 'cx' in insn_name: - res['0'] = sRS+dCR # RS: Rsrc1_EXTRA2 CR0: dest - else: - res['0'] = sRS # RS: Rsrc1_EXTRA2 - res['1'] = 's:RA' # RA: Rsrc2_EXTRA2 - res['2'] = 's:RB' # RA: Rsrc3_EXTRA2 - - elif value == 'RM-2P-1S1D': - res['Etype'] = 'EXTRA3' # RM EXTRA3 type - if insn_name == 'mtspr': - res['0'] = 'd:SPR' # SPR: Rdest1_EXTRA3 - res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 - elif insn_name == 'mfspr': - res['0'] = 'd:RS' # RS: Rdest1_EXTRA3 - res['1'] = 's:SPR' # SPR: Rsrc1_EXTRA3 - elif name == 'CRio' and insn_name == 'mcrf': - res['0'] = 'd:BF' # BFA: Rdest1_EXTRA3 - res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3 - elif 'mfcr' in insn_name or 'mfocrf' in insn_name: - res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 - res['1'] = 's:CR' # CR: Rsrc1_EXTRA3 - elif insn_name == 'setb': - res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 - res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3 - elif insn_name.startswith('cmp'): # cmpi - res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - elif regs == ['RA', '', '', 'RT', '', '']: - res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - elif regs == ['RA', '', '', 'RT', '', 'CR0']: - res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - elif (regs == ['RS', '', '', 'RA', '', 'CR0'] or - regs == ['', '', 'RS', 'RA', '', 'CR0']): - res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3 - res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 - elif regs == ['RS', '', '', 'RA', '', '']: - res['0'] = 'd:RA' # RA: Rdest1_EXTRA3 - res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 - elif regs == ['', 'FRB', '', 'FRT', '0', 'CR1']: - res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 - res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 - elif regs == ['', 'FRB', '', '', '', 'CR1']: - res['0'] = 'd:CR1' # CR1: Rdest1_EXTRA3 - res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3 - elif regs == ['', 'FRB', '', '', '', 'BF']: - res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 - res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3 - elif regs == ['', 'FRB', '', 'FRT', '', 'CR1']: - res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 - res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3 - elif insn_name.startswith('bc'): - res['0'] = 'd:BI' # BI: Rdest1_EXTRA3 - res['1'] = 's:BI' # BI: Rsrc1_EXTRA3 - else: - res['0'] = 'TODO' - - elif value == 'RM-1P-2S1D': - res['Etype'] = 'EXTRA3' # RM EXTRA3 type - if insn_name.startswith('cr'): - res['0'] = 'd:BT' # BT: Rdest1_EXTRA3 - res['1'] = 's:BA' # BA: Rsrc1_EXTRA3 - res['2'] = 's:BB' # BB: Rsrc2_EXTRA3 - elif regs == ['FRA', '', 'FRC', 'FRT', '', 'CR1']: - res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 - res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 - res['2'] = 's:FRC' # FRC: Rsrc1_EXTRA3 - # should be for fcmp - elif regs == ['FRA', 'FRB', '', '', '', 'BF']: - res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 - res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 - res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3 - elif regs == ['FRA', 'FRB', '', 'FRT', '', '']: - res['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3 - res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 - res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3 - elif regs == ['FRA', 'FRB', '', 'FRT', '', 'CR1']: - res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3 - res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3 - res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3 - elif name == '2R-1W' or insn_name == 'cmpb': # cmpb - if insn_name in ['bpermd', 'cmpb']: - res['0'] = 'd:RA' # RA: Rdest1_EXTRA3 - res['1'] = 's:RS' # RS: Rsrc1_EXTRA3 - else: - res['0'] = 'd:RT' # RT: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 - elif insn_name.startswith('cmp'): # cmp - res['0'] = 'd:BF' # BF: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 - elif (regs == ['', 'RB', 'RS', 'RA', '', 'CR0'] or - regs == ['RS', 'RB', '', 'RA', '', 'CR0']): - res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3 - res['1'] = 's:RB' # RB: Rsrc1_EXTRA3 - res['2'] = 's:RS' # RS: Rsrc1_EXTRA3 - elif regs == ['RA', 'RB', '', 'RT', '', 'CR0']: - res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - res['2'] = 's:RB' # RB: Rsrc1_EXTRA3 - elif regs == ['RA', '', 'RS', 'RA', '', 'CR0']: - res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 - res['2'] = 's:RS' # RS: Rsrc1_EXTRA3 - else: - res['0'] = 'TODO' - - elif value == 'RM-2P-2S1D': - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - if insn_name.startswith('mt'): # mtcrf - res['0'] = 'd:CR' # CR: Rdest1_EXTRA2 - res['1'] = 's:RS' # RS: Rsrc1_EXTRA2 - res['2'] = 's:CR' # CR: Rsrc2_EXTRA2 - else: - res['0'] = 'TODO' - - elif value == 'RM-1P-3S1D': - res['Etype'] = 'EXTRA2' # RM EXTRA2 type - if regs == ['RA', 'RB', 'RT', 'RT', '', 'CR0']: - res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 - res['2'] = 's:RB' # RT: Rsrc2_EXTRA2 - res['3'] = 's:RT' # RT: Rsrc3_EXTRA2 - elif insn_name == 'isel': - res['0'] = 'd:RT' # RT: Rdest1_EXTRA2 - res['1'] = 's:RA' # RA: Rsrc1_EXTRA2 - res['2'] = 's:RB' # RT: Rsrc2_EXTRA2 - res['3'] = 's:BC' # BC: Rsrc3_EXTRA2 - else: - res['0'] = 'd:FRT;d:CR1' # FRT, CR1: Rdest1_EXTRA2 - res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA2 - res['2'] = 's:FRB' # FRB: Rsrc2_EXTRA2 - res['3'] = 's:FRC' # FRC: Rsrc3_EXTRA2 - - elif value == 'RM-1P-1D': - res['Etype'] = 'EXTRA3' # RM EXTRA3 type - if insn_name == 'svstep': - res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2 - + res['SM'] = 'NO' # add to svp64 csvs # for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']: # del res[k] @@ -612,11 +836,23 @@ def process_csvs(): # now write out the csv files for value, csv in svp64.items(): + if value == '-': + continue + from time import sleep + print("WARNING, filename '-' should NOT exist. instrs missing") + print("TODO: fix this (and put in the bugreport number here)") + sleep(2) # print out svp64 tables by category print("## %s" % value) print('') - print('[[!table format=csv file="openpower/isatables/%s.csv"]]' % - value) + cols = csvcols + ['out2'] + print(tformat(cols)) + print(tformat([" - "] * (len(cols)))) + for d in csv: + row = [] + for k in cols: + row.append(d[k]) + print(tformat(row)) print('') #csvcols = ['insn', 'Ptype', 'Etype', '0', '1', '2', '3'] @@ -630,106 +866,115 @@ def process_csvs(): pth = find_wiki_file("*.csv") # Ignore those containing: valid test sprs - for fname in glob(pth): - print("post-checking", fname) - _, name = os.path.split(fname) - if '-' in name: - continue - if 'valid' in fname: - continue - if 'test' in fname: - continue - if fname.endswith('sprs.csv'): - continue - if fname.endswith('minor_19_valid.csv'): - continue - if 'RM' in fname: - continue + for fname in glob_valid_csvs(pth): svp64_csv = svt.get_svp64_csv(fname) - csvcols = ['insn', 'Ptype', 'Etype'] + csvcols = ['insn', 'mode', 'Ptype', 'Etype', 'SM'] csvcols += ['in1', 'in2', 'in3', 'out', 'out2', 'CR in', 'CR out'] - # and a nice microwatt VHDL file - file_path = find_wiki_file("sv_decode.vhdl") - with open(file_path, 'w') as vhdl: - # autogeneration warning - vhdl.write("-- this file is auto-generated, do not edit\n") - vhdl.write("-- http://libre-soc.org/openpower/sv_analysis.py\n") - vhdl.write("-- part of Libre-SOC, sponsored by NLnet\n") - vhdl.write("\n") - - # first create array types - lens = {'major': 63, - 'minor_4': 63, - 'minor_19': 7, - 'minor_30': 15, - 'minor_31': 1023, - 'minor_58': 63, - 'minor_59': 31, - 'minor_62': 63, - 'minor_63l': 511, - 'minor_63h': 16, - } - for value, csv in csvs_svp64.items(): - # munge name - value = value.lower() - value = value.replace("-", "_") - if value not in lens: - todo = " -- TODO %s (or no SVP64 augmentation)\n" - vhdl.write(todo % value) - continue - width = lens[value] - typarray = " type sv_%s_rom_array_t is " \ - "array(0 to %d) of sv_decode_rom_t;\n" - vhdl.write(typarray % (value, width)) - - # now output structs - sv_cols = ['sv_in1', 'sv_in2', 'sv_in3', 'sv_out', 'sv_out2', - 'sv_cr_in', 'sv_cr_out'] - fullcols = csvcols + sv_cols - hdr = "\n" \ - " constant sv_%s_decode_rom_array :\n" \ - " sv_%s_rom_array_t := (\n" \ - " -- %s\n" - ftr = " others => sv_illegal_inst\n" \ - " );\n\n" - for value, csv in csvs_svp64.items(): - # munge name - value = value.lower() - value = value.replace("-", "_") - if value not in lens: - continue - vhdl.write(hdr % (value, value, " ".join(fullcols))) - for entry in csv: - insn = str(entry['insn']) - condition = str(entry['CONDITIONS']) - sventry = svt.svp64_instrs.get(insn, None) - op = insns[(insn, condition)]['opcode'] - # binary-to-vhdl-binary - if op.startswith("0b"): - op = "2#%s#" % op[2:] - row = [] - for colname in csvcols[1:]: - re = entry[colname] - # zero replace with NONE - if re == '0': - re = 'NONE' - # 1/2 predication - re = re.replace("1P", "P1") - re = re.replace("2P", "P2") - row.append(re) - print("sventry", sventry) - for colname in sv_cols: - if sventry is None: - re = 'NONE' - else: - re = sventry[colname] - row.append(re) - row = ', '.join(row) - vhdl.write(" %13s => (%s), -- %s\n" % (op, row, insn)) - vhdl.write(ftr) + if format is Format.VHDL: + # and a nice microwatt VHDL file + file_path = find_wiki_file("sv_decode.vhdl") + elif format is Format.BINUTILS: + file_path = find_wiki_file("binutils.c") + + with open(file_path, 'w') as stream: + output(format, svt, csvcols, insns, csvs_svp64, stream) + + +def output_autogen_disclaimer(format, stream): + lines = ( + "this file is auto-generated, do not edit", + "http://libre-soc.org/openpower/sv_analysis.py", + "part of Libre-SOC, sponsored by NLnet", + ) + for line in format.wrap_comment(lines): + stream.write(line) + stream.write("\n") + stream.write("\n") + + +def output(format, svt, csvcols, insns, csvs_svp64, stream): + lens = { + 'major': 63, + 'minor_4': 63, + 'minor_19': 7, + 'minor_30': 15, + 'minor_31': 1023, + 'minor_58': 63, + 'minor_59': 31, + 'minor_62': 63, + 'minor_63l': 511, + 'minor_63h': 16, + } + + def svp64_canonicalize(item): + (value, csv) = item + value = value.lower().replace("-", "_") + return (value, csv) + + csvs_svp64_canon = dict(map(svp64_canonicalize, csvs_svp64.items())) + + # disclaimer + output_autogen_disclaimer(format, stream) + + # declarations + for line in format.declarations(csvs_svp64_canon.keys(), lens): + stream.write(f"{line}\n") + + # definitions + sv_cols = ['sv_in1', 'sv_in2', 'sv_in3', 'sv_out', 'sv_out2', + 'sv_cr_in', 'sv_cr_out'] + fullcols = csvcols + sv_cols + + entries_svp64 = defaultdict(list) + for (value, csv) in filter(lambda kv: kv[0] in lens, csvs_svp64_canon.items()): + for entry in csv: + insn = str(entry['insn']) + condition = str(entry['CONDITIONS']) + mode = str(entry['mode']) + sventry = svt.svp64_instrs.get(insn, None) + if sventry is not None: + sventry['mode'] = mode + op = insns[(insn, condition)]['opcode'] + # binary-to-vhdl-binary + if op.startswith("0b"): + op = "2#%s#" % op[2:] + row = [] + for colname in csvcols[1:]: + re = entry[colname] + # zero replace with NONE + if re == '0': + re = 'NONE' + # 1/2 predication + re = re.replace("1P", "P1") + re = re.replace("2P", "P2") + row.append(re) + #print("sventry", sventry) + for colname in sv_cols: + if sventry is None: + re = 'NONE' + else: + re = sventry[colname] + row.append(re) + entries_svp64[value].append((op, insn, row)) + + for line in format.definitions(entries_svp64, fullcols): + stream.write(f"{line}\n") + + +def main(): + import os + os.environ['SILENCELOG'] = '1' + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--format", + type=Format, choices=Format, default=Format.VHDL, + help="format to be used (binutils or VHDL)") + args = parser.parse_args() + process_csvs(args.format) if __name__ == '__main__': - process_csvs() + # don't do anything other than call main() here, cuz this code is bypassed + # by the sv_analysis command created by setup.py + main()