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 soc
.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 soc
.decoder
.power_fields
import DecodeFields
101 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
102 from soc
.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
,
135 'sv_cr_out': SVEXTRA
,
142 power_op_csvmap
= {'function_unit': 'unit',
144 'internal_op': 'internal op',
153 'sv_cr_in': 'sv_cr_in',
154 'sv_cr_out': 'sv_cr_out',
155 'SV_Etype': 'SV_Etype',
156 'SV_Ptype': 'SV_Ptype',
159 'ldst_len': 'ldst len',
166 def get_pname(field
, pname
):
169 return "%s_%s" % (pname
, field
)
172 class PatternOpcode(str):
176 def parse_opcode(opcode
, opint
=True):
178 if isinstance(opcode
, (int, PatternOpcode
)):
180 assert isinstance(opcode
, str)
181 assert opcode
.startswith('0b')
182 if '-' not in opcode
:
183 opcode
= int(opcode
, 0)
185 opcode
= PatternOpcode(opcode
[2:])
190 """PowerOp - a dynamic class that stores (subsets of) CSV rows of data
191 about a PowerISA instruction. this is a "micro-code" expanded format
192 which generates an awful lot of wires, hence the subsetting
195 def __init__(self
, incl_asm
=True, name
=None, subset
=None):
199 for field
, ptype
in power_op_types
.items():
201 if subset
and field
not in subset
:
203 fname
= get_pname(field
, name
)
204 setattr(self
, field
, Signal(ptype
, reset_less
=True, name
=fname
))
205 debug_report
.add(field
)
206 for bit
in single_bit_flags
:
207 field
= get_signal_name(bit
)
209 if subset
and field
not in subset
:
211 debug_report
.add(field
)
212 fname
= get_pname(field
, name
)
213 setattr(self
, field
, Signal(reset_less
=True, name
=fname
))
214 print("PowerOp debug", name
, debug_report
)
215 print(" fields", fields
)
217 def _eq(self
, row
=None):
220 # TODO: this conversion process from a dict to an object
221 # should really be done using e.g. namedtuple and then
223 if False: # debugging
224 if row
['CR in'] == '1':
228 if row
['CR out'] == '0':
233 ldst_mode
= row
['upd']
234 if ldst_mode
.isdigit():
235 row
['upd'] = int(ldst_mode
)
237 for field
, ptype
in power_op_types
.items():
238 if not hasattr(self
, field
):
240 if field
not in power_op_csvmap
:
242 csvname
= power_op_csvmap
[field
]
243 print(field
, ptype
, csvname
, row
)
245 if csvname
== 'upd' and isinstance(val
, int): # LDSTMode different
249 res
.append(getattr(self
, field
).eq(val
))
252 asmcode
= row
['comment']
253 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
254 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
255 for bit
in single_bit_flags
:
256 field
= get_signal_name(bit
)
257 if not hasattr(self
, field
):
259 sig
= getattr(self
, field
)
260 res
.append(sig
.eq(int(row
.get(bit
, 0))))
263 def _get_eq(self
, res
, field
, otherop
):
264 copyfrom
= getattr(otherop
, field
, None)
265 copyto
= getattr(self
, field
, None)
266 if copyfrom
is not None and copyto
is not None:
267 res
.append(copyto
.eq(copyfrom
))
269 def eq(self
, otherop
):
271 for field
in power_op_types
.keys():
272 self
._get
_eq
(res
, field
, otherop
)
273 for bit
in single_bit_flags
:
274 self
._get
_eq
(res
, get_signal_name(bit
), otherop
)
279 for field
in power_op_types
.keys():
280 if hasattr(self
, field
):
281 res
.append(getattr(self
, field
))
282 if hasattr(self
, "asmcode"):
283 res
.append(self
.asmcode
)
284 for field
in single_bit_flags
:
285 field
= get_signal_name(field
)
286 if hasattr(self
, field
):
287 res
.append(getattr(self
, field
))
291 class PowerDecoder(Elaboratable
):
292 """PowerDecoder - decodes an incoming opcode into the type of operation
294 this is a recursive algorithm, creating Switch statements that can
295 have further match-and-decode on other parts of the opcode field before
296 finally landing at a "this CSV entry details gets returned" thing.
298 the complicating factor is the row and col subsetting. column subsetting
299 dynamically chooses only the CSV columns requested, whilst row subsetting
300 allows a function to be called on the row to determine if the Case
301 statement is to be generated for that row. this not only generates
302 completely different Decoders, it also means that some sub-decoders
303 will turn up blank (empty switch statements). if that happens we do
304 not want the parent to include a Mux for an entirely blank switch statement
305 so we have to store the switch/case statements in a tree, and
308 the reason for the tree is because elaborate can only be called *after*
309 the constructor is called. all quite messy.
312 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
313 self
.actually_does_something
= False
315 self
.col_subset
= col_subset
316 self
.row_subsetfn
= row_subset
317 if not isinstance(dec
, list):
320 self
.opcode_in
= Signal(width
, reset_less
=True)
322 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
324 if d
.suffix
is not None and d
.suffix
>= width
:
328 def suffix_mask(self
, d
):
329 return ((1 << d
.suffix
) - 1)
331 def divide_opcodes(self
, d
):
333 mask
= self
.suffix_mask(d
)
334 print("mask", hex(mask
))
335 for row
in d
.opcodes
:
336 opcode
= parse_opcode(row
['opcode'], d
.opint
)
338 opcode
= opcode
>> d
.suffix
339 if key
not in divided
:
343 divided
[key
].append(r
)
346 def tree_analyse(self
):
347 self
.decs
= decs
= []
348 self
.submodules
= submodules
= {}
351 # go through the list of CSV decoders first
354 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
357 case_does_something
= False
358 eq
.append(opcode_switch
.eq(
359 self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]]))
361 opcodes
= self
.divide_opcodes(d
)
362 opc_in
= Signal(d
.suffix
, reset_less
=True)
363 eq
.append(opc_in
.eq(opcode_switch
[:d
.suffix
]))
364 # begin the dynamic Switch statement here
366 cases
.append([opc_in
, switch_case
])
368 for key
, row
in opcodes
.items():
369 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
370 sd
= Subdecoder(pattern
=None, opcodes
=row
,
371 bitsel
=bitsel
, suffix
=None,
372 opint
=True, subdecoders
=[])
373 mname
= get_pname("dec_sub%d" % key
, self
.pname
)
374 subdecoder
= PowerDecoder(width
=32, dec
=sd
,
376 col_subset
=self
.col_subset
,
377 row_subset
=self
.row_subsetfn
)
378 if not subdecoder
.tree_analyse():
381 submodules
[mname
] = subdecoder
382 sub_eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
383 # add in the dynamic Case statement here
384 switch_case
[key
] = self
.op
.eq(subdecoder
.op
)
385 self
.actually_does_something
= True
386 case_does_something
= True
387 if case_does_something
:
390 # TODO: arguments, here (all of them) need to be a list.
391 # a for-loop around the *list* of decoder args.
393 cases
.append([opcode_switch
, switch_case
])
394 seqs
= self
.handle_subdecoders(switch_case
, submodules
, d
)
396 case_does_something
= True
398 for row
in d
.opcodes
:
399 opcode
= parse_opcode(row
['opcode'], d
.opint
)
402 if self
.row_subsetfn
:
403 if not self
.row_subsetfn(opcode
, row
):
405 # add in the dynamic Case statement here
406 switch_case
[opcode
] = self
.op
._eq
(row
)
407 self
.actually_does_something
= True
408 case_does_something
= True
412 if case_does_something
:
414 print("submodule eqs", self
.pname
, eq
)
416 print("submodules", self
.pname
, submodules
)
419 return self
.actually_does_something
421 def handle_subdecoders(self
, switch_case
, submodules
, d
):
423 for dec
in d
.subdecoders
:
424 if isinstance(dec
, list): # XXX HACK: take first pattern
426 print("subdec", dec
.pattern
, self
.pname
)
427 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
428 subdecoder
= PowerDecoder(self
.width
, dec
,
430 col_subset
=self
.col_subset
,
431 row_subset
=self
.row_subsetfn
)
432 if not subdecoder
.tree_analyse(): # doesn't do anything
435 submodules
[mname
] = subdecoder
436 eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
437 switch_case
[dec
.pattern
] = self
.op
.eq(subdecoder
.op
)
438 self
.actually_does_something
= True
442 def elaborate(self
, platform
):
443 print("decoder elaborate", self
.pname
, self
.submodules
)
449 for mname
, subdecoder
in self
.submodules
.items():
450 setattr(m
.submodules
, mname
, subdecoder
)
452 for switch_case
in self
.decs
:
453 for (switch
, cases
) in switch_case
:
454 with m
.Switch(switch
):
455 for key
, eqs
in cases
.items():
461 return [self
.opcode_in
] + self
.op
.ports()
464 class TopPowerDecoder(PowerDecoder
):
467 top-level hierarchical decoder for POWER ISA
468 bigendian dynamically switches between big and little endian decoding
469 (reverses byte order). See V3.0B p44 1.11.2
472 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
473 PowerDecoder
.__init
__(self
, width
, dec
, name
, col_subset
, row_subset
)
474 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
475 self
.fields
.create_specs()
476 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
477 self
.bigendian
= Signal(reset_less
=True)
479 for fname
, value
in self
.fields
.common_fields
.items():
480 signame
= get_pname(fname
, name
)
481 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=signame
)
482 setattr(self
, fname
, sig
)
484 # create signals for all field forms
485 forms
= self
.form_names
488 fields
= self
.fields
.instrs
[form
]
490 Fields
= namedtuple("Fields", fk
)
492 for k
, value
in fields
.items():
493 fname
= "%s_%s" % (form
, k
)
494 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=fname
)
497 setattr(self
, "Form%s" % form
, instr
)
498 self
.sigforms
[form
] = instr
503 def form_names(self
):
504 return self
.fields
.instrs
.keys()
506 def elaborate(self
, platform
):
507 m
= PowerDecoder
.elaborate(self
, platform
)
509 # sigh duplicated in SVP64PowerDecoder
510 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
511 raw_le
= self
.raw_opcode_in
513 for i
in range(0, self
.width
, 8):
514 l
.append(raw_le
[i
:i
+8])
517 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
519 # add all signal from commonly-used fields
520 for fname
, value
in self
.fields
.common_fields
.items():
521 sig
= getattr(self
, fname
)
522 comb
+= sig
.eq(value
[0:-1])
524 # link signals for all field forms
525 forms
= self
.form_names
527 sf
= self
.sigforms
[form
]
528 fields
= self
.fields
.instrs
[form
]
529 for k
, value
in fields
.items():
531 comb
+= sig
.eq(value
[0:-1])
536 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
539 ####################################################
540 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
542 def create_pdecode(name
=None, col_subset
=None, row_subset
=None):
543 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
545 subsetting of the PowerOp decoding is possible by setting col_subset
548 # some alteration to the CSV files is required for SV so we use
551 get_csv
= isa
.get_svp64_csv
553 # minor 19 has extra patterns
555 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
556 opint
=True, bitsel
=(1, 11), suffix
=None,
558 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
559 opint
=True, bitsel
=(1, 6), suffix
=None,
565 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
566 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
567 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
568 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
569 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
570 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
571 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
572 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
573 Subdecoder(pattern
=22, opcodes
=get_csv("minor_22.csv"),
574 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
577 # top level: extra merged with major
579 opcodes
= get_csv("major.csv")
580 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
581 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
582 opcodes
= get_csv("extra.csv")
583 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
584 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
586 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
587 row_subset
=row_subset
)
590 if __name__
== '__main__':
595 def rowsubsetfn(opcode
, row
):
596 print("row_subset", opcode
, row
)
597 return row
['unit'] == 'ALU'
599 pdecode
= create_pdecode(name
="rowsub",
600 col_subset
={'function_unit', 'in1_sel'},
601 row_subset
=rowsubsetfn
)
602 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
603 with
open("row_subset_decoder.il", "w") as f
:
608 pdecode
= create_pdecode(name
="fusubset", col_subset
={'function_unit'})
609 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
610 with
open("col_subset_decoder.il", "w") as f
:
615 pdecode
= create_pdecode()
616 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
617 with
open("decoder.il", "w") as f
: