sv_analysis: support format argument
[openpower-isa.git] / src / openpower / sv / sv_analysis.py
1 #!/usr/bin/env python2
2 #
3 # NOTE that this program is python2 compatible, please do not stop it
4 # from working by adding syntax that prevents that.
5 #
6 # Initial version written by lkcl Oct 2020
7 # This program analyses the Power 9 op codes and looks at in/out register uses
8 # The results are displayed:
9 # https://libre-soc.org/openpower/opcode_regs_deduped/
10 #
11 # It finds .csv files in the directory isatables/
12 # then goes through the categories and creates svp64 CSV augmentation
13 # tables on a per-opcode basis
14
15 import argparse
16 import csv
17 import enum
18 import os
19 from os.path import dirname, join
20 from glob import glob
21 from collections import OrderedDict
22 from openpower.decoder.power_svp64 import SVP64RM
23 from openpower.decoder.power_enums import find_wiki_file, get_csv
24
25
26 # Write an array of dictionaries to the CSV file name:
27 def write_csv(name, items, headers):
28 file_path = find_wiki_file(name)
29 with open(file_path, 'w') as csvfile:
30 writer = csv.DictWriter(csvfile, headers, lineterminator="\n")
31 writer.writeheader()
32 writer.writerows(items)
33
34 # This will return True if all values are true.
35 # Not sure what this is about
36
37
38 def blank_key(row):
39 # for v in row.values():
40 # if 'SPR' in v: # skip all SPRs
41 # return True
42 for v in row.values():
43 if v:
44 return False
45 return True
46
47 # General purpose registers have names like: RA, RT, R1, ...
48 # Floating point registers names like: FRT, FRA, FR1, ..., FRTp, ...
49 # Return True if field is a register
50
51
52 def isreg(field):
53 return (field.startswith('R') or field.startswith('FR') or
54 field == 'SPR')
55
56
57 # These are the attributes of the instructions,
58 # register names
59 keycolumns = ['unit', 'in1', 'in2', 'in3', 'out', 'CR in', 'CR out',
60 ] # don't think we need these: 'ldst len', 'rc', 'lk']
61
62 tablecols = ['unit', 'in', 'outcnt', 'CR in', 'CR out', 'imm'
63 ] # don't think we need these: 'ldst len', 'rc', 'lk']
64
65
66 def create_key(row):
67 res = OrderedDict()
68 #print ("row", row)
69 for key in keycolumns:
70 # registers IN - special-case: count number of regs RA/RB/RC/RS
71 if key in ['in1', 'in2', 'in3']:
72 if 'in' not in res:
73 res['in'] = 0
74 if row['unit'] == 'BRANCH': # branches must not include Vector SPRs
75 continue
76 if isreg(row[key]):
77 res['in'] += 1
78
79 # registers OUT
80 if key == 'out':
81 # If upd is 1 then increment the count of outputs
82 if 'outcnt' not in res:
83 res['outcnt'] = 0
84 if isreg(row[key]):
85 res['outcnt'] += 1
86 if row['upd'] == '1':
87 res['outcnt'] += 1
88
89 # CRs (Condition Register) (CR0 .. CR7)
90 if key.startswith('CR'):
91 if row[key].startswith('NONE'):
92 res[key] = '0'
93 else:
94 res[key] = '1'
95 if row['comment'].startswith('cr'):
96 res['crop'] = '1'
97 # unit
98 if key == 'unit':
99 if row[key] == 'LDST': # we care about LDST units
100 res[key] = row[key]
101 else:
102 res[key] = 'OTHER'
103 # LDST len (LoadStore length)
104 if key.startswith('ldst'):
105 if row[key].startswith('NONE'):
106 res[key] = '0'
107 else:
108 res[key] = '1'
109 # rc, lk
110 if key in ['rc', 'lk']:
111 if row[key] == 'ONE':
112 res[key] = '1'
113 elif row[key] == 'NONE':
114 res[key] = '0'
115 else:
116 res[key] = 'R'
117 if key == 'lk':
118 res[key] = row[key]
119
120 # Convert the numerics 'in' & 'outcnt' to strings
121 res['in'] = str(res['in'])
122 res['outcnt'] = str(res['outcnt'])
123
124 # constants
125 if row['in2'].startswith('CONST_'):
126 res['imm'] = "1" # row['in2'].split("_")[1]
127 else:
128 res['imm'] = ''
129
130 return res
131
132 #
133
134
135 def dformat(d):
136 res = []
137 for k, v in d.items():
138 res.append("%s: %s" % (k, v))
139 return ' '.join(res)
140
141
142 def tformat(d):
143 return ' | '.join(d) + " |"
144
145
146 def keyname(row):
147 res = []
148 if row['unit'] != 'OTHER':
149 res.append(row['unit'])
150 if row['in'] != '0':
151 res.append('%sR' % row['in'])
152 if row['outcnt'] != '0':
153 res.append('%sW' % row['outcnt'])
154 if row['CR in'] == '1' and row['CR out'] == '1':
155 if 'crop' in row:
156 res.append("CR=2R1W")
157 else:
158 res.append("CRio")
159 elif row['CR in'] == '1':
160 res.append("CRi")
161 elif row['CR out'] == '1':
162 res.append("CRo")
163 elif 'imm' in row and row['imm']:
164 res.append("imm")
165 return '-'.join(res)
166
167
168 class Format(enum.Enum):
169 VHDL = enum.auto()
170
171 @classmethod
172 def _missing_(cls, value):
173 return {
174 "vhdl": Format.VHDL,
175 }[value.lower()]
176
177 def __str__(self):
178 return self.name.lower()
179
180
181 def process_csvs(format):
182 csvs = {}
183 csvs_svp64 = {}
184 bykey = {}
185 primarykeys = set()
186 dictkeys = OrderedDict()
187 immediates = {}
188 insns = {} # dictionary of CSV row, by instruction
189 insn_to_csv = {}
190
191 print("# OpenPOWER ISA register 'profile's")
192 print('')
193 print("this page is auto-generated, do not edit")
194 print("created by http://libre-soc.org/openpower/sv_analysis.py")
195 print('')
196
197 # Expand that (all .csv files)
198 pth = find_wiki_file("*.csv")
199
200 # Ignore those containing: valid test sprs
201 for fname in glob(pth):
202 print("sv analysis checking", fname)
203 _, name = os.path.split(fname)
204 if '-' in name:
205 continue
206 if 'valid' in fname:
207 continue
208 if 'test' in fname:
209 continue
210 if fname.endswith('sprs.csv'):
211 continue
212 if fname.endswith('minor_19_valid.csv'):
213 continue
214 if 'RM' in fname:
215 continue
216 csvname = os.path.split(fname)[1]
217 csvname_ = csvname.split(".")[0]
218 # csvname is something like: minor_59.csv, fname the whole path
219 csv = get_csv(fname)
220 csvs[fname] = csv
221 csvs_svp64[csvname_] = []
222 for row in csv:
223 if blank_key(row):
224 continue
225 print("row", row)
226 insn_name = row['comment']
227 condition = row['CONDITIONS']
228 # skip instructions that are not suitable
229 if insn_name.startswith("l") and insn_name.endswith("br"):
230 continue # skip pseudo-alias lxxxbr
231 if insn_name in ['mcrxr', 'mcrxrx', 'darn']:
232 continue
233 if insn_name in ['bctar', 'bcctr']:
234 continue
235 if 'rfid' in insn_name:
236 continue
237 if insn_name in ['setvl', ]: # SVP64 opcodes
238 continue
239
240 insns[(insn_name, condition)] = row # accumulate csv data
241 insn_to_csv[insn_name] = csvname_ # CSV file name by instruction
242 dkey = create_key(row)
243 key = tuple(dkey.values())
244 # print("key=", key)
245 dictkeys[key] = dkey
246 primarykeys.add(key)
247 if key not in bykey:
248 bykey[key] = []
249 bykey[key].append((csvname, row['opcode'], insn_name, condition,
250 row['form'].upper() + '-Form'))
251
252 # detect immediates, collate them (useful info)
253 if row['in2'].startswith('CONST_'):
254 imm = row['in2'].split("_")[1]
255 if key not in immediates:
256 immediates[key] = set()
257 immediates[key].add(imm)
258
259 primarykeys = list(primarykeys)
260 primarykeys.sort()
261
262 # mapping to old SVPrefix "Forms"
263 mapsto = {'3R-1W-CRo': 'RM-1P-3S1D',
264 '2R-1W-CRio': 'RM-1P-2S1D',
265 '2R-1W-CRi': 'RM-1P-3S1D',
266 '2R-1W-CRo': 'RM-1P-2S1D',
267 '2R': 'non-SV',
268 '2R-1W': 'RM-1P-2S1D',
269 '1R-CRio': 'RM-2P-2S1D',
270 '2R-CRio': 'RM-1P-2S1D',
271 '2R-CRo': 'RM-1P-2S1D',
272 '1R': 'non-SV',
273 '1R-1W-CRio': 'RM-2P-1S1D',
274 '1R-1W-CRo': 'RM-2P-1S1D',
275 '1R-1W': 'RM-2P-1S1D',
276 '1R-1W-imm': 'RM-2P-1S1D',
277 '1R-CRo': 'RM-2P-1S1D',
278 '1R-imm': 'non-SV',
279 '1W-CRo': 'RM-1P-1D',
280 '1W': 'non-SV',
281 '1W-CRi': 'RM-2P-1S1D',
282 'CRio': 'RM-2P-1S1D',
283 'CR=2R1W': 'RM-1P-2S1D',
284 'CRi': 'non-SV',
285 'imm': 'non-SV',
286 '': 'non-SV',
287 'LDST-2R-imm': 'LDSTRM-2P-2S',
288 'LDST-2R-1W-imm': 'LDSTRM-2P-2S1D',
289 'LDST-2R-1W': 'LDSTRM-2P-2S1D',
290 'LDST-2R-2W': 'LDSTRM-2P-2S1D',
291 'LDST-1R-1W-imm': 'LDSTRM-2P-1S1D',
292 'LDST-1R-2W-imm': 'LDSTRM-2P-1S2D',
293 'LDST-3R': 'LDSTRM-2P-3S',
294 'LDST-3R-CRo': 'LDSTRM-2P-3S', # st*x
295 'LDST-3R-1W': 'LDSTRM-2P-2S1D', # st*x
296 }
297 print("# map to old SV Prefix")
298 print('')
299 print('[[!table data="""')
300 for key in primarykeys:
301 name = keyname(dictkeys[key])
302 value = mapsto.get(name, "-")
303 print(tformat([name, value + " "]))
304 print('"""]]')
305 print('')
306
307 print("# keys")
308 print('')
309 print('[[!table data="""')
310 print(tformat(tablecols) + " imms | name |")
311
312 # print out the keys and the table from which they're derived
313 for key in primarykeys:
314 name = keyname(dictkeys[key])
315 row = tformat(dictkeys[key].values())
316 imms = list(immediates.get(key, ""))
317 imms.sort()
318 row += " %s | " % ("/".join(imms))
319 row += " %s |" % name
320 print(row)
321 print('"""]]')
322 print('')
323
324 # print out, by remap name, all the instructions under that category
325 for key in primarykeys:
326 name = keyname(dictkeys[key])
327 value = mapsto.get(name, "-")
328 print("## %s (%s)" % (name, value))
329 print('')
330 print('[[!table data="""')
331 print(tformat(['CSV', 'opcode', 'asm', 'form']))
332 rows = bykey[key]
333 rows.sort()
334 for row in rows:
335 print(tformat(row))
336 print('"""]]')
337 print('')
338
339 # for fname, csv in csvs.items():
340 # print (fname)
341
342 # for insn, row in insns.items():
343 # print (insn, row)
344
345 print("# svp64 remaps")
346 svp64 = OrderedDict()
347 # create a CSV file, per category, with SV "augmentation" info
348 # XXX note: 'out2' not added here, needs to be added to CSV files
349 # KEEP TRACK OF THESE https://bugs.libre-soc.org/show_bug.cgi?id=619
350 csvcols = ['insn', 'CONDITIONS', 'Ptype', 'Etype', '0', '1', '2', '3']
351 csvcols += ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out'] # temporary
352 for key in primarykeys:
353 # get the decoded key containing row-analysis, and name/value
354 dkey = dictkeys[key]
355 name = keyname(dkey)
356 value = mapsto.get(name, "-")
357 if value == 'non-SV':
358 continue
359
360 # print out svp64 tables by category
361 print("* **%s**: %s" % (name, value))
362
363 # store csv entries by svp64 RM category
364 if value not in svp64:
365 svp64[value] = []
366
367 rows = bykey[key]
368 rows.sort()
369
370 for row in rows:
371 # for idx in range(len(row)):
372 # if row[idx] == 'NONE':
373 # row[idx] = ''
374 # get the instruction
375 print(key, row)
376 insn_name = row[2]
377 condition = row[3]
378 insn = insns[(insn_name, condition)]
379 # start constructing svp64 CSV row
380 res = OrderedDict()
381 res['insn'] = insn_name
382 res['CONDITIONS'] = condition
383 res['Ptype'] = value.split('-')[1] # predication type (RM-xN-xxx)
384 # get whether R_xxx_EXTRAn fields are 2-bit or 3-bit
385 res['Etype'] = 'EXTRA2'
386 # go through each register matching to Rxxxx_EXTRAx
387 for k in ['0', '1', '2', '3']:
388 res[k] = ''
389 # create "fake" out2 (TODO, needs to be added to CSV files)
390 # KEEP TRACK HERE https://bugs.libre-soc.org/show_bug.cgi?id=619
391 res['out2'] = 'NONE'
392 if insn['upd'] == '1': # LD/ST with update has RA as out2
393 res['out2'] = 'RA'
394
395 # temporary useful info
396 regs = []
397 for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
398 if insn[k].startswith('CONST'):
399 res[k] = ''
400 regs.append('')
401 else:
402 res[k] = insn[k]
403 if insn[k] == 'RA_OR_ZERO':
404 regs.append('RA')
405 elif insn[k] != 'NONE':
406 regs.append(insn[k])
407 else:
408 regs.append('')
409
410 print("regs", insn_name, regs)
411
412 # for LD/ST FP, use FRT/FRS not RT/RS, and use CR1 not CR0
413 if insn_name.startswith("lf"):
414 dRT = 'd:FRT'
415 dCR = 'd:CR1'
416 else:
417 dRT = 'd:RT'
418 dCR = 'd:CR0'
419 if insn_name.startswith("stf"):
420 sRS = 's:FRS'
421 dCR = 'd:CR1'
422 else:
423 sRS = 's:RS'
424 dCR = 'd:CR0'
425
426 # sigh now the fun begins. this isn't the sanest way to do it
427 # but the patterns are pretty regular.
428
429 if value == 'LDSTRM-2P-1S1D':
430 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
431 res['0'] = dRT # RT: Rdest_EXTRA3
432 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
433
434 elif value == 'LDSTRM-2P-1S2D':
435 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
436 res['0'] = dRT # RT: Rdest_EXTRA3
437 res['1'] = 'd:RA' # RA: Rdest2_EXTRA2
438 res['2'] = 's:RA' # RA: Rsrc1_EXTRA2
439
440 elif value == 'LDSTRM-2P-2S':
441 # stw, std, sth, stb
442 res['Etype'] = 'EXTRA3' # RM EXTRA2 type
443 res['0'] = sRS # RS: Rdest1_EXTRA2
444 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
445
446 elif value == 'LDSTRM-2P-2S1D':
447 if 'st' in insn_name and 'x' not in insn_name: # stwu/stbu etc
448 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
449 res['0'] = 'd:RA' # RA: Rdest1_EXTRA2
450 res['1'] = sRS # RS: Rdsrc1_EXTRA2
451 res['2'] = 's:RA' # RA: Rsrc2_EXTRA2
452 elif 'st' in insn_name and 'x' in insn_name: # stwux
453 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
454 res['0'] = 'd:RA' # RA: Rdest1_EXTRA2
455 # RS: Rdest2_EXTRA2, RA: Rsrc1_EXTRA2
456 res['1'] = sRS+'s:RA'
457 res['2'] = 's:RB' # RB: Rsrc2_EXTRA2
458 elif 'u' in insn_name: # ldux etc.
459 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
460 res['0'] = dRT # RT: Rdest1_EXTRA2
461 res['1'] = 'd:RA' # RA: Rdest2_EXTRA2
462 res['2'] = 's:RB' # RB: Rsrc1_EXTRA2
463 else:
464 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
465 res['0'] = dRT # RT: Rdest1_EXTRA2
466 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
467 res['2'] = 's:RB' # RB: Rsrc2_EXTRA2
468
469 elif value == 'LDSTRM-2P-3S':
470 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
471 if 'cx' in insn_name:
472 res['0'] = sRS+dCR # RS: Rsrc1_EXTRA2 CR0: dest
473 else:
474 res['0'] = sRS # RS: Rsrc1_EXTRA2
475 res['1'] = 's:RA' # RA: Rsrc2_EXTRA2
476 res['2'] = 's:RB' # RA: Rsrc3_EXTRA2
477
478 elif value == 'RM-2P-1S1D':
479 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
480 if insn_name == 'mtspr':
481 res['0'] = 'd:SPR' # SPR: Rdest1_EXTRA3
482 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
483 elif insn_name == 'mfspr':
484 res['0'] = 'd:RS' # RS: Rdest1_EXTRA3
485 res['1'] = 's:SPR' # SPR: Rsrc1_EXTRA3
486 elif name == 'CRio' and insn_name == 'mcrf':
487 res['0'] = 'd:BF' # BFA: Rdest1_EXTRA3
488 res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
489 elif 'mfcr' in insn_name or 'mfocrf' in insn_name:
490 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
491 res['1'] = 's:CR' # CR: Rsrc1_EXTRA3
492 elif insn_name == 'setb':
493 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
494 res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
495 elif insn_name.startswith('cmp'): # cmpi
496 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
497 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
498 elif regs == ['RA', '', '', 'RT', '', '']:
499 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
500 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
501 elif regs == ['RA', '', '', 'RT', '', 'CR0']:
502 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
503 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
504 elif (regs == ['RS', '', '', 'RA', '', 'CR0'] or
505 regs == ['', '', 'RS', 'RA', '', 'CR0']):
506 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
507 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
508 elif regs == ['RS', '', '', 'RA', '', '']:
509 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
510 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
511 elif regs == ['', 'FRB', '', 'FRT', '0', 'CR1']:
512 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
513 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
514 elif regs == ['', 'FRB', '', '', '', 'CR1']:
515 res['0'] = 'd:CR1' # CR1: Rdest1_EXTRA3
516 res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
517 elif regs == ['', 'FRB', '', '', '', 'BF']:
518 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
519 res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
520 elif regs == ['', 'FRB', '', 'FRT', '', 'CR1']:
521 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
522 res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3
523 elif insn_name.startswith('bc'):
524 res['0'] = 'd:BI' # BI: Rdest1_EXTRA3
525 res['1'] = 's:BI' # BI: Rsrc1_EXTRA3
526 else:
527 res['0'] = 'TODO'
528
529 elif value == 'RM-1P-2S1D':
530 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
531 if insn_name.startswith('cr'):
532 res['0'] = 'd:BT' # BT: Rdest1_EXTRA3
533 res['1'] = 's:BA' # BA: Rsrc1_EXTRA3
534 res['2'] = 's:BB' # BB: Rsrc2_EXTRA3
535 elif regs == ['FRA', '', 'FRC', 'FRT', '', 'CR1']:
536 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
537 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
538 res['2'] = 's:FRC' # FRC: Rsrc1_EXTRA3
539 # should be for fcmp
540 elif regs == ['FRA', 'FRB', '', '', '', 'BF']:
541 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
542 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
543 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
544 elif regs == ['FRA', 'FRB', '', 'FRT', '', '']:
545 res['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3
546 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
547 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
548 elif regs == ['FRA', 'FRB', '', 'FRT', '', 'CR1']:
549 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
550 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
551 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
552 elif name == '2R-1W' or insn_name == 'cmpb': # cmpb
553 if insn_name in ['bpermd', 'cmpb']:
554 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
555 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
556 else:
557 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
558 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
559 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
560 elif insn_name.startswith('cmp'): # cmp
561 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
562 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
563 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
564 elif (regs == ['', 'RB', 'RS', 'RA', '', 'CR0'] or
565 regs == ['RS', 'RB', '', 'RA', '', 'CR0']):
566 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
567 res['1'] = 's:RB' # RB: Rsrc1_EXTRA3
568 res['2'] = 's:RS' # RS: Rsrc1_EXTRA3
569 elif regs == ['RA', 'RB', '', 'RT', '', 'CR0']:
570 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
571 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
572 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
573 elif regs == ['RA', '', 'RS', 'RA', '', 'CR0']:
574 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
575 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
576 res['2'] = 's:RS' # RS: Rsrc1_EXTRA3
577 else:
578 res['0'] = 'TODO'
579
580 elif value == 'RM-2P-2S1D':
581 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
582 if insn_name.startswith('mt'): # mtcrf
583 res['0'] = 'd:CR' # CR: Rdest1_EXTRA2
584 res['1'] = 's:RS' # RS: Rsrc1_EXTRA2
585 res['2'] = 's:CR' # CR: Rsrc2_EXTRA2
586 else:
587 res['0'] = 'TODO'
588
589 elif value == 'RM-1P-3S1D':
590 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
591 if regs == ['RA', 'RB', 'RT', 'RT', '', 'CR0']:
592 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
593 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
594 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
595 res['3'] = 's:RT' # RT: Rsrc3_EXTRA2
596 elif insn_name == 'isel':
597 res['0'] = 'd:RT' # RT: Rdest1_EXTRA2
598 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
599 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
600 res['3'] = 's:BC' # BC: Rsrc3_EXTRA2
601 else:
602 res['0'] = 'd:FRT;d:CR1' # FRT, CR1: Rdest1_EXTRA2
603 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA2
604 res['2'] = 's:FRB' # FRB: Rsrc2_EXTRA2
605 res['3'] = 's:FRC' # FRC: Rsrc3_EXTRA2
606
607 elif value == 'RM-1P-1D':
608 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
609 if insn_name == 'svstep':
610 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
611
612 # add to svp64 csvs
613 # for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
614 # del res[k]
615 # if res['0'] != 'TODO':
616 for k in res:
617 if k == 'CONDITIONS':
618 continue
619 if res[k] == 'NONE' or res[k] == '':
620 res[k] = '0'
621 svp64[value].append(res)
622 # also add to by-CSV version
623 csv_fname = insn_to_csv[insn_name]
624 csvs_svp64[csv_fname].append(res)
625
626 print('')
627
628 # now write out the csv files
629 for value, csv in svp64.items():
630 # print out svp64 tables by category
631 print("## %s" % value)
632 print('')
633 print('[[!table format=csv file="openpower/isatables/%s.csv"]]' %
634 value)
635 print('')
636
637 #csvcols = ['insn', 'Ptype', 'Etype', '0', '1', '2', '3']
638 write_csv("%s.csv" % value, csv, csvcols + ['out2'])
639
640 # okaaay, now we re-read them back in for producing microwatt SV
641
642 # get SVP64 augmented CSV files
643 svt = SVP64RM(microwatt_format=True)
644 # Expand that (all .csv files)
645 pth = find_wiki_file("*.csv")
646
647 # Ignore those containing: valid test sprs
648 for fname in glob(pth):
649 print("post-checking", fname)
650 _, name = os.path.split(fname)
651 if '-' in name:
652 continue
653 if 'valid' in fname:
654 continue
655 if 'test' in fname:
656 continue
657 if fname.endswith('sprs.csv'):
658 continue
659 if fname.endswith('minor_19_valid.csv'):
660 continue
661 if 'RM' in fname:
662 continue
663 svp64_csv = svt.get_svp64_csv(fname)
664
665 csvcols = ['insn', 'Ptype', 'Etype']
666 csvcols += ['in1', 'in2', 'in3', 'out', 'out2', 'CR in', 'CR out']
667
668 if format == Format.VHDL:
669 # and a nice microwatt VHDL file
670 file_path = find_wiki_file("sv_decode.vhdl")
671
672 with open(file_path, 'w') as stream:
673 output(format, svt, csvcols, insns, csvs_svp64, stream)
674
675
676 def output(format, svt, csvcols, insns, csvs_svp64, stream):
677 _ = format
678
679 # autogeneration warning
680 stream.write("-- this file is auto-generated, do not edit\n")
681 stream.write("-- http://libre-soc.org/openpower/sv_analysis.py\n")
682 stream.write("-- part of Libre-SOC, sponsored by NLnet\n")
683 stream.write("\n")
684
685 # first create array types
686 lens = {'major': 63,
687 'minor_4': 63,
688 'minor_19': 7,
689 'minor_30': 15,
690 'minor_31': 1023,
691 'minor_58': 63,
692 'minor_59': 31,
693 'minor_62': 63,
694 'minor_63l': 511,
695 'minor_63h': 16,
696 }
697 for value, csv in csvs_svp64.items():
698 # munge name
699 value = value.lower()
700 value = value.replace("-", "_")
701 if value not in lens:
702 todo = " -- TODO %s (or no SVP64 augmentation)\n"
703 stream.write(todo % value)
704 continue
705 width = lens[value]
706 typarray = " type sv_%s_rom_array_t is " \
707 "array(0 to %d) of sv_decode_rom_t;\n"
708 stream.write(typarray % (value, width))
709
710 # now output structs
711 sv_cols = ['sv_in1', 'sv_in2', 'sv_in3', 'sv_out', 'sv_out2',
712 'sv_cr_in', 'sv_cr_out']
713 fullcols = csvcols + sv_cols
714 hdr = "\n" \
715 " constant sv_%s_decode_rom_array :\n" \
716 " sv_%s_rom_array_t := (\n" \
717 " -- %s\n"
718 ftr = " others => sv_illegal_inst\n" \
719 " );\n\n"
720 for value, csv in csvs_svp64.items():
721 # munge name
722 value = value.lower()
723 value = value.replace("-", "_")
724 if value not in lens:
725 continue
726 stream.write(hdr % (value, value, " ".join(fullcols)))
727 for entry in csv:
728 insn = str(entry['insn'])
729 condition = str(entry['CONDITIONS'])
730 sventry = svt.svp64_instrs.get(insn, None)
731 op = insns[(insn, condition)]['opcode']
732 # binary-to-vhdl-binary
733 if op.startswith("0b"):
734 op = "2#%s#" % op[2:]
735 row = []
736 for colname in csvcols[1:]:
737 re = entry[colname]
738 # zero replace with NONE
739 if re == '0':
740 re = 'NONE'
741 # 1/2 predication
742 re = re.replace("1P", "P1")
743 re = re.replace("2P", "P2")
744 row.append(re)
745 print("sventry", sventry)
746 for colname in sv_cols:
747 if sventry is None:
748 re = 'NONE'
749 else:
750 re = sventry[colname]
751 row.append(re)
752 row = ', '.join(row)
753 stream.write(" %13s => (%s), -- %s\n" % (op, row, insn))
754 stream.write(ftr)
755
756
757 if __name__ == '__main__':
758 parser = argparse.ArgumentParser()
759 parser.add_argument("-f", "--format",
760 type=Format, choices=Format, default=Format.VHDL,
761 help="format to be used (VHDL)")
762 args = parser.parse_args()
763 process_csvs(args.format)