capture CR 3 and 5 bit sv encodings
[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 # decode GPR into sv extra
43 def get_extra_gpr(etype, regmode, field):
44 if regmode == 'scalar':
45 # cut into 2-bits 5-bits SS FFFFF
46 sv_extra = field >> 5
47 field = field & 0b11111
48 else:
49 # cut into 5-bits 2-bits FFFFF SS
50 sv_extra = field & 0b11
51 field = field >> 2
52 return sv_extra, field
53
54
55 # decode 3-bit CR into sv extra
56 def get_extra_cr_3bit(etype, regmode, field):
57 if regmode == 'scalar':
58 # cut into 2-bits 3-bits SS FFF
59 sv_extra = field >> 3
60 field = field & 0b111
61 else:
62 # cut into 3-bits 4-bits FFF SSSS but will cut 2 zeros off later
63 sv_extra = field & 0b1111
64 field = field >> 4
65 return sv_extra, field
66
67
68 # gets SVP64 ReMap information
69 class SVP64RM:
70 def __init__(self):
71 self.instrs = {}
72 pth = find_wiki_dir()
73 for fname in os.listdir(pth):
74 if fname.startswith("RM"):
75 for entry in get_csv(fname):
76 self.instrs[entry['insn']] = entry
77
78
79 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
80 class SVP64:
81 def __init__(self, lst):
82 self.lst = lst
83 self.trans = self.translate(lst)
84
85 def __iter__(self):
86 for insn in self.trans:
87 yield insn
88
89 def translate(self, lst):
90 isa = ISA() # reads the v3.0B pseudo-code markdown files
91 svp64 = SVP64RM() # reads the svp64 Remap entries for registers
92 res = []
93 for insn in lst:
94 # find first space, to get opcode
95 ls = insn.split(' ')
96 opcode = ls[0]
97 # now find opcode fields
98 fields = ''.join(ls[1:]).split(',')
99 fields = list(map(str.strip, fields))
100 print (opcode, fields)
101
102 # identify if is a svp64 mnemonic
103 if not opcode.startswith('sv.'):
104 res.append(insn) # unaltered
105 continue
106
107 # start working on decoding the svp64 op: sv.baseop.vec2.mode
108 opmodes = opcode.split(".")[1:] # strip leading "sv."
109 v30b_op = opmodes.pop(0) # first is the v3.0B
110 if v30b_op not in isa.instr:
111 raise Exception("opcode %s of '%s' not supported" % \
112 (v30b_op, insn))
113 if v30b_op not in svp64.instrs:
114 raise Exception("opcode %s of '%s' not an svp64 instruction" % \
115 (v30b_op, insn))
116 isa.instr[v30b_op].regs[0]
117 v30b_regs = isa.instr[v30b_op].regs[0]
118 rm = svp64.instrs[v30b_op]
119 print ("v3.0B regs", opcode, v30b_regs)
120 print (rm)
121
122 # right. the first thing to do is identify the ordering of
123 # the registers, by name. the EXTRA2/3 ordering is in
124 # rm['0']..rm['3'] but those fields contain the names RA, BB
125 # etc. we have to read the pseudocode to understand which
126 # reg is which in our instruction. sigh.
127
128 # first turn the svp64 rm into a "by name" dict, recording
129 # which position in the RM EXTRA it goes into
130 svp64_reg_byname = {}
131 for i in range(4):
132 rfield = rm[str(i)]
133 if not rfield or rfield == '0':
134 continue
135 print ("EXTRA field", i, rfield)
136 rfield = rfield.split(";") # s:RA;d:CR1 etc.
137 for r in rfield:
138 r = r[2:] # ignore s: and d:
139 svp64_reg_byname[r] = i # this reg in EXTRA position 0-3
140 print ("EXTRA field index, by regname", svp64_reg_byname)
141
142 # okaaay now we identify the field value (opcode N,N,N) with
143 # the pseudo-code info (opcode RT, RA, RB)
144 opregfields = zip(fields, v30b_regs) # err that was easy
145
146 # now for each of those find its place in the EXTRA encoding
147 extras = OrderedDict()
148 for idx, (field, regname) in enumerate(opregfields):
149 extra = svp64_reg_byname.get(regname, None)
150 regtype = get_regtype(regname)
151 extras[extra] = (idx, field, regname, regtype)
152 print (" ", extra, extras[extra])
153
154 # great! got the extra fields in their associated positions:
155 # also we know the register type. now to create the EXTRA encodings
156 etype = rm['Etype'] # Extra type: EXTRA3/EXTRA2
157 extra_bits = 0
158 v30b_newfields = []
159 for extra_idx, (idx, field, regname, regtype) in extras.items():
160 # is it a field we don't alter/examine? if so just put it
161 # into newfields
162 if regtype is None:
163 v30b_newfields.append(field)
164
165 # first, decode the field number. "5.v" or "3.s" or "9"
166 field = field.split(".")
167 regmode = 'scalar' # default
168 if len(field) == 2:
169 if field[1] == 's':
170 regmode = 'scalar'
171 elif field[1] == 'v':
172 regmode = 'vector'
173 field = int(field[0]) # actual register number
174 print (" ", regmode, field, end=" ")
175
176 # XXX TODO: the following is a bit of a laborious repeated
177 # mess, which could (and should) easily be parameterised.
178
179 # encode SV-GPR field into extra, v3.0field
180 if regtype == 'GPR':
181 sv_extra, field = get_extra_gpr(etype, regmode, field)
182 # now sanity-check. EXTRA3 is ok, EXTRA2 has limits
183 # (and shrink to a single bit if ok)
184 if etype == 'EXTRA2':
185 if regmode == 'scalar':
186 # range is r0-r63 in increments of 1
187 assert (sv_extra >> 1) == 0, \
188 "scalar GPR %s cannot fit into EXTRA2 %s" % \
189 (regname, str(extras[extra_idx]))
190 # all good: encode as scalar
191 sv_extra = sv_extra & 0b01
192 else:
193 # range is r0-r127 in increments of 4
194 assert sv_extra & 0b01 == 0, \
195 "vector field %s cannot fit into EXTRA2 %s" % \
196 (regname, str(extras[extra_idx]))
197 # all good: encode as vector (bit 2 set)
198 sv_extra = 0b10 | (sv_extra >> 1)
199 elif regmode == 'vector':
200 # EXTRA3 vector bit needs marking
201 sv_extra |= 0b100
202
203 # encode SV-CR 3-bit field into extra, v3.0field
204 elif regtype == 'CR_3bit':
205 sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
206 # now sanity-check (and shrink afterwards)
207 if etype == 'EXTRA2':
208 if regmode == 'scalar':
209 # range is CR0-CR15 in increments of 1
210 assert (sv_extra >> 1) == 0, \
211 "scalar CR %s cannot fit into EXTRA2 %s" % \
212 (regname, str(extras[extra_idx]))
213 # all good: encode as scalar
214 sv_extra = sv_extra & 0b01
215 else:
216 # range is CR0-CR127 in increments of 16
217 assert sv_extra & 0b111 == 0, \
218 "vector CR %s cannot fit into EXTRA2 %s" % \
219 (regname, str(extras[extra_idx]))
220 # all good: encode as vector (bit 2 set)
221 sv_extra = 0b10 | (sv_extra >> 3)
222 else:
223 if regmode == 'scalar':
224 # range is CR0-CR31 in increments of 1
225 assert (sv_extra >> 2) == 0, \
226 "scalar CR %s cannot fit into EXTRA2 %s" % \
227 (regname, str(extras[extra_idx]))
228 # all good: encode as scalar
229 sv_extra = sv_extra & 0b11
230 else:
231 # range is CR0-CR127 in increments of 8
232 assert sv_extra & 0b11 == 0, \
233 "vector CR %s cannot fit into EXTRA2 %s" % \
234 (regname, str(extras[extra_idx]))
235 # all good: encode as vector (bit 3 set)
236 sv_extra = 0b100 | (sv_extra >> 2)
237
238 # encode SV-CR 5-bit field into extra, v3.0field
239 # *sigh* this is the same as 3-bit except the 2 LSBs are
240 # passed through
241 elif regtype == 'CR_5bit':
242 cr_subfield = field & 0b11
243 field = field >> 2 # strip bottom 2 bits
244 sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
245 # now sanity-check (and shrink afterwards)
246 if etype == 'EXTRA2':
247 if regmode == 'scalar':
248 # range is CR0-CR15 in increments of 1
249 assert (sv_extra >> 1) == 0, \
250 "scalar CR %s cannot fit into EXTRA2 %s" % \
251 (regname, str(extras[extra_idx]))
252 # all good: encode as scalar
253 sv_extra = sv_extra & 0b01
254 else:
255 # range is CR0-CR127 in increments of 16
256 assert sv_extra & 0b111 == 0, \
257 "vector CR %s cannot fit into EXTRA2 %s" % \
258 (regname, str(extras[extra_idx]))
259 # all good: encode as vector (bit 2 set)
260 sv_extra = 0b10 | (sv_extra >> 3)
261 else:
262 if regmode == 'scalar':
263 # range is CR0-CR31 in increments of 1
264 assert (sv_extra >> 2) == 0, \
265 "scalar CR %s cannot fit into EXTRA2 %s" % \
266 (regname, str(extras[extra_idx]))
267 # all good: encode as scalar
268 sv_extra = sv_extra & 0b11
269 else:
270 # range is CR0-CR127 in increments of 8
271 assert sv_extra & 0b11 == 0, \
272 "vector CR %s cannot fit into EXTRA2 %s" % \
273 (regname, str(extras[extra_idx]))
274 # all good: encode as vector (bit 3 set)
275 sv_extra = 0b100 | (sv_extra >> 2)
276
277 # reconstruct the actual 5-bit CR field
278 field = (field << 2) | cr_subfield
279
280 # capture the extra field info
281 print ("=>", "%5s" % bin(sv_extra), field)
282 extras[extra_idx] = sv_extra
283
284 # append altered field value to v3.0b
285 v30b_newfields.append(str(field))
286
287 print ("new v3.0B fields", v30b_op, v30b_newfields)
288 print ("extras", extras)
289 print ()
290
291 return res
292
293 if __name__ == '__main__':
294 isa = SVP64(['slw 3, 1, 4',
295 'extsw 5, 3',
296 'sv.extsw 5, 3',
297 'sv.cmpi 5, 1, 3, 2',
298 'sv.setb 5, 31',
299 'sv.isel 64.v, 3, 2, 65.v'
300 ])
301 csvs = SVP64RM()