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