X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=openpower%2Fsv_analysis.py;h=487602312b0680fd6820017bb1a15d2f147345e1;hb=ee860187c6508de5e246799e3580bfc7766a8aaf;hp=91a1dbb03cc3880ec247e4308d48c20fd67ea2dd;hpb=e5fab95dcfc6ff22b922db3c951a217430342dd8;p=libreriscv.git diff --git a/openpower/sv_analysis.py b/openpower/sv_analysis.py index 91a1dbb03..487602312 100644 --- a/openpower/sv_analysis.py +++ b/openpower/sv_analysis.py @@ -1,48 +1,108 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python2 +# +# NOTE that this program is python2 compatible, please do not stop it +# from working by adding syntax that prevents that. +# +# Initial version written by lkcl Oct 2020 +# This program analyses the Power 9 op codes and looks at in/out register uses +# The results are displayed: +# https://libre-soc.org/openpower/opcode_regs_deduped/ +# +# It finds .csv files in the directory isatables/ +# then goes through the categories and creates svp64 CSV augmentation +# tables on a per-opcode basis + import csv import os from os.path import dirname, join from glob import glob from collections import OrderedDict - +# Return absolute path (ie $PWD) + isatables + name def find_wiki_file(name): filedir = os.path.dirname(os.path.abspath(__file__)) tabledir = join(filedir, 'isatables') - file_path = join(tabledir, name) + file_path = join(tabledir, name) return file_path +# Return an array of dictionaries from the CSV file name: def get_csv(name): file_path = find_wiki_file(name) with open(file_path, 'r') as csvfile: reader = csv.DictReader(csvfile) return list(reader) +# Write an array of dictionaries to the CSV file name: +def write_csv(name, items, headers): + file_path = find_wiki_file(name) + with open(file_path, 'wb') as csvfile: + writer = csv.DictWriter(csvfile, headers, lineterminator="\n") + writer.writeheader() + writer.writerows(items) + +# This will return True if all values are true. +# Not sure what this is about def blank_key(row): - for v in row.items(): + #for v in row.values(): + # if 'SPR' in v: # skip all SPRs + # return True + for v in row.values(): if v: return False return True -keycolumns = ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out', - 'ldst len', 'rc', 'lk'] +# General purpose registers have names like: RA, RT, R1, ... +# Floating point registers names like: FRT, FRA, FR1, ..., FRTp, ... +# Return True if field is a register +def isreg(field): + return (field.startswith('R') or field.startswith('FR') or + field == 'SPR') + + +# These are the attributes of the instructions, +# register names +keycolumns = ['unit', 'in1', 'in2', 'in3', 'out', 'CR in', 'CR out', + ] # don't think we need these: 'ldst len', 'rc', 'lk'] + +tablecols = ['unit', 'in', 'outcnt', 'CR in', 'CR out', 'imm' + ] # don't think we need these: 'ldst len', 'rc', 'lk'] + def create_key(row): res = OrderedDict() #print ("row", row) for key in keycolumns: - # registers - if key in ['in1', 'in2', 'in3', 'out']: - if row[key].startswith('R'): - res[key] = 'R' - else: - res[key] = '0' - # CRs + # registers IN - special-case: count number of regs RA/RB/RC/RS + if key in ['in1', 'in2', 'in3']: + if 'in' not in res: + res['in'] = 0 + if isreg(row[key]): + res['in'] += 1 + + # registers OUT + if key == 'out': + # If upd is 1 then increment the count of outputs + if 'outcnt' not in res: + res['outcnt'] = 0 + if isreg(row[key]): + res['outcnt'] += 1 + if row['upd'] == '1': + res['outcnt'] += 1 + + # CRs (Condition Register) (CR0 .. CR7) if key.startswith('CR'): if row[key].startswith('NONE'): res[key] = '0' else: res[key] = '1' - # LDST len + if row['comment'].startswith('cr'): + res['crop'] = '1' + # unit + if key == 'unit': + if row[key] == 'LDST': # we care about LDST units + res[key] = row[key] + else: + res[key] = 'OTHER' + # LDST len (LoadStore length) if key.startswith('ldst'): if row[key].startswith('NONE'): res[key] = '0' @@ -59,8 +119,20 @@ def create_key(row): if key == 'lk': res[key] = row[key] + # Convert the numerics 'in' & 'outcnt' to strings + res['in'] = str(res['in']) + res['outcnt'] = str(res['outcnt']) + + + # constants + if row['in2'].startswith('CONST_'): + res['imm'] = "1" # row['in2'].split("_")[1] + else: + res['imm'] = '' + return res +# def dformat(d): res = [] for k, v in d.items(): @@ -68,55 +140,425 @@ def dformat(d): return ' '.join(res) def tformat(d): - return "|" + ' | '.join(d) + "|" + return ' | '.join(d) + " |" + +def keyname(row): + res = [] + if row['unit'] != 'OTHER': + res.append(row['unit']) + if row['in'] != '0': + res.append('%sR' % row['in']) + if row['outcnt'] != '0': + res.append('%sW' % row['outcnt']) + if row['CR in'] == '1' and row['CR out'] == '1': + if 'crop' in row: + res.append("CR=2R1W") + else: + res.append("CRio") + elif row['CR in'] == '1': + res.append("CRi") + elif row['CR out'] == '1': + res.append("CRo") + elif 'imm' in row and row['imm']: + res.append("imm") + return '-'.join(res) def process_csvs(): csvs = {} bykey = {} primarykeys = set() - dictkeys = {} + dictkeys = OrderedDict() + immediates = {} + insns = {} # dictionary of CSV row, by instruction + + 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): + if '-' in fname: + continue if 'valid' in fname: continue if 'test' in fname: continue - if 'sprs' in fname: + if fname.endswith('sprs.csv'): + continue + if fname.endswith('minor_19_valid.csv'): + continue + if 'RM' in fname: continue #print (fname) csvname = os.path.split(fname)[1] + # csvname is something like: minor_59.csv, fname the whole path csv = get_csv(fname) csvs[fname] = csv for row in csv: if blank_key(row): continue + insn_name = row['comment'] + # skip instructions that are not suitable + if insn_name in ['mcrxr', 'mcrxrx', 'darn']: + continue + if insn_name.startswith('bc') or 'rfid' in insn_name: + continue + insns[insn_name] = row # accumulate csv data by instruction dkey = create_key(row) key = tuple(dkey.values()) + # print("key=", key) dictkeys[key] = dkey primarykeys.add(key) if key not in bykey: bykey[key] = [] - bykey[key].append((csvname, row['opcode'], row['comment'])) + bykey[key].append((csvname, row['opcode'], insn_name, + row['form'].upper() + '-Form')) + + # detect immediates, collate them (useful info) + if row['in2'].startswith('CONST_'): + imm = row['in2'].split("_")[1] + if key not in immediates: + immediates[key] = set() + immediates[key].add(imm) + + primarykeys = list(primarykeys) + primarykeys.sort() + + # mapping to old SVPrefix "Forms" + mapsto = {'3R-1W-CRio': 'RM-1P-3S1D', + '2R-1W-CRio': 'RM-1P-2S1D', + '2R-1W-CRi': 'RM-1P-3S1D', + '2R-1W-CRo': 'RM-1P-2S1D', + '2R': 'non-SV', + '2R-1W': 'RM-1P-2S1D', + '1R-CRio': 'RM-2P-2S1D', + '2R-CRio': 'RM-1P-2S1D', + '2R-CRo': 'RM-1P-2S1D', + '1R': 'non-SV', + '1R-1W-CRio': 'RM-2P-1S1D', + '1R-1W-CRo': 'RM-2P-1S1D', + '1R-1W': 'RM-2P-1S1D', + '1R-1W-imm': 'RM-2P-1S1D', + '1R-CRo': 'RM-2P-1S1D', + '1R-imm': 'non-SV', + '1W': 'non-SV', + '1W-CRi': 'RM-2P-1S1D', + 'CRio': 'RM-2P-1S1D', + 'CR=2R1W': 'RM-1P-2S1D', + 'CRi': 'non-SV', + 'imm': 'non-SV', + '': 'non-SV', + 'LDST-2R-imm': 'LDSTRM-2P-2S', + 'LDST-2R-1W-imm': 'LDSTRM-2P-2S1D', + 'LDST-2R-1W': 'LDSTRM-2P-2S1D', + 'LDST-2R-2W': 'LDSTRM-2P-2S1D', + 'LDST-1R-1W-imm': 'LDSTRM-2P-1S1D', + 'LDST-1R-2W-imm': 'LDSTRM-2P-1S2D', + 'LDST-3R': 'LDSTRM-2P-3S', + 'LDST-3R-CRo': 'LDSTRM-2P-3S', # st*x + 'LDST-3R-1W': 'LDSTRM-2P-2S1D', # st*x + } + print ("# map to old SV Prefix") + print ('') + print ('[[!table data="""') + for key in primarykeys: + name = keyname(dictkeys[key]) + value = mapsto.get(name, "-") + print (tformat([name, value+ " "])) + print ('"""]]') + print ('') print ("# keys") - print () - print (tformat(keycolumns)) + print ('') + print ('[[!table data="""') + print (tformat(tablecols) + " imms | name |") + + # print out the keys and the table from which they're derived for key in primarykeys: - print (tformat(dictkeys[key].values())) - print () + name = keyname(dictkeys[key]) + row = tformat(dictkeys[key].values()) + imms = list(immediates.get(key, "")) + imms.sort() + row += " %s | " % ("/".join(imms)) + row += " %s |" % name + print (row) + print ('"""]]') + print ('') + # print out, by remap name, all the instructions under that category for key in primarykeys: - print ("## ", dformat(dictkeys[key])) - print () - for row in bykey[key]: - print (" * ", row) - print () + name = keyname(dictkeys[key]) + value = mapsto.get(name, "-") + print ("## %s (%s)" % (name, value)) + print ('') + print ('[[!table data="""') + print (tformat(['CSV', 'opcode', 'asm', 'form'])) + rows = bykey[key] + rows.sort() + for row in rows: + print (tformat(row)) + print ('"""]]') + print ('') - bykey = {} - for fname, csv in csvs.items(): - key + #for fname, csv in csvs.items(): + # print (fname) + + #for insn, row in insns.items(): + # print (insn, row) + + print ("# svp64 remaps") + svp64 = OrderedDict() + # create a CSV file, per category, with SV "augmentation" info + csvcols = ['insn', 'Ptype', 'Etype', '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 + dkey = dictkeys[key] + name = keyname(dkey) + value = mapsto.get(name, "-") + if value == 'non-SV': + continue + + # print out svp64 tables by category + print ("* **%s**: %s" % (name, value)) + + # store csv entries by svp64 RM category + if value not in svp64: + svp64[value] = [] + + rows = bykey[key] + rows.sort() + + for row in rows: + #for idx in range(len(row)): + # if row[idx] == 'NONE': + # row[idx] = '' + # get the instruction + insn_name = row[2] + insn = insns[insn_name] + # start constructing svp64 CSV row + res = OrderedDict() + res['insn'] = insn_name + res['Ptype'] = value.split('-')[1] # predication type (RM-xN-xxx) + # get whether R_xxx_EXTRAn fields are 2-bit or 3-bit + res['Etype'] = 'EXTRA2' + # go through each register matching to Rxxxx_EXTRAx + for k in ['0', '1', '2', '3']: + res[k] = '' + + # temporary useful info + 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('') + + # 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'] = 'd:RT' # RT: Rdest_EXTRA3 + res['1'] = 's:RA' # RA: Rsrc1_EXTRA3 + + elif value == 'LDSTRM-2P-1S2D': + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = 'd:RT' # RT: Rdest1_EXTRA2 + res['1'] = 'd:RA' # RA: Rdest2_EXTRA2 + res['2'] = 's:RA' # RA: Rsrc1_EXTRA2 + + elif value == 'LDSTRM-2P-2S': + res['Etype'] = 'EXTRA3' # RM EXTRA2 type + res['0'] = 'd:RS' # RT: 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'] = 's:RS' # 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 + res['1'] = 's:RS,s:RA' # RS: Rdest2_EXTRA2, RA: Rsrc1_EXTRA2 + res['2'] = 's:RB' # RB: Rsrc2_EXTRA2 + elif 'u' in insn_name: # ldux etc. + res['Etype'] = 'EXTRA2' # RM EXTRA2 type + res['0'] = 'd:RT' # 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'] = 'd:RT' # 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:RS,d:CR0' # RS: Rsrc1_EXTRA2 CR0: dest + else: + res['0'] = 's:RS' # 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 + 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 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 + + # add to svp64 csvs + #for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']: + # del res[k] + #if res['0'] != 'TODO': + for k in res: + if res[k] == 'NONE' or res[k] == '': + res[k] = '0' + svp64[value].append(res) + + print ('') + + # now write out the csv files + for value, csv in svp64.items(): + # print out svp64 tables by category + print ("## %s" % value) + print ('') + print ('[[!table format=csv file="openpower/isatables/%s.csv"]]' % \ + value) + print ('') + + #csvcols = ['insn', 'Ptype', 'Etype', '0', '1', '2', '3'] + write_csv("%s.csv" % value, csv, csvcols) if __name__ == '__main__': process_csvs()