9464ddfd4a1ede816c6740e825d557a7a18cbbb9
[libreriscv.git] / openpower / sv_analysis.py
1 #!/usr/bin/env python3
2 # Initial version written by lkcl Oct 2020
3 # This program analyses the Power 9 op codes and looks at in/out register uses
4 # The results are displayed:
5 # https://libre-soc.org/openpower/opcode_regs_deduped/
6 #
7 # It finds .csv files in the directory isatables/
8
9 import csv
10 import os
11 from os.path import dirname, join
12 from glob import glob
13 from collections import OrderedDict
14
15 # Return absolute path (ie $PWD) + isatables + name
16 def find_wiki_file(name):
17 filedir = os.path.dirname(os.path.abspath(__file__))
18 tabledir = join(filedir, 'isatables')
19 file_path = join(tabledir, name)
20 return file_path
21
22 # Return an array of dictionaries from the CSV file name:
23 def get_csv(name):
24 file_path = find_wiki_file(name)
25 with open(file_path, 'r') as csvfile:
26 reader = csv.DictReader(csvfile)
27 return list(reader)
28
29 # This will return True if all values are true.
30 # Not sure what this is about
31 def blank_key(row):
32 #for v in row.values():
33 # if 'SPR' in v: # skip all SPRs
34 # return True
35 for v in row.values():
36 if v:
37 return False
38 return True
39
40 # General purpose registers have names like: RA, RT, R1, ...
41 # Floating point registers names like: FRT, FRA, FR1, ..., FRTp, ...
42 # Return True if field is a register
43 def isreg(field):
44 return field.startswith('R') or field.startswith('FR')
45
46
47 # These are the attributes of the instructions,
48 # register names
49 keycolumns = ['unit', 'in1', 'in2', 'in3', 'out', 'CR in', 'CR out',
50 ] # don't think we need these: 'ldst len', 'rc', 'lk']
51
52 tablecols = ['unit', 'in', 'outcnt', 'CR in', 'CR out', 'imm'
53 ] # don't think we need these: 'ldst len', 'rc', 'lk']
54
55 def create_key(row):
56 res = OrderedDict()
57 #print ("row", row)
58 for key in keycolumns:
59 # registers IN - special-case: count number of regs RA/RB/RC/RS
60 if key in ['in1', 'in2', 'in3']:
61 if 'in' not in res:
62 res['in'] = 0
63 if isreg(row[key]):
64 res['in'] += 1
65
66 # registers OUT
67 if key == 'out':
68 # If upd is 1 then increment the count of outputs
69 if 'outcnt' not in res:
70 res['outcnt'] = 0
71 if isreg(row[key]):
72 res['outcnt'] += 1
73 if row['upd'] == '1':
74 res['outcnt'] += 1
75
76 # CRs (Condition Register) (CR0 .. CR7)
77 if key.startswith('CR'):
78 if row[key].startswith('NONE'):
79 res[key] = '0'
80 else:
81 res[key] = '1'
82 # unit
83 if key == 'unit':
84 if row[key] == 'LDST': # we care about LDST units
85 res[key] = row[key]
86 else:
87 res[key] = 'OTHER'
88 # LDST len (LoadStore length)
89 if key.startswith('ldst'):
90 if row[key].startswith('NONE'):
91 res[key] = '0'
92 else:
93 res[key] = '1'
94 # rc, lk
95 if key in ['rc', 'lk']:
96 if row[key] == 'ONE':
97 res[key] = '1'
98 elif row[key] == 'NONE':
99 res[key] = '0'
100 else:
101 res[key] = 'R'
102 if key == 'lk':
103 res[key] = row[key]
104
105 # Convert the numerics 'in' & 'outcnt' to strings
106 res['in'] = str(res['in'])
107 res['outcnt'] = str(res['outcnt'])
108
109 # constants
110 if row['in2'].startswith('CONST_'):
111 res['imm'] = "1" # row['in2'].split("_")[1]
112 else:
113 res['imm'] = ''
114
115 return res
116
117 #
118 def dformat(d):
119 res = []
120 for k, v in d.items():
121 res.append("%s: %s" % (k, v))
122 return ' '.join(res)
123
124 def tformat(d):
125 return ' | '.join(d) + "|"
126
127 def keyname(row):
128 res = []
129 if row['unit'] != 'OTHER':
130 res.append(row['unit'])
131 if row['in'] != '0':
132 res.append('%sR' % row['in'])
133 if row['outcnt'] != '0':
134 res.append('%sW' % row['outcnt'])
135 if row['CR in'] == '1' and row['CR out'] == '1':
136 #if row['comment'].startswith('cr'):
137 # res.append("CR-2io")
138 #else:
139 res.append("CRio")
140 elif row['CR in'] == '1':
141 res.append("CRi")
142 elif row['CR out'] == '1':
143 res.append("CRo")
144 elif 'imm' in row and row['imm']:
145 res.append("imm")
146 return '-'.join(res)
147
148
149 def process_csvs():
150 csvs = {}
151 bykey = {}
152 primarykeys = set()
153 dictkeys = OrderedDict()
154 immediates = {}
155
156 print ("# OpenPOWER ISA register 'profile's")
157 print ('')
158 print ("this page is auto-generated, do not edit")
159 print ("created by http://libre-soc.org/openpower/sv_analysis.py")
160 print ('')
161
162 # Expand that (all .csv files)
163 pth = find_wiki_file("*.csv")
164
165 # Ignore those containing: valid test sprs
166 for fname in glob(pth):
167 if 'valid' in fname:
168 continue
169 if 'test' in fname:
170 continue
171 if 'sprs' in fname:
172 continue
173
174 #print (fname)
175 csvname = os.path.split(fname)[1]
176 # csvname is something like: minor_59.csv, fname the whole path
177 csv = get_csv(fname)
178 csvs[fname] = csv
179 for row in csv:
180 if blank_key(row):
181 continue
182 dkey = create_key(row)
183 key = tuple(dkey.values())
184 # print("key=", key)
185 dictkeys[key] = dkey
186 primarykeys.add(key)
187 if key not in bykey:
188 bykey[key] = []
189 bykey[key].append((csvname, row['opcode'], row['comment'],
190 row['form'].upper() + '-Form'))
191
192 # detect immediates, collate them (useful info)
193 if row['in2'].startswith('CONST_'):
194 imm = row['in2'].split("_")[1]
195 if key not in immediates:
196 immediates[key] = set()
197 immediates[key].add(imm)
198
199 primarykeys = list(primarykeys)
200 primarykeys.sort()
201
202 # mapping to old SVPrefix "Forms"
203 mapsto = {'3R-1W-CRio': 'FR4',
204 '2R-1W-CRio': 'R',
205 '2R-1W-CRi': 'R',
206 '2R-1W-CRo': 'R',
207 '2R': 'non-SV',
208 '2R-1W': 'R',
209 '1R-CRio': 'TBD - need close inspection',
210 '2R-CRio': 'R',
211 '2R-CRo': 'R',
212 '1R': 'non-SV',
213 '1R-1W-CRio': 'R',
214 '1R-1W-CRo': 'R',
215 '1R-1W': 'R',
216 '1R-1W-imm': 'I',
217 '1R-CRo': 'I',
218 '1R-imm': 'non-SV',
219 '1W': 'non-SV',
220 '1W-CRi': 'TBD - needs close inspection',
221 'CRio': 'R/TBD - needs subdivision (cr ops)',
222 'CRi': 'non-SV',
223 'imm': 'non-SV',
224 '': 'non-SV',
225 'LDST-2R-imm': 'S',
226 'LDST-2R-1W-imm': 'S',
227 'LDST-2R-1W': 'I',
228 'LDST-2R-2W': 'I',
229 'LDST-1R-1W-imm': 'I',
230 'LDST-1R-2W-imm': 'I',
231 'LDST-3R': 'R/TBD - st*x', # these are st*x
232 'LDST-3R-CRo': 'R/TBD - st*x', # st*x
233 'LDST-3R-1W': 'R/TBD - st*x', # st*x
234 }
235 print ("# map to old SV Prefix")
236 print ('')
237 print ('[[!table data="""')
238 for key in primarykeys:
239 name = keyname(dictkeys[key])
240 value = mapsto.get(name, "-")
241 print (tformat([name, value+ " "]))
242 print ('"""]]')
243 print ('')
244
245 print ("# keys")
246 print ('')
247 print ('[[!table data="""')
248 print (tformat(tablecols) + " imms | name |")
249
250 for key in primarykeys:
251 name = keyname(dictkeys[key])
252 row = tformat(dictkeys[key].values())
253 row += " %s | " % ("/".join(list(immediates.get(key, ""))))
254 row += " %s |" % name
255 print (row)
256 print ('"""]]')
257 print ('')
258
259 for key in primarykeys:
260 name = keyname(dictkeys[key])
261 value = mapsto.get(name, "-")
262 print ("## %s (%s)" % (name, value))
263 print ('')
264 print ('[[!table data="""')
265 print (tformat(['CSV', 'opcode', 'asm', 'form']))
266 rows = bykey[key]
267 rows.sort()
268 for row in rows:
269 print (tformat(row))
270 print ('"""]]')
271 print ('')
272
273 bykey = {}
274 for fname, csv in csvs.items():
275 key
276
277 if __name__ == '__main__':
278 process_csvs()