grev[w][i][.] pseudo-code works
[openpower-isa.git] / src / openpower / 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, OrderedDict
91 from nmigen import Module, Elaboratable, Signal, Cat, Mux, Const
92 from nmigen.cli import rtlil, verilog
93 from openpower.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 asmlen)
101 from openpower.decoder.power_fields import DecodeFields
102 from openpower.decoder.power_fieldsn import SigDecode, SignalBitRange
103 from openpower.decoder.power_svp64 import SVP64RM
104
105 from openpower.util import log
106
107 # key data structure in which the POWER decoder is specified,
108 # in a hierarchical fashion
109 Subdecoder = namedtuple( # fix autoformatter
110 "Subdecoder",
111 ["pattern", # the major pattern to search for (e.g. major opcode)
112 "opcodes", # a dictionary of minor patterns to find
113 "opint", # true => the pattern must not be in "10----11" format
114 # the bits (as a range) against which "pattern" matches
115 "bitsel",
116 "suffix", # shift the opcode down before decoding
117 "subdecoders" # list of further subdecoders for *additional* matches,
118 # *ONLY* after "pattern" has *ALSO* been matched against.
119 ])
120
121 power_op_types = {'function_unit': Function,
122 'internal_op': MicrOp,
123 'form': Form,
124 'asmcode': asmlen,
125 'SV_Etype': SVEtype,
126 'SV_Ptype': SVPtype,
127 'in1_sel': In1Sel,
128 'in2_sel': In2Sel,
129 'in3_sel': In3Sel,
130 'out_sel': OutSel,
131 'cr_in': CRInSel,
132 'cr_out': CROutSel,
133 'sv_in1': SVEXTRA,
134 'sv_in2': SVEXTRA,
135 'sv_in3': SVEXTRA,
136 'sv_out': SVEXTRA,
137 'sv_out2': SVEXTRA,
138 'sv_cr_in': SVEXTRA,
139 'sv_cr_out': SVEXTRA,
140 'ldst_len': LdstLen,
141 'upd': LDSTMode,
142 'rc_sel': RC,
143 'cry_in': CryIn
144 }
145
146 power_op_csvmap = {'function_unit': 'unit',
147 'form': 'form',
148 'internal_op': 'internal op',
149 'in1_sel': 'in1',
150 'in2_sel': 'in2',
151 'in3_sel': 'in3',
152 'out_sel': 'out',
153 'sv_in1': 'sv_in1',
154 'sv_in2': 'sv_in2',
155 'sv_in3': 'sv_in3',
156 'sv_out': 'sv_out',
157 'sv_out2': 'sv_out2',
158 'sv_cr_in': 'sv_cr_in',
159 'sv_cr_out': 'sv_cr_out',
160 'SV_Etype': 'SV_Etype',
161 'SV_Ptype': 'SV_Ptype',
162 'cr_in': 'CR in',
163 'cr_out': 'CR out',
164 'ldst_len': 'ldst len',
165 'upd': 'upd',
166 'rsrv': 'rsrv', # atomic operation
167 'rc_sel': 'rc',
168 'cry_in': 'cry in',
169 }
170
171
172 def get_pname(field, pname):
173 if pname is None:
174 return field
175 return "%s_%s" % (pname, field)
176
177
178 class PowerOp:
179 """PowerOp - a dynamic class that stores (subsets of) CSV rows of data
180 about a PowerISA instruction. this is a "micro-code" expanded format
181 which generates an awful lot of wires, hence the subsetting
182 """
183
184 def __init__(self, incl_asm=True, name=None, subset=None, fields=None):
185 self.name = name
186 self.subset = subset
187 if fields is not None:
188 for k, v in fields.items():
189 setattr(self, k, v)
190 return
191 debug_report = set()
192 fields = set()
193 for field, ptype in power_op_types.items():
194 fields.add(field)
195 if subset and field not in subset:
196 continue
197 fname = get_pname(field, name)
198 setattr(self, field, Signal(ptype, reset_less=True, name=fname))
199 debug_report.add(field)
200 for bit in single_bit_flags:
201 field = get_signal_name(bit)
202 fields.add(field)
203 if subset and field not in subset:
204 continue
205 debug_report.add(field)
206 fname = get_pname(field, name)
207 setattr(self, field, Signal(reset_less=True, name=fname))
208 self._fields = fields
209 # comment out, bit too high debug level
210 #print("PowerOp debug", name, debug_report)
211 #print(" fields", fields)
212
213 @staticmethod
214 def like(other):
215 """PowerOp.like: creates a duplicate of a given PowerOp instance
216 """
217 fields = {}
218 for fname in other._fields:
219 sig = getattr(other, fname, None)
220 if sig is not None:
221 fields[fname] = sig.__class__.like(sig)
222 return PowerOp(subset=other.subset, fields=fields)
223
224 def _eq(self, row=None):
225 if row is None:
226 row = default_values
227 # TODO: this conversion process from a dict to an object
228 # should really be done using e.g. namedtuple and then
229 # call eq not _eq
230 if False: # debugging
231 if row['CR in'] == '1':
232 import pdb
233 pdb.set_trace()
234 log(row)
235 if row['CR out'] == '0':
236 import pdb
237 pdb.set_trace()
238 log(row)
239 log(row)
240 ldst_mode = row['upd']
241 if ldst_mode.isdigit():
242 row['upd'] = int(ldst_mode)
243 res = []
244 for field, ptype in power_op_types.items():
245 if not hasattr(self, field):
246 continue
247 if field not in power_op_csvmap:
248 continue
249 csvname = power_op_csvmap[field]
250 # log(field, ptype, csvname, row)
251 val = row[csvname]
252 if csvname == 'upd' and isinstance(val, int): # LDSTMode different
253 val = ptype(val)
254 else:
255 val = ptype[val]
256 res.append(getattr(self, field).eq(val))
257 if False:
258 log(row.keys())
259 asmcode = row['comment']
260 # process the comment field, strip out "equals" for FP
261 if "=" in asmcode:
262 asmcode = asmcode.split("=")[-1]
263 log("asmcode stripping =", asmcode,
264 asmcode in asmidx, hasattr(self, "asmcode"))
265 if hasattr(self, "asmcode") and asmcode in asmidx:
266 res.append(self.asmcode.eq(asmidx[asmcode]))
267 for bit in single_bit_flags:
268 field = get_signal_name(bit)
269 if not hasattr(self, field):
270 continue
271 sig = getattr(self, field)
272 res.append(sig.eq(int(row.get(bit, 0))))
273 return res
274
275 def _get_eq(self, res, field, otherop):
276 copyfrom = getattr(otherop, field, None)
277 copyto = getattr(self, field, None)
278 if copyfrom is not None and copyto is not None:
279 res.append(copyto.eq(copyfrom))
280
281 def eq(self, otherop):
282 res = []
283 for field in power_op_types.keys():
284 self._get_eq(res, field, otherop)
285 for bit in single_bit_flags:
286 self._get_eq(res, get_signal_name(bit), otherop)
287 return res
288
289 def ports(self):
290 res = []
291 for field in power_op_types.keys():
292 if hasattr(self, field):
293 res.append(getattr(self, field))
294 if hasattr(self, "asmcode"):
295 res.append(self.asmcode)
296 for field in single_bit_flags:
297 field = get_signal_name(field)
298 if hasattr(self, field):
299 res.append(getattr(self, field))
300 return res
301
302
303 class PowerDecoder(Elaboratable):
304 """PowerDecoder - decodes an incoming opcode into the type of operation
305
306 this is a recursive algorithm, creating Switch statements that can
307 have further match-and-decode on other parts of the opcode field before
308 finally landing at a "this CSV entry details gets returned" thing.
309
310 the complicating factor is the row and col subsetting. column subsetting
311 dynamically chooses only the CSV columns requested, whilst row subsetting
312 allows a function to be called on the row to determine if the Case
313 statement is to be generated for that row. this not only generates
314 completely different Decoders, it also means that some sub-decoders
315 will turn up blank (empty switch statements). if that happens we do
316 not want the parent to include a Mux for an entirely blank switch statement
317 so we have to store the switch/case statements in a tree, and
318 post-analyse it.
319
320 the reason for the tree is because elaborate can only be called *after*
321 the constructor is called. all quite messy.
322 """
323
324 def __init__(self, width, dec, name=None, col_subset=None,
325 row_subset=None, conditions=None):
326 if conditions is None:
327 # XXX conditions = {}
328 conditions = {'SVP64BREV': Const(0, 1),
329 'SVP64FFT': Const(0, 1),
330 }
331 self.actually_does_something = False
332 self.pname = name
333 self.conditions = conditions
334 self.col_subset = col_subset
335 self.row_subsetfn = row_subset
336 if not isinstance(dec, list):
337 dec = [dec]
338 self.dec = dec
339 self.opcode_in = Signal(width, reset_less=True)
340
341 self.op = PowerOp(name=name, subset=col_subset)
342 for d in dec:
343 if d.suffix is not None and d.suffix >= width:
344 d.suffix = None
345
346 self.width = width
347
348 # create some case statement condition patterns for matching
349 # a single condition. "1----" for the first condition,
350 # "-1----" for the 2nd etc.
351 # also create a matching ordered list of conditions, for the switch,
352 # which will Cat() them together
353 self.ccases = {}
354 self.ckeys = list(conditions.keys())
355 self.ckeys.sort()
356
357 def find_conditions(self, opcodes):
358 # look for conditions, create dictionary entries for them
359 # sorted by opcode
360 rows = OrderedDict() # start as a dictionary, get as list (after)
361 for row in opcodes:
362 condition = row['CONDITIONS']
363 opcode = row['opcode']
364 if condition:
365 # check it's expected
366 assert (condition in self.conditions or
367 (condition[0] == '~' and
368 condition[1:] in self.conditions)), \
369 "condition %s not in %s" % (condition, str(conditions))
370 if opcode not in rows:
371 rows[opcode] = {}
372 rows[opcode][condition] = row
373 else:
374 # check it's unique
375 assert opcode not in rows, \
376 "opcode %s already in rows for %s" % \
377 (opcode, self.pname)
378 rows[opcode] = row
379 # after checking for conditions, get just the values (ordered)
380 return list(rows.values())
381
382 def suffix_mask(self, d):
383 return ((1 << d.suffix) - 1)
384
385 def divide_opcodes(self, d):
386 divided = {}
387 mask = self.suffix_mask(d)
388 #print("mask", hex(mask))
389 for row in d.opcodes:
390 opcode = row['opcode']
391 if d.opint and '-' not in opcode:
392 opcode = int(opcode, 0)
393 key = opcode & mask
394 opcode = opcode >> d.suffix
395 if key not in divided:
396 divided[key] = []
397 r = row.copy()
398 r['opcode'] = opcode
399 divided[key].append(r)
400 return divided
401
402 def tree_analyse(self):
403 self.decs = decs = []
404 self.submodules = submodules = {}
405 self.eqs = eqs = []
406
407 # go through the list of CSV decoders first
408 for d in self.dec:
409 cases = []
410 opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
411 reset_less=True)
412 eq = []
413 case_does_something = False
414 look_for = self.opcode_in[d.bitsel[0]:d.bitsel[1]]
415 eq.append(opcode_switch.eq(look_for))
416 if d.suffix:
417 opcodes = self.divide_opcodes(d)
418 # TODO opcodes = self.find_conditions(opcodes)
419 opc_in = Signal(d.suffix, reset_less=True)
420 eq.append(opc_in.eq(opcode_switch[:d.suffix]))
421 # begin the dynamic Switch statement here
422 switch_case = {}
423 cases.append([opc_in, switch_case])
424 sub_eqs = []
425 for key, row in opcodes.items():
426 bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
427 sd = Subdecoder(pattern=None, opcodes=row,
428 bitsel=bitsel, suffix=None,
429 opint=False, subdecoders=[])
430 mname = get_pname("dec_sub%d" % key, self.pname)
431 subdecoder = PowerDecoder(width=32, dec=sd,
432 name=mname,
433 col_subset=self.col_subset,
434 row_subset=self.row_subsetfn,
435 conditions=self.conditions)
436 if not subdecoder.tree_analyse():
437 del subdecoder
438 continue
439 submodules[mname] = subdecoder
440 sub_eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
441 # add in the dynamic Case statement here
442 switch_case[key] = self.op.eq(subdecoder.op)
443 self.actually_does_something = True
444 case_does_something = True
445 if case_does_something:
446 eq += sub_eqs
447 else:
448 # TODO: arguments, here (all of them) need to be a list.
449 # a for-loop around the *list* of decoder args.
450 switch_case = {}
451 cases.append([opcode_switch, switch_case])
452 seqs = self.handle_subdecoders(switch_case, submodules, d)
453 if seqs:
454 case_does_something = True
455 eq += seqs
456 opcodes = self.find_conditions(d.opcodes)
457 for row in opcodes:
458 # urrr this is an awful hack. if "conditions" are active
459 # get the FIRST item (will be the same opcode), and it
460 # had BETTER have the same unit and also pass other
461 # row subset conditions.
462 if 'opcode' not in row: # must be a "CONDITIONS" dict...
463 is_conditions = True
464 _row = row[list(row.keys())[0]]
465 else:
466 is_conditions = False
467 _row = row
468 opcode = _row['opcode']
469 if d.opint and '-' not in opcode:
470 opcode = int(opcode, 0)
471 if not _row['unit']:
472 continue
473 if self.row_subsetfn:
474 if not self.row_subsetfn(opcode, _row):
475 continue
476 # add in the dynamic Case statement here
477 if is_conditions:
478 switch_case[opcode] = {}
479 for k, crow in row.items():
480 # log("ordered", k, crow)
481 switch_case[opcode][k] = self.op._eq(crow)
482 else:
483 switch_case[opcode] = self.op._eq(row)
484 self.actually_does_something = True
485 case_does_something = True
486
487 if cases:
488 decs.append(cases)
489 if case_does_something:
490 eqs += eq
491 #print("submodule eqs", self.pname, eq)
492
493 #print("submodules", self.pname, submodules)
494
495 gc.collect()
496 return self.actually_does_something
497
498 def handle_subdecoders(self, switch_case, submodules, d):
499 eqs = []
500 for dlist in d.subdecoders:
501 if not isinstance(dlist, list): # XXX HACK: take first pattern
502 dlist = [dlist]
503 for dec in dlist:
504 #print("subdec", dec.pattern, self.pname)
505 mname = get_pname("dec%d" % dec.pattern, self.pname)
506 if mname in submodules:
507 # sigh, HACK...
508 mname += "_1"
509 assert mname not in submodules
510 subdecoder = PowerDecoder(self.width, dec,
511 name=mname,
512 col_subset=self.col_subset,
513 row_subset=self.row_subsetfn,
514 conditions=self.conditions)
515 log("subdecoder", mname, subdecoder)
516 if not subdecoder.tree_analyse(): # doesn't do anything
517 log("analysed, DELETING", mname)
518 del subdecoder
519 continue # skip
520 submodules[mname] = subdecoder
521 eqs.append(subdecoder.opcode_in.eq(self.opcode_in))
522 switch_case[dec.pattern] = self.op.eq(subdecoder.op)
523 self.actually_does_something = True
524
525 return eqs
526
527 def elaborate(self, platform):
528 #print("decoder elaborate", self.pname, self.submodules)
529 m = Module()
530 comb = m.d.comb
531
532 comb += self.eqs
533
534 for mname, subdecoder in self.submodules.items():
535 setattr(m.submodules, mname, subdecoder)
536
537 for switch_case in self.decs:
538 for (switch, cases) in switch_case:
539 with m.Switch(switch):
540 for key, eqs in cases.items():
541 with m.Case(key):
542 # "conditions" are a further switch statement
543 if isinstance(eqs, dict):
544 self.condition_switch(m, eqs)
545 else:
546 comb += eqs
547 return m
548
549 def condition_switch(self, m, cases):
550 """against the global list of "conditions", having matched against
551 bits of the opcode, we FINALLY now have to match against some
552 additional "conditions". this is because there can be **MULTIPLE**
553 entries for a given opcode match. here we discern them.
554 """
555 comb = m.d.comb
556 cswitch = []
557 ccases = []
558 for casekey, eqs in cases.items():
559 if casekey.startswith('~'):
560 with m.If(~self.conditions[casekey[1:]]):
561 comb += eqs
562 else:
563 with m.If(self.conditions[casekey]):
564 comb += eqs
565
566 def ports(self):
567 return [self.opcode_in] + self.op.ports()
568
569
570 class TopPowerDecoder(PowerDecoder):
571 """TopPowerDecoder
572
573 top-level hierarchical decoder for POWER ISA
574 bigendian dynamically switches between big and little endian decoding
575 (reverses byte order). See V3.0B p44 1.11.2
576 """
577
578 def __init__(self, width, dec, name=None, col_subset=None,
579 row_subset=None, conditions=None):
580 PowerDecoder.__init__(self, width, dec, name,
581 col_subset, row_subset, conditions)
582 self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
583 self.fields.create_specs()
584 self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
585 self.bigendian = Signal(reset_less=True)
586
587 for fname, value in self.fields.common_fields.items():
588 signame = get_pname(fname, name)
589 sig = Signal(value[0:-1].shape(), reset_less=True, name=signame)
590 setattr(self, fname, sig)
591
592 # create signals for all field forms
593 forms = self.form_names
594 self.sigforms = {}
595 for form in forms:
596 fields = self.fields.instrs[form]
597 fk = fields.keys()
598 Fields = namedtuple("Fields", fk)
599 sf = {}
600 for k, value in fields.items():
601 fname = "%s_%s" % (form, k)
602 sig = Signal(value[0:-1].shape(), reset_less=True, name=fname)
603 sf[k] = sig
604 instr = Fields(**sf)
605 setattr(self, "Form%s" % form, instr)
606 self.sigforms[form] = instr
607
608 self.tree_analyse()
609
610 @property
611 def form_names(self):
612 return self.fields.instrs.keys()
613
614 def elaborate(self, platform):
615 m = PowerDecoder.elaborate(self, platform)
616 comb = m.d.comb
617 # sigh duplicated in SVP64PowerDecoder
618 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
619 raw_le = self.raw_opcode_in
620 l = []
621 for i in range(0, self.width, 8):
622 l.append(raw_le[i:i+8])
623 l.reverse()
624 raw_be = Cat(*l)
625 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
626
627 # add all signal from commonly-used fields
628 for fname, value in self.fields.common_fields.items():
629 sig = getattr(self, fname)
630 comb += sig.eq(value[0:-1])
631
632 # link signals for all field forms
633 forms = self.form_names
634 for form in forms:
635 sf = self.sigforms[form]
636 fields = self.fields.instrs[form]
637 for k, value in fields.items():
638 sig = getattr(sf, k)
639 comb += sig.eq(value[0:-1])
640
641 return m
642
643 def ports(self):
644 res = [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
645 for condition in self.conditions.values():
646 res.append(condition)
647 return res
648
649
650 #############################################################
651 # PRIMARY FUNCTION SPECIFYING ALTERNATIVE SVP64 POWER DECODER
652
653 def create_pdecode_svp64_ldst(name=None, col_subset=None, row_subset=None,
654 include_fp=False):
655 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
656
657 subsetting of the PowerOp decoding is possible by setting col_subset
658 """
659 log("create_pdecode_svp64_ldst", name, col_subset, row_subset, include_fp)
660
661 # some alteration to the CSV files is required for SV so we use
662 # a class to do it
663 isa = SVP64RM()
664 get_csv = isa.get_svp64_csv
665
666 # minor opcodes.
667 pminor = [
668 Subdecoder(pattern=58, opcodes=get_csv("svldst_minor_58.csv"),
669 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
670 # nope - needs 4-in regs
671 # Subdecoder(pattern=62, opcodes=get_csv("svldst_minor_62.csv"),
672 # opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
673 ]
674
675 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
676 if False and include_fp:
677 pminor.append(
678 Subdecoder(pattern=63, opcodes=get_csv("minor_63.csv"),
679 opint=False, bitsel=(1, 11), suffix=None,
680 subdecoders=[]),
681 )
682 pminor.append(
683 Subdecoder(pattern=59, opcodes=get_csv("minor_59.csv"),
684 opint=False, bitsel=(1, 11), suffix=None,
685 subdecoders=[]),
686 )
687
688 # top level: extra merged with major
689 dec = []
690 opcodes = get_csv("svldst_major.csv")
691 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
692 bitsel=(26, 32), suffix=None, subdecoders=pminor))
693
694 return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
695 row_subset=row_subset)
696
697
698 ####################################################
699 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
700
701 def create_pdecode(name=None, col_subset=None, row_subset=None,
702 include_fp=False, conditions=None):
703 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
704
705 subsetting of the PowerOp decoding is possible by setting col_subset
706 """
707 log("create_pdecode", name, col_subset, row_subset, include_fp)
708
709 # some alteration to the CSV files is required for SV so we use
710 # a class to do it
711 isa = SVP64RM()
712 get_csv = isa.get_svp64_csv
713
714 # minor 19 has extra patterns
715 m19 = []
716 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
717 opint=True, bitsel=(1, 11), suffix=None,
718 subdecoders=[]))
719 # XXX problem with sub-decoders (can only handle one),
720 # sort this another time
721 # m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
722 # opint=True, bitsel=(1, 6), suffix=None,
723 # subdecoders=[]))
724
725 # minor opcodes.
726 pminor = [
727 m19,
728 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
729 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
730 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
731 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
732 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
733 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
734 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
735 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
736 Subdecoder(pattern=22, opcodes=get_csv("minor_22.csv"),
737 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
738 Subdecoder(pattern=5, opcodes=get_csv("minor_5.csv"),
739 opint=True, bitsel=(0, 11), suffix=None, subdecoders=[]),
740 ]
741
742 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
743 if include_fp:
744 pminor.append(
745 Subdecoder(pattern=63, opcodes=get_csv("minor_63.csv"),
746 opint=False, bitsel=(1, 11), suffix=None,
747 subdecoders=[]),
748 )
749 pminor.append(
750 Subdecoder(pattern=59, opcodes=get_csv("minor_59.csv"),
751 opint=False, bitsel=(1, 11), suffix=None,
752 subdecoders=[]),
753 )
754
755 # top level: extra merged with major
756 dec = []
757 opcodes = get_csv("major.csv")
758 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
759 bitsel=(26, 32), suffix=None, subdecoders=pminor))
760 opcodes = get_csv("extra.csv")
761 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
762 bitsel=(0, 32), suffix=None, subdecoders=[]))
763
764 return TopPowerDecoder(32, dec, name=name, col_subset=col_subset,
765 row_subset=row_subset,
766 conditions=conditions)
767
768 # test function from
769 # https://github.com/apertus-open-source-cinema/naps/blob/9ebbc0/naps/soc/cli.py#L17
770
771
772 def fragment_repr(original):
773 from textwrap import indent
774 attrs_str = "\n"
775 for attr in ['ports', 'drivers', 'statements', 'attrs',
776 'generated', 'flatten']:
777 attrs_str += f"{attr}={repr(getattr(original, attr))},\n"
778
779 domains_str = "\n"
780 for name, domain in original.domains.items():
781 # TODO: this is not really sound because domains could be non local
782 domains_str += f"{name}: {domain.name}\n"
783 attrs_str += f"domains={{{indent(domains_str, ' ')}}},\n"
784
785 children_str = "\n"
786 for child, name in original.subfragments:
787 children_str += f"[{name}, {fragment_repr(child)}]\n"
788 attrs_str += f"children=[{indent(children_str, ' ')}],\n"
789
790 return f"Fragment({indent(attrs_str, ' ')})"
791
792
793 if __name__ == '__main__':
794
795 if True:
796 # row subset
797
798 def rowsubsetfn(opcode, row):
799 log("row_subset", opcode, row)
800 return row['unit'] in ['LDST', 'FPU']
801
802 conditions = {'SVP64BREV': Signal(name="svp64brev", reset_less=True),
803 'SVP64FFT': Signal(name="svp64fft", reset_less=True),
804 }
805 pdecode = create_pdecode(name="rowsub",
806 col_subset={'opcode', 'function_unit',
807 'asmcode',
808 'in2_sel', 'in3_sel'},
809 row_subset=rowsubsetfn,
810 include_fp=True,
811 conditions=conditions)
812 vl = rtlil.convert(pdecode, ports=pdecode.ports())
813 with open("row_subset_decoder.il", "w") as f:
814 f.write(vl)
815
816 vl = verilog.convert(pdecode, ports=pdecode.ports())
817 with open("row_subset_decoder.v", "w") as f:
818 f.write(vl)
819
820 # col subset
821
822 pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'},
823 conditions=conditions)
824 vl = rtlil.convert(pdecode, ports=pdecode.ports())
825 with open("col_subset_decoder.il", "w") as f:
826 f.write(vl)
827
828 from nmigen.hdl.ir import Fragment
829 elaborated = Fragment.get(pdecode, platform=None)
830 elaborated_repr = fragment_repr(elaborated)
831 print(elaborated_repr)
832
833 exit(0)
834
835 exit(0)
836
837 # full decoder
838 pdecode = create_pdecode(include_fp=True)
839 vl = rtlil.convert(pdecode, ports=pdecode.ports())
840 with open("decoder.il", "w") as f:
841 f.write(vl)
842
843 # full SVP64 decoder
844 pdecode = create_pdecode_svp64_ldst(include_fp=True)
845 vl = rtlil.convert(pdecode, ports=pdecode.ports())
846 with open("decoder_svp64.il", "w") as f:
847 f.write(vl)