add SVP64 tests for cfuged, cntlzdm, cnttzdm, pdepd, and pextd
[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 "/* TODO: implement binutils declaration " \
226 "(value=%x, width=%x) */" % (value, width)
227
228 def declaration_vhdl(value, width):
229 yield " type sv_%s_rom_array_t is " \
230 "array(0 to %d) of sv_decode_rom_t;" % (value, width)
231
232 for value in values:
233 if value not in lens:
234 todo = ["TODO %s (or no SVP64 augmentation)" % value]
235 todo = self.wrap_comment(todo)
236 yield from map(lambda line: f" {line}", todo)
237 else:
238 width = lens[value]
239 yield from {
240 Format.BINUTILS: declaration_binutils,
241 Format.VHDL: declaration_vhdl,
242 }[self](value, width)
243
244 def definitions(self, entries_svp64, fullcols):
245 def definitions_vhdl():
246 for (value, entries) in entries_svp64.items():
247 yield ""
248 yield " constant sv_%s_decode_rom_array :" % value
249 yield " sv_%s_rom_array_t := (" % value
250 yield " -- %s" % ' '.join(fullcols)
251
252 for (op, insn, row) in entries:
253 yield f" {op:>13} => ({', '.join(row)}), -- {insn}"
254
255 yield f" {'others':>13} => sv_illegal_inst"
256 yield " );"
257 yield ""
258
259 def definitions_binutils():
260 yield f"/* TODO: implement binutils definitions */"
261
262 yield from {
263 Format.BINUTILS: definitions_binutils,
264 Format.VHDL: definitions_vhdl,
265 }[self]()
266
267 def wrap_comment(self, lines):
268 def wrap_comment_binutils(lines):
269 lines = tuple(lines)
270 if len(lines) == 1:
271 yield "/* %s */" % lines[0]
272 else:
273 yield "/*"
274 yield from map(lambda line: " * %s" % line, lines)
275 yield " */"
276
277 def wrap_comment_vhdl(lines):
278 yield from map(lambda line: "-- %s" % line, lines)
279
280 yield from {
281 Format.BINUTILS: wrap_comment_binutils,
282 Format.VHDL: wrap_comment_vhdl,
283 }[self](lines)
284
285
286 def read_csvs():
287 csvs = {}
288 csvs_svp64 = {}
289 bykey = {}
290 primarykeys = set()
291 dictkeys = OrderedDict()
292 immediates = {}
293 insns = {} # dictionary of CSV row, by instruction
294 insn_to_csv = {}
295
296 # Expand that (all .csv files)
297 pth = find_wiki_file("*.csv")
298
299 # Ignore those containing: valid test sprs
300 for fname in glob_valid_csvs(pth):
301 csvname = os.path.split(fname)[1]
302 csvname_ = csvname.split(".")[0]
303 # csvname is something like: minor_59.csv, fname the whole path
304 csv = get_csv(fname)
305 csvs[fname] = csv
306 csvs_svp64[csvname_] = []
307 for row in csv:
308 if blank_key(row):
309 continue
310 #print("row", row)
311 insn_name = row['comment']
312 condition = row['CONDITIONS']
313 # skip instructions that are not suitable
314 if insn_name.startswith("l") and insn_name.endswith("br"):
315 continue # skip pseudo-alias lxxxbr
316 if insn_name in ['mcrxr', 'mcrxrx', 'darn']:
317 continue
318 if insn_name in ['bctar', 'bcctr']: # for now. TODO
319 continue
320 if 'rfid' in insn_name:
321 continue
322 if 'addpcis' in insn_name: # skip for now
323 continue
324
325 # sv.bc is being classified as 2P-2S-1D by mistake due to SPRs
326 if insn_name.startswith('bc'):
327 # whoops: remove out reg (SPRs CTR etc)
328 row['in1'] = 'NONE'
329 row['in2'] = 'NONE'
330 row['in3'] = 'NONE'
331 row['out'] = 'NONE'
332
333 insns[(insn_name, condition)] = row # accumulate csv data
334 insn_to_csv[insn_name] = csvname_ # CSV file name by instruction
335 dkey = create_key(row)
336 key = tuple(dkey.values())
337 #print("key=", key, dkey)
338 dictkeys[key] = dkey
339 primarykeys.add(key)
340 if key not in bykey:
341 bykey[key] = []
342 bykey[key].append((csvname, row['opcode'], insn_name, condition,
343 row['form'].upper() + '-Form'))
344
345 # detect immediates, collate them (useful info)
346 if row['in2'].startswith('CONST_'):
347 imm = row['in2'].split("_")[1]
348 if key not in immediates:
349 immediates[key] = set()
350 immediates[key].add(imm)
351
352 primarykeys = list(primarykeys)
353 primarykeys.sort()
354
355 return (csvs, csvs_svp64, primarykeys, bykey, insn_to_csv, insns,
356 dictkeys, immediates)
357
358
359 def regs_profile(insn, res):
360 """get a more detailed register profile: 1st operand is RA,
361 2nd is RB, etc. etc
362 """
363 regs = []
364 for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
365 if insn[k].startswith('CONST'):
366 res[k] = ''
367 regs.append('')
368 else:
369 res[k] = insn[k]
370 if insn[k] == 'RA_OR_ZERO':
371 regs.append('RA')
372 elif insn[k] != 'NONE':
373 regs.append(insn[k])
374 else:
375 regs.append('')
376 return regs
377
378
379 def extra_classifier(insn_name, value, name, res, regs):
380 """extra_classifier: creates the SVP64.RM EXTRA2/3 classification.
381 there is very little space (9 bits) to mark register operands
382 (RT RA RB, BA BB, BFA, FRS etc.) with the "extra" information
383 needed to tell if *EACH* operand (of which there can be up to five!)
384 is Vectorised, and whether its numbering is extended into the
385 0..127 range rather than the limited 3/5 bit of Scalar v3.0 Power ISA.
386
387 thus begins the rather tedious but by-rote examination of EVERY
388 Scalar instruction, working out how best to tell a decoder how to
389 extend the registers. EXTRA2 can have up to 4 slots (of 2 bit each)
390 where due to RM.EXTRA being 9 bits, EXTRA3 can have up to 3 slots
391 (of 3 bit each). the index REGNAME says which slot the register
392 named REGNAME must read its decoding from. d: means destination,
393 s: means source. some are *shared slots* especially LDST update.
394 some Rc=1 ops have the CR0/CR1 as a co-result which is also
395 obviously Vectorised if the result is Vectorised.
396
397 it is actually quite straightforward but the sheer quantity of
398 Scalar Power ISA instructions made it prudent to do this in an
399 intelligent way, almost by-rote, by analysing the register profiles.
400 """
401 # for LD/ST FP, use FRT/FRS not RT/RS, and use CR1 not CR0
402 if insn_name.startswith("lf"):
403 dRT = 'd:FRT'
404 dCR = 'd:CR1'
405 else:
406 dRT = 'd:RT'
407 dCR = 'd:CR0'
408 if insn_name.startswith("stf"):
409 sRS = 's:FRS'
410 dCR = 'd:CR1'
411 else:
412 sRS = 's:RS'
413 dCR = 'd:CR0'
414
415 # sigh now the fun begins. this isn't the sanest way to do it
416 # but the patterns are pretty regular. we start with the "profile"
417 # because that determines how much space is available (total num
418 # regs to decode) then if necessary begin apecialising either
419 # by the instruction name or through more detailed register
420 # profiling. example:
421 # if regs == ['RA', '', '', 'RT', '', '']:
422 # is in the order in1 in2 in3 out1 out2 Rc=1
423
424 # ********
425 # start with LD/ST
426
427 if value == 'LDSTRM-2P-1S1D':
428 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
429 res['0'] = dRT # RT: Rdest_EXTRA3
430 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
431
432 elif value == 'LDSTRM-2P-1S2D':
433 res['Etype'] = 'EXTRA3' # RM EXTRA2 type
434 res['0'] = dRT # RT: Rdest_EXTRA3
435 res['1'] = 'd:RA;s:RA' # RA: Rdest2_EXTRA3
436
437 elif value == 'LDSTRM-2P-2S':
438 # stw, std, sth, stb
439 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
440 res['0'] = sRS # RS: Rdest1_EXTRA3
441 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
442
443 elif value == 'LDSTRM-2P-2S1D':
444 if 'st' in insn_name and 'x' not in insn_name: # stwu/stbu etc
445 res['Etype'] = 'EXTRA3' # RM EXTRA2 type
446 res['0'] = 'd:RA;s:RA' # RA: Rdest_EXTRA3 / Rsrc_EXTRA3
447 res['1'] = sRS # RS: Rdsrc1_EXTRA3
448 elif 'st' in insn_name and 'x' in insn_name: # stwux
449 res['Etype'] = 'EXTRA3' # RM EXTRA2 type
450 # RS: Rdest2_EXTRA2, RA: Rsrc1_EXTRA2 / Rdest
451 res['0'] = "%s;s:RA;d:RA" % (sRS)
452 res['1'] = 's:RB' # RB: Rsrc2_EXTRA2
453 elif 'u' in insn_name: # ldux etc.
454 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
455 res['0'] = dRT # RT: Rdest1_EXTRA2
456 res['1'] = 's:RA;d:RA' # RA: Rdest2_EXTRA2
457 res['2'] = 's:RB' # RB: Rsrc1_EXTRA2
458 else:
459 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
460 res['0'] = dRT # RT: Rdest1_EXTRA2
461 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
462 res['2'] = 's:RB' # RB: Rsrc2_EXTRA2
463
464 elif value == 'LDSTRM-2P-3S':
465 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
466 if 'cx' in insn_name:
467 res['0'] = "%s;%s" % (sRS, dCR) # RS: Rsrc1_EXTRA2 CR0: dest
468 else:
469 res['0'] = sRS # RS: Rsrc1_EXTRA2
470 res['1'] = 's:RA' # RA: Rsrc2_EXTRA2
471 res['2'] = 's:RB' # RA: Rsrc3_EXTRA2
472
473 # **********
474 # now begins,arithmetic
475
476 elif value == 'RM-2P-1S1D':
477 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
478 if insn_name == 'mtspr':
479 res['0'] = 'd:SPR' # SPR: Rdest1_EXTRA3
480 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
481 elif insn_name == 'rlwinm':
482 # weird one, RA is a dest but not in bits 6:10
483 res['0'] = 'd:RA;d:CR0' # RA: Rdest1_EXTRA3
484 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
485 elif insn_name == 'mfspr':
486 res['0'] = 'd:RS' # RS: Rdest1_EXTRA3
487 res['1'] = 's:SPR' # SPR: Rsrc1_EXTRA3
488 elif name == 'CRio' and insn_name == 'mcrf':
489 res['0'] = 'd:BF' # BFA: Rdest1_EXTRA3
490 res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
491 elif 'mfcr' in insn_name or 'mfocrf' in insn_name:
492 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
493 res['1'] = 's:CR' # CR: Rsrc1_EXTRA3
494 elif insn_name == 'setb':
495 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
496 res['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
497 elif insn_name.startswith('cmp'): # cmpi
498 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
499 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
500 elif regs == ['RA', '', '', 'RT', '', '']:
501 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
502 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
503 elif regs == ['RA', '', '', 'RT', '', 'CR0']:
504 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
505 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
506 elif (regs == ['RS', '', '', 'RA', '', 'CR0'] or
507 regs == ['', '', 'RS', 'RA', '', 'CR0']):
508 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
509 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
510 elif regs == ['RS', '', '', 'RA', '', '']:
511 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
512 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
513 elif regs == ['', 'FRB', '', 'FRT', '0', 'CR1']:
514 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
515 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
516 elif regs == ['', 'FRB', '', '', '', 'CR1']:
517 res['0'] = 'd:CR1' # CR1: Rdest1_EXTRA3
518 res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
519 elif regs == ['', 'FRB', '', '', '', 'BF']:
520 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
521 res['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
522 elif regs == ['', 'FRB', '', 'FRT', '', 'CR1']:
523 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
524 res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3
525 elif regs == ['', 'RB', '', 'FRT', '', 'CR1']:
526 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
527 res['1'] = 's:RB' # RB: Rsrc1_EXTRA3
528 elif regs == ['', 'RB', '', 'FRT', '', '']:
529 res['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3
530 res['1'] = 's:RB' # RB: Rsrc1_EXTRA3
531 elif regs == ['', 'FRB', '', 'RT', '', 'CR0']:
532 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
533 res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3
534 elif insn_name == 'fishmv':
535 # an overwrite instruction
536 res['0'] = 'd:FRS' # FRS: Rdest1_EXTRA3
537 res['1'] = 's:FRS' # FRS: Rsrc1_EXTRA3
538 elif insn_name == 'setvl':
539 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
540 res['1'] = 's:RA' # RS: Rsrc1_EXTRA3
541 else:
542 raise NotImplementedError(insn_name)
543
544 elif value == 'RM-1P-2S1D':
545 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
546 if insn_name.startswith('cr'):
547 res['0'] = 'd:BT' # BT: Rdest1_EXTRA3
548 res['1'] = 's:BA' # BA: Rsrc1_EXTRA3
549 res['2'] = 's:BB' # BB: Rsrc2_EXTRA3
550 elif regs == ['FRA', '', 'FRC', 'FRT', '', 'CR1']:
551 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
552 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
553 res['2'] = 's:FRC' # FRC: Rsrc1_EXTRA3
554 # should be for fcmp
555 elif regs == ['FRA', 'FRB', '', '', '', 'BF']:
556 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
557 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
558 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
559 elif regs == ['FRA', 'FRB', '', 'FRT', '', '']:
560 res['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3
561 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
562 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
563 elif regs == ['FRA', 'FRB', '', 'FRT', '', 'CR1']:
564 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
565 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
566 res['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
567 elif regs == ['FRA', 'RB', '', 'FRT', '', 'CR1']:
568 res['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
569 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
570 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
571 elif regs == ['RS', 'RB', '', 'RA', '', '']:
572 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
573 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
574 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
575 elif name == '2R-1W' or insn_name == 'cmpb': # cmpb
576 if insn_name in ['bpermd', 'cmpb']:
577 res['0'] = 'd:RA' # RA: Rdest1_EXTRA3
578 res['1'] = 's:RS' # RS: Rsrc1_EXTRA3
579 else:
580 res['0'] = 'd:RT' # RT: Rdest1_EXTRA3
581 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
582 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
583 elif insn_name.startswith('cmp'): # cmp
584 res['0'] = 'd:BF' # BF: Rdest1_EXTRA3
585 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
586 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
587 elif (regs == ['', 'RB', 'RS', 'RA', '', 'CR0'] or
588 regs == ['RS', 'RB', '', 'RA', '', 'CR0']):
589 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
590 res['1'] = 's:RB' # RB: Rsrc1_EXTRA3
591 res['2'] = 's:RS' # RS: Rsrc1_EXTRA3
592 elif regs == ['RA', 'RB', '', 'RT', '', 'CR0']:
593 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
594 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
595 res['2'] = 's:RB' # RB: Rsrc1_EXTRA3
596 elif regs == ['RA', '', 'RS', 'RA', '', 'CR0']:
597 res['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
598 res['1'] = 's:RA' # RA: Rsrc1_EXTRA3
599 res['2'] = 's:RS' # RS: Rsrc1_EXTRA3
600 elif regs == ['RA', '', 'RB', 'RT', '', '']: # maddsubrs
601 res['0'] = 's:RT;d:RT' # RT: Rdest1_EXTRA2
602 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
603 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
604 else:
605 raise NotImplementedError(insn_name)
606
607 elif value == 'RM-2P-2S1D':
608 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
609 if insn_name.startswith('mt'): # mtcrf
610 res['0'] = 'd:CR' # CR: Rdest1_EXTRA2
611 res['1'] = 's:RS' # RS: Rsrc1_EXTRA2
612 res['2'] = 's:CR' # CR: Rsrc2_EXTRA2
613 else:
614 raise NotImplementedError(insn_name)
615
616 elif value == 'RM-1P-3S1D':
617 res['Etype'] = 'EXTRA2' # RM EXTRA2 type
618 if regs == ['FRT', 'FRB', 'FRA', 'FRT', '', 'CR1']: # ffmadds/fdmadds
619 res['0'] = 's:FRT;d:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA2
620 res['1'] = 's:FRB' # FRB: Rsrc1_EXTRA2
621 res['2'] = 's:FRA' # FRA: Rsrc2_EXTRA2
622 elif regs == ['RA', 'RB', 'RC', 'RT', '', '']: # madd*
623 res['0'] = 'd:RT' # RT,CR0: Rdest1_EXTRA2
624 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
625 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
626 res['3'] = 's:RC' # RT: Rsrc3_EXTRA2
627 elif regs == ['RA', 'RB', 'RC', 'RT', '', 'CR0']: # pcdec
628 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
629 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
630 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
631 res['3'] = 's:RC' # RT: Rsrc3_EXTRA2
632 elif regs == ['RA', 'RB', 'RT', 'RT', '', 'CR0']: # overwrite 3-in
633 res['0'] = 's:RT;d:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
634 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
635 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
636 elif insn_name == 'isel':
637 res['0'] = 'd:RT' # RT: Rdest1_EXTRA2
638 res['1'] = 's:RA' # RA: Rsrc1_EXTRA2
639 res['2'] = 's:RB' # RT: Rsrc2_EXTRA2
640 res['3'] = 's:BC' # BC: Rsrc3_EXTRA2
641 else: # fmadd*
642 res['0'] = 'd:FRT;d:CR1' # FRT, CR1: Rdest1_EXTRA2
643 res['1'] = 's:FRA' # FRA: Rsrc1_EXTRA2
644 res['2'] = 's:FRB' # FRB: Rsrc2_EXTRA2
645 res['3'] = 's:FRC' # FRC: Rsrc3_EXTRA2
646
647 elif value == 'RM-1P-1D':
648 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
649 if insn_name == 'svstep':
650 res['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
651 if insn_name == 'fmvis':
652 res['0'] = 'd:FRS' # FRS: Rdest1_EXTRA3
653
654 # HACK! thos should be RM-1P-1S butvthere is a bug with sv.bc
655 elif value == 'RM-2P-1S':
656 res['Etype'] = 'EXTRA3' # RM EXTRA3 type
657 if insn_name.startswith('bc'):
658 res['0'] = 's:BI' # BI: Rsrc1_EXTRA3
659
660 elif value == 'RM-1P-1S':
661 pass # FIXME
662
663 elif value == 'non-SV':
664 return
665
666 else:
667 raise NotImplementedError(insn_name)
668
669 #if insn_name.startswith("rlw"):
670 # print("regs ", value, insn_name, regs, res)
671
672
673
674 def process_csvs(format):
675
676 print("# Draft SVP64 Power ISA register 'profile's")
677 print('')
678 print("this page is auto-generated, do not edit")
679 print("created by http://libre-soc.org/openpower/sv_analysis.py")
680 print('')
681
682 (csvs, csvs_svp64, primarykeys, bykey, insn_to_csv, insns,
683 dictkeys, immediates) = read_csvs()
684
685 # mapping to old SVPrefix "Forms"
686 mapsto = {'3R-1W-CRo': 'RM-1P-3S1D',
687 '3R-1W': 'RM-1P-3S1D',
688 '2R-1W-CRio': 'RM-1P-2S1D',
689 '2R-1W-CRi': 'RM-1P-3S1D',
690 '2R-1W-CRo': 'RM-1P-2S1D',
691 '2R': 'non-SV',
692 '2R-1W': 'RM-1P-2S1D',
693 '2R-1W-imm': 'RM-1P-2S1D',
694 '1R-CRio': 'RM-2P-2S1D',
695 '2R-CRio': 'RM-1P-2S1D',
696 '2R-CRo': 'RM-1P-2S1D',
697 '1R': 'non-SV',
698 '1R-1W-CRio': 'RM-2P-1S1D',
699 '1R-1W-CRo': 'RM-2P-1S1D',
700 '1R-1W': 'RM-2P-1S1D',
701 '1R-1W-imm': 'RM-2P-1S1D',
702 '1R-CRo': 'RM-2P-1S1D',
703 '1R-imm': 'RM-1P-1S',
704 '1W-CRo': 'RM-1P-1D',
705 '1W': 'non-SV',
706 '1W-imm': 'RM-1P-1D',
707 '1W-CRi': 'RM-2P-1S1D',
708 'CRio': 'RM-2P-1S1D',
709 'CR=2R1W': 'RM-1P-2S1D',
710 'CRi': 'RM-2P-1S', # HACK, bc here, it should be 1P
711 'imm': 'non-SV',
712 '': 'non-SV',
713 'LDST-2R-imm': 'LDSTRM-2P-2S',
714 'LDST-2R-1W-imm': 'LDSTRM-2P-2S1D',
715 'LDST-2R-1W': 'LDSTRM-2P-2S1D',
716 'LDST-2R-2W': 'LDSTRM-2P-2S1D',
717 'LDST-1R-1W-imm': 'LDSTRM-2P-1S1D',
718 'LDST-1R-2W-imm': 'LDSTRM-2P-1S2D',
719 'LDST-3R': 'LDSTRM-2P-3S',
720 'LDST-3R-CRo': 'LDSTRM-2P-3S', # st*x
721 'LDST-3R-1W': 'LDSTRM-2P-2S1D', # st*x
722 'LDST-2R': 'non-SV', # dcbz -- TODO: any vectorizable?
723 'CRo': 'non-SV', # mtfsb1 -- TODO: any vectorizable?
724 }
725 print("# map to old SV Prefix")
726 print('')
727 print('|internal key | public name |')
728 print('|----- | ---------- |')
729 for key in primarykeys:
730 name = keyname(dictkeys[key])
731 value = mapsto.get(name, "-")
732 print(tformat([name, value + " "]))
733 print('')
734 print('')
735
736 print("# keys")
737 print('')
738 print(tformat(tablecols) + " imms | name |")
739 print(tformat([" - "] * (len(tablecols)+2)))
740
741 # print out the keys and the table from which they're derived
742 for key in primarykeys:
743 name = keyname(dictkeys[key])
744 row = tformat(dictkeys[key].values())
745 imms = list(immediates.get(key, ""))
746 imms.sort()
747 row += " %s | " % ("/".join(imms))
748 row += " %s |" % name
749 print(row)
750 print('')
751 print('')
752
753 # print out, by remap name, all the instructions under that category
754 for key in primarykeys:
755 name = keyname(dictkeys[key])
756 value = mapsto.get(name, "-")
757 print("## %s (%s)" % (name, value))
758 print('')
759 print(tformat(['CSV', 'opcode', 'asm', 'flags', 'form']))
760 print(tformat(['---', '------', '---', '-----', '----']))
761 rows = bykey[key]
762 rows.sort()
763 for row in rows:
764 print(tformat(row))
765 print('')
766 print('')
767
768 # for fname, csv in csvs.items():
769 # print (fname)
770
771 # for insn, row in insns.items():
772 # print (insn, row)
773
774 print("# svp64 remaps")
775 svp64 = OrderedDict()
776 # create a CSV file, per category, with SV "augmentation" info
777 # XXX note: 'out2' not added here, needs to be added to CSV files
778 # KEEP TRACK OF THESE https://bugs.libre-soc.org/show_bug.cgi?id=619
779 csvcols = ['insn', 'mode', 'CONDITIONS', 'Ptype', 'Etype', 'SM']
780 csvcols += ['0', '1', '2', '3']
781 csvcols += ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out'] # temporary
782 for key in primarykeys:
783 # get the decoded key containing row-analysis, and name/value
784 dkey = dictkeys[key]
785 name = keyname(dkey)
786 value = mapsto.get(name, "-")
787 if value == 'non-SV':
788 continue
789
790 # print out svp64 tables by category
791 print("* **%s**: %s" % (name, value))
792
793 # store csv entries by svp64 RM category
794 if value not in svp64:
795 svp64[value] = []
796
797 rows = bykey[key]
798 rows.sort()
799
800 for row in rows:
801 # for idx in range(len(row)):
802 # if row[idx] == 'NONE':
803 # row[idx] = ''
804 # get the instruction
805 #print(key, row)
806 insn_name = row[2]
807 condition = row[3]
808 insn = insns[(insn_name, condition)]
809
810 #if insn_name == 'rlwinm':
811 # print ("upd rlwinm", insn)
812
813 # start constructing svp64 CSV row
814 res = OrderedDict()
815 res['insn'] = insn_name
816 res['CONDITIONS'] = condition
817 res['Ptype'] = value.split('-')[1] # predication type (RM-xN-xxx)
818 # get whether R_xxx_EXTRAn fields are 2-bit or 3-bit
819 res['Etype'] = 'EXTRA2'
820 # go through each register matching to Rxxxx_EXTRAx
821 for k in ['0', '1', '2', '3']:
822 res[k] = ''
823 # create "fake" out2 (TODO, needs to be added to CSV files)
824 # KEEP TRACK HERE https://bugs.libre-soc.org/show_bug.cgi?id=619
825 res['out2'] = 'NONE'
826 if insn['upd'] == '1': # LD/ST with update has RA as out2
827 res['out2'] = 'RA'
828
829 # set the SVP64 mode to NORMAL, LDST, BRANCH or CR
830 crops = ['mfcr', 'mfocrf', 'mtcrf', 'mtocrf',
831 ]
832 mode = 'NORMAL'
833 if value.startswith('LDST'):
834 if 'x' in insn_name: # Indexed detection
835 mode = 'LDST_IDX'
836 else:
837 mode = 'LDST_IMM'
838 elif insn_name.startswith('bc'):
839 mode = 'BRANCH'
840 elif (insn_name.startswith('cmp') or
841 insn_name.startswith('cr') or
842 insn_name in crops):
843 mode = 'CROP'
844 res['mode'] = mode
845
846 # create a register profile list (update res row as well)
847 regs = regs_profile(insn, res)
848
849 #print("regs", insn_name, regs)
850 extra_classifier(insn_name, value, name, res, regs)
851
852 # source-mask is hard to detect, it's part of RM-nn-nn.
853 # to make style easier, create a yes/no decision here
854 # see https://libre-soc.org/openpower/sv/svp64/#extra_remap
855 # MASK_SRC
856 vstripped = value.replace("LDST", "")
857 if vstripped in ['RM-2P-1S1D', 'RM-2P-2S',
858 'RM-2P-2S1D', 'RM-2P-1S2D', 'RM-2P-3S',
859 ]:
860 res['SM'] = 'EN'
861 else:
862 res['SM'] = 'NO'
863 # add to svp64 csvs
864 # for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
865 # del res[k]
866 # if res['0'] != 'TODO':
867 for k in res:
868 if k == 'CONDITIONS':
869 continue
870 if res[k] == 'NONE' or res[k] == '':
871 res[k] = '0'
872 svp64[value].append(res)
873 # also add to by-CSV version
874 csv_fname = insn_to_csv[insn_name]
875 csvs_svp64[csv_fname].append(res)
876
877 print('')
878
879 # now write out the csv files
880 for value, csv in svp64.items():
881 if value == '-':
882 continue
883 from time import sleep
884 print("WARNING, filename '-' should NOT exist. instrs missing")
885 print("TODO: fix this (and put in the bugreport number here)")
886 sleep(2)
887 # print out svp64 tables by category
888 print("## %s" % value)
889 print('')
890 cols = csvcols + ['out2']
891 print(tformat(cols))
892 print(tformat([" - "] * (len(cols))))
893 for d in csv:
894 row = []
895 for k in cols:
896 row.append(d[k])
897 print(tformat(row))
898 print('')
899
900 #csvcols = ['insn', 'Ptype', 'Etype', '0', '1', '2', '3']
901 write_csv("%s.csv" % value, csv, csvcols + ['out2'])
902
903 # okaaay, now we re-read them back in for producing microwatt SV
904
905 # get SVP64 augmented CSV files
906 svt = SVP64RM(microwatt_format=True)
907 # Expand that (all .csv files)
908 pth = find_wiki_file("*.csv")
909
910 # Ignore those containing: valid test sprs
911 for fname in glob_valid_csvs(pth):
912 svp64_csv = svt.get_svp64_csv(fname)
913
914 csvcols = ['insn', 'mode', 'Ptype', 'Etype', 'SM']
915 csvcols += ['in1', 'in2', 'in3', 'out', 'out2', 'CR in', 'CR out']
916
917 if format is Format.VHDL:
918 # and a nice microwatt VHDL file
919 file_path = find_wiki_file("sv_decode.vhdl")
920 elif format is Format.BINUTILS:
921 file_path = find_wiki_file("binutils.c")
922
923 with open(file_path, 'w') as stream:
924 output(format, svt, csvcols, insns, csvs_svp64, stream)
925
926
927 def output_autogen_disclaimer(format, stream):
928 lines = (
929 "this file is auto-generated, do not edit",
930 "http://libre-soc.org/openpower/sv_analysis.py",
931 "part of Libre-SOC, sponsored by NLnet",
932 )
933 for line in format.wrap_comment(lines):
934 stream.write(line)
935 stream.write("\n")
936 stream.write("\n")
937
938
939 def output(format, svt, csvcols, insns, csvs_svp64, stream):
940 lens = {
941 'major': 63,
942 'minor_4': 63,
943 'minor_19': 7,
944 'minor_30': 15,
945 'minor_31': 1023,
946 'minor_58': 63,
947 'minor_59': 31,
948 'minor_62': 63,
949 'minor_63l': 511,
950 'minor_63h': 16,
951 }
952
953 def svp64_canonicalize(item):
954 (value, csv) = item
955 value = value.lower().replace("-", "_")
956 return (value, csv)
957
958 csvs_svp64_canon = dict(map(svp64_canonicalize, csvs_svp64.items()))
959
960 # disclaimer
961 output_autogen_disclaimer(format, stream)
962
963 # declarations
964 for line in format.declarations(csvs_svp64_canon.keys(), lens):
965 stream.write(f"{line}\n")
966
967 # definitions
968 sv_cols = ['sv_in1', 'sv_in2', 'sv_in3', 'sv_out', 'sv_out2',
969 'sv_cr_in', 'sv_cr_out']
970 fullcols = csvcols + sv_cols
971
972 entries_svp64 = defaultdict(list)
973 for (value, csv) in filter(lambda kv: kv[0] in lens,
974 csvs_svp64_canon.items()):
975 for entry in csv:
976 insn = str(entry['insn'])
977 condition = str(entry['CONDITIONS'])
978 mode = str(entry['mode'])
979 sventry = svt.svp64_instrs.get(insn, None)
980 if sventry is not None:
981 sventry['mode'] = mode
982 op = insns[(insn, condition)]['opcode']
983 # binary-to-vhdl-binary
984 if op.startswith("0b"):
985 op = "2#%s#" % op[2:]
986 row = []
987 for colname in csvcols[1:]:
988 re = entry[colname]
989 # zero replace with NONE
990 if re == '0':
991 re = 'NONE'
992 # 1/2 predication
993 re = re.replace("1P", "P1")
994 re = re.replace("2P", "P2")
995 row.append(re)
996 #print("sventry", sventry)
997 for colname in sv_cols:
998 if sventry is None:
999 re = 'NONE'
1000 else:
1001 re = sventry[colname]
1002 row.append(re)
1003 entries_svp64[value].append((op, insn, row))
1004
1005 for line in format.definitions(entries_svp64, fullcols):
1006 stream.write(f"{line}\n")
1007
1008
1009 def main():
1010 import os
1011 os.environ['SILENCELOG'] = '1'
1012 parser = argparse.ArgumentParser()
1013 parser.add_argument("-f", "--format",
1014 type=Format, choices=Format, default=Format.VHDL,
1015 help="format to be used (binutils or VHDL)")
1016 args = parser.parse_args()
1017 process_csvs(args.format)
1018
1019
1020 if __name__ == '__main__':
1021 # don't do anything other than call main() here, cuz this code is bypassed
1022 # by the sv_analysis command created by setup.py
1023 main()