1682941e1047afd5d442a904277aa013c29729ba
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:
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)
214 for fname
in other
._fields
:
215 sig
= getattr(other
, fname
)
216 fields
[fname
] = sig
.__class
__.like(sig
)
217 return PowerOp(subset
=other
.subset
, fields
=fields
)
219 def _eq(self
, row
=None):
222 # TODO: this conversion process from a dict to an object
223 # should really be done using e.g. namedtuple and then
225 if False: # debugging
226 if row
['CR in'] == '1':
230 if row
['CR out'] == '0':
235 ldst_mode
= row
['upd']
236 if ldst_mode
.isdigit():
237 row
['upd'] = int(ldst_mode
)
239 for field
, ptype
in power_op_types
.items():
240 if not hasattr(self
, field
):
242 if field
not in power_op_csvmap
:
244 csvname
= power_op_csvmap
[field
]
245 # log(field, ptype, csvname, row)
247 if csvname
== 'upd' and isinstance(val
, int): # LDSTMode different
251 res
.append(getattr(self
, field
).eq(val
))
254 asmcode
= row
['comment']
255 # process the comment field, strip out "equals" for FP
257 asmcode
= asmcode
.split("=")[-1]
258 log ("asmcode stripping =", asmcode
,
259 asmcode
in asmidx
, hasattr(self
, "asmcode"))
260 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
261 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
262 for bit
in single_bit_flags
:
263 field
= get_signal_name(bit
)
264 if not hasattr(self
, field
):
266 sig
= getattr(self
, field
)
267 res
.append(sig
.eq(int(row
.get(bit
, 0))))
270 def _get_eq(self
, res
, field
, otherop
):
271 copyfrom
= getattr(otherop
, field
, None)
272 copyto
= getattr(self
, field
, None)
273 if copyfrom
is not None and copyto
is not None:
274 res
.append(copyto
.eq(copyfrom
))
276 def eq(self
, otherop
):
278 for field
in power_op_types
.keys():
279 self
._get
_eq
(res
, field
, otherop
)
280 for bit
in single_bit_flags
:
281 self
._get
_eq
(res
, get_signal_name(bit
), otherop
)
286 for field
in power_op_types
.keys():
287 if hasattr(self
, field
):
288 res
.append(getattr(self
, field
))
289 if hasattr(self
, "asmcode"):
290 res
.append(self
.asmcode
)
291 for field
in single_bit_flags
:
292 field
= get_signal_name(field
)
293 if hasattr(self
, field
):
294 res
.append(getattr(self
, field
))
298 class PowerDecoder(Elaboratable
):
299 """PowerDecoder - decodes an incoming opcode into the type of operation
301 this is a recursive algorithm, creating Switch statements that can
302 have further match-and-decode on other parts of the opcode field before
303 finally landing at a "this CSV entry details gets returned" thing.
305 the complicating factor is the row and col subsetting. column subsetting
306 dynamically chooses only the CSV columns requested, whilst row subsetting
307 allows a function to be called on the row to determine if the Case
308 statement is to be generated for that row. this not only generates
309 completely different Decoders, it also means that some sub-decoders
310 will turn up blank (empty switch statements). if that happens we do
311 not want the parent to include a Mux for an entirely blank switch statement
312 so we have to store the switch/case statements in a tree, and
315 the reason for the tree is because elaborate can only be called *after*
316 the constructor is called. all quite messy.
319 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
320 self
.actually_does_something
= False
322 self
.col_subset
= col_subset
323 self
.row_subsetfn
= row_subset
324 if not isinstance(dec
, list):
327 self
.opcode_in
= Signal(width
, reset_less
=True)
329 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
331 if d
.suffix
is not None and d
.suffix
>= width
:
335 def suffix_mask(self
, d
):
336 return ((1 << d
.suffix
) - 1)
338 def divide_opcodes(self
, d
):
340 mask
= self
.suffix_mask(d
)
341 #print("mask", hex(mask))
342 for row
in d
.opcodes
:
343 opcode
= row
['opcode']
344 if d
.opint
and '-' not in opcode
:
345 opcode
= int(opcode
, 0)
347 opcode
= opcode
>> d
.suffix
348 if key
not in divided
:
352 divided
[key
].append(r
)
355 def tree_analyse(self
):
356 self
.decs
= decs
= []
357 self
.submodules
= submodules
= {}
360 # go through the list of CSV decoders first
363 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
366 case_does_something
= False
367 eq
.append(opcode_switch
.eq(
368 self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]]))
370 opcodes
= self
.divide_opcodes(d
)
371 opc_in
= Signal(d
.suffix
, reset_less
=True)
372 eq
.append(opc_in
.eq(opcode_switch
[:d
.suffix
]))
373 # begin the dynamic Switch statement here
375 cases
.append([opc_in
, switch_case
])
377 for key
, row
in opcodes
.items():
378 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
379 sd
= Subdecoder(pattern
=None, opcodes
=row
,
380 bitsel
=bitsel
, suffix
=None,
381 opint
=False, subdecoders
=[])
382 mname
= get_pname("dec_sub%d" % key
, self
.pname
)
383 subdecoder
= PowerDecoder(width
=32, dec
=sd
,
385 col_subset
=self
.col_subset
,
386 row_subset
=self
.row_subsetfn
)
387 if not subdecoder
.tree_analyse():
390 submodules
[mname
] = subdecoder
391 sub_eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
392 # add in the dynamic Case statement here
393 switch_case
[key
] = self
.op
.eq(subdecoder
.op
)
394 self
.actually_does_something
= True
395 case_does_something
= True
396 if case_does_something
:
399 # TODO: arguments, here (all of them) need to be a list.
400 # a for-loop around the *list* of decoder args.
402 cases
.append([opcode_switch
, switch_case
])
403 seqs
= self
.handle_subdecoders(switch_case
, submodules
, d
)
405 case_does_something
= True
407 for row
in d
.opcodes
:
408 opcode
= row
['opcode']
409 if d
.opint
and '-' not in opcode
:
410 opcode
= int(opcode
, 0)
413 if self
.row_subsetfn
:
414 if not self
.row_subsetfn(opcode
, row
):
416 # add in the dynamic Case statement here
417 switch_case
[opcode
] = self
.op
._eq
(row
)
418 self
.actually_does_something
= True
419 case_does_something
= True
423 if case_does_something
:
425 #print("submodule eqs", self.pname, eq)
427 #print("submodules", self.pname, submodules)
430 return self
.actually_does_something
432 def handle_subdecoders(self
, switch_case
, submodules
, d
):
434 for dlist
in d
.subdecoders
:
435 if not isinstance(dlist
, list): # XXX HACK: take first pattern
438 #print("subdec", dec.pattern, self.pname)
439 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
440 if mname
in submodules
:
443 assert mname
not in submodules
444 subdecoder
= PowerDecoder(self
.width
, dec
,
446 col_subset
=self
.col_subset
,
447 row_subset
=self
.row_subsetfn
)
448 log ("subdecoder", mname
, subdecoder
)
449 if not subdecoder
.tree_analyse(): # doesn't do anything
450 log ("analysed, DELETING", mname
)
453 submodules
[mname
] = subdecoder
454 eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
455 switch_case
[dec
.pattern
] = self
.op
.eq(subdecoder
.op
)
456 self
.actually_does_something
= True
460 def elaborate(self
, platform
):
461 #print("decoder elaborate", self.pname, self.submodules)
467 for mname
, subdecoder
in self
.submodules
.items():
468 setattr(m
.submodules
, mname
, subdecoder
)
470 for switch_case
in self
.decs
:
471 for (switch
, cases
) in switch_case
:
472 with m
.Switch(switch
):
473 for key
, eqs
in cases
.items():
479 return [self
.opcode_in
] + self
.op
.ports()
482 class TopPowerDecoder(PowerDecoder
):
485 top-level hierarchical decoder for POWER ISA
486 bigendian dynamically switches between big and little endian decoding
487 (reverses byte order). See V3.0B p44 1.11.2
490 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
491 PowerDecoder
.__init
__(self
, width
, dec
, name
, col_subset
, row_subset
)
492 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
493 self
.fields
.create_specs()
494 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
495 self
.bigendian
= Signal(reset_less
=True)
497 for fname
, value
in self
.fields
.common_fields
.items():
498 signame
= get_pname(fname
, name
)
499 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=signame
)
500 setattr(self
, fname
, sig
)
502 # create signals for all field forms
503 forms
= self
.form_names
506 fields
= self
.fields
.instrs
[form
]
508 Fields
= namedtuple("Fields", fk
)
510 for k
, value
in fields
.items():
511 fname
= "%s_%s" % (form
, k
)
512 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=fname
)
515 setattr(self
, "Form%s" % form
, instr
)
516 self
.sigforms
[form
] = instr
521 def form_names(self
):
522 return self
.fields
.instrs
.keys()
524 def elaborate(self
, platform
):
525 m
= PowerDecoder
.elaborate(self
, platform
)
527 # sigh duplicated in SVP64PowerDecoder
528 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
529 raw_le
= self
.raw_opcode_in
531 for i
in range(0, self
.width
, 8):
532 l
.append(raw_le
[i
:i
+8])
535 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
537 # add all signal from commonly-used fields
538 for fname
, value
in self
.fields
.common_fields
.items():
539 sig
= getattr(self
, fname
)
540 comb
+= sig
.eq(value
[0:-1])
542 # link signals for all field forms
543 forms
= self
.form_names
545 sf
= self
.sigforms
[form
]
546 fields
= self
.fields
.instrs
[form
]
547 for k
, value
in fields
.items():
549 comb
+= sig
.eq(value
[0:-1])
554 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
557 #############################################################
558 # PRIMARY FUNCTION SPECIFYING ALTERNATIVE SVP64 POWER DECODER
560 def create_pdecode_svp64(name
=None, col_subset
=None, row_subset
=None,
562 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
564 subsetting of the PowerOp decoding is possible by setting col_subset
566 log ("create_pdecode_svp64", name
, col_subset
, row_subset
, include_fp
)
568 # some alteration to the CSV files is required for SV so we use
571 get_csv
= isa
.get_svp64_csv
575 Subdecoder(pattern
=58, opcodes
=get_csv("svldst_minor_58.csv"),
576 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
577 # nope - needs 4-in regs
578 #Subdecoder(pattern=62, opcodes=get_csv("svldst_minor_62.csv"),
579 # opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
582 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
583 if False and include_fp
:
585 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
586 opint
=False, bitsel
=(1, 11), suffix
=None,
590 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
591 opint
=False, bitsel
=(1, 11), suffix
=None,
595 # top level: extra merged with major
597 opcodes
= get_csv("svldst_major.csv")
598 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
599 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
601 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
602 row_subset
=row_subset
)
605 ####################################################
606 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
608 def create_pdecode(name
=None, col_subset
=None, row_subset
=None,
610 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
612 subsetting of the PowerOp decoding is possible by setting col_subset
614 log ("create_pdecode", name
, col_subset
, row_subset
, include_fp
)
616 # some alteration to the CSV files is required for SV so we use
619 get_csv
= isa
.get_svp64_csv
621 # minor 19 has extra patterns
623 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
624 opint
=True, bitsel
=(1, 11), suffix
=None,
626 # XXX problem with sub-decoders (can only handle one),
627 # sort this another time
628 #m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
629 # opint=True, bitsel=(1, 6), suffix=None,
635 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
636 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
637 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
638 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
639 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
640 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
641 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
642 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
643 Subdecoder(pattern
=22, opcodes
=get_csv("minor_22.csv"),
644 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
647 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
650 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
651 opint
=False, bitsel
=(1, 11), suffix
=None,
655 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
656 opint
=False, bitsel
=(1, 11), suffix
=None,
660 # top level: extra merged with major
662 opcodes
= get_csv("major.csv")
663 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
664 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
665 opcodes
= get_csv("extra.csv")
666 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
667 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
669 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
670 row_subset
=row_subset
)
673 if __name__
== '__main__':
678 def rowsubsetfn(opcode
, row
):
679 log("row_subset", opcode
, row
)
680 return row
['unit'] == 'FPU'
682 pdecode
= create_pdecode(name
="rowsub",
683 col_subset
={'opcode', 'function_unit',
685 row_subset
=rowsubsetfn
,
687 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
688 with
open("row_subset_decoder.il", "w") as f
:
693 pdecode
= create_pdecode(name
="fusubset", col_subset
={'function_unit'})
694 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
695 with
open("col_subset_decoder.il", "w") as f
:
699 pdecode
= create_pdecode(include_fp
=True)
700 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
701 with
open("decoder.il", "w") as f
:
705 pdecode
= create_pdecode_svp64(include_fp
=True)
706 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
707 with
open("decoder_svp64.il", "w") as f
: