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