add generated output
[libreriscv.git] / openpower / sv_analysis.py
index ac03ac7416454ac8c9e9dd60c42e10028ebb5f1c..ec0a442b0fcc0514f79776784b3148393419ac8d 100644 (file)
@@ -1,41 +1,65 @@
 #!/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()
@@ -47,25 +71,32 @@ def create_key(row):
                 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'
@@ -82,34 +113,57 @@ 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():
         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():
@@ -117,8 +171,18 @@ 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
@@ -126,8 +190,10 @@ def process_csvs():
             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:
@@ -135,6 +201,7 @@ def process_csvs():
                 continue
             dkey = create_key(row)
             key = tuple(dkey.values())
+            # print("key=", key)
             dictkeys[key] = dkey
             primarykeys.add(key)
             if key not in bykey:
@@ -142,36 +209,86 @@ def process_csvs():
             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()