radix: reading first page table entry
[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 # sigh duplicated in SVP64PowerDecoder
497 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
498 raw_le = self.raw_opcode_in
499 l = []
500 for i in range(0, self.width, 8):
501 l.append(raw_le[i:i+8])
502 l.reverse()
503 raw_be = Cat(*l)
504 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
505
506 # add all signal from commonly-used fields
507 for fname, value in self.fields.common_fields.items():
508 sig = getattr(self, fname)
509 comb += sig.eq(value[0:-1])
510
511 # link signals for all field forms
512 forms = self.form_names
513 for form in forms:
514 sf = self.sigforms[form]
515 fields = self.fields.instrs[form]
516 for k, value in fields.items():
517 sig = getattr(sf, k)
518 comb += sig.eq(value[0:-1])
519
520 return m
521
522 def ports(self):
523 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
524
525
526 ####################################################
527 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
528
529 def create_pdecode(name=None, col_subset=None, row_subset=None):
530 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
531
532 subsetting of the PowerOp decoding is possible by setting col_subset
533 """
534
535 # some alteration to the CSV files is required for SV so we use
536 # a class to do it
537 isa = SVP64RM()
538 get_csv = isa.get_svp64_csv
539
540 # minor 19 has extra patterns
541 m19 = []
542 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
543 opint=True, bitsel=(1, 11), suffix=None,
544 subdecoders=[]))
545 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
546 opint=True, bitsel=(1, 6), suffix=None,
547 subdecoders=[]))
548
549 # minor opcodes.
550 pminor = [
551 m19,
552 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
553 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
554 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
555 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
556 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
557 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
558 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
559 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
560 ]
561
562 # top level: extra merged with major
563 dec = []
564 opcodes = get_csv("major.csv")
565 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
566 bitsel=(26, 32), suffix=None, subdecoders=pminor))
567 opcodes = get_csv("extra.csv")
568 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
569 bitsel=(0, 32), suffix=None, subdecoders=[]))
570
571 return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
572 row_subset=row_subset)
573
574
575 if __name__ == '__main__':
576
577 if True:
578 # row subset
579
580 def rowsubsetfn(opcode, row):
581 print("row_subset", opcode, row)
582 return row['unit'] == 'ALU'
583
584 pdecode = create_pdecode(name="rowsub",
585 col_subset={'function_unit', 'in1_sel'},
586 row_subset=rowsubsetfn)
587 vl = rtlil.convert(pdecode, ports=pdecode.ports())
588 with open("row_subset_decoder.il", "w") as f:
589 f.write(vl)
590
591 # col subset
592
593 pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'})
594 vl = rtlil.convert(pdecode, ports=pdecode.ports())
595 with open("col_subset_decoder.il", "w") as f:
596 f.write(vl)
597
598 # full decoder
599
600 pdecode = create_pdecode()
601 vl = rtlil.convert(pdecode, ports=pdecode.ports())
602 with open("decoder.il", "w") as f:
603 f.write(vl)