test SVP64 major opcode, start checking if it is EXT001 soon
[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 and '-' not in opcode:
386 opcode = int(opcode, 0)
387 if not row['unit']:
388 continue
389 if self.row_subsetfn:
390 if not self.row_subsetfn(opcode, row):
391 continue
392 # add in the dynamic Case statement here
393 switch_case[opcode] = self.op._eq(row)
394 self.actually_does_something = True
395 case_does_something = True
396
397 if cases:
398 decs.append(cases)
399 if case_does_something:
400 eqs += eq
401 print("submodule eqs", self.pname, eq)
402
403 print("submodules", self.pname, submodules)
404
405 gc.collect()
406 return self.actually_does_something
407
408 def handle_subdecoders(self, switch_case, submodules, d):
409 eqs = []
410 for dec in d.subdecoders:
411 if isinstance(dec, list): # XXX HACK: take first pattern
412 dec = dec[0]
413 print("subdec", dec.pattern, self.pname)
414 mname = get_pname("dec%d" % dec.pattern, self.pname)
415 subdecoder = PowerDecoder(self.width, dec,
416 name=mname,
417 col_subset=self.col_subset,
418 row_subset=self.row_subsetfn)
419 if not subdecoder.tree_analyse(): # doesn't do anything
420 del subdecoder
421 continue # skip
422 submodules[mname] = subdecoder
423 eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
424 switch_case[dec.pattern] = self.op.eq(subdecoder.op)
425 self.actually_does_something = True
426
427 return eqs
428
429 def elaborate(self, platform):
430 print("decoder elaborate", self.pname, self.submodules)
431 m = Module()
432 comb = m.d.comb
433
434 comb += self.eqs
435
436 for mname, subdecoder in self.submodules.items():
437 setattr(m.submodules, mname, subdecoder)
438
439 for switch_case in self.decs:
440 for (switch, cases) in switch_case:
441 with m.Switch(switch):
442 for key, eqs in cases.items():
443 with m.Case(key):
444 comb += eqs
445 return m
446
447 def ports(self):
448 return [self.opcode_in] + self.op.ports()
449
450
451 class TopPowerDecoder(PowerDecoder):
452 """TopPowerDecoder
453
454 top-level hierarchical decoder for POWER ISA
455 bigendian dynamically switches between big and little endian decoding
456 (reverses byte order). See V3.0B p44 1.11.2
457 """
458
459 def __init__(self, width, dec, name=None, col_subset=None, row_subset=None):
460 PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset)
461 self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
462 self.fields.create_specs()
463 self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
464 self.bigendian = Signal(reset_less=True)
465
466 for fname, value in self.fields.common_fields.items():
467 signame = get_pname(fname, name)
468 sig = Signal(value[0:-1].shape(), reset_less=True, name=signame)
469 setattr(self, fname, sig)
470
471 # create signals for all field forms
472 forms = self.form_names
473 self.sigforms = {}
474 for form in forms:
475 fields = self.fields.instrs[form]
476 fk = fields.keys()
477 Fields = namedtuple("Fields", fk)
478 sf = {}
479 for k, value in fields.items():
480 fname = "%s_%s" % (form, k)
481 sig = Signal(value[0:-1].shape(), reset_less=True, name=fname)
482 sf[k] = sig
483 instr = Fields(**sf)
484 setattr(self, "Form%s" % form, instr)
485 self.sigforms[form] = instr
486
487 self.tree_analyse()
488
489 @property
490 def form_names(self):
491 return self.fields.instrs.keys()
492
493 def elaborate(self, platform):
494 m = PowerDecoder.elaborate(self, platform)
495 comb = m.d.comb
496 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
497 raw_le = self.raw_opcode_in
498 l = []
499 for i in range(0, self.width, 8):
500 l.append(raw_le[i:i+8])
501 l.reverse()
502 raw_be = Cat(*l)
503 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
504
505 # add all signal from commonly-used fields
506 for fname, value in self.fields.common_fields.items():
507 sig = getattr(self, fname)
508 comb += sig.eq(value[0:-1])
509
510 # link signals for all field forms
511 forms = self.form_names
512 for form in forms:
513 sf = self.sigforms[form]
514 fields = self.fields.instrs[form]
515 for k, value in fields.items():
516 sig = getattr(sf, k)
517 comb += sig.eq(value[0:-1])
518
519 return m
520
521 def ports(self):
522 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
523
524
525 ####################################################
526 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
527
528 def create_pdecode(name=None, col_subset=None, row_subset=None):
529 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
530
531 subsetting of the PowerOp decoding is possible by setting col_subset
532 """
533
534 # some alteration to the CSV files is required for SV so we use
535 # a class to do it
536 isa = SVP64RM()
537 get_csv = isa.get_svp64_csv
538
539 # minor 19 has extra patterns
540 m19 = []
541 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
542 opint=True, bitsel=(1, 11), suffix=None,
543 subdecoders=[]))
544 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
545 opint=True, bitsel=(1, 6), suffix=None,
546 subdecoders=[]))
547
548 # minor opcodes.
549 pminor = [
550 m19,
551 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
552 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
553 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
554 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
555 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
556 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
557 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
558 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
559 ]
560
561 # top level: extra merged with major
562 dec = []
563 opcodes = get_csv("major.csv")
564 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
565 bitsel=(26, 32), suffix=None, subdecoders=pminor))
566 opcodes = get_csv("extra.csv")
567 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
568 bitsel=(0, 32), suffix=None, subdecoders=[]))
569
570 return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
571 row_subset=row_subset)
572
573
574 if __name__ == '__main__':
575
576 if True:
577 # row subset
578
579 def rowsubsetfn(opcode, row):
580 print("row_subset", opcode, row)
581 return row['unit'] == 'ALU'
582
583 pdecode = create_pdecode(name="rowsub",
584 col_subset={'function_unit', 'in1_sel'},
585 row_subset=rowsubsetfn)
586 vl = rtlil.convert(pdecode, ports=pdecode.ports())
587 with open("row_subset_decoder.il", "w") as f:
588 f.write(vl)
589
590 # col subset
591
592 pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'})
593 vl = rtlil.convert(pdecode, ports=pdecode.ports())
594 with open("col_subset_decoder.il", "w") as f:
595 f.write(vl)
596
597 # full decoder
598
599 pdecode = create_pdecode()
600 vl = rtlil.convert(pdecode, ports=pdecode.ports())
601 with open("decoder.il", "w") as f:
602 f.write(vl)