add svp64 subvl encoding
[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 # decodes SUBVL
69 def decode_subvl(encoding):
70 pmap = {'2': 0b01, '3': 0b10, '4': 0b11}
71 assert encoding in pmap, \
72 "encoding %s for SUBVL not recognised" % encoding
73 return pmap[encoding]
74
75
76 # decodes predicate register encoding
77 def decode_predicate(encoding):
78 pmap = { # integer
79 '1<<r3': (0, 0b001),
80 'r3' : (0, 0b010),
81 '~r3' : (0, 0b011),
82 'r10' : (0, 0b100),
83 '~r10' : (0, 0b101),
84 'r30' : (0, 0b110),
85 '~r30' : (0, 0b111),
86 # CR
87 'lt' : (1, 0b000),
88 'nl' : (1, 0b001), 'ge' : (1, 0b001), # same value
89 'gt' : (1, 0b010),
90 'ng' : (1, 0b011), 'le' : (1, 0b011), # same value
91 'eq' : (1, 0b100),
92 'ne' : (1, 0b101),
93 'so' : (1, 0b110), 'un' : (1, 0b110), # same value
94 'ns' : (1, 0b111), 'nu' : (1, 0b111), # same value
95 }
96 assert encoding in pmap, \
97 "encoding %s for predicate not recognised" % encoding
98 return pmap[encoding]
99
100
101 # gets SVP64 ReMap information
102 class SVP64RM:
103 def __init__(self):
104 self.instrs = {}
105 pth = find_wiki_dir()
106 for fname in os.listdir(pth):
107 if fname.startswith("RM"):
108 for entry in get_csv(fname):
109 self.instrs[entry['insn']] = entry
110
111
112 # decodes svp64 assembly listings and creates EXT001 svp64 prefixes
113 class SVP64:
114 def __init__(self, lst):
115 self.lst = lst
116 self.trans = self.translate(lst)
117
118 def __iter__(self):
119 for insn in self.trans:
120 yield insn
121
122 def translate(self, lst):
123 isa = ISA() # reads the v3.0B pseudo-code markdown files
124 svp64 = SVP64RM() # reads the svp64 Remap entries for registers
125 res = []
126 for insn in lst:
127 # find first space, to get opcode
128 ls = insn.split(' ')
129 opcode = ls[0]
130 # now find opcode fields
131 fields = ''.join(ls[1:]).split(',')
132 fields = list(map(str.strip, fields))
133 print ("opcode, fields", ls, opcode, fields)
134
135 # identify if is a svp64 mnemonic
136 if not opcode.startswith('sv.'):
137 res.append(insn) # unaltered
138 continue
139
140 # start working on decoding the svp64 op: sv.baseop.vec2.mode
141 opmodes = opcode.split(".")[1:] # strip leading "sv."
142 v30b_op = opmodes.pop(0) # first is the v3.0B
143 if v30b_op not in isa.instr:
144 raise Exception("opcode %s of '%s' not supported" % \
145 (v30b_op, insn))
146 if v30b_op not in svp64.instrs:
147 raise Exception("opcode %s of '%s' not an svp64 instruction" % \
148 (v30b_op, insn))
149 isa.instr[v30b_op].regs[0]
150 v30b_regs = isa.instr[v30b_op].regs[0]
151 rm = svp64.instrs[v30b_op]
152 print ("v3.0B regs", opcode, v30b_regs)
153 print (rm)
154
155 # right. the first thing to do is identify the ordering of
156 # the registers, by name. the EXTRA2/3 ordering is in
157 # rm['0']..rm['3'] but those fields contain the names RA, BB
158 # etc. we have to read the pseudocode to understand which
159 # reg is which in our instruction. sigh.
160
161 # first turn the svp64 rm into a "by name" dict, recording
162 # which position in the RM EXTRA it goes into
163 svp64_reg_byname = {}
164 for i in range(4):
165 rfield = rm[str(i)]
166 if not rfield or rfield == '0':
167 continue
168 print ("EXTRA field", i, rfield)
169 rfield = rfield.split(";") # s:RA;d:CR1 etc.
170 for r in rfield:
171 r = r[2:] # ignore s: and d:
172 svp64_reg_byname[r] = i # this reg in EXTRA position 0-3
173 print ("EXTRA field index, by regname", svp64_reg_byname)
174
175 # okaaay now we identify the field value (opcode N,N,N) with
176 # the pseudo-code info (opcode RT, RA, RB)
177 opregfields = zip(fields, v30b_regs) # err that was easy
178
179 # now for each of those find its place in the EXTRA encoding
180 extras = OrderedDict()
181 for idx, (field, regname) in enumerate(opregfields):
182 extra = svp64_reg_byname.get(regname, None)
183 regtype = get_regtype(regname)
184 extras[extra] = (idx, field, regname, regtype)
185 print (" ", extra, extras[extra])
186
187 # great! got the extra fields in their associated positions:
188 # also we know the register type. now to create the EXTRA encodings
189 etype = rm['Etype'] # Extra type: EXTRA3/EXTRA2
190 ptype = rm['Ptype'] # Predication type: Twin / Single
191 extra_bits = 0
192 v30b_newfields = []
193 for extra_idx, (idx, field, regname, regtype) in extras.items():
194 # is it a field we don't alter/examine? if so just put it
195 # into newfields
196 if regtype is None:
197 v30b_newfields.append(field)
198
199 # first, decode the field number. "5.v" or "3.s" or "9"
200 field = field.split(".")
201 regmode = 'scalar' # default
202 if len(field) == 2:
203 if field[1] == 's':
204 regmode = 'scalar'
205 elif field[1] == 'v':
206 regmode = 'vector'
207 field = int(field[0]) # actual register number
208 print (" ", regmode, field, end=" ")
209
210 # XXX TODO: the following is a bit of a laborious repeated
211 # mess, which could (and should) easily be parameterised.
212
213 # encode SV-GPR field into extra, v3.0field
214 if regtype == 'GPR':
215 sv_extra, field = get_extra_gpr(etype, regmode, field)
216 # now sanity-check. EXTRA3 is ok, EXTRA2 has limits
217 # (and shrink to a single bit if ok)
218 if etype == 'EXTRA2':
219 if regmode == 'scalar':
220 # range is r0-r63 in increments of 1
221 assert (sv_extra >> 1) == 0, \
222 "scalar GPR %s cannot fit into EXTRA2 %s" % \
223 (regname, str(extras[extra_idx]))
224 # all good: encode as scalar
225 sv_extra = sv_extra & 0b01
226 else:
227 # range is r0-r127 in increments of 4
228 assert sv_extra & 0b01 == 0, \
229 "vector field %s cannot fit into EXTRA2 %s" % \
230 (regname, str(extras[extra_idx]))
231 # all good: encode as vector (bit 2 set)
232 sv_extra = 0b10 | (sv_extra >> 1)
233 elif regmode == 'vector':
234 # EXTRA3 vector bit needs marking
235 sv_extra |= 0b100
236
237 # encode SV-CR 3-bit field into extra, v3.0field
238 elif regtype == 'CR_3bit':
239 sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
240 # now sanity-check (and shrink afterwards)
241 if etype == 'EXTRA2':
242 if regmode == 'scalar':
243 # range is CR0-CR15 in increments of 1
244 assert (sv_extra >> 1) == 0, \
245 "scalar CR %s cannot fit into EXTRA2 %s" % \
246 (regname, str(extras[extra_idx]))
247 # all good: encode as scalar
248 sv_extra = sv_extra & 0b01
249 else:
250 # range is CR0-CR127 in increments of 16
251 assert sv_extra & 0b111 == 0, \
252 "vector CR %s cannot fit into EXTRA2 %s" % \
253 (regname, str(extras[extra_idx]))
254 # all good: encode as vector (bit 2 set)
255 sv_extra = 0b10 | (sv_extra >> 3)
256 else:
257 if regmode == 'scalar':
258 # range is CR0-CR31 in increments of 1
259 assert (sv_extra >> 2) == 0, \
260 "scalar CR %s cannot fit into EXTRA2 %s" % \
261 (regname, str(extras[extra_idx]))
262 # all good: encode as scalar
263 sv_extra = sv_extra & 0b11
264 else:
265 # range is CR0-CR127 in increments of 8
266 assert sv_extra & 0b11 == 0, \
267 "vector CR %s cannot fit into EXTRA2 %s" % \
268 (regname, str(extras[extra_idx]))
269 # all good: encode as vector (bit 3 set)
270 sv_extra = 0b100 | (sv_extra >> 2)
271
272 # encode SV-CR 5-bit field into extra, v3.0field
273 # *sigh* this is the same as 3-bit except the 2 LSBs are
274 # passed through
275 elif regtype == 'CR_5bit':
276 cr_subfield = field & 0b11
277 field = field >> 2 # strip bottom 2 bits
278 sv_extra, field = get_extra_cr_3bit(etype, regmode, field)
279 # now sanity-check (and shrink afterwards)
280 if etype == 'EXTRA2':
281 if regmode == 'scalar':
282 # range is CR0-CR15 in increments of 1
283 assert (sv_extra >> 1) == 0, \
284 "scalar CR %s cannot fit into EXTRA2 %s" % \
285 (regname, str(extras[extra_idx]))
286 # all good: encode as scalar
287 sv_extra = sv_extra & 0b01
288 else:
289 # range is CR0-CR127 in increments of 16
290 assert sv_extra & 0b111 == 0, \
291 "vector CR %s cannot fit into EXTRA2 %s" % \
292 (regname, str(extras[extra_idx]))
293 # all good: encode as vector (bit 2 set)
294 sv_extra = 0b10 | (sv_extra >> 3)
295 else:
296 if regmode == 'scalar':
297 # range is CR0-CR31 in increments of 1
298 assert (sv_extra >> 2) == 0, \
299 "scalar CR %s cannot fit into EXTRA2 %s" % \
300 (regname, str(extras[extra_idx]))
301 # all good: encode as scalar
302 sv_extra = sv_extra & 0b11
303 else:
304 # range is CR0-CR127 in increments of 8
305 assert sv_extra & 0b11 == 0, \
306 "vector CR %s cannot fit into EXTRA2 %s" % \
307 (regname, str(extras[extra_idx]))
308 # all good: encode as vector (bit 3 set)
309 sv_extra = 0b100 | (sv_extra >> 2)
310
311 # reconstruct the actual 5-bit CR field
312 field = (field << 2) | cr_subfield
313
314 # capture the extra field info
315 print ("=>", "%5s" % bin(sv_extra), field)
316 extras[extra_idx] = sv_extra
317
318 # append altered field value to v3.0b
319 v30b_newfields.append(str(field))
320
321 print ("new v3.0B fields", v30b_op, v30b_newfields)
322 print ("extras", extras)
323
324 # rright. now we have all the info. start creating SVP64 RM
325 svp64_rm = 0b0
326
327 # begin with EXTRA fields
328 for idx, sv_extra in extras.items():
329 if idx is None: continue
330 # start at bit 10, work up 2/3 times EXTRA idx
331 offs = 2 if etype == 'EXTRA2' else 3 # 2 or 3 bits
332 svp64_rm |= sv_extra << (10+idx*offs)
333
334 # parts of svp64_rm
335 mmode = 0 # bit 0
336 pmask = 0 # bits 1-3
337 destwid = 0 # bits 4-5
338 srcwid = 0 # bits 6-7
339 subvl = 0 # bits 8-9
340 smask = 0 # bits 16-18 but only for twin-predication
341 mode = 0 # bits 19-23
342
343 has_pmask = False
344 has_smask = False
345
346 # ok let's start identifying opcode augmentation fields
347 for encmode in opmodes:
348 # predicate mask (dest)
349 if encmode.startswith("m="):
350 pme = encmode
351 pmmode, pmask = decode_predicate(encmode[2:])
352 mmode = pmmode
353 has_pmask = True
354 # predicate mask (src, twin-pred)
355 if encmode.startswith("sm="):
356 sme = encmode
357 smmode, smask = decode_predicate(encmode[3:])
358 mmode = smmode
359 has_smask = True
360 # vec2/3/4
361 if encmode.startswith("vec"):
362 subvl = decode_subvl(encmode[3:])
363
364 # sanity-check that 2Pred mask is same mode
365 if has_pmask and has_smask:
366 assert smmode == pmmode, \
367 "predicate masks %s and %s must be same reg type" % \
368 (pme, sme)
369
370 # sanity-check that twin-predication mask only specified in 2P mode
371 if ptype == '1P':
372 assert has_smask == False, \
373 "source-mask can only be specified on Twin-predicate ops"
374
375 # put in predicate masks into svp64_rm
376 if ptype == '2P':
377 svp64_rm |= (smask << 16) # source pred: bits 16-18
378 svp64_rm |= (mmode) # mask mode: bit 0
379 svp64_rm |= (pmask << 1) # 1-pred: bits 1-3
380
381 # and subvl
382 svp64_rm += (subvl << 8) # subvl: bits 8-9
383
384 print ("svp64_rm", hex(svp64_rm), bin(svp64_rm))
385 print ()
386
387 return res
388
389 if __name__ == '__main__':
390 isa = SVP64(['slw 3, 1, 4',
391 'extsw 5, 3',
392 'sv.extsw 5, 3',
393 'sv.cmpi 5, 1, 3, 2',
394 'sv.setb 5, 31',
395 'sv.isel 64.v, 3, 2, 65.v',
396 'sv.setb.m=r3.sm=1<<r3 5, 31',
397 'sv.setb.vec2 5, 31',
398 ])
399 csvs = SVP64RM()