8d7fcfa921b1dbbee1e5dc65303338818861bd16
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, row_subset
=None):
323 self
.actually_does_something
= False
325 self
.col_subset
= col_subset
326 self
.row_subsetfn
= row_subset
327 if not isinstance(dec
, list):
330 self
.opcode_in
= Signal(width
, reset_less
=True)
332 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
334 if d
.suffix
is not None and d
.suffix
>= width
:
338 def suffix_mask(self
, d
):
339 return ((1 << d
.suffix
) - 1)
341 def divide_opcodes(self
, d
):
343 mask
= self
.suffix_mask(d
)
344 #print("mask", hex(mask))
345 for row
in d
.opcodes
:
346 opcode
= row
['opcode']
347 if d
.opint
and '-' not in opcode
:
348 opcode
= int(opcode
, 0)
350 opcode
= opcode
>> d
.suffix
351 if key
not in divided
:
355 divided
[key
].append(r
)
358 def tree_analyse(self
):
359 self
.decs
= decs
= []
360 self
.submodules
= submodules
= {}
363 # go through the list of CSV decoders first
366 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
369 case_does_something
= False
370 look_for
= self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]]
371 eq
.append(opcode_switch
.eq(look_for
))
373 opcodes
= self
.divide_opcodes(d
)
374 opc_in
= Signal(d
.suffix
, reset_less
=True)
375 eq
.append(opc_in
.eq(opcode_switch
[:d
.suffix
]))
376 # begin the dynamic Switch statement here
378 cases
.append([opc_in
, switch_case
])
380 for key
, row
in opcodes
.items():
381 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
382 sd
= Subdecoder(pattern
=None, opcodes
=row
,
383 bitsel
=bitsel
, suffix
=None,
384 opint
=False, subdecoders
=[])
385 mname
= get_pname("dec_sub%d" % key
, self
.pname
)
386 subdecoder
= PowerDecoder(width
=32, dec
=sd
,
388 col_subset
=self
.col_subset
,
389 row_subset
=self
.row_subsetfn
)
390 if not subdecoder
.tree_analyse():
393 submodules
[mname
] = subdecoder
394 sub_eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
395 # add in the dynamic Case statement here
396 switch_case
[key
] = self
.op
.eq(subdecoder
.op
)
397 self
.actually_does_something
= True
398 case_does_something
= True
399 if case_does_something
:
402 # TODO: arguments, here (all of them) need to be a list.
403 # a for-loop around the *list* of decoder args.
405 cases
.append([opcode_switch
, switch_case
])
406 seqs
= self
.handle_subdecoders(switch_case
, submodules
, d
)
408 case_does_something
= True
410 for row
in d
.opcodes
:
411 opcode
= row
['opcode']
412 if d
.opint
and '-' not in opcode
:
413 opcode
= int(opcode
, 0)
416 if self
.row_subsetfn
:
417 if not self
.row_subsetfn(opcode
, row
):
419 # add in the dynamic Case statement here
420 switch_case
[opcode
] = self
.op
._eq
(row
)
421 self
.actually_does_something
= True
422 case_does_something
= True
426 if case_does_something
:
428 #print("submodule eqs", self.pname, eq)
430 #print("submodules", self.pname, submodules)
433 return self
.actually_does_something
435 def handle_subdecoders(self
, switch_case
, submodules
, d
):
437 for dlist
in d
.subdecoders
:
438 if not isinstance(dlist
, list): # XXX HACK: take first pattern
441 #print("subdec", dec.pattern, self.pname)
442 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
443 if mname
in submodules
:
446 assert mname
not in submodules
447 subdecoder
= PowerDecoder(self
.width
, dec
,
449 col_subset
=self
.col_subset
,
450 row_subset
=self
.row_subsetfn
)
451 log ("subdecoder", mname
, subdecoder
)
452 if not subdecoder
.tree_analyse(): # doesn't do anything
453 log ("analysed, DELETING", mname
)
456 submodules
[mname
] = subdecoder
457 eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
458 switch_case
[dec
.pattern
] = self
.op
.eq(subdecoder
.op
)
459 self
.actually_does_something
= True
463 def elaborate(self
, platform
):
464 #print("decoder elaborate", self.pname, self.submodules)
470 for mname
, subdecoder
in self
.submodules
.items():
471 setattr(m
.submodules
, mname
, subdecoder
)
473 for switch_case
in self
.decs
:
474 for (switch
, cases
) in switch_case
:
475 with m
.Switch(switch
):
476 for key
, eqs
in cases
.items():
482 return [self
.opcode_in
] + self
.op
.ports()
485 class TopPowerDecoder(PowerDecoder
):
488 top-level hierarchical decoder for POWER ISA
489 bigendian dynamically switches between big and little endian decoding
490 (reverses byte order). See V3.0B p44 1.11.2
493 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
494 PowerDecoder
.__init
__(self
, width
, dec
, name
, col_subset
, row_subset
)
495 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
496 self
.fields
.create_specs()
497 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
498 self
.bigendian
= Signal(reset_less
=True)
500 for fname
, value
in self
.fields
.common_fields
.items():
501 signame
= get_pname(fname
, name
)
502 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=signame
)
503 setattr(self
, fname
, sig
)
505 # create signals for all field forms
506 forms
= self
.form_names
509 fields
= self
.fields
.instrs
[form
]
511 Fields
= namedtuple("Fields", fk
)
513 for k
, value
in fields
.items():
514 fname
= "%s_%s" % (form
, k
)
515 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=fname
)
518 setattr(self
, "Form%s" % form
, instr
)
519 self
.sigforms
[form
] = instr
524 def form_names(self
):
525 return self
.fields
.instrs
.keys()
527 def elaborate(self
, platform
):
528 m
= PowerDecoder
.elaborate(self
, platform
)
530 # sigh duplicated in SVP64PowerDecoder
531 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
532 raw_le
= self
.raw_opcode_in
534 for i
in range(0, self
.width
, 8):
535 l
.append(raw_le
[i
:i
+8])
538 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
540 # add all signal from commonly-used fields
541 for fname
, value
in self
.fields
.common_fields
.items():
542 sig
= getattr(self
, fname
)
543 comb
+= sig
.eq(value
[0:-1])
545 # link signals for all field forms
546 forms
= self
.form_names
548 sf
= self
.sigforms
[form
]
549 fields
= self
.fields
.instrs
[form
]
550 for k
, value
in fields
.items():
552 comb
+= sig
.eq(value
[0:-1])
557 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
560 #############################################################
561 # PRIMARY FUNCTION SPECIFYING ALTERNATIVE SVP64 POWER DECODER
563 def create_pdecode_svp64_ldst(name
=None, col_subset
=None, row_subset
=None,
565 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
567 subsetting of the PowerOp decoding is possible by setting col_subset
569 log ("create_pdecode_svp64_ldst", name
, col_subset
, row_subset
, include_fp
)
571 # some alteration to the CSV files is required for SV so we use
574 get_csv
= isa
.get_svp64_csv
578 Subdecoder(pattern
=58, opcodes
=get_csv("svldst_minor_58.csv"),
579 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
580 # nope - needs 4-in regs
581 #Subdecoder(pattern=62, opcodes=get_csv("svldst_minor_62.csv"),
582 # opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
585 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
586 if False and include_fp
:
588 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
589 opint
=False, bitsel
=(1, 11), suffix
=None,
593 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
594 opint
=False, bitsel
=(1, 11), suffix
=None,
598 # top level: extra merged with major
600 opcodes
= get_csv("svldst_major.csv")
601 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
602 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
604 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
605 row_subset
=row_subset
)
608 ####################################################
609 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
611 def create_pdecode(name
=None, col_subset
=None, row_subset
=None,
613 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
615 subsetting of the PowerOp decoding is possible by setting col_subset
617 log ("create_pdecode", name
, col_subset
, row_subset
, include_fp
)
619 # some alteration to the CSV files is required for SV so we use
622 get_csv
= isa
.get_svp64_csv
624 # minor 19 has extra patterns
626 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
627 opint
=True, bitsel
=(1, 11), suffix
=None,
629 # XXX problem with sub-decoders (can only handle one),
630 # sort this another time
631 #m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
632 # opint=True, bitsel=(1, 6), suffix=None,
638 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
639 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
640 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
641 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
642 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
643 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
644 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
645 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
646 Subdecoder(pattern
=22, opcodes
=get_csv("minor_22.csv"),
647 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
650 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
653 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
654 opint
=False, bitsel
=(1, 11), suffix
=None,
658 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
659 opint
=False, bitsel
=(1, 11), suffix
=None,
663 # top level: extra merged with major
665 opcodes
= get_csv("major.csv")
666 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
667 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
668 opcodes
= get_csv("extra.csv")
669 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
670 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
672 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
673 row_subset
=row_subset
)
676 if __name__
== '__main__':
681 def rowsubsetfn(opcode
, row
):
682 log("row_subset", opcode
, row
)
683 return row
['unit'] == 'FPU'
685 pdecode
= create_pdecode(name
="rowsub",
686 col_subset
={'opcode', 'function_unit',
688 row_subset
=rowsubsetfn
,
690 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
691 with
open("row_subset_decoder.il", "w") as f
:
696 pdecode
= create_pdecode(name
="fusubset", col_subset
={'function_unit'})
697 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
698 with
open("col_subset_decoder.il", "w") as f
:
702 pdecode
= create_pdecode(include_fp
=True)
703 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
704 with
open("decoder.il", "w") as f
:
708 pdecode
= create_pdecode_svp64_ldst(include_fp
=True)
709 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
710 with
open("decoder_svp64.il", "w") as f
: