#!/usr/bin/env python3
+# 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/
+
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)
+# This will return True if all values are true.
+# Not sure what this is about
+
+
def blank_key(row):
- #for v in row.values():
- # if 'SPR' in v: # skip all SPRs
- # return True
+ # 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
+# 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')
+# 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']
+ ] # 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']
-tablecols = ['unit', 'in', 'out', 'CR in', 'CR out',
- ] # don't think we need these: 'ldst len', 'rc', 'lk']
def create_key(row):
res = OrderedDict()
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[key] = 'R'
- else:
- res[key] = '0'
- # CRs
+ 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'
+ if row['comment'].startswith('cr'):
+ res['crop'] = '1'
# unit
if key == 'unit':
- if row[key] == 'LDST': # we care about LDST units
+ if row[key] == 'LDST': # we care about LDST units
res[key] = row[key]
else:
res[key] = 'OTHER'
- # LDST len
+ # LDST len (LoadStore length)
if key.startswith('ldst'):
if row[key].startswith('NONE'):
res[key] = '0'
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():
res.append("%s: %s" % (k, v))
return ' '.join(res)
-def tformat(d):
- return ' | '.join(d) + "|"
+
+def print_table_row(*cols):
+ print(f"| {' | '.join(cols)} |")
+
+
+def print_table_header(*cols):
+ print_table_row(*cols)
+ print_table_row(*map(lambda v: '-'*max(1, len(v)), cols))
+
def keyname(row):
- if row['out'] == 'R':
- out = '1'
- else:
- out = '0'
- res = '%sR-%sW' % (row['in'], out)
+ 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':
- res += "-CRio"
+ if 'crop' in row:
+ res.append("CR=2R1W")
+ else:
+ res.append("CRio")
elif row['CR in'] == '1':
- res += "-CRi"
+ res.append("CRi")
elif row['CR out'] == '1':
- res += "-CRo"
- if row['unit'] != 'OTHER':
- return '%s-' % row['unit'] + res
- return res
+ res.append("CRo")
+ elif 'imm' in row and row['imm']:
+ res.append("imm")
+ return '-'.join(res)
def process_csvs():
bykey = {}
primarykeys = set()
dictkeys = OrderedDict()
+ immediates = {}
+ print("# OpenPOWER ISA register 'profile's")
+ print()
+ print("this page is auto-generated, do not edit")
+ print("created by https://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 'valid' in fname:
continue
continue
if 'sprs' 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:
continue
dkey = create_key(row)
key = tuple(dkey.values())
+ # print("key=", key)
dictkeys[key] = dkey
primarykeys.add(key)
if key not in bykey:
bykey[key].append((csvname, row['opcode'], row['comment'],
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()
- print ("# keys")
- print ('')
- print ('[[!table data="""')
- print (tformat(tablecols) + " name |")
+ # mapping to old SVPrefix "Forms"
+ mapsto = {'3R-1W-CRio': 'FR4',
+ '2R-1W-CRio': 'R',
+ '2R-1W-CRi': 'R',
+ '2R-1W-CRo': 'R',
+ '2R': 'non-SV',
+ '2R-1W': 'R',
+ '1R-CRio': 'TBD - need close inspection',
+ '2R-CRio': 'R',
+ '2R-CRo': 'R',
+ '1R': 'non-SV',
+ '1R-1W-CRio': 'R',
+ '1R-1W-CRo': 'R',
+ '1R-1W': 'R',
+ '1R-1W-imm': 'I',
+ '1R-CRo': 'I',
+ '1R-imm': 'non-SV',
+ '1W': 'non-SV',
+ '1W-CRi': 'TBD - needs close inspection',
+ 'CRio': 'R',
+ 'CR=2R1W': 'R',
+ 'CRi': 'non-SV',
+ 'imm': 'non-SV',
+ '': 'non-SV',
+ 'LDST-2R-imm': 'S',
+ 'LDST-2R-1W-imm': 'S',
+ 'LDST-2R-1W': 'I',
+ 'LDST-2R-2W': 'I',
+ 'LDST-1R-1W-imm': 'I',
+ 'LDST-1R-2W-imm': 'I',
+ 'LDST-3R': 'R/TBD - st*x', # these are st*x
+ 'LDST-3R-CRo': 'R/TBD - st*x', # st*x
+ 'LDST-3R-1W': 'R/TBD - st*x', # st*x
+ }
+ print("# map to old SV Prefix")
+ print()
+ print_table_header("register profile", "old SV Prefix")
+ for key in primarykeys:
+ name = keyname(dictkeys[key])
+ value = mapsto.get(name, "-")
+ print_table_row(name, value)
+ print()
+ print("# keys")
+ print()
+ print_table_header(*tablecols, "imms", "name")
for key in primarykeys:
name = keyname(dictkeys[key])
- print (tformat(dictkeys[key].values()) + " %s |" % name)
- print ('"""]]')
- print ('')
+ imms = list(immediates.get(key, ""))
+ imms.sort()
+ print_table_row(*dictkeys[key].values(), "/".join(imms), name)
+ print()
for key in primarykeys:
name = keyname(dictkeys[key])
- print ("## %s " % name)
- print ('')
- print ('[[!table data="""')
- print (tformat(['CSV', 'opcode', 'asm', 'form']))
+ value = mapsto.get(name, "-")
+ print("## %s (%s)" % (name, value))
+ print()
+ print_table_header('CSV', 'opcode', 'asm', 'form')
rows = bykey[key]
rows.sort()
for row in rows:
- print (tformat(row))
- print ('"""]]')
- print ('')
+ print_table_row(*row)
+ print()
+
+ # TODO(lkcl): what did this do:
+ # bykey = {}
+ # for fname, csv in csvs.items():
+ # key
- bykey = {}
- for fname, csv in csvs.items():
- key
if __name__ == '__main__':
process_csvs()