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