2ac2e468daf47840fbbd1bd71769deb9fca1b5ad
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 # key data structure in which the POWER decoder is specified,
105 # in a hierarchical fashion
106 Subdecoder
= namedtuple( # fix autoformatter
108 ["pattern", # the major pattern to search for (e.g. major opcode)
109 "opcodes", # a dictionary of minor patterns to find
110 "opint", # true => the pattern must not be in "10----11" format
111 # the bits (as a range) against which "pattern" matches
113 "suffix", # shift the opcode down before decoding
114 "subdecoders" # list of further subdecoders for *additional* matches,
115 # *ONLY* after "pattern" has *ALSO* been matched against.
118 power_op_types
= {'function_unit': Function
,
119 'internal_op': MicrOp
,
136 'sv_cr_out': SVEXTRA
,
143 power_op_csvmap
= {'function_unit': 'unit',
145 'internal_op': 'internal op',
154 'sv_out2': 'sv_out2',
155 'sv_cr_in': 'sv_cr_in',
156 'sv_cr_out': 'sv_cr_out',
157 'SV_Etype': 'SV_Etype',
158 'SV_Ptype': 'SV_Ptype',
161 'ldst_len': 'ldst len',
168 def get_pname(field
, pname
):
171 return "%s_%s" % (pname
, field
)
175 """PowerOp - a dynamic class that stores (subsets of) CSV rows of data
176 about a PowerISA instruction. this is a "micro-code" expanded format
177 which generates an awful lot of wires, hence the subsetting
180 def __init__(self
, incl_asm
=True, name
=None, subset
=None):
184 for field
, ptype
in power_op_types
.items():
186 if subset
and field
not in subset
:
188 fname
= get_pname(field
, name
)
189 setattr(self
, field
, Signal(ptype
, reset_less
=True, name
=fname
))
190 debug_report
.add(field
)
191 for bit
in single_bit_flags
:
192 field
= get_signal_name(bit
)
194 if subset
and field
not in subset
:
196 debug_report
.add(field
)
197 fname
= get_pname(field
, name
)
198 setattr(self
, field
, Signal(reset_less
=True, name
=fname
))
199 # comment out, bit too high debug level
200 #print("PowerOp debug", name, debug_report)
201 #print(" fields", fields)
203 def _eq(self
, row
=None):
206 # TODO: this conversion process from a dict to an object
207 # should really be done using e.g. namedtuple and then
209 if False: # debugging
210 if row
['CR in'] == '1':
214 if row
['CR out'] == '0':
219 ldst_mode
= row
['upd']
220 if ldst_mode
.isdigit():
221 row
['upd'] = int(ldst_mode
)
223 for field
, ptype
in power_op_types
.items():
224 if not hasattr(self
, field
):
226 if field
not in power_op_csvmap
:
228 csvname
= power_op_csvmap
[field
]
229 #print(field, ptype, csvname, row)
231 if csvname
== 'upd' and isinstance(val
, int): # LDSTMode different
235 res
.append(getattr(self
, field
).eq(val
))
238 asmcode
= row
['comment']
239 # process the comment field, strip out "equals" for FP
241 asmcode
= asmcode
.split("=")[-1]
242 print ("asmcode stripping =", asmcode
,
243 asmcode
in asmidx
, hasattr(self
, "asmcode"))
244 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
245 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
246 for bit
in single_bit_flags
:
247 field
= get_signal_name(bit
)
248 if not hasattr(self
, field
):
250 sig
= getattr(self
, field
)
251 res
.append(sig
.eq(int(row
.get(bit
, 0))))
254 def _get_eq(self
, res
, field
, otherop
):
255 copyfrom
= getattr(otherop
, field
, None)
256 copyto
= getattr(self
, field
, None)
257 if copyfrom
is not None and copyto
is not None:
258 res
.append(copyto
.eq(copyfrom
))
260 def eq(self
, otherop
):
262 for field
in power_op_types
.keys():
263 self
._get
_eq
(res
, field
, otherop
)
264 for bit
in single_bit_flags
:
265 self
._get
_eq
(res
, get_signal_name(bit
), otherop
)
270 for field
in power_op_types
.keys():
271 if hasattr(self
, field
):
272 res
.append(getattr(self
, field
))
273 if hasattr(self
, "asmcode"):
274 res
.append(self
.asmcode
)
275 for field
in single_bit_flags
:
276 field
= get_signal_name(field
)
277 if hasattr(self
, field
):
278 res
.append(getattr(self
, field
))
282 class PowerDecoder(Elaboratable
):
283 """PowerDecoder - decodes an incoming opcode into the type of operation
285 this is a recursive algorithm, creating Switch statements that can
286 have further match-and-decode on other parts of the opcode field before
287 finally landing at a "this CSV entry details gets returned" thing.
289 the complicating factor is the row and col subsetting. column subsetting
290 dynamically chooses only the CSV columns requested, whilst row subsetting
291 allows a function to be called on the row to determine if the Case
292 statement is to be generated for that row. this not only generates
293 completely different Decoders, it also means that some sub-decoders
294 will turn up blank (empty switch statements). if that happens we do
295 not want the parent to include a Mux for an entirely blank switch statement
296 so we have to store the switch/case statements in a tree, and
299 the reason for the tree is because elaborate can only be called *after*
300 the constructor is called. all quite messy.
303 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
304 self
.actually_does_something
= False
306 self
.col_subset
= col_subset
307 self
.row_subsetfn
= row_subset
308 if not isinstance(dec
, list):
311 self
.opcode_in
= Signal(width
, reset_less
=True)
313 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
315 if d
.suffix
is not None and d
.suffix
>= width
:
319 def suffix_mask(self
, d
):
320 return ((1 << d
.suffix
) - 1)
322 def divide_opcodes(self
, d
):
324 mask
= self
.suffix_mask(d
)
325 #print("mask", hex(mask))
326 for row
in d
.opcodes
:
327 opcode
= row
['opcode']
328 if d
.opint
and '-' not in opcode
:
329 opcode
= int(opcode
, 0)
331 opcode
= opcode
>> d
.suffix
332 if key
not in divided
:
336 divided
[key
].append(r
)
339 def tree_analyse(self
):
340 self
.decs
= decs
= []
341 self
.submodules
= submodules
= {}
344 # go through the list of CSV decoders first
347 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
350 case_does_something
= False
351 eq
.append(opcode_switch
.eq(
352 self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]]))
354 opcodes
= self
.divide_opcodes(d
)
355 opc_in
= Signal(d
.suffix
, reset_less
=True)
356 eq
.append(opc_in
.eq(opcode_switch
[:d
.suffix
]))
357 # begin the dynamic Switch statement here
359 cases
.append([opc_in
, switch_case
])
361 for key
, row
in opcodes
.items():
362 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
363 sd
= Subdecoder(pattern
=None, opcodes
=row
,
364 bitsel
=bitsel
, suffix
=None,
365 opint
=False, subdecoders
=[])
366 mname
= get_pname("dec_sub%d" % key
, self
.pname
)
367 subdecoder
= PowerDecoder(width
=32, dec
=sd
,
369 col_subset
=self
.col_subset
,
370 row_subset
=self
.row_subsetfn
)
371 if not subdecoder
.tree_analyse():
374 submodules
[mname
] = subdecoder
375 sub_eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
376 # add in the dynamic Case statement here
377 switch_case
[key
] = self
.op
.eq(subdecoder
.op
)
378 self
.actually_does_something
= True
379 case_does_something
= True
380 if case_does_something
:
383 # TODO: arguments, here (all of them) need to be a list.
384 # a for-loop around the *list* of decoder args.
386 cases
.append([opcode_switch
, switch_case
])
387 seqs
= self
.handle_subdecoders(switch_case
, submodules
, d
)
389 case_does_something
= True
391 for row
in d
.opcodes
:
392 opcode
= row
['opcode']
393 if d
.opint
and '-' not in opcode
:
394 opcode
= int(opcode
, 0)
397 if self
.row_subsetfn
:
398 if not self
.row_subsetfn(opcode
, row
):
400 # add in the dynamic Case statement here
401 switch_case
[opcode
] = self
.op
._eq
(row
)
402 self
.actually_does_something
= True
403 case_does_something
= True
407 if case_does_something
:
409 #print("submodule eqs", self.pname, eq)
411 #print("submodules", self.pname, submodules)
414 return self
.actually_does_something
416 def handle_subdecoders(self
, switch_case
, submodules
, d
):
418 for dlist
in d
.subdecoders
:
419 if not isinstance(dlist
, list): # XXX HACK: take first pattern
422 #print("subdec", dec.pattern, self.pname)
423 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
424 if mname
in submodules
:
427 assert mname
not in submodules
428 subdecoder
= PowerDecoder(self
.width
, dec
,
430 col_subset
=self
.col_subset
,
431 row_subset
=self
.row_subsetfn
)
432 print ("subdecoder", mname
, subdecoder
)
433 if not subdecoder
.tree_analyse(): # doesn't do anything
434 print ("analysed, DELETING", mname
)
437 submodules
[mname
] = subdecoder
438 eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
439 switch_case
[dec
.pattern
] = self
.op
.eq(subdecoder
.op
)
440 self
.actually_does_something
= True
444 def elaborate(self
, platform
):
445 #print("decoder elaborate", self.pname, self.submodules)
451 for mname
, subdecoder
in self
.submodules
.items():
452 setattr(m
.submodules
, mname
, subdecoder
)
454 for switch_case
in self
.decs
:
455 for (switch
, cases
) in switch_case
:
456 with m
.Switch(switch
):
457 for key
, eqs
in cases
.items():
463 return [self
.opcode_in
] + self
.op
.ports()
466 class TopPowerDecoder(PowerDecoder
):
469 top-level hierarchical decoder for POWER ISA
470 bigendian dynamically switches between big and little endian decoding
471 (reverses byte order). See V3.0B p44 1.11.2
474 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
475 PowerDecoder
.__init
__(self
, width
, dec
, name
, col_subset
, row_subset
)
476 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
477 self
.fields
.create_specs()
478 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
479 self
.bigendian
= Signal(reset_less
=True)
481 for fname
, value
in self
.fields
.common_fields
.items():
482 signame
= get_pname(fname
, name
)
483 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=signame
)
484 setattr(self
, fname
, sig
)
486 # create signals for all field forms
487 forms
= self
.form_names
490 fields
= self
.fields
.instrs
[form
]
492 Fields
= namedtuple("Fields", fk
)
494 for k
, value
in fields
.items():
495 fname
= "%s_%s" % (form
, k
)
496 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=fname
)
499 setattr(self
, "Form%s" % form
, instr
)
500 self
.sigforms
[form
] = instr
505 def form_names(self
):
506 return self
.fields
.instrs
.keys()
508 def elaborate(self
, platform
):
509 m
= PowerDecoder
.elaborate(self
, platform
)
511 # sigh duplicated in SVP64PowerDecoder
512 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
513 raw_le
= self
.raw_opcode_in
515 for i
in range(0, self
.width
, 8):
516 l
.append(raw_le
[i
:i
+8])
519 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
521 # add all signal from commonly-used fields
522 for fname
, value
in self
.fields
.common_fields
.items():
523 sig
= getattr(self
, fname
)
524 comb
+= sig
.eq(value
[0:-1])
526 # link signals for all field forms
527 forms
= self
.form_names
529 sf
= self
.sigforms
[form
]
530 fields
= self
.fields
.instrs
[form
]
531 for k
, value
in fields
.items():
533 comb
+= sig
.eq(value
[0:-1])
538 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
541 ####################################################
542 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
544 def create_pdecode(name
=None, col_subset
=None, row_subset
=None,
546 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
548 subsetting of the PowerOp decoding is possible by setting col_subset
550 print ("create_pdecode", name
, col_subset
, row_subset
, include_fp
)
552 # some alteration to the CSV files is required for SV so we use
555 get_csv
= isa
.get_svp64_csv
557 # minor 19 has extra patterns
559 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
560 opint
=True, bitsel
=(1, 11), suffix
=None,
562 # XXX problem with sub-decoders (can only handle one),
563 # sort this another time
564 #m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
565 # opint=True, bitsel=(1, 6), suffix=None,
571 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
572 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
573 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
574 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
575 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
576 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
577 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
578 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
579 Subdecoder(pattern
=22, opcodes
=get_csv("minor_22.csv"),
580 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
583 # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder
586 Subdecoder(pattern
=63, opcodes
=get_csv("minor_63.csv"),
587 opint
=False, bitsel
=(1, 11), suffix
=None,
591 Subdecoder(pattern
=59, opcodes
=get_csv("minor_59.csv"),
592 opint
=False, bitsel
=(1, 11), suffix
=None,
596 # top level: extra merged with major
598 opcodes
= get_csv("major.csv")
599 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
600 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
601 opcodes
= get_csv("extra.csv")
602 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
603 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
605 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
606 row_subset
=row_subset
)
609 if __name__
== '__main__':
614 def rowsubsetfn(opcode
, row
):
615 print("row_subset", opcode
, row
)
616 return row
['unit'] == 'FPU'
618 pdecode
= create_pdecode(name
="rowsub",
619 col_subset
={'opcode', 'function_unit',
621 row_subset
=rowsubsetfn
,
623 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
624 with
open("row_subset_decoder.il", "w") as f
:
629 pdecode
= create_pdecode(name
="fusubset", col_subset
={'function_unit'})
630 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
631 with
open("col_subset_decoder.il", "w") as f
:
636 pdecode
= create_pdecode(include_fp
=True)
637 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
638 with
open("decoder.il", "w") as f
: