b1b59f18a1b7682e1fa98e77d8b4e760d57553b4
[soc.git] / src / soc / sv / trans / svp64.py
1 # SPDX-License-Identifier: LGPLv3+
2 # Copyright (C) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Funded by NLnet http://nlnet.nl
4
5 """SVP64 OpenPOWER v3.0B assembly translator
6
7 This class takes raw svp64 assembly mnemonics (aliases excluded) and
8 creates an EXT001-encoded "svp64 prefix" followed by a v3.0B opcode.
9
10 It is very simple and straightforward, the only weirdness being the
11 extraction of the register information and conversion to v3.0B numbering.
12
13 Encoding format of svp64: https://libre-soc.org/openpower/sv/svp64/
14 Bugtracker: https://bugs.libre-soc.org/show_bug.cgi?id=578
15 """
16
17 import os, sys
18 from collections import OrderedDict
19
20 from soc.decoder.pseudo.pagereader import ISA
21 from soc.decoder.power_enums import get_csv, find_wiki_dir
22
23
24 # identifies register by type
25 def is_CR_3bit(regname):
26 return regname in ['BF', 'BFA']
27
28 def is_CR_5bit(regname):
29 return regname in ['BA', 'BB', 'BC', 'BI', 'BT']
30
31 def is_GPR(regname):
32 return regname in ['RA', 'RB', 'RC', 'RS', 'RT']
33
34 def get_regtype(regname):
35 if is_CR_3bit(regname):
36 return "CR_3bit"
37 if is_CR_5bit(regname):
38 return "CR_5bit"
39 if is_GPR(regname):
40 return "GPR"
41
42
43 # gets SVP64 ReMap information
44 class SVP64RM:
45 def __init__(self):
46 self.instrs = {}
47 pth = find_wiki_dir()
48 for fname in os.listdir(pth):
49 if fname.startswith("RM"):
50 for entry in get_csv(fname):
51 self.instrs[entry['insn']] = entry
52
53
54 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
55 class SVP64:
56 def __init__(self, lst):
57 self.lst = lst
58 self.trans = self.translate(lst)
59
60 def __iter__(self):
61 for insn in self.trans:
62 yield insn
63
64 def translate(self, lst):
65 isa = ISA() # reads the v3.0B pseudo-code markdown files
66 svp64 = SVP64RM() # reads the svp64 Remap entries for registers
67 res = []
68 for insn in lst:
69 # find first space, to get opcode
70 ls = insn.split(' ')
71 opcode = ls[0]
72 # now find opcode fields
73 fields = ''.join(ls[1:]).split(',')
74 fields = list(map(str.strip, fields))
75 print (opcode, fields)
76
77 # identify if is a svp64 mnemonic
78 if not opcode.startswith('sv.'):
79 res.append(insn) # unaltered
80 continue
81
82 # start working on decoding the svp64 op: sv.baseop.vec2.mode
83 opmodes = opcode.split(".")[1:] # strip leading "sv."
84 v30b_op = opmodes.pop(0) # first is the v3.0B
85 if v30b_op not in isa.instr:
86 raise Exception("opcode %s of '%s' not supported" % \
87 (v30b_op, insn))
88 if v30b_op not in svp64.instrs:
89 raise Exception("opcode %s of '%s' not an svp64 instruction" % \
90 (v30b_op, insn))
91 isa.instr[v30b_op].regs[0]
92 v30b_regs = isa.instr[v30b_op].regs[0]
93 rm = svp64.instrs[v30b_op]
94 print ("v3.0B regs", opcode, v30b_regs)
95 print (rm)
96
97 # right. the first thing to do is identify the ordering of
98 # the registers, by name. the EXTRA2/3 ordering is in
99 # rm['0']..rm['3'] but those fields contain the names RA, BB
100 # etc. we have to read the pseudocode to understand which
101 # reg is which in our instruction. sigh.
102
103 # first turn the svp64 rm into a "by name" dict, recording
104 # which position in the RM EXTRA it goes into
105 svp64_reg_byname = {}
106 for i in range(4):
107 rfield = rm[str(i)]
108 if not rfield or rfield == '0':
109 continue
110 print ("EXTRA field", i, rfield)
111 rfield = rfield.split(";") # s:RA;d:CR1 etc.
112 for r in rfield:
113 r = r[2:] # ignore s: and d:
114 svp64_reg_byname[r] = i # this reg in EXTRA position 0-3
115 print ("EXTRA field index, by regname", svp64_reg_byname)
116
117 # okaaay now we identify the field value (opcode N,N,N) with
118 # the pseudo-code info (opcode RT, RA, RB)
119 opregfields = zip(fields, v30b_regs) # err that was easy
120
121 # now for each of those find its place in the EXTRA encoding
122 extras = OrderedDict()
123 for idx, (field, regname) in enumerate(opregfields):
124 extra = svp64_reg_byname.get(regname, None)
125 regtype = get_regtype(regname)
126 extras[extra] = (idx, field, regname, regtype)
127 print (" ", extra, extras[extra])
128
129 # great! got the extra fields in their associated positions:
130 # also we know the register type. now to create the EXTRA encodings
131 etype = rm['Etype'] # Extra type: EXTRA3/EXTRA2
132 extra_bits = 0
133 v30b_newfields = []
134 for extra_idx, (idx, field, regname, regtype) in extras.items():
135 # is it a field we don't alter/examine? if so just put it
136 # into newfields
137 if regtype is None:
138 v30b_newfields.append(field)
139
140 # first, decode the field number. "5.v" or "3.s" or "9"
141 field = field.split(".")
142 regmode = 'scalar' # default
143 if len(field) == 2:
144 if field[1] == 's':
145 regmode = 'scalar'
146 elif field[1] == 'v':
147 regmode = 'vector'
148 field = int(field[0]) # actual register number
149 print (" ", regmode, field)
150 if regtype == 'GPR':
151 if regmode == 'scalar':
152 # cut into 2-bits 5-bits SS FFFFF
153 sv_extra = field >> 5
154 field = field & 0b11111
155 else:
156 # cut into 5-bits 2-bits FFFFF SS
157 sv_extra = field & 0b11
158 field = field >> 2
159 # now sanity-check. EXTRA3 is ok, EXTRA2 has limits
160 if etype == 'EXTRA2':
161 if regmode == 'scalar':
162 assert sv_extra & 0b10 == 0, \
163 "scalar field %s cannot fit into EXTRA2 %s" % \
164 (regname, str(extras[extra_idx]))
165 else:
166 assert sv_extra & 0b01 == 0, \
167 "vector field %s cannot fit into EXTRA2 %s" % \
168 (regname, str(extras[extra_idx]))
169
170 # append altered field value to v3.0b
171 v30b_newfields.append(str(field))
172
173 print ("new v3.0B fields", v30b_op, v30b_newfields)
174 print ()
175
176 return res
177
178 if __name__ == '__main__':
179 isa = SVP64(['slw 3, 1, 4',
180 'extsw 5, 3',
181 'sv.extsw 5, 3',
182 'sv.cmpi 5, 1, 3, 2',
183 'sv.setb 5, 3',
184 'sv.isel 64.v, 3, 2, 0'
185 ])
186 csvs = SVP64RM()