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