3 # NOTE that this program is python2 compatible, please do not stop it
4 # from working by adding syntax that prevents that.
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/
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
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.
27 from os
.path
import dirname
, join
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
36 # Ignore those containing: valid test sprs
37 def glob_valid_csvs(root
):
39 _
, name
= os
.path
.split(fname
)
46 if fname
.endswith('insndb.csv'):
48 if fname
.endswith('sprs.csv'):
50 if fname
.endswith('minor_19_valid.csv'):
56 yield from filter(check_csv
, glob(root
))
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")
65 writer
.writerows(items
)
67 # This will return True if all values are true.
68 # Not sure what this is about
72 # for v in row.values():
73 # if 'SPR' in v: # skip all SPRs
75 for v
in row
.values():
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
86 return (field
.startswith('R') or field
.startswith('FR') or
90 # These are the attributes of the instructions,
92 keycolumns
= ['unit', 'in1', 'in2', 'in3', 'out', 'CR in', 'CR out',
93 ] # don't think we need these: 'ldst len', 'rc', 'lk']
95 tablecols
= ['unit', 'in', 'outcnt', 'CR in', 'CR out', 'imm'
96 ] # don't think we need these: 'ldst len', 'rc', 'lk']
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
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']:
112 if row
['unit'] == 'BRANCH': # branches must not include Vector SPRs
119 # If upd is 1 then increment the count of outputs
120 if 'outcnt' not in res
:
124 if row
['upd'] == '1':
127 # CRs (Condition Register) (CR0 .. CR7)
128 if key
.startswith('CR'):
129 if row
[key
].startswith('NONE'):
133 if row
['comment'].startswith('cr'):
137 if row
[key
] == 'LDST': # we care about LDST units
141 # LDST len (LoadStore length)
142 if key
.startswith('ldst'):
143 if row
[key
].startswith('NONE'):
148 if key
in ['rc', 'lk']:
149 if row
[key
] == 'ONE':
151 elif row
[key
] == 'NONE':
158 # Convert the numerics 'in' & 'outcnt' to strings
159 res
['in'] = str(res
['in'])
160 res
['outcnt'] = str(res
['outcnt'])
163 if row
['in2'].startswith('CONST_'):
164 res
['imm'] = "1" # row['in2'].split("_")[1]
175 for k
, v
in d
.items():
176 res
.append("%s: %s" % (k
, v
))
181 return "| " + ' | '.join(d
) + " |"
185 """converts a key into a readable string. anything null or zero
186 is skipped, shortening the readable string
189 if row
['unit'] != 'OTHER':
190 res
.append(row
['unit'])
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':
197 res
.append("CR=2R1W")
200 elif row
['CR in'] == '1':
202 elif row
['CR out'] == '1':
204 elif 'imm' in row
and row
['imm']:
209 class Format(enum
.Enum
):
210 BINUTILS
= enum
.auto()
214 def _missing_(cls
, value
):
216 "binutils": Format
.BINUTILS
,
221 return self
.name
.lower()
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
)
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
)
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
)
240 Format
.BINUTILS
: declaration_binutils
,
241 Format
.VHDL
: declaration_vhdl
,
242 }[self
](value
, width
)
244 def definitions(self
, entries_svp64
, fullcols
):
245 def definitions_vhdl():
246 for (value
, entries
) in entries_svp64
.items():
248 yield " constant sv_%s_decode_rom_array :" % value
249 yield " sv_%s_rom_array_t := (" % value
250 yield " -- %s" % ' '.join(fullcols
)
252 for (op
, insn
, row
) in entries
:
253 yield f
" {op:>13} => ({', '.join(row)}), -- {insn}"
255 yield f
" {'others':>13} => sv_illegal_inst"
259 def definitions_binutils():
260 yield f
"/* TODO: implement binutils definitions */"
263 Format
.BINUTILS
: definitions_binutils
,
264 Format
.VHDL
: definitions_vhdl
,
267 def wrap_comment(self
, lines
):
268 def wrap_comment_binutils(lines
):
271 yield "/* %s */" % lines
[0]
274 yield from map(lambda line
: " * %s" % line
, lines
)
277 def wrap_comment_vhdl(lines
):
278 yield from map(lambda line
: "-- %s" % line
, lines
)
281 Format
.BINUTILS
: wrap_comment_binutils
,
282 Format
.VHDL
: wrap_comment_vhdl
,
291 dictkeys
= OrderedDict()
293 insns
= {} # dictionary of CSV row, by instruction
296 # Expand that (all .csv files)
297 pth
= find_wiki_file("*.csv")
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
306 csvs_svp64
[csvname_
] = []
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']:
318 if insn_name
in ['bctar', 'bcctr']: # for now. TODO
320 if 'rfid' in insn_name
:
322 if 'addpcis' in insn_name
: # skip for now
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)
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)
342 bykey
[key
].append((csvname
, row
['opcode'], insn_name
, condition
,
343 row
['form'].upper() + '-Form'))
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
)
352 primarykeys
= list(primarykeys
)
355 return (csvs
, csvs_svp64
, primarykeys
, bykey
, insn_to_csv
, insns
,
356 dictkeys
, immediates
)
359 def regs_profile(insn
, res
):
360 """get a more detailed register profile: 1st operand is RA,
364 for k
in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
365 if insn
[k
].startswith('CONST'):
370 if insn
[k
] == 'RA_OR_ZERO':
372 elif insn
[k
] != 'NONE':
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.
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.
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.
401 # for LD/ST FP, use FRT/FRS not RT/RS, and use CR1 not CR0
402 if insn_name
.startswith("lf"):
408 if insn_name
.startswith("stf"):
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
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
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
437 elif value
== 'LDSTRM-2P-2S':
439 res
['Etype'] = 'EXTRA3' # RM EXTRA3 type
440 res
['0'] = sRS
# RS: Rdest1_EXTRA3
441 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
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
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
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
469 res
['0'] = sRS
# RS: Rsrc1_EXTRA2
470 res
['1'] = 's:RA' # RA: Rsrc2_EXTRA2
471 res
['2'] = 's:RB' # RA: Rsrc3_EXTRA2
474 # now begins,arithmetic
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 regs
== ['', '', '', 'RT', 'BI', '']:
495 res
['0'] = 'd:RT' # RT: Rdest1_EXTRA3
496 res
['1'] = 's:BI' # BI: Rsrc1_EXTRA3
497 elif insn_name
== 'setb':
498 res
['0'] = 'd:RT' # RT: Rdest1_EXTRA3
499 res
['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
500 elif insn_name
.startswith('cmp'): # cmpi
501 res
['0'] = 'd:BF' # BF: Rdest1_EXTRA3
502 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
503 elif regs
== ['RA', '', '', 'RT', '', '']:
504 res
['0'] = 'd:RT' # RT: Rdest1_EXTRA3
505 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
506 elif regs
== ['RA', '', '', 'RT', '', 'CR0']:
507 res
['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
508 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
509 elif (regs
== ['RS', '', '', 'RA', '', 'CR0'] or
510 regs
== ['', '', 'RS', 'RA', '', 'CR0']):
511 res
['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
512 res
['1'] = 's:RS' # RS: Rsrc1_EXTRA3
513 elif regs
== ['RS', '', '', 'RA', '', '']:
514 res
['0'] = 'd:RA' # RA: Rdest1_EXTRA3
515 res
['1'] = 's:RS' # RS: Rsrc1_EXTRA3
516 elif regs
== ['', 'FRB', '', 'FRT', '0', 'CR1']:
517 res
['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
518 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
519 elif regs
== ['', 'FRB', '', '', '', 'CR1']:
520 res
['0'] = 'd:CR1' # CR1: Rdest1_EXTRA3
521 res
['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
522 elif regs
== ['', 'FRB', '', '', '', 'BF']:
523 res
['0'] = 'd:BF' # BF: Rdest1_EXTRA3
524 res
['1'] = 's:FRB' # FRA: Rsrc1_EXTRA3
525 elif regs
== ['', 'FRB', '', 'FRT', '', 'CR1']:
526 res
['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
527 res
['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3
528 elif regs
== ['', 'RB', '', 'FRT', '', 'CR1']:
529 res
['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
530 res
['1'] = 's:RB' # RB: Rsrc1_EXTRA3
531 elif regs
== ['', 'RB', '', 'FRT', '', '']:
532 res
['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3
533 res
['1'] = 's:RB' # RB: Rsrc1_EXTRA3
534 elif regs
== ['', 'FRB', '', 'RT', '', 'CR0']:
535 res
['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
536 res
['1'] = 's:FRB' # FRB: Rsrc1_EXTRA3
537 elif insn_name
== 'fishmv':
538 # an overwrite instruction
539 res
['0'] = 'd:FRS' # FRS: Rdest1_EXTRA3
540 res
['1'] = 's:FRS' # FRS: Rsrc1_EXTRA3
541 elif insn_name
== 'setvl':
542 res
['0'] = 'd:RT' # RT: Rdest1_EXTRA3
543 res
['1'] = 's:RA' # RS: Rsrc1_EXTRA3
544 elif insn_name
== 'svstep':
545 res
['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
546 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
548 raise NotImplementedError(insn_name
)
550 elif value
== 'RM-1P-2S1D':
551 res
['Etype'] = 'EXTRA3' # RM EXTRA3 type
552 if insn_name
in ['crfbinlog', 'crfternlogi']:
553 res
['0'] = 'd:BF' # BF: Rdest1_EXTRA3
554 res
['1'] = 's:BFA' # BFA: Rsrc1_EXTRA3
555 res
['2'] = 's:BFB' # BFB: Rsrc2_EXTRA3
556 elif insn_name
== 'crbinlog':
557 res
['0'] = 'd:BT' # BT: Rdest1_EXTRA3
558 res
['1'] = 's:BA' # BA: Rsrc1_EXTRA3
559 res
['2'] = 's:BFB' # BFB: Rsrc2_EXTRA3
560 elif insn_name
.startswith('cr'):
561 res
['0'] = 'd:BT' # BT: Rdest1_EXTRA3
562 res
['1'] = 's:BA' # BA: Rsrc1_EXTRA3
563 res
['2'] = 's:BB' # BB: Rsrc2_EXTRA3
564 elif regs
== ['FRA', '', 'FRC', 'FRT', '', 'CR1']:
565 res
['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
566 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
567 res
['2'] = 's:FRC' # FRC: Rsrc1_EXTRA3
569 elif regs
== ['FRA', 'FRB', '', '', '', 'BF']:
570 res
['0'] = 'd:BF' # BF: Rdest1_EXTRA3
571 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
572 res
['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
573 elif regs
== ['FRA', 'FRB', '', 'FRT', '', '']:
574 res
['0'] = 'd:FRT' # FRT: Rdest1_EXTRA3
575 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
576 res
['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
577 elif regs
== ['FRA', 'FRB', '', 'FRT', '', 'CR1']:
578 res
['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
579 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
580 res
['2'] = 's:FRB' # FRB: Rsrc1_EXTRA3
581 elif regs
== ['FRA', 'RB', '', 'FRT', '', 'CR1']:
582 res
['0'] = 'd:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA3
583 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA3
584 res
['2'] = 's:RB' # RB: Rsrc1_EXTRA3
585 elif regs
== ['RS', 'RB', '', 'RA', '', '']:
586 res
['0'] = 'd:RA' # RA: Rdest1_EXTRA3
587 res
['1'] = 's:RS' # RS: Rsrc1_EXTRA3
588 res
['2'] = 's:RB' # RB: Rsrc1_EXTRA3
589 elif name
== '2R-1W' or insn_name
== 'cmpb': # cmpb
590 if insn_name
in ['bpermd', 'cmpb']:
591 res
['0'] = 'd:RA' # RA: Rdest1_EXTRA3
592 res
['1'] = 's:RS' # RS: Rsrc1_EXTRA3
594 res
['0'] = 'd:RT' # RT: Rdest1_EXTRA3
595 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
596 res
['2'] = 's:RB' # RB: Rsrc1_EXTRA3
597 elif insn_name
.startswith('cmp'): # cmp
598 res
['0'] = 'd:BF' # BF: Rdest1_EXTRA3
599 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
600 res
['2'] = 's:RB' # RB: Rsrc1_EXTRA3
601 elif (regs
== ['', 'RB', 'RS', 'RA', '', 'CR0'] or
602 regs
== ['RS', 'RB', '', 'RA', '', 'CR0']):
603 res
['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
604 res
['1'] = 's:RB' # RB: Rsrc1_EXTRA3
605 res
['2'] = 's:RS' # RS: Rsrc1_EXTRA3
606 elif regs
== ['RA', 'RB', '', 'RT', '', 'CR0']:
607 res
['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA3
608 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
609 res
['2'] = 's:RB' # RB: Rsrc1_EXTRA3
610 elif regs
== ['RA', '', 'RS', 'RA', '', 'CR0']:
611 res
['0'] = 'd:RA;d:CR0' # RA,CR0: Rdest1_EXTRA3
612 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA3
613 res
['2'] = 's:RS' # RS: Rsrc1_EXTRA3
614 elif regs
== ['RA', '', 'RB', 'RT', '', '']: # maddsubrs
615 res
['0'] = 's:RT;d:RT' # RT: Rdest1_EXTRA2
616 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA2
617 res
['2'] = 's:RB' # RT: Rsrc2_EXTRA2
619 raise NotImplementedError(insn_name
)
621 elif value
== 'RM-2P-2S1D':
622 res
['Etype'] = 'EXTRA2' # RM EXTRA2 type
623 if insn_name
.startswith('mt'): # mtcrf
624 res
['0'] = 'd:CR' # CR: Rdest1_EXTRA2
625 res
['1'] = 's:RS' # RS: Rsrc1_EXTRA2
626 res
['2'] = 's:CR' # CR: Rsrc2_EXTRA2
628 raise NotImplementedError(insn_name
)
630 elif value
== 'RM-1P-3S1D':
631 res
['Etype'] = 'EXTRA2' # RM EXTRA2 type
632 if regs
== ['FRT', 'FRB', 'FRA', 'FRT', '', 'CR1']: # ffmadds/fdmadds
633 res
['0'] = 's:FRT;d:FRT;d:CR1' # FRT,CR1: Rdest1_EXTRA2
634 res
['1'] = 's:FRB' # FRB: Rsrc1_EXTRA2
635 res
['2'] = 's:FRA' # FRA: Rsrc2_EXTRA2
636 elif regs
== ['RA', 'RB', 'RC', 'RT', '', '']: # madd*
637 res
['0'] = 'd:RT' # RT,CR0: Rdest1_EXTRA2
638 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA2
639 res
['2'] = 's:RB' # RT: Rsrc2_EXTRA2
640 res
['3'] = 's:RC' # RT: Rsrc3_EXTRA2
641 elif regs
== ['RA', 'RB', 'RC', 'RT', '', 'CR0']: # pcdec
642 res
['0'] = 'd:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
643 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA2
644 res
['2'] = 's:RB' # RT: Rsrc2_EXTRA2
645 res
['3'] = 's:RC' # RT: Rsrc3_EXTRA2
646 elif regs
== ['RA', 'RB', 'RT', 'RT', '', 'CR0']: # overwrite 3-in
647 res
['0'] = 's:RT;d:RT;d:CR0' # RT,CR0: Rdest1_EXTRA2
648 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA2
649 res
['2'] = 's:RB' # RT: Rsrc2_EXTRA2
650 elif insn_name
== 'isel':
651 res
['0'] = 'd:RT' # RT: Rdest1_EXTRA2
652 res
['1'] = 's:RA' # RA: Rsrc1_EXTRA2
653 res
['2'] = 's:RB' # RT: Rsrc2_EXTRA2
654 res
['3'] = 's:BC' # BC: Rsrc3_EXTRA2
656 res
['0'] = 'd:FRT;d:CR1' # FRT, CR1: Rdest1_EXTRA2
657 res
['1'] = 's:FRA' # FRA: Rsrc1_EXTRA2
658 res
['2'] = 's:FRB' # FRB: Rsrc2_EXTRA2
659 res
['3'] = 's:FRC' # FRC: Rsrc3_EXTRA2
661 elif value
== 'RM-1P-1D':
662 res
['Etype'] = 'EXTRA3' # RM EXTRA3 type
663 if insn_name
== 'fmvis':
664 res
['0'] = 'd:FRS' # FRS: Rdest1_EXTRA3
666 # HACK! thos should be RM-1P-1S butvthere is a bug with sv.bc
667 elif value
== 'RM-2P-1S':
668 res
['Etype'] = 'EXTRA3' # RM EXTRA3 type
669 if insn_name
.startswith('bc'):
670 res
['0'] = 's:BI' # BI: Rsrc1_EXTRA3
672 elif value
== 'RM-1P-1S':
675 elif value
== 'non-SV':
679 raise NotImplementedError(insn_name
)
681 #if insn_name.startswith("rlw"):
682 # print("regs ", value, insn_name, regs, res)
686 def process_csvs(format
):
688 print("# Draft SVP64 Power ISA register 'profile's")
690 print("this page is auto-generated, do not edit")
691 print("created by http://libre-soc.org/openpower/sv_analysis.py")
694 (csvs
, csvs_svp64
, primarykeys
, bykey
, insn_to_csv
, insns
,
695 dictkeys
, immediates
) = read_csvs()
697 # mapping to old SVPrefix "Forms"
698 mapsto
= {'3R-1W-CRo': 'RM-1P-3S1D',
699 '3R-1W': 'RM-1P-3S1D',
700 '2R-1W-CRio': 'RM-1P-2S1D',
701 '2R-1W-CRi': 'RM-1P-3S1D',
702 '2R-1W-CRo': 'RM-1P-2S1D',
704 '2R-1W': 'RM-1P-2S1D',
705 '2R-1W-imm': 'RM-1P-2S1D',
706 '1R-CRio': 'RM-2P-2S1D',
707 '2R-CRio': 'RM-1P-2S1D',
708 '2R-CRo': 'RM-1P-2S1D',
710 '1R-1W-CRio': 'RM-2P-1S1D',
711 '1R-1W-CRo': 'RM-2P-1S1D',
712 '1R-1W': 'RM-2P-1S1D',
713 '1R-1W-imm': 'RM-2P-1S1D',
714 '1R-CRo': 'RM-2P-1S1D',
715 '1R-imm': 'RM-1P-1S',
716 '1W-CRo': 'RM-1P-1D',
718 '1W-imm': 'RM-1P-1D',
719 '1W-CRi': 'RM-2P-1S1D',
720 'CRio': 'RM-2P-1S1D',
721 'CR=2R1W': 'RM-1P-2S1D',
722 'CRi': 'RM-2P-1S', # HACK, bc here, it should be 1P
725 'LDST-2R-imm': 'LDSTRM-2P-2S',
726 'LDST-2R-1W-imm': 'LDSTRM-2P-2S1D',
727 'LDST-2R-1W': 'LDSTRM-2P-2S1D',
728 'LDST-2R-2W': 'LDSTRM-2P-2S1D',
729 'LDST-1R-1W-imm': 'LDSTRM-2P-1S1D',
730 'LDST-1R-2W-imm': 'LDSTRM-2P-1S2D',
731 'LDST-3R': 'LDSTRM-2P-3S',
732 'LDST-3R-CRo': 'LDSTRM-2P-3S', # st*x
733 'LDST-3R-1W': 'LDSTRM-2P-2S1D', # st*x
734 'LDST-2R': 'non-SV', # dcbz -- TODO: any vectorizable?
735 'CRo': 'non-SV', # mtfsb1 -- TODO: any vectorizable?
737 print("# map to old SV Prefix")
739 print('|internal key | public name |')
740 print('|----- | ---------- |')
741 for key
in primarykeys
:
742 name
= keyname(dictkeys
[key
])
743 value
= mapsto
.get(name
, "-")
744 print(tformat([name
, value
+ " "]))
750 print(tformat(tablecols
) + " imms | name |")
751 print(tformat([" - "] * (len(tablecols
)+2)))
753 # print out the keys and the table from which they're derived
754 for key
in primarykeys
:
755 name
= keyname(dictkeys
[key
])
756 row
= tformat(dictkeys
[key
].values())
757 imms
= list(immediates
.get(key
, ""))
759 row
+= " %s | " % ("/".join(imms
))
760 row
+= " %s |" % name
765 # print out, by remap name, all the instructions under that category
766 for key
in primarykeys
:
767 name
= keyname(dictkeys
[key
])
768 value
= mapsto
.get(name
, "-")
769 print("## %s (%s)" % (name
, value
))
771 print(tformat(['CSV', 'opcode', 'asm', 'flags', 'form']))
772 print(tformat(['---', '------', '---', '-----', '----']))
780 # for fname, csv in csvs.items():
783 # for insn, row in insns.items():
786 print("# svp64 remaps")
787 svp64
= OrderedDict()
788 # create a CSV file, per category, with SV "augmentation" info
789 # XXX note: 'out2' not added here, needs to be added to CSV files
790 # KEEP TRACK OF THESE https://bugs.libre-soc.org/show_bug.cgi?id=619
791 csvcols
= ['insn', 'mode', 'CONDITIONS', 'Ptype', 'Etype', 'SM']
792 csvcols
+= ['0', '1', '2', '3']
793 csvcols
+= ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out'] # temporary
794 for key
in primarykeys
:
795 # get the decoded key containing row-analysis, and name/value
798 value
= mapsto
.get(name
, "-")
799 if value
== 'non-SV':
802 # print out svp64 tables by category
803 print("* **%s**: %s" % (name
, value
))
805 # store csv entries by svp64 RM category
806 if value
not in svp64
:
813 # for idx in range(len(row)):
814 # if row[idx] == 'NONE':
816 # get the instruction
820 insn
= insns
[(insn_name
, condition
)]
822 #if insn_name == 'rlwinm':
823 # print ("upd rlwinm", insn)
825 # start constructing svp64 CSV row
827 res
['insn'] = insn_name
828 res
['CONDITIONS'] = condition
829 res
['Ptype'] = value
.split('-')[1] # predication type (RM-xN-xxx)
830 # get whether R_xxx_EXTRAn fields are 2-bit or 3-bit
831 res
['Etype'] = 'EXTRA2'
832 # go through each register matching to Rxxxx_EXTRAx
833 for k
in ['0', '1', '2', '3']:
835 # create "fake" out2 (TODO, needs to be added to CSV files)
836 # KEEP TRACK HERE https://bugs.libre-soc.org/show_bug.cgi?id=619
838 if insn
['upd'] == '1': # LD/ST with update has RA as out2
841 # set the SVP64 mode to NORMAL, LDST, BRANCH or CR
842 crops
= ['mfcr', 'mfocrf', 'mtcrf', 'mtocrf',
845 if value
.startswith('LDST'):
846 if 'x' in insn_name
: # Indexed detection
850 elif insn_name
.startswith('bc'):
852 elif (insn_name
.startswith('cmp') or
853 insn_name
.startswith('cr') or
858 # create a register profile list (update res row as well)
859 regs
= regs_profile(insn
, res
)
861 #print("regs", insn_name, regs)
862 extra_classifier(insn_name
, value
, name
, res
, regs
)
864 # source-mask is hard to detect, it's part of RM-nn-nn.
865 # to make style easier, create a yes/no decision here
866 # see https://libre-soc.org/openpower/sv/svp64/#extra_remap
868 vstripped
= value
.replace("LDST", "")
869 if vstripped
in ['RM-2P-1S1D', 'RM-2P-2S',
870 'RM-2P-2S1D', 'RM-2P-1S2D', 'RM-2P-3S',
876 # for k in ['in1', 'in2', 'in3', 'out', 'CR in', 'CR out']:
878 # if res['0'] != 'TODO':
880 if k
== 'CONDITIONS':
882 if res
[k
] == 'NONE' or res
[k
] == '':
884 svp64
[value
].append(res
)
885 # also add to by-CSV version
886 csv_fname
= insn_to_csv
[insn_name
]
887 csvs_svp64
[csv_fname
].append(res
)
891 # now write out the csv files
892 for value
, csv
in svp64
.items():
895 from time
import sleep
896 print("WARNING, filename '-' should NOT exist. instrs missing")
897 print("TODO: fix this (and put in the bugreport number here)")
899 # print out svp64 tables by category
900 print("## %s" % value
)
902 cols
= csvcols
+ ['out2']
904 print(tformat([" - "] * (len(cols
))))
912 #csvcols = ['insn', 'Ptype', 'Etype', '0', '1', '2', '3']
913 write_csv("%s.csv" % value
, csv
, csvcols
+ ['out2'])
915 # okaaay, now we re-read them back in for producing microwatt SV
917 # get SVP64 augmented CSV files
918 svt
= SVP64RM(microwatt_format
=True)
919 # Expand that (all .csv files)
920 pth
= find_wiki_file("*.csv")
922 # Ignore those containing: valid test sprs
923 for fname
in glob_valid_csvs(pth
):
924 svp64_csv
= svt
.get_svp64_csv(fname
)
926 csvcols
= ['insn', 'mode', 'Ptype', 'Etype', 'SM']
927 csvcols
+= ['in1', 'in2', 'in3', 'out', 'out2', 'CR in', 'CR out']
929 if format
is Format
.VHDL
:
930 # and a nice microwatt VHDL file
931 file_path
= find_wiki_file("sv_decode.vhdl")
932 elif format
is Format
.BINUTILS
:
933 file_path
= find_wiki_file("binutils.c")
935 with
open(file_path
, 'w') as stream
:
936 output(format
, svt
, csvcols
, insns
, csvs_svp64
, stream
)
939 def output_autogen_disclaimer(format
, stream
):
941 "this file is auto-generated, do not edit",
942 "http://libre-soc.org/openpower/sv_analysis.py",
943 "part of Libre-SOC, sponsored by NLnet",
945 for line
in format
.wrap_comment(lines
):
951 def output(format
, svt
, csvcols
, insns
, csvs_svp64
, stream
):
965 def svp64_canonicalize(item
):
967 value
= value
.lower().replace("-", "_")
970 csvs_svp64_canon
= dict(map(svp64_canonicalize
, csvs_svp64
.items()))
973 output_autogen_disclaimer(format
, stream
)
976 for line
in format
.declarations(csvs_svp64_canon
.keys(), lens
):
977 stream
.write(f
"{line}\n")
980 sv_cols
= ['sv_in1', 'sv_in2', 'sv_in3', 'sv_out', 'sv_out2',
981 'sv_cr_in', 'sv_cr_out']
982 fullcols
= csvcols
+ sv_cols
984 entries_svp64
= defaultdict(list)
985 for (value
, csv
) in filter(lambda kv
: kv
[0] in lens
,
986 csvs_svp64_canon
.items()):
988 insn
= str(entry
['insn'])
989 condition
= str(entry
['CONDITIONS'])
990 mode
= str(entry
['mode'])
991 sventry
= svt
.svp64_instrs
.get(insn
, None)
992 if sventry
is not None:
993 sventry
['mode'] = mode
994 op
= insns
[(insn
, condition
)]['opcode']
995 # binary-to-vhdl-binary
996 if op
.startswith("0b"):
997 op
= "2#%s#" % op
[2:]
999 for colname
in csvcols
[1:]:
1001 # zero replace with NONE
1005 re
= re
.replace("1P", "P1")
1006 re
= re
.replace("2P", "P2")
1008 #print("sventry", sventry)
1009 for colname
in sv_cols
:
1013 re
= sventry
[colname
]
1015 entries_svp64
[value
].append((op
, insn
, row
))
1017 for line
in format
.definitions(entries_svp64
, fullcols
):
1018 stream
.write(f
"{line}\n")
1023 os
.environ
['SILENCELOG'] = '1'
1024 parser
= argparse
.ArgumentParser()
1025 parser
.add_argument("-f", "--format",
1026 type=Format
, choices
=Format
, default
=Format
.VHDL
,
1027 help="format to be used (binutils or VHDL)")
1028 args
= parser
.parse_args()
1029 process_csvs(args
.format
)
1032 if __name__
== '__main__':
1033 # don't do anything other than call main() here, cuz this code is bypassed
1034 # by the sv_analysis command created by setup.py