e89060f59cc8d43d7b6959d9ee867c992459ebae
[openpower-isa.git] / src / openpower / decoder / power_decoder.py
1 """Cascading Power ISA Decoder
2
3 License: LGPLv3+
4
5 # Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
6 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
7
8 This module uses CSV tables in a hierarchical/peer cascading fashion,
9 to create a multi-level instruction decoder by recognising appropriate
10 patterns. The output is a wide, flattened (1-level) series of bitfields,
11 suitable for a simple RISC engine.
12
13 This is based on Anton Blanchard's excellent microwatt work:
14 https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
15
16 The basic principle is that the python code does the heavy lifting
17 (reading the CSV files, constructing the hierarchy), creating the HDL
18 AST with for-loops generating switch-case statements.
19
20 Where "normal" HDL would do this, in laborious excruciating detail:
21
22 switch (opcode & major_mask_bits):
23 case opcode_2: decode_opcode_2()
24 case opcode_19:
25 switch (opcode & minor_19_mask_bits)
26 case minor_opcode_19_operation_X:
27 case minor_opcode_19_operation_y:
28
29 we take *full* advantage of the decoupling between python and the
30 nmigen AST data structure, to do this:
31
32 with m.Switch(opcode & self.mask):
33 for case_bitmask in subcases:
34 with m.If(opcode & case_bitmask): {do_something}
35
36 this includes specifying the information sufficient to perform subdecoding.
37
38 create_pdecode()
39
40 the full hierarchical tree for decoding POWER9 is specified here
41 subsetting is possible by specifying col_subset (row_subset TODO)
42
43 PowerDecoder
44
45 takes a *list* of CSV files with an associated bit-range that it
46 is requested to match against the "opcode" row of the CSV file.
47 This pattern can be either an integer, a binary number, *or* a
48 wildcard nmigen Case pattern of the form "001--1-100".
49
50 Subdecoders
51
52 these are *additional* cases with further decoding. The "pattern"
53 argument is specified as one of the Case statements (a peer of the
54 opcode row in the CSV file), and thus further fields of the opcode
55 may be decoded giving increasing levels of detail.
56
57 Top Level:
58
59 [ (extra.csv: bit-fields entire 32-bit range
60 opcode -> matches
61 000000---------------01000000000 -> ILLEGAL instruction
62 01100000000000000000000000000000 -> SIM_CONFIG instruction
63 ................................ ->
64 ),
65 (major.csv: first 6 bits ONLY
66 opcode -> matches
67 001100 -> ALU,OP_ADD (add)
68 001101 -> ALU,OP_ADD (another type of add)
69 ...... -> ...
70 ...... -> ...
71 subdecoders:
72 001011 this must match *MAJOR*.CSV
73 [ (minor_19.csv: bits 21 through 30 inclusive:
74 opcode -> matches
75 0b0000000000 -> ALU,OP_MCRF
76 ............ -> ....
77 ),
78 (minor_19_00000.csv: bits 21 through 25 inclusive:
79 opcode -> matches
80 0b00010 -> ALU,add_pcis
81 )
82 ]
83 ),
84 ]
85
86
87 """
88
89 import gc
90 from collections import namedtuple
91 from nmigen import Module, Elaboratable, Signal, Cat, Mux
92 from nmigen.cli import rtlil
93 from openpower.decoder.power_enums import (Function, Form, MicrOp,
94 In1Sel, In2Sel, In3Sel, OutSel,
95 SVEXTRA, SVEtype, SVPtype, # Simple-V
96 RC, LdstLen, LDSTMode, CryIn,
97 single_bit_flags, CRInSel,
98 CROutSel, get_signal_name,
99 default_values, insns, asmidx)
100 from openpower.decoder.power_fields import DecodeFields
101 from openpower.decoder.power_fieldsn import SigDecode, SignalBitRange
102 from openpower.decoder.power_svp64 import SVP64RM
103
104 # key data structure in which the POWER decoder is specified,
105 # in a hierarchical fashion
106 Subdecoder = namedtuple( # fix autoformatter
107 "Subdecoder",
108 ["pattern", # the major pattern to search for (e.g. major opcode)
109 "opcodes", # a dictionary of minor patterns to find
110 "opint", # true => the pattern must not be in "10----11" format
111 # the bits (as a range) against which "pattern" matches
112 "bitsel",
113 "suffix", # shift the opcode down before decoding
114 "subdecoders" # list of further subdecoders for *additional* matches,
115 # *ONLY* after "pattern" has *ALSO* been matched against.
116 ])
117
118 power_op_types = {'function_unit': Function,
119 'internal_op': MicrOp,
120 'form': Form,
121 'asmcode': 8,
122 'SV_Etype': SVEtype,
123 'SV_Ptype': SVPtype,
124 'in1_sel': In1Sel,
125 'in2_sel': In2Sel,
126 'in3_sel': In3Sel,
127 'out_sel': OutSel,
128 'cr_in': CRInSel,
129 'cr_out': CROutSel,
130 'sv_in1': SVEXTRA,
131 'sv_in2': SVEXTRA,
132 'sv_in3': SVEXTRA,
133 'sv_out': SVEXTRA,
134 'sv_out2': SVEXTRA,
135 'sv_cr_in': SVEXTRA,
136 'sv_cr_out': SVEXTRA,
137 'ldst_len': LdstLen,
138 'upd': LDSTMode,
139 'rc_sel': RC,
140 'cry_in': CryIn
141 }
142
143 power_op_csvmap = {'function_unit': 'unit',
144 'form': 'form',
145 'internal_op': 'internal op',
146 'in1_sel': 'in1',
147 'in2_sel': 'in2',
148 'in3_sel': 'in3',
149 'out_sel': 'out',
150 'sv_in1': 'sv_in1',
151 'sv_in2': 'sv_in2',
152 'sv_in3': 'sv_in3',
153 'sv_out': 'sv_out',
154 'sv_out2': 'sv_out2',
155 'sv_cr_in': 'sv_cr_in',
156 'sv_cr_out': 'sv_cr_out',
157 'SV_Etype': 'SV_Etype',
158 'SV_Ptype': 'SV_Ptype',
159 'cr_in': 'CR in',
160 'cr_out': 'CR out',
161 'ldst_len': 'ldst len',
162 'upd': 'upd',
163 'rc_sel': 'rc',
164 'cry_in': 'cry in',
165 }
166
167
168 def get_pname(field, pname):
169 if pname is None:
170 return field
171 return "%s_%s" % (pname, field)
172
173
174 class PowerOp:
175 """PowerOp - a dynamic class that stores (subsets of) CSV rows of data
176 about a PowerISA instruction. this is a "micro-code" expanded format
177 which generates an awful lot of wires, hence the subsetting
178 """
179
180 def __init__(self, incl_asm=True, name=None, subset=None):
181 self.subset = subset
182 debug_report = set()
183 fields = set()
184 for field, ptype in power_op_types.items():
185 fields.add(field)
186 if subset and field not in subset:
187 continue
188 fname = get_pname(field, name)
189 setattr(self, field, Signal(ptype, reset_less=True, name=fname))
190 debug_report.add(field)
191 for bit in single_bit_flags:
192 field = get_signal_name(bit)
193 fields.add(field)
194 if subset and field not in subset:
195 continue
196 debug_report.add(field)
197 fname = get_pname(field, name)
198 setattr(self, field, Signal(reset_less=True, name=fname))
199 # comment out, bit too high debug level
200 #print("PowerOp debug", name, debug_report)
201 #print(" fields", fields)
202
203 def _eq(self, row=None):
204 if row is None:
205 row = default_values
206 # TODO: this conversion process from a dict to an object
207 # should really be done using e.g. namedtuple and then
208 # call eq not _eq
209 if False: # debugging
210 if row['CR in'] == '1':
211 import pdb
212 pdb.set_trace()
213 print(row)
214 if row['CR out'] == '0':
215 import pdb
216 pdb.set_trace()
217 print(row)
218 print(row)
219 ldst_mode = row['upd']
220 if ldst_mode.isdigit():
221 row['upd'] = int(ldst_mode)
222 res = []
223 for field, ptype in power_op_types.items():
224 if not hasattr(self, field):
225 continue
226 if field not in power_op_csvmap:
227 continue
228 csvname = power_op_csvmap[field]
229 #print(field, ptype, csvname, row)
230 val = row[csvname]
231 if csvname == 'upd' and isinstance(val, int): # LDSTMode different
232 val = ptype(val)
233 else:
234 val = ptype[val]
235 res.append(getattr(self, field).eq(val))
236 if False:
237 print(row.keys())
238 asmcode = row['comment']
239 if hasattr(self, "asmcode") and asmcode in asmidx:
240 res.append(self.asmcode.eq(asmidx[asmcode]))
241 for bit in single_bit_flags:
242 field = get_signal_name(bit)
243 if not hasattr(self, field):
244 continue
245 sig = getattr(self, field)
246 res.append(sig.eq(int(row.get(bit, 0))))
247 return res
248
249 def _get_eq(self, res, field, otherop):
250 copyfrom = getattr(otherop, field, None)
251 copyto = getattr(self, field, None)
252 if copyfrom is not None and copyto is not None:
253 res.append(copyto.eq(copyfrom))
254
255 def eq(self, otherop):
256 res = []
257 for field in power_op_types.keys():
258 self._get_eq(res, field, otherop)
259 for bit in single_bit_flags:
260 self._get_eq(res, get_signal_name(bit), otherop)
261 return res
262
263 def ports(self):
264 res = []
265 for field in power_op_types.keys():
266 if hasattr(self, field):
267 res.append(getattr(self, field))
268 if hasattr(self, "asmcode"):
269 res.append(self.asmcode)
270 for field in single_bit_flags:
271 field = get_signal_name(field)
272 if hasattr(self, field):
273 res.append(getattr(self, field))
274 return res
275
276
277 class PowerDecoder(Elaboratable):
278 """PowerDecoder - decodes an incoming opcode into the type of operation
279
280 this is a recursive algorithm, creating Switch statements that can
281 have further match-and-decode on other parts of the opcode field before
282 finally landing at a "this CSV entry details gets returned" thing.
283
284 the complicating factor is the row and col subsetting. column subsetting
285 dynamically chooses only the CSV columns requested, whilst row subsetting
286 allows a function to be called on the row to determine if the Case
287 statement is to be generated for that row. this not only generates
288 completely different Decoders, it also means that some sub-decoders
289 will turn up blank (empty switch statements). if that happens we do
290 not want the parent to include a Mux for an entirely blank switch statement
291 so we have to store the switch/case statements in a tree, and
292 post-analyse it.
293
294 the reason for the tree is because elaborate can only be called *after*
295 the constructor is called. all quite messy.
296 """
297
298 def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
299 self.actually_does_something = False
300 self.pname = name
301 self.col_subset = col_subset
302 self.row_subsetfn = row_subset
303 if not isinstance(dec, list):
304 dec = [dec]
305 self.dec = dec
306 self.opcode_in = Signal(width, reset_less=True)
307
308 self.op = PowerOp(name=name, subset=col_subset)
309 for d in dec:
310 if d.suffix is not None and d.suffix >= width:
311 d.suffix = None
312 self.width = width
313
314 def suffix_mask(self, d):
315 return ((1 << d.suffix) - 1)
316
317 def divide_opcodes(self, d):
318 divided = {}
319 mask = self.suffix_mask(d)
320 #print("mask", hex(mask))
321 for row in d.opcodes:
322 opcode = row['opcode']
323 if d.opint and '-' not in opcode:
324 opcode = int(opcode, 0)
325 key = opcode & mask
326 opcode = opcode >> d.suffix
327 if key not in divided:
328 divided[key] = []
329 r = row.copy()
330 r['opcode'] = opcode
331 divided[key].append(r)
332 return divided
333
334 def tree_analyse(self):
335 self.decs = decs = []
336 self.submodules = submodules = {}
337 self.eqs = eqs = []
338
339 # go through the list of CSV decoders first
340 for d in self.dec:
341 cases = []
342 opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
343 reset_less=True)
344 eq = []
345 case_does_something = False
346 eq.append(opcode_switch.eq(
347 self.opcode_in[d.bitsel[0]:d.bitsel[1]]))
348 if d.suffix:
349 opcodes = self.divide_opcodes(d)
350 opc_in = Signal(d.suffix, reset_less=True)
351 eq.append(opc_in.eq(opcode_switch[:d.suffix]))
352 # begin the dynamic Switch statement here
353 switch_case = {}
354 cases.append([opc_in, switch_case])
355 sub_eqs = []
356 for key, row in opcodes.items():
357 bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
358 sd = Subdecoder(pattern=None, opcodes=row,
359 bitsel=bitsel, suffix=None,
360 opint=False, subdecoders=[])
361 mname = get_pname("dec_sub%d" % key, self.pname)
362 subdecoder = PowerDecoder(width=32, dec=sd,
363 name=mname,
364 col_subset=self.col_subset,
365 row_subset=self.row_subsetfn)
366 if not subdecoder.tree_analyse():
367 del subdecoder
368 continue
369 submodules[mname] = subdecoder
370 sub_eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
371 # add in the dynamic Case statement here
372 switch_case[key] = self.op.eq(subdecoder.op)
373 self.actually_does_something = True
374 case_does_something = True
375 if case_does_something:
376 eq += sub_eqs
377 else:
378 # TODO: arguments, here (all of them) need to be a list.
379 # a for-loop around the *list* of decoder args.
380 switch_case = {}
381 cases.append([opcode_switch, switch_case])
382 seqs = self.handle_subdecoders(switch_case, submodules, d)
383 if seqs:
384 case_does_something = True
385 eq += seqs
386 for row in d.opcodes:
387 opcode = row['opcode']
388 if d.opint and '-' not in opcode:
389 opcode = int(opcode, 0)
390 if not row['unit']:
391 continue
392 if self.row_subsetfn:
393 if not self.row_subsetfn(opcode, row):
394 continue
395 # add in the dynamic Case statement here
396 switch_case[opcode] = self.op._eq(row)
397 self.actually_does_something = True
398 case_does_something = True
399
400 if cases:
401 decs.append(cases)
402 if case_does_something:
403 eqs += eq
404 #print("submodule eqs", self.pname, eq)
405
406 #print("submodules", self.pname, submodules)
407
408 gc.collect()
409 return self.actually_does_something
410
411 def handle_subdecoders(self, switch_case, submodules, d):
412 eqs = []
413 for dec in d.subdecoders:
414 if isinstance(dec, list): # XXX HACK: take first pattern
415 dec = dec[0]
416 #print("subdec", dec.pattern, self.pname)
417 mname = get_pname("dec%d" % dec.pattern, self.pname)
418 subdecoder = PowerDecoder(self.width, dec,
419 name=mname,
420 col_subset=self.col_subset,
421 row_subset=self.row_subsetfn)
422 if not subdecoder.tree_analyse(): # doesn't do anything
423 del subdecoder
424 continue # skip
425 submodules[mname] = subdecoder
426 eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
427 switch_case[dec.pattern] = self.op.eq(subdecoder.op)
428 self.actually_does_something = True
429
430 return eqs
431
432 def elaborate(self, platform):
433 #print("decoder elaborate", self.pname, self.submodules)
434 m = Module()
435 comb = m.d.comb
436
437 comb += self.eqs
438
439 for mname, subdecoder in self.submodules.items():
440 setattr(m.submodules, mname, subdecoder)
441
442 for switch_case in self.decs:
443 for (switch, cases) in switch_case:
444 with m.Switch(switch):
445 for key, eqs in cases.items():
446 with m.Case(key):
447 comb += eqs
448 return m
449
450 def ports(self):
451 return [self.opcode_in] + self.op.ports()
452
453
454 class TopPowerDecoder(PowerDecoder):
455 """TopPowerDecoder
456
457 top-level hierarchical decoder for POWER ISA
458 bigendian dynamically switches between big and little endian decoding
459 (reverses byte order). See V3.0B p44 1.11.2
460 """
461
462 def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
463 PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset)
464 self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
465 self.fields.create_specs()
466 self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
467 self.bigendian = Signal(reset_less=True)
468
469 for fname, value in self.fields.common_fields.items():
470 signame = get_pname(fname, name)
471 sig = Signal(value[0:-1].shape(), reset_less=True, name=signame)
472 setattr(self, fname, sig)
473
474 # create signals for all field forms
475 forms = self.form_names
476 self.sigforms = {}
477 for form in forms:
478 fields = self.fields.instrs[form]
479 fk = fields.keys()
480 Fields = namedtuple("Fields", fk)
481 sf = {}
482 for k, value in fields.items():
483 fname = "%s_%s" % (form, k)
484 sig = Signal(value[0:-1].shape(), reset_less=True, name=fname)
485 sf[k] = sig
486 instr = Fields(**sf)
487 setattr(self, "Form%s" % form, instr)
488 self.sigforms[form] = instr
489
490 self.tree_analyse()
491
492 @property
493 def form_names(self):
494 return self.fields.instrs.keys()
495
496 def elaborate(self, platform):
497 m = PowerDecoder.elaborate(self, platform)
498 comb = m.d.comb
499 # sigh duplicated in SVP64PowerDecoder
500 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
501 raw_le = self.raw_opcode_in
502 l = []
503 for i in range(0, self.width, 8):
504 l.append(raw_le[i:i+8])
505 l.reverse()
506 raw_be = Cat(*l)
507 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
508
509 # add all signal from commonly-used fields
510 for fname, value in self.fields.common_fields.items():
511 sig = getattr(self, fname)
512 comb += sig.eq(value[0:-1])
513
514 # link signals for all field forms
515 forms = self.form_names
516 for form in forms:
517 sf = self.sigforms[form]
518 fields = self.fields.instrs[form]
519 for k, value in fields.items():
520 sig = getattr(sf, k)
521 comb += sig.eq(value[0:-1])
522
523 return m
524
525 def ports(self):
526 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
527
528
529 ####################################################
530 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
531
532 def create_pdecode(name=None, col_subset=None, row_subset=None):
533 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
534
535 subsetting of the PowerOp decoding is possible by setting col_subset
536 """
537
538 # some alteration to the CSV files is required for SV so we use
539 # a class to do it
540 isa = SVP64RM()
541 get_csv = isa.get_svp64_csv
542
543 # minor 19 has extra patterns
544 m19 = []
545 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
546 opint=True, bitsel=(1, 11), suffix=None,
547 subdecoders=[]))
548 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
549 opint=True, bitsel=(1, 6), suffix=None,
550 subdecoders=[]))
551
552 # minor opcodes.
553 pminor = [
554 m19,
555 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
556 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
557 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
558 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
559 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
560 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
561 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
562 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
563 Subdecoder(pattern=22, opcodes=get_csv("minor_22.csv"),
564 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
565 ]
566
567 # top level: extra merged with major
568 dec = []
569 opcodes = get_csv("major.csv")
570 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
571 bitsel=(26, 32), suffix=None, subdecoders=pminor))
572 opcodes = get_csv("extra.csv")
573 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
574 bitsel=(0, 32), suffix=None, subdecoders=[]))
575
576 return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
577 row_subset=row_subset)
578
579
580 if __name__ == '__main__':
581
582 if True:
583 # row subset
584
585 def rowsubsetfn(opcode, row):
586 print("row_subset", opcode, row)
587 return row['unit'] == 'ALU'
588
589 pdecode = create_pdecode(name="rowsub",
590 col_subset={'function_unit', 'in1_sel'},
591 row_subset=rowsubsetfn)
592 vl = rtlil.convert(pdecode, ports=pdecode.ports())
593 with open("row_subset_decoder.il", "w") as f:
594 f.write(vl)
595
596 # col subset
597
598 pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'})
599 vl = rtlil.convert(pdecode, ports=pdecode.ports())
600 with open("col_subset_decoder.il", "w") as f:
601 f.write(vl)
602
603 # full decoder
604
605 pdecode = create_pdecode()
606 vl = rtlil.convert(pdecode, ports=pdecode.ports())
607 with open("decoder.il", "w") as f:
608 f.write(vl)