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