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