517748de5a63a9661371ece038016c8059722589
1 """Cascading Power ISA Decoder
5 # Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
6 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
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.
13 This is based on Anton Blanchard's excellent microwatt work:
14 https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
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.
20 Where "normal" HDL would do this, in laborious excruciating detail:
22 switch (opcode & major_mask_bits):
23 case opcode_2: decode_opcode_2()
25 switch (opcode & minor_19_mask_bits)
26 case minor_opcode_19_operation_X:
27 case minor_opcode_19_operation_y:
29 we take *full* advantage of the decoupling between python and the
30 nmigen AST data structure, to do this:
32 with m.Switch(opcode & self.mask):
33 for case_bitmask in subcases:
34 with m.If(opcode & case_bitmask): {do_something}
36 this includes specifying the information sufficient to perform subdecoding.
40 the full hierarchical tree for decoding POWER9 is specified here
41 subsetting is possible by specifying col_subset (row_subset TODO)
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".
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.
59 [ (extra.csv: bit-fields entire 32-bit range
61 000000---------------01000000000 -> ILLEGAL instruction
62 01100000000000000000000000000000 -> SIM_CONFIG instruction
63 ................................ ->
65 (major.csv: first 6 bits ONLY
67 001100 -> ALU,OP_ADD (add)
68 001101 -> ALU,OP_ADD (another type of add)
72 001011 this must match *MAJOR*.CSV
73 [ (minor_19.csv: bits 21 through 30 inclusive:
75 0b0000000000 -> ALU,OP_MCRF
78 (minor_19_00000.csv: bits 21 through 25 inclusive:
80 0b00010 -> ALU,add_pcis
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
104 from openpower
.util
import log
106 # key data structure in which the POWER decoder is specified,
107 # in a hierarchical fashion
108 Subdecoder
= namedtuple( # fix autoformatter
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
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.
120 power_op_types
= {'function_unit': Function
,
121 'internal_op': MicrOp
,
138 'sv_cr_out': SVEXTRA
,
145 power_op_csvmap
= {'function_unit': 'unit',
147 'internal_op': 'internal op',
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',
163 'ldst_len': 'ldst len',
170 def get_pname(field
, pname
):
173 return "%s_%s" % (pname
, field
)
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
182 def __init__(self
, incl_asm
=True, name
=None, subset
=None, fields
=None):
185 if fields
is not None:
186 for k
, v
in fields
.items():
191 for field
, ptype
in power_op_types
.items():
193 if subset
and field
not in subset
:
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
)
201 if subset
and field
not in subset
:
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)
213 """PowerOp.like: creates a duplicate of a given PowerOp instance
216 for fname
in other
._fields
:
217 sig
= getattr(other
, fname
, None)
219 fields
[fname
] = sig
.__class
__.like(sig
)
220 return PowerOp(subset
=other
.subset
, fields
=fields
)
222 def _eq(self
, row
=None):
225 # TODO: this conversion process from a dict to an object
226 # should really be done using e.g. namedtuple and then
228 if False: # debugging
229 if row
['CR in'] == '1':
233 if row
['CR out'] == '0':
238 ldst_mode
= row
['upd']
239 if ldst_mode
.isdigit():
240 row
['upd'] = int(ldst_mode
)
242 for field
, ptype
in power_op_types
.items():
243 if not hasattr(self
, field
):
245 if field
not in power_op_csvmap
:
247 csvname
= power_op_csvmap
[field
]
248 # log(field, ptype, csvname, row)
250 if csvname
== 'upd' and isinstance(val
, int): # LDSTMode different
254 res
.append(getattr(self
, field
).eq(val
))
257 asmcode
= row
['comment']
258 # process the comment field, strip out "equals" for FP
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
):
269 sig
= getattr(self
, field
)
270 res
.append(sig
.eq(int(row
.get(bit
, 0))))
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
))
279 def eq(self
, otherop
):
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
)
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
))
301 class PowerDecoder(Elaboratable
):
302 """PowerDecoder - decodes an incoming opcode into the type of operation
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.
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
318 the reason for the tree is because elaborate can only be called *after*
319 the constructor is called. all quite messy.
322 def __init__(self
, width
, dec
, name
=None, col_subset
=None,
323 row_subset
=None, conditions
=None):
324 if conditions
is None:
326 self
.actually_does_something
= False
328 self
.conditions
= conditions
329 self
.col_subset
= col_subset
330 self
.row_subsetfn
= row_subset
331 if not isinstance(dec
, list):
334 self
.opcode_in
= Signal(width
, reset_less
=True)
336 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
338 if d
.suffix
is not None and d
.suffix
>= width
:
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
348 self
.ckeys
= list(conditions
.keys())
351 for i
, ckey
in enumerate(self
.ckeys
):
352 case
= '-' * len(self
.ckeys
)
354 self
.ccases
[ckey
] = case
355 cswitch
.append(conditions
[ckey
])
356 self
.cswitch
= cswitch
358 def suffix_mask(self
, d
):
359 return ((1 << d
.suffix
) - 1)
361 def divide_opcodes(self
, d
):
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)
370 opcode
= opcode
>> d
.suffix
371 if key
not in divided
:
375 divided
[key
].append(r
)
378 def tree_analyse(self
):
379 self
.decs
= decs
= []
380 self
.submodules
= submodules
= {}
383 # go through the list of CSV decoders first
386 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
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
))
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
398 cases
.append([opc_in
, switch_case
])
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
,
408 col_subset
=self
.col_subset
,
409 row_subset
=self
.row_subsetfn
)
410 if not subdecoder
.tree_analyse():
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
:
422 # TODO: arguments, here (all of them) need to be a list.
423 # a for-loop around the *list* of decoder args.
425 cases
.append([opcode_switch
, switch_case
])
426 seqs
= self
.handle_subdecoders(switch_case
, submodules
, d
)
428 case_does_something
= True
430 for row
in d
.opcodes
:
431 opcode
= row
['opcode']
432 if d
.opint
and '-' not in opcode
:
433 opcode
= int(opcode
, 0)
436 if self
.row_subsetfn
:
437 if not self
.row_subsetfn(opcode
, row
):
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
446 if case_does_something
:
448 #print("submodule eqs", self.pname, eq)
450 #print("submodules", self.pname, submodules)
453 return self
.actually_does_something
455 def handle_subdecoders(self
, switch_case
, submodules
, d
):
457 for dlist
in d
.subdecoders
:
458 if not isinstance(dlist
, list): # XXX HACK: take first pattern
461 #print("subdec", dec.pattern, self.pname)
462 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
463 if mname
in submodules
:
466 assert mname
not in submodules
467 subdecoder
= PowerDecoder(self
.width
, dec
,
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
)
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
483 def elaborate(self
, platform
):
484 #print("decoder elaborate", self.pname, self.submodules)
490 for mname
, subdecoder
in self
.submodules
.items():
491 setattr(m
.submodules
, mname
, subdecoder
)
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():
498 # "conditions" are a further switch statement
499 if isinstance(eqs
, dict):
500 self
.condition_switch(m
, eqs
)
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.
512 with m
.Switch(Cat(*self
.ccswitch
)):
513 for ckey
, eqs
in cases
.items():
514 with m
.Case(self
.ccases
[key
]):
518 return [self
.opcode_in
] + self
.op
.ports()
521 class TopPowerDecoder(PowerDecoder
):
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
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)
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
)
543 # create signals for all field forms
544 forms
= self
.form_names
547 fields
= self
.fields
.instrs
[form
]
549 Fields
= namedtuple("Fields", fk
)
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
)
556 setattr(self
, "Form%s" % form
, instr
)
557 self
.sigforms
[form
] = instr
562 def form_names(self
):
563 return self
.fields
.instrs
.keys()
565 def elaborate(self
, platform
):
566 m
= PowerDecoder
.elaborate(self
, platform
)
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
572 for i
in range(0, self
.width
, 8):
573 l
.append(raw_le
[i
:i
+8])
576 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
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])
583 # link signals for all field forms
584 forms
= self
.form_names
586 sf
= self
.sigforms
[form
]
587 fields
= self
.fields
.instrs
[form
]
588 for k
, value
in fields
.items():
590 comb
+= sig
.eq(value
[0:-1])
595 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
598 #############################################################
599 # PRIMARY FUNCTION SPECIFYING ALTERNATIVE SVP64 POWER DECODER
601 def create_pdecode_svp64_ldst(name
=None, col_subset
=None, row_subset
=None,
603 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
605 subsetting of the PowerOp decoding is possible by setting col_subset
607 log ("create_pdecode_svp64_ldst", name
, col_subset
, row_subset
, include_fp
)
609 # some alteration to the CSV files is required for SV so we use
612 get_csv
= isa
.get_svp64_csv
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=[]),
623 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
624 if False and include_fp
:
626 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
627 opint
=False, bitsel
=(1, 11), suffix
=None,
631 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
632 opint
=False, bitsel
=(1, 11), suffix
=None,
636 # top level: extra merged with major
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
))
642 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
643 row_subset
=row_subset
)
646 ####################################################
647 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
649 def create_pdecode(name
=None, col_subset
=None, row_subset
=None,
651 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
653 subsetting of the PowerOp decoding is possible by setting col_subset
655 log ("create_pdecode", name
, col_subset
, row_subset
, include_fp
)
657 # some alteration to the CSV files is required for SV so we use
660 get_csv
= isa
.get_svp64_csv
662 # minor 19 has extra patterns
664 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
665 opint
=True, bitsel
=(1, 11), suffix
=None,
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,
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
=[]),
688 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
691 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
692 opint
=False, bitsel
=(1, 11), suffix
=None,
696 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
697 opint
=False, bitsel
=(1, 11), suffix
=None,
701 # top level: extra merged with major
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
=[]))
710 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
711 row_subset
=row_subset
)
714 if __name__
== '__main__':
719 def rowsubsetfn(opcode
, row
):
720 log("row_subset", opcode
, row
)
721 return row
['unit'] == 'FPU'
723 pdecode
= create_pdecode(name
="rowsub",
724 col_subset
={'opcode', 'function_unit',
726 row_subset
=rowsubsetfn
,
728 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
729 with
open("row_subset_decoder.il", "w") as f
:
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
:
740 pdecode
= create_pdecode(include_fp
=True)
741 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
742 with
open("decoder.il", "w") as f
:
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
: