pysvp64db: fix traversal
[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 BINUTILS = enum.auto()
170 VHDL = enum.auto()
171
172 @classmethod
173 def _missing_(cls, value):
174 return {
175 "binutils": Format.BINUTILS,
176 "vhdl": Format.VHDL,
177 }[value.lower()]
178
179 def __str__(self):
180 return self.name.lower()
181
182 def wrap_comment(self, lines):
183 def wrap_comment_binutils(lines):
184 yield "/*"
185 yield from map(lambda line: f" * {line}", lines)
186 yield "*/"
187
188 def wrap_comment_vhdl(lines):
189 yield from map(lambda line: f"-- {line}", lines)
190
191 yield from {
192 Format.BINUTILS: wrap_comment_binutils,
193 Format.VHDL: wrap_comment_vhdl,
194 }[self](lines)
195
196
197 def process_csvs(format):
198 csvs = {}
199 csvs_svp64 = {}
200 bykey = {}
201 primarykeys = set()
202 dictkeys = OrderedDict()
203 immediates = {}
204 insns = {} # dictionary of CSV row, by instruction
205 insn_to_csv = {}
206
207 print("# OpenPOWER ISA register 'profile's")
208 print('')
209 print("this page is auto-generated, do not edit")
210 print("created by http://libre-soc.org/openpower/sv_analysis.py")
211 print('')
212
213 # Expand that (all .csv files)
214 pth = find_wiki_file("*.csv")
215
216 # Ignore those containing: valid test sprs
217 for fname in glob(pth):
218 print("sv analysis checking", fname)
219 _, name = os.path.split(fname)
220 if '-' in name:
221 continue
222 if 'valid' in fname:
223 continue
224 if 'test' in fname:
225 continue
226 if fname.endswith('sprs.csv'):
227 continue
228 if fname.endswith('minor_19_valid.csv'):
229 continue
230 if 'RM' in fname:
231 continue
232 csvname = os.path.split(fname)[1]
233 csvname_ = csvname.split(".")[0]
234 # csvname is something like: minor_59.csv, fname the whole path
235 csv = get_csv(fname)
236 csvs[fname] = csv
237 csvs_svp64[csvname_] = []
238 for row in csv:
239 if blank_key(row):
240 continue
241 print("row", row)
242 insn_name = row['comment']
243 condition = row['CONDITIONS']
244 # skip instructions that are not suitable
245 if insn_name.startswith("l") and insn_name.endswith("br"):
246 continue # skip pseudo-alias lxxxbr
247 if insn_name in ['mcrxr', 'mcrxrx', 'darn']:
248 continue
249 if insn_name in ['bctar', 'bcctr']:
250 continue
251 if 'rfid' in insn_name:
252 continue
253 if insn_name in ['setvl', ]: # SVP64 opcodes
254 continue
255
256 insns[(insn_name, condition)] = row # accumulate csv data
257 insn_to_csv[insn_name] = csvname_ # CSV file name by instruction
258 dkey = create_key(row)
259 key = tuple(dkey.values())
260 # print("key=", key)
261 dictkeys[key] = dkey
262 primarykeys.add(key)
263 if key not in bykey:
264 bykey[key] = []
265 bykey[key].append((csvname, row['opcode'], insn_name, condition,
266 row['form'].upper() + '-Form'))
267
268 # detect immediates, collate them (useful info)
269 if row['in2'].startswith('CONST_'):
270 imm = row['in2'].split("_")[1]
271 if key not in immediates:
272 immediates[key] = set()
273 immediates[key].add(imm)
274
275 primarykeys = list(primarykeys)
276 primarykeys.sort()
277
278 # mapping to old SVPrefix "Forms"
279 mapsto = {'3R-1W-CRo': 'RM-1P-3S1D',
280 '2R-1W-CRio': 'RM-1P-2S1D',
281 '2R-1W-CRi': 'RM-1P-3S1D',
282 '2R-1W-CRo': 'RM-1P-2S1D',
283 '2R': 'non-SV',
284 '2R-1W': 'RM-1P-2S1D',
285 '1R-CRio': 'RM-2P-2S1D',
286 '2R-CRio': 'RM-1P-2S1D',
287 '2R-CRo': 'RM-1P-2S1D',
288 '1R': 'non-SV',
289 '1R-1W-CRio': 'RM-2P-1S1D',
290 '1R-1W-CRo': 'RM-2P-1S1D',
291 '1R-1W': 'RM-2P-1S1D',
292 '1R-1W-imm': 'RM-2P-1S1D',
293 '1R-CRo': 'RM-2P-1S1D',
294 '1R-imm': 'non-SV',
295 '1W-CRo': 'RM-1P-1D',
296 '1W': 'non-SV',
297 '1W-CRi': 'RM-2P-1S1D',
298 'CRio': 'RM-2P-1S1D',
299 'CR=2R1W': 'RM-1P-2S1D',
300 'CRi': 'non-SV',
301 'imm': 'non-SV',
302 '': 'non-SV',
303 'LDST-2R-imm': 'LDSTRM-2P-2S',
304 'LDST-2R-1W-imm': 'LDSTRM-2P-2S1D',
305 'LDST-2R-1W': 'LDSTRM-2P-2S1D',
306 'LDST-2R-2W': 'LDSTRM-2P-2S1D',
307 'LDST-1R-1W-imm': 'LDSTRM-2P-1S1D',
308 'LDST-1R-2W-imm': 'LDSTRM-2P-1S2D',
309 'LDST-3R': 'LDSTRM-2P-3S',
310 'LDST-3R-CRo': 'LDSTRM-2P-3S', # st*x
311 'LDST-3R-1W': 'LDSTRM-2P-2S1D', # st*x
312 }
313 print("# map to old SV Prefix")
314 print('')
315 print('[[!table data="""')
316 for key in primarykeys:
317 name = keyname(dictkeys[key])
318 value = mapsto.get(name, "-")
319 print(tformat([name, value + " "]))
320 print('"""]]')
321 print('')
322
323 print("# keys")
324 print('')
325 print('[[!table data="""')
326 print(tformat(tablecols) + " imms | name |")
327
328 # print out the keys and the table from which they're derived
329 for key in primarykeys:
330 name = keyname(dictkeys[key])
331 row = tformat(dictkeys[key].values())
332 imms = list(immediates.get(key, ""))
333 imms.sort()
334 row += " %s | " % ("/".join(imms))
335 row += " %s |" % name
336 print(row)
337 print('"""]]')
338 print('')
339
340 # print out, by remap name, all the instructions under that category
341 for key in primarykeys:
342 name = keyname(dictkeys[key])
343 value = mapsto.get(name, "-")
344 print("## %s (%s)" % (name, value))
345 print('')
346 print('[[!table data="""')
347 print(tformat(['CSV', 'opcode', 'asm', 'form']))
348 rows = bykey[key]
349 rows.sort()
350 for row in rows:
351 print(tformat(row))
352 print('"""]]')
353 print('')
354
355 # for fname, csv in csvs.items():
356 # print (fname)
357
358 # for insn, row in insns.items():
359 # print (insn, row)
360
361 print("# svp64 remaps")
362 svp64 = OrderedDict()
363 # create a CSV file, per category, with SV "augmentation" info
364 # XXX note: 'out2' not added here, needs to be added to CSV files
365 # KEEP TRACK OF THESE https://bugs.libre-soc.org/show_bug.cgi?id=619
366 csvcols = ['insn', 'CONDITIONS', 'Ptype', 'Etype', '0', '1', '2', '3']
367 csvcols += ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out'] # temporary
368 for key in primarykeys:
369 # get the decoded key containing row-analysis, and name/value
370 dkey = dictkeys[key]
371 name = keyname(dkey)
372 value = mapsto.get(name, "-")
373 if value == 'non-SV':
374 continue
375
376 # print out svp64 tables by category
377 print("* **%s**: %s" % (name, value))
378
379 # store csv entries by svp64 RM category
380 if value not in svp64:
381 svp64[value] = []
382
383 rows = bykey[key]
384 rows.sort()
385
386 for row in rows:
387 # for idx in range(len(row)):
388 # if row[idx] == 'NONE':
389 # row[idx] = ''
390 # get the instruction
391 print(key, row)
392 insn_name = row[2]
393 condition = row[3]
394 insn = insns[(insn_name, condition)]
395 # start constructing svp64 CSV row
396 res = OrderedDict()
397 res['insn'] = insn_name
398 res['CONDITIONS'] = condition
399 res['Ptype'] = value.split('-')[1] # predication type (RM-xN-xxx)
400 # get whether R_xxx_EXTRAn fields are 2-bit or 3-bit
401 res['Etype'] = 'EXTRA2'
402 # go through each register matching to Rxxxx_EXTRAx
403 for k in ['0', '1', '2', '3']:
404 res[k] = ''
405 # create "fake" out2 (TODO, needs to be added to CSV files)
406 # KEEP TRACK HERE https://bugs.libre-soc.org/show_bug.cgi?id=619
407 res['out2'] = 'NONE'
408 if insn['upd'] == '1': # LD/ST with update has RA as out2
409 res['out2'] = 'RA'
410
411 # temporary useful info
412 regs = []
413 for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
414 if insn[k].startswith('CONST'):
415 res[k] = ''
416 regs.append('')
417 else:
418 res[k] = insn[k]
419 if insn[k] == 'RA_OR_ZERO':
420 regs.append('RA')
421 elif insn[k] != 'NONE':
422 regs.append(insn[k])
423 else:
424 regs.append('')
425
426 print("regs", insn_name, regs)
427
428 # for LD/ST FP, use FRT/FRS not RT/RS, and use CR1 not CR0
429 if insn_name.startswith("lf"):
430 dRT = 'd:FRT'
431 dCR = 'd:CR1'
432 else:
433 dRT = 'd:RT'
434 dCR = 'd:CR0'
435 if insn_name.startswith("stf"):
436 sRS = 's:FRS'
437 dCR = 'd:CR1'
438 else:
439 sRS = 's:RS'
440 dCR = 'd:CR0'
441
442 # sigh now the fun begins. this isn't the sanest way to do it
443 # but the patterns are pretty regular.
444
445 if value == 'LDSTRM-2P-1S1D':
446 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
447 res['0'] = dRT # RT: Rdest_EXTRA3
448 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
449
450 elif value == 'LDSTRM-2P-1S2D':
451 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
452 res['0'] = dRT # RT: Rdest_EXTRA3
453 res['1'] = 'd:RA' # RA: Rdest2_EXTRA2
454 res['2'] = 's:RA' # RA: Rsrc1_EXTRA2
455
456 elif value == 'LDSTRM-2P-2S':
457 # stw, std, sth, stb
458 res['Etype'] = 'EXTRA3' # RM EXTRA2 type
459 res['0'] = sRS # RS: Rdest1_EXTRA2
460 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
461
462 elif value == 'LDSTRM-2P-2S1D':
463 if 'st' in insn_name and 'x' not in insn_name: # stwu/stbu etc
464 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
465 res['0'] = 'd:RA' # RA: Rdest1_EXTRA2
466 res['1'] = sRS # RS: Rdsrc1_EXTRA2
467 res['2'] = 's:RA' # RA: Rsrc2_EXTRA2
468 elif 'st' in insn_name and 'x' in insn_name: # stwux
469 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
470 res['0'] = 'd:RA' # RA: Rdest1_EXTRA2
471 # RS: Rdest2_EXTRA2, RA: Rsrc1_EXTRA2
472 res['1'] = sRS+'s:RA'
473 res['2'] = 's:RB' # RB: Rsrc2_EXTRA2
474 elif 'u' in insn_name: # ldux etc.
475 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
476 res['0'] = dRT # RT: Rdest1_EXTRA2
477 res['1'] = 'd:RA' # RA: Rdest2_EXTRA2
478 res['2'] = 's:RB' # RB: Rsrc1_EXTRA2
479 else:
480 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
481 res['0'] = dRT # RT: Rdest1_EXTRA2
482 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
483 res['2'] = 's:RB' # RB: Rsrc2_EXTRA2
484
485 elif value == 'LDSTRM-2P-3S':
486 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
487 if 'cx' in insn_name:
488 res['0'] = sRS+dCR # RS: Rsrc1_EXTRA2 CR0: dest
489 else:
490 res['0'] = sRS # RS: Rsrc1_EXTRA2
491 res['1'] = 's:RA' # RA: Rsrc2_EXTRA2
492 res['2'] = 's:RB' # RA: Rsrc3_EXTRA2
493
494 elif value == 'RM-2P-1S1D':
495 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
496 if insn_name == 'mtspr':
497 res['0'] = 'd:SPR' # SPR: Rdest1_EXTRA3
498 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
499 elif insn_name == 'mfspr':
500 res['0'] = 'd:RS' # RS: Rdest1_EXTRA3
501 res['1'] = 's:SPR' # SPR: Rsrc1_EXTRA3
502 elif name == 'CRio' and insn_name == 'mcrf':
503 res['0'] = 'd:BF' # BFA: Rdest1_EXTRA3
504 res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
505 elif 'mfcr' in insn_name or 'mfocrf' in insn_name:
506 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
507 res['1'] = 's:CR' # CR: Rsrc1_EXTRA3
508 elif insn_name == 'setb':
509 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
510 res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
511 elif insn_name.startswith('cmp'): # cmpi
512 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
513 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
514 elif regs == ['RA', '', '', 'RT', '', '']:
515 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
516 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
517 elif regs == ['RA', '', '', 'RT', '', 'CR0']:
518 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
519 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
520 elif (regs == ['RS', '', '', 'RA', '', 'CR0'] or
521 regs == ['', '', 'RS', 'RA', '', 'CR0']):
522 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
523 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
524 elif regs == ['RS', '', '', 'RA', '', '']:
525 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
526 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
527 elif regs == ['', 'FRB', '', 'FRT', '0', 'CR1']:
528 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
529 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
530 elif regs == ['', 'FRB', '', '', '', 'CR1']:
531 res['0'] = 'd:CR1' # CR1: Rdest1_EXTRA3
532 res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
533 elif regs == ['', 'FRB', '', '', '', 'BF']:
534 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
535 res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
536 elif regs == ['', 'FRB', '', 'FRT', '', 'CR1']:
537 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
538 res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3
539 elif insn_name.startswith('bc'):
540 res['0'] = 'd:BI' # BI: Rdest1_EXTRA3
541 res['1'] = 's:BI' # BI: Rsrc1_EXTRA3
542 else:
543 res['0'] = 'TODO'
544
545 elif value == 'RM-1P-2S1D':
546 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
547 if insn_name.startswith('cr'):
548 res['0'] = 'd:BT' # BT: Rdest1_EXTRA3
549 res['1'] = 's:BA' # BA: Rsrc1_EXTRA3
550 res['2'] = 's:BB' # BB: Rsrc2_EXTRA3
551 elif regs == ['FRA', '', 'FRC', 'FRT', '', 'CR1']:
552 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
553 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
554 res['2'] = 's:FRC' # FRC: Rsrc1_EXTRA3
555 # should be for fcmp
556 elif regs == ['FRA', 'FRB', '', '', '', 'BF']:
557 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
558 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
559 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
560 elif regs == ['FRA', 'FRB', '', 'FRT', '', '']:
561 res['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3
562 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
563 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
564 elif regs == ['FRA', 'FRB', '', 'FRT', '', 'CR1']:
565 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
566 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
567 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
568 elif name == '2R-1W' or insn_name == 'cmpb': # cmpb
569 if insn_name in ['bpermd', 'cmpb']:
570 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
571 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
572 else:
573 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
574 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
575 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
576 elif insn_name.startswith('cmp'): # cmp
577 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
578 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
579 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
580 elif (regs == ['', 'RB', 'RS', 'RA', '', 'CR0'] or
581 regs == ['RS', 'RB', '', 'RA', '', 'CR0']):
582 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
583 res['1'] = 's:RB' # RB: Rsrc1_EXTRA3
584 res['2'] = 's:RS' # RS: Rsrc1_EXTRA3
585 elif regs == ['RA', 'RB', '', 'RT', '', 'CR0']:
586 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
587 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
588 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
589 elif regs == ['RA', '', 'RS', 'RA', '', 'CR0']:
590 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
591 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
592 res['2'] = 's:RS' # RS: Rsrc1_EXTRA3
593 else:
594 res['0'] = 'TODO'
595
596 elif value == 'RM-2P-2S1D':
597 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
598 if insn_name.startswith('mt'): # mtcrf
599 res['0'] = 'd:CR' # CR: Rdest1_EXTRA2
600 res['1'] = 's:RS' # RS: Rsrc1_EXTRA2
601 res['2'] = 's:CR' # CR: Rsrc2_EXTRA2
602 else:
603 res['0'] = 'TODO'
604
605 elif value == 'RM-1P-3S1D':
606 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
607 if regs == ['RA', 'RB', 'RT', 'RT', '', 'CR0']:
608 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
609 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
610 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
611 res['3'] = 's:RT' # RT: Rsrc3_EXTRA2
612 elif insn_name == 'isel':
613 res['0'] = 'd:RT' # RT: Rdest1_EXTRA2
614 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
615 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
616 res['3'] = 's:BC' # BC: Rsrc3_EXTRA2
617 else:
618 res['0'] = 'd:FRT;d:CR1' # FRT, CR1: Rdest1_EXTRA2
619 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA2
620 res['2'] = 's:FRB' # FRB: Rsrc2_EXTRA2
621 res['3'] = 's:FRC' # FRC: Rsrc3_EXTRA2
622
623 elif value == 'RM-1P-1D':
624 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
625 if insn_name == 'svstep':
626 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
627
628 # add to svp64 csvs
629 # for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
630 # del res[k]
631 # if res['0'] != 'TODO':
632 for k in res:
633 if k == 'CONDITIONS':
634 continue
635 if res[k] == 'NONE' or res[k] == '':
636 res[k] = '0'
637 svp64[value].append(res)
638 # also add to by-CSV version
639 csv_fname = insn_to_csv[insn_name]
640 csvs_svp64[csv_fname].append(res)
641
642 print('')
643
644 # now write out the csv files
645 for value, csv in svp64.items():
646 # print out svp64 tables by category
647 print("## %s" % value)
648 print('')
649 print('[[!table format=csv file="openpower/isatables/%s.csv"]]' %
650 value)
651 print('')
652
653 #csvcols = ['insn', 'Ptype', 'Etype', '0', '1', '2', '3']
654 write_csv("%s.csv" % value, csv, csvcols + ['out2'])
655
656 # okaaay, now we re-read them back in for producing microwatt SV
657
658 # get SVP64 augmented CSV files
659 svt = SVP64RM(microwatt_format=True)
660 # Expand that (all .csv files)
661 pth = find_wiki_file("*.csv")
662
663 # Ignore those containing: valid test sprs
664 for fname in glob(pth):
665 print("post-checking", fname)
666 _, name = os.path.split(fname)
667 if '-' in name:
668 continue
669 if 'valid' in fname:
670 continue
671 if 'test' in fname:
672 continue
673 if fname.endswith('sprs.csv'):
674 continue
675 if fname.endswith('minor_19_valid.csv'):
676 continue
677 if 'RM' in fname:
678 continue
679 svp64_csv = svt.get_svp64_csv(fname)
680
681 csvcols = ['insn', 'Ptype', 'Etype']
682 csvcols += ['in1', 'in2', 'in3', 'out', 'out2', 'CR in', 'CR out']
683
684 if format == Format.VHDL:
685 # and a nice microwatt VHDL file
686 file_path = find_wiki_file("sv_decode.vhdl")
687 elif format == Format.BINUTILS:
688 file_path = find_wiki_file("binutils.c")
689
690 with open(file_path, 'w') as stream:
691 output(format, svt, csvcols, insns, csvs_svp64, stream)
692
693
694 def output_autogen_disclaimer(format, stream):
695 lines = (
696 "this file is auto-generated, do not edit",
697 "http://libre-soc.org/openpower/sv_analysis.py",
698 "part of Libre-SOC, sponsored by NLnet",
699 )
700 for line in format.wrap_comment(lines):
701 stream.write(line)
702 stream.write("\n")
703 stream.write("\n")
704
705
706 def output(format, svt, csvcols, insns, csvs_svp64, stream):
707 output_autogen_disclaimer(format, stream)
708
709 if format == Format.BINUTILS:
710 stream.write("/* TODO: implement proper support */\n")
711 return
712
713 # first create array types
714 lens = {'major': 63,
715 'minor_4': 63,
716 'minor_19': 7,
717 'minor_30': 15,
718 'minor_31': 1023,
719 'minor_58': 63,
720 'minor_59': 31,
721 'minor_62': 63,
722 'minor_63l': 511,
723 'minor_63h': 16,
724 }
725 for value, csv in csvs_svp64.items():
726 # munge name
727 value = value.lower()
728 value = value.replace("-", "_")
729 if value not in lens:
730 todo = " -- TODO %s (or no SVP64 augmentation)\n"
731 stream.write(todo % value)
732 continue
733 width = lens[value]
734 typarray = " type sv_%s_rom_array_t is " \
735 "array(0 to %d) of sv_decode_rom_t;\n"
736 stream.write(typarray % (value, width))
737
738 # now output structs
739 sv_cols = ['sv_in1', 'sv_in2', 'sv_in3', 'sv_out', 'sv_out2',
740 'sv_cr_in', 'sv_cr_out']
741 fullcols = csvcols + sv_cols
742 hdr = "\n" \
743 " constant sv_%s_decode_rom_array :\n" \
744 " sv_%s_rom_array_t := (\n" \
745 " -- %s\n"
746 ftr = " others => sv_illegal_inst\n" \
747 " );\n\n"
748 for value, csv in csvs_svp64.items():
749 # munge name
750 value = value.lower()
751 value = value.replace("-", "_")
752 if value not in lens:
753 continue
754 stream.write(hdr % (value, value, " ".join(fullcols)))
755 for entry in csv:
756 insn = str(entry['insn'])
757 condition = str(entry['CONDITIONS'])
758 sventry = svt.svp64_instrs.get(insn, None)
759 op = insns[(insn, condition)]['opcode']
760 # binary-to-vhdl-binary
761 if op.startswith("0b"):
762 op = "2#%s#" % op[2:]
763 row = []
764 for colname in csvcols[1:]:
765 re = entry[colname]
766 # zero replace with NONE
767 if re == '0':
768 re = 'NONE'
769 # 1/2 predication
770 re = re.replace("1P", "P1")
771 re = re.replace("2P", "P2")
772 row.append(re)
773 print("sventry", sventry)
774 for colname in sv_cols:
775 if sventry is None:
776 re = 'NONE'
777 else:
778 re = sventry[colname]
779 row.append(re)
780 row = ', '.join(row)
781 stream.write(" %13s => (%s), -- %s\n" % (op, row, insn))
782 stream.write(ftr)
783
784
785 if __name__ == '__main__':
786 parser = argparse.ArgumentParser()
787 parser.add_argument("-f", "--format",
788 type=Format, choices=Format, default=Format.VHDL,
789 help="format to be used (binutils or VHDL)")
790 args = parser.parse_args()
791 process_csvs(args.format)