79dd79518be18a680315091e4230eaac1cf3438c
[soc.git] / src / soc / 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 soc.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 soc.decoder.power_fields import DecodeFields
101 from soc.decoder.power_fieldsn import SigDecode, SignalBitRange
102 from soc.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_cr_in': SVEXTRA,
135 'sv_cr_out': SVEXTRA,
136 'ldst_len': LdstLen,
137 'upd': LDSTMode,
138 'rc_sel': RC,
139 'cry_in': CryIn
140 }
141
142 power_op_csvmap = {'function_unit': 'unit',
143 'form': 'form',
144 'internal_op': 'internal op',
145 'in1_sel': 'in1',
146 'in2_sel': 'in2',
147 'in3_sel': 'in3',
148 'out_sel': 'out',
149 'sv_in1': 'sv_in1',
150 'sv_in2': 'sv_in2',
151 'sv_in3': 'sv_in3',
152 'sv_out': 'sv_out',
153 'sv_cr_in': 'sv_cr_in',
154 'sv_cr_out': 'sv_cr_out',
155 'SV_Etype': 'SV_Etype',
156 'SV_Ptype': 'SV_Ptype',
157 'cr_in': 'CR in',
158 'cr_out': 'CR out',
159 'ldst_len': 'ldst len',
160 'upd': 'upd',
161 'rc_sel': 'rc',
162 'cry_in': 'cry in',
163 }
164
165
166 def get_pname(field, pname):
167 if pname is None:
168 return field
169 return "%s_%s" % (pname, field)
170
171
172 class PowerOp:
173 """PowerOp - a dynamic class that stores (subsets of) CSV rows of data
174 about a PowerISA instruction. this is a "micro-code" expanded format
175 which generates an awful lot of wires, hence the subsetting
176 """
177
178 def __init__(self, incl_asm=True, name=None, subset=None):
179 self.subset = subset
180 debug_report = set()
181 fields = set()
182 for field, ptype in power_op_types.items():
183 fields.add(field)
184 if subset and field not in subset:
185 continue
186 fname = get_pname(field, name)
187 setattr(self, field, Signal(ptype, reset_less=True, name=fname))
188 debug_report.add(field)
189 for bit in single_bit_flags:
190 field = get_signal_name(bit)
191 fields.add(field)
192 if subset and field not in subset:
193 continue
194 debug_report.add(field)
195 fname = get_pname(field, name)
196 setattr(self, field, Signal(reset_less=True, name=fname))
197 print("PowerOp debug", name, debug_report)
198 print(" fields", fields)
199
200 def _eq(self, row=None):
201 if row is None:
202 row = default_values
203 # TODO: this conversion process from a dict to an object
204 # should really be done using e.g. namedtuple and then
205 # call eq not _eq
206 if False: # debugging
207 if row['CR in'] == '1':
208 import pdb
209 pdb.set_trace()
210 print(row)
211 if row['CR out'] == '0':
212 import pdb
213 pdb.set_trace()
214 print(row)
215 print(row)
216 ldst_mode = row['upd']
217 if ldst_mode.isdigit():
218 row['upd'] = int(ldst_mode)
219 res = []
220 for field, ptype in power_op_types.items():
221 if not hasattr(self, field):
222 continue
223 if field not in power_op_csvmap:
224 continue
225 csvname = power_op_csvmap[field]
226 print(field, ptype, csvname, row)
227 val = row[csvname]
228 if csvname == 'upd' and isinstance(val, int): # LDSTMode different
229 val = ptype(val)
230 else:
231 val = ptype[val]
232 res.append(getattr(self, field).eq(val))
233 if False:
234 print(row.keys())
235 asmcode = row['comment']
236 if hasattr(self, "asmcode") and asmcode in asmidx:
237 res.append(self.asmcode.eq(asmidx[asmcode]))
238 for bit in single_bit_flags:
239 field = get_signal_name(bit)
240 if not hasattr(self, field):
241 continue
242 sig = getattr(self, field)
243 res.append(sig.eq(int(row.get(bit, 0))))
244 return res
245
246 def _get_eq(self, res, field, otherop):
247 copyfrom = getattr(otherop, field, None)
248 copyto = getattr(self, field, None)
249 if copyfrom is not None and copyto is not None:
250 res.append(copyto.eq(copyfrom))
251
252 def eq(self, otherop):
253 res = []
254 for field in power_op_types.keys():
255 self._get_eq(res, field, otherop)
256 for bit in single_bit_flags:
257 self._get_eq(res, get_signal_name(bit), otherop)
258 return res
259
260 def ports(self):
261 res = []
262 for field in power_op_types.keys():
263 if hasattr(self, field):
264 res.append(getattr(self, field))
265 if hasattr(self, "asmcode"):
266 res.append(self.asmcode)
267 for field in single_bit_flags:
268 field = get_signal_name(field)
269 if hasattr(self, field):
270 res.append(getattr(self, field))
271 return res
272
273
274 class PowerDecoder(Elaboratable):
275 """PowerDecoder - decodes an incoming opcode into the type of operation
276
277 this is a recursive algorithm, creating Switch statements that can
278 have further match-and-decode on other parts of the opcode field before
279 finally landing at a "this CSV entry details gets returned" thing.
280
281 the complicating factor is the row and col subsetting. column subsetting
282 dynamically chooses only the CSV columns requested, whilst row subsetting
283 allows a function to be called on the row to determine if the Case
284 statement is to be generated for that row. this not only generates
285 completely different Decoders, it also means that some sub-decoders
286 will turn up blank (empty switch statements). if that happens we do
287 not want the parent to include a Mux for an entirely blank switch statement
288 so we have to store the switch/case statements in a tree, and
289 post-analyse it.
290
291 the reason for the tree is because elaborate can only be called *after*
292 the constructor is called. all quite messy.
293 """
294
295 def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
296 self.actually_does_something = False
297 self.pname = name
298 self.col_subset = col_subset
299 self.row_subsetfn = row_subset
300 if not isinstance(dec, list):
301 dec = [dec]
302 self.dec = dec
303 self.opcode_in = Signal(width, reset_less=True)
304
305 self.op = PowerOp(name=name, subset=col_subset)
306 for d in dec:
307 if d.suffix is not None and d.suffix >= width:
308 d.suffix = None
309 self.width = width
310
311 def suffix_mask(self, d):
312 return ((1 << d.suffix) - 1)
313
314 def divide_opcodes(self, d):
315 divided = {}
316 mask = self.suffix_mask(d)
317 print("mask", hex(mask))
318 for row in d.opcodes:
319 opcode = row['opcode']
320 if d.opint and '-' not in opcode:
321 opcode = int(opcode, 0)
322 key = opcode & mask
323 opcode = opcode >> d.suffix
324 if key not in divided:
325 divided[key] = []
326 r = row.copy()
327 r['opcode'] = opcode
328 divided[key].append(r)
329 return divided
330
331 def tree_analyse(self):
332 self.decs = decs = []
333 self.submodules = submodules = {}
334 self.eqs = eqs = []
335
336 # go through the list of CSV decoders first
337 for d in self.dec:
338 cases = []
339 opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
340 reset_less=True)
341 eq = []
342 case_does_something = False
343 eq.append(opcode_switch.eq(
344 self.opcode_in[d.bitsel[0]:d.bitsel[1]]))
345 if d.suffix:
346 opcodes = self.divide_opcodes(d)
347 opc_in = Signal(d.suffix, reset_less=True)
348 eq.append(opc_in.eq(opcode_switch[:d.suffix]))
349 # begin the dynamic Switch statement here
350 switch_case = {}
351 cases.append([opc_in, switch_case])
352 sub_eqs = []
353 for key, row in opcodes.items():
354 bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
355 sd = Subdecoder(pattern=None, opcodes=row,
356 bitsel=bitsel, suffix=None,
357 opint=False, subdecoders=[])
358 mname = get_pname("dec_sub%d" % key, self.pname)
359 subdecoder = PowerDecoder(width=32, dec=sd,
360 name=mname,
361 col_subset=self.col_subset,
362 row_subset=self.row_subsetfn)
363 if not subdecoder.tree_analyse():
364 del subdecoder
365 continue
366 submodules[mname] = subdecoder
367 sub_eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
368 # add in the dynamic Case statement here
369 switch_case[key] = self.op.eq(subdecoder.op)
370 self.actually_does_something = True
371 case_does_something = True
372 if case_does_something:
373 eq += sub_eqs
374 else:
375 # TODO: arguments, here (all of them) need to be a list.
376 # a for-loop around the *list* of decoder args.
377 switch_case = {}
378 cases.append([opcode_switch, switch_case])
379 seqs = self.handle_subdecoders(switch_case, submodules, d)
380 if seqs:
381 case_does_something = True
382 eq += seqs
383 for row in d.opcodes:
384 opcode = row['opcode']
385 if d.opint:
386 assert opcode.startswith('0b')
387 if '-' not in opcode:
388 opcode = int(opcode, 0)
389 else:
390 opcode = opcode[2:]
391 if not row['unit']:
392 continue
393 if self.row_subsetfn:
394 if not self.row_subsetfn(opcode, row):
395 continue
396 # add in the dynamic Case statement here
397 switch_case[opcode] = self.op._eq(row)
398 self.actually_does_something = True
399 case_does_something = True
400
401 if cases:
402 decs.append(cases)
403 if case_does_something:
404 eqs += eq
405 print("submodule eqs", self.pname, eq)
406
407 print("submodules", self.pname, submodules)
408
409 gc.collect()
410 return self.actually_does_something
411
412 def handle_subdecoders(self, switch_case, submodules, d):
413 eqs = []
414 for dec in d.subdecoders:
415 if isinstance(dec, list): # XXX HACK: take first pattern
416 dec = dec[0]
417 print("subdec", dec.pattern, self.pname)
418 mname = get_pname("dec%d" % dec.pattern, self.pname)
419 subdecoder = PowerDecoder(self.width, dec,
420 name=mname,
421 col_subset=self.col_subset,
422 row_subset=self.row_subsetfn)
423 if not subdecoder.tree_analyse(): # doesn't do anything
424 del subdecoder
425 continue # skip
426 submodules[mname] = subdecoder
427 eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
428 switch_case[dec.pattern] = self.op.eq(subdecoder.op)
429 self.actually_does_something = True
430
431 return eqs
432
433 def elaborate(self, platform):
434 print("decoder elaborate", self.pname, self.submodules)
435 m = Module()
436 comb = m.d.comb
437
438 comb += self.eqs
439
440 for mname, subdecoder in self.submodules.items():
441 setattr(m.submodules, mname, subdecoder)
442
443 for switch_case in self.decs:
444 for (switch, cases) in switch_case:
445 with m.Switch(switch):
446 for key, eqs in cases.items():
447 with m.Case(key):
448 comb += eqs
449 return m
450
451 def ports(self):
452 return [self.opcode_in] + self.op.ports()
453
454
455 class TopPowerDecoder(PowerDecoder):
456 """TopPowerDecoder
457
458 top-level hierarchical decoder for POWER ISA
459 bigendian dynamically switches between big and little endian decoding
460 (reverses byte order). See V3.0B p44 1.11.2
461 """
462
463 def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
464 PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset)
465 self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
466 self.fields.create_specs()
467 self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
468 self.bigendian = Signal(reset_less=True)
469
470 for fname, value in self.fields.common_fields.items():
471 signame = get_pname(fname, name)
472 sig = Signal(value[0:-1].shape(), reset_less=True, name=signame)
473 setattr(self, fname, sig)
474
475 # create signals for all field forms
476 forms = self.form_names
477 self.sigforms = {}
478 for form in forms:
479 fields = self.fields.instrs[form]
480 fk = fields.keys()
481 Fields = namedtuple("Fields", fk)
482 sf = {}
483 for k, value in fields.items():
484 fname = "%s_%s" % (form, k)
485 sig = Signal(value[0:-1].shape(), reset_less=True, name=fname)
486 sf[k] = sig
487 instr = Fields(**sf)
488 setattr(self, "Form%s" % form, instr)
489 self.sigforms[form] = instr
490
491 self.tree_analyse()
492
493 @property
494 def form_names(self):
495 return self.fields.instrs.keys()
496
497 def elaborate(self, platform):
498 m = PowerDecoder.elaborate(self, platform)
499 comb = m.d.comb
500 # sigh duplicated in SVP64PowerDecoder
501 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
502 raw_le = self.raw_opcode_in
503 l = []
504 for i in range(0, self.width, 8):
505 l.append(raw_le[i:i+8])
506 l.reverse()
507 raw_be = Cat(*l)
508 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
509
510 # add all signal from commonly-used fields
511 for fname, value in self.fields.common_fields.items():
512 sig = getattr(self, fname)
513 comb += sig.eq(value[0:-1])
514
515 # link signals for all field forms
516 forms = self.form_names
517 for form in forms:
518 sf = self.sigforms[form]
519 fields = self.fields.instrs[form]
520 for k, value in fields.items():
521 sig = getattr(sf, k)
522 comb += sig.eq(value[0:-1])
523
524 return m
525
526 def ports(self):
527 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
528
529
530 ####################################################
531 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
532
533 def create_pdecode(name=None, col_subset=None, row_subset=None):
534 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
535
536 subsetting of the PowerOp decoding is possible by setting col_subset
537 """
538
539 # some alteration to the CSV files is required for SV so we use
540 # a class to do it
541 isa = SVP64RM()
542 get_csv = isa.get_svp64_csv
543
544 # minor 19 has extra patterns
545 m19 = []
546 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
547 opint=True, bitsel=(1, 11), suffix=None,
548 subdecoders=[]))
549 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
550 opint=True, bitsel=(1, 6), suffix=None,
551 subdecoders=[]))
552
553 # minor opcodes.
554 pminor = [
555 m19,
556 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
557 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
558 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
559 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
560 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
561 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
562 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
563 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
564 Subdecoder(pattern=22, opcodes=get_csv("minor_22.csv"),
565 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
566 ]
567
568 # top level: extra merged with major
569 dec = []
570 opcodes = get_csv("major.csv")
571 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
572 bitsel=(26, 32), suffix=None, subdecoders=pminor))
573 opcodes = get_csv("extra.csv")
574 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
575 bitsel=(0, 32), suffix=None, subdecoders=[]))
576
577 return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
578 row_subset=row_subset)
579
580
581 if __name__ == '__main__':
582
583 if True:
584 # row subset
585
586 def rowsubsetfn(opcode, row):
587 print("row_subset", opcode, row)
588 return row['unit'] == 'ALU'
589
590 pdecode = create_pdecode(name="rowsub",
591 col_subset={'function_unit', 'in1_sel'},
592 row_subset=rowsubsetfn)
593 vl = rtlil.convert(pdecode, ports=pdecode.ports())
594 with open("row_subset_decoder.il", "w") as f:
595 f.write(vl)
596
597 # col subset
598
599 pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'})
600 vl = rtlil.convert(pdecode, ports=pdecode.ports())
601 with open("col_subset_decoder.il", "w") as f:
602 f.write(vl)
603
604 # full decoder
605
606 pdecode = create_pdecode()
607 vl = rtlil.convert(pdecode, ports=pdecode.ports())
608 with open("decoder.il", "w") as f:
609 f.write(vl)