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