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 RC
, LdstLen
, LDSTMode
, CryIn
, get_csv
,
96 single_bit_flags
, CRInSel
,
97 CROutSel
, get_signal_name
,
98 default_values
, insns
, asmidx
)
99 from soc
.decoder
.power_fields
import DecodeFields
100 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
103 # key data structure in which the POWER decoder is specified,
104 # in a hierarchical fashion
105 Subdecoder
= namedtuple("Subdecoder",
106 ["pattern", # the major pattern to search for (e.g. major opcode)
107 "opcodes", # a dictionary of minor patterns to find
108 "opint", # true => the pattern must not be in "10----11" format
109 # the bits (as a range) against which "pattern" matches
111 "suffix", # shift the opcode down before decoding
112 "subdecoders" # list of further subdecoders for *additional* matches,
113 # *ONLY* after "pattern" has *ALSO* been matched against.
116 power_op_types
= {'function_unit': Function
,
117 'internal_op': MicrOp
,
132 power_op_csvmap
= {'function_unit': 'unit',
134 'internal_op' : 'internal op',
141 'ldst_len' : 'ldst len',
147 def get_pname(field
, pname
):
150 return "%s_%s" % (pname
, field
)
154 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
156 this is an internal data structure, set up by reading CSV files
157 (which uses _eq to initialise each instance, not eq)
159 the "public" API (as far as actual usage as a useful decoder is concerned)
160 is Decode2ToExecute1Type
162 the "subset" allows for only certain columns to be decoded
165 def __init__(self
, incl_asm
=True, name
=None, subset
=None):
169 for field
, ptype
in power_op_types
.items():
171 if subset
and field
not in subset
:
173 fname
= get_pname(field
, name
)
174 setattr(self
, field
, Signal(ptype
, reset_less
=True, name
=fname
))
175 debug_report
.add(field
)
176 for bit
in single_bit_flags
:
177 field
= get_signal_name(bit
)
179 if subset
and field
not in subset
:
181 debug_report
.add(field
)
182 fname
= get_pname(field
, name
)
183 setattr(self
, field
, Signal(reset_less
=True, name
=fname
))
184 print ("PowerOp debug", name
, debug_report
)
185 print (" fields", fields
)
187 def _eq(self
, row
=None):
190 # TODO: this conversion process from a dict to an object
191 # should really be done using e.g. namedtuple and then
193 if False: # debugging
194 if row
['CR in'] == '1':
198 if row
['CR out'] == '0':
203 ldst_mode
= row
['upd']
204 if ldst_mode
.isdigit():
205 row
['upd'] = int(ldst_mode
)
207 for field
, ptype
in power_op_types
.items():
208 if not hasattr(self
, field
):
210 if field
not in power_op_csvmap
:
212 csvname
= power_op_csvmap
[field
]
214 if csvname
== 'upd' and isinstance(val
, int): # LDSTMode different
218 res
.append(getattr(self
, field
).eq(val
))
221 asmcode
= row
['comment']
222 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
223 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
224 for bit
in single_bit_flags
:
225 field
= get_signal_name(bit
)
226 if not hasattr(self
, field
):
228 sig
= getattr(self
, field
)
229 res
.append(sig
.eq(int(row
.get(bit
, 0))))
232 def _get_eq(self
, res
, field
, otherop
):
233 copyfrom
= getattr(otherop
, field
, None)
234 copyto
= getattr(self
, field
, None)
235 if copyfrom
is not None and copyto
is not None:
236 res
.append(copyto
.eq(copyfrom
))
238 def eq(self
, otherop
):
240 for field
in power_op_types
.keys():
241 self
._get
_eq
(res
, field
, otherop
)
242 for bit
in single_bit_flags
:
243 self
._get
_eq
(res
, get_signal_name(bit
), otherop
)
248 for field
in power_op_types
.keys():
249 if hasattr(self
, field
):
250 res
.append(getattr(self
, field
))
251 if hasattr(self
, "asmcode"):
252 res
.append(self
.asmcode
)
253 for field
in single_bit_flags
:
254 field
= get_signal_name(field
)
255 if hasattr(self
, field
):
256 res
.append(getattr(self
, field
))
260 class PowerDecoder(Elaboratable
):
261 """PowerDecoder - decodes an incoming opcode into the type of operation
264 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
265 self
.actually_does_something
= False
267 self
.col_subset
= col_subset
268 self
.row_subsetfn
= row_subset
269 if not isinstance(dec
, list):
272 self
.opcode_in
= Signal(width
, reset_less
=True)
274 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
276 if d
.suffix
is not None and d
.suffix
>= width
:
280 def suffix_mask(self
, d
):
281 return ((1 << d
.suffix
) - 1)
283 def divide_opcodes(self
, d
):
285 mask
= self
.suffix_mask(d
)
286 print("mask", hex(mask
))
287 for row
in d
.opcodes
:
288 opcode
= row
['opcode']
289 if d
.opint
and '-' not in opcode
:
290 opcode
= int(opcode
, 0)
292 opcode
= opcode
>> d
.suffix
293 if key
not in divided
:
297 divided
[key
].append(r
)
300 def tree_analyse(self
):
301 self
.decs
= decs
= []
302 self
.submodules
= submodules
= {}
305 # go through the list of CSV decoders first
308 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
311 case_does_something
= False
312 eq
.append(opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]]))
314 opcodes
= self
.divide_opcodes(d
)
315 opc_in
= Signal(d
.suffix
, reset_less
=True)
316 eq
.append(opc_in
.eq(opcode_switch
[:d
.suffix
]))
317 # begin the dynamic Switch statement here
319 cases
.append([opc_in
, switch_case
])
321 for key
, row
in opcodes
.items():
322 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
323 sd
= Subdecoder(pattern
=None, opcodes
=row
,
324 bitsel
=bitsel
, suffix
=None,
325 opint
=False, subdecoders
=[])
326 mname
= get_pname("dec_sub%d" % key
, self
.pname
)
327 subdecoder
= PowerDecoder(width
=32, dec
=sd
,
329 col_subset
=self
.col_subset
,
330 row_subset
=self
.row_subsetfn
)
331 if not subdecoder
.tree_analyse():
334 submodules
[mname
] = subdecoder
335 sub_eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
336 # add in the dynamic Case statement here
337 switch_case
[key
] = self
.op
.eq(subdecoder
.op
)
338 self
.actually_does_something
= True
339 case_does_something
= True
340 if case_does_something
:
343 # TODO: arguments, here (all of them) need to be a list.
344 # a for-loop around the *list* of decoder args.
346 cases
.append([opcode_switch
, switch_case
])
347 seqs
= self
.handle_subdecoders(switch_case
, submodules
, d
)
349 case_does_something
= True
351 for row
in d
.opcodes
:
352 opcode
= row
['opcode']
353 if d
.opint
and '-' not in opcode
:
354 opcode
= int(opcode
, 0)
357 if self
.row_subsetfn
:
358 if not self
.row_subsetfn(opcode
, row
):
360 # add in the dynamic Case statement here
361 switch_case
[opcode
] = self
.op
._eq
(row
)
362 self
.actually_does_something
= True
363 case_does_something
= True
367 if case_does_something
:
369 print ("submodule eqs", self
.pname
, eq
)
371 print ("submodules", self
.pname
, submodules
)
374 return self
.actually_does_something
376 def handle_subdecoders(self
, switch_case
, submodules
, d
):
378 for dec
in d
.subdecoders
:
379 if isinstance(dec
, list): # XXX HACK: take first pattern
381 print ("subdec", dec
.pattern
, self
.pname
)
382 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
383 subdecoder
= PowerDecoder(self
.width
, dec
,
385 col_subset
=self
.col_subset
,
386 row_subset
=self
.row_subsetfn
)
387 if not subdecoder
.tree_analyse(): # doesn't do anything
390 submodules
[mname
] = subdecoder
391 eqs
.append(subdecoder
.opcode_in
.eq(self
.opcode_in
))
392 switch_case
[dec
.pattern
] = self
.op
.eq(subdecoder
.op
)
393 self
.actually_does_something
= True
397 def elaborate(self
, platform
):
398 print ("decoder elaborate", self
.pname
, self
.submodules
)
404 for mname
, subdecoder
in self
.submodules
.items():
405 setattr(m
.submodules
, mname
, subdecoder
)
407 for switch_case
in self
.decs
:
408 for (switch
, cases
) in switch_case
:
409 with m
.Switch(switch
):
410 for key
, eqs
in cases
.items():
416 return [self
.opcode_in
] + self
.op
.ports()
419 class TopPowerDecoder(PowerDecoder
):
422 top-level hierarchical decoder for POWER ISA
423 bigendian dynamically switches between big and little endian decoding
424 (reverses byte order). See V3.0B p44 1.11.2
427 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
428 PowerDecoder
.__init
__(self
, width
, dec
, name
, col_subset
, row_subset
)
429 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
430 self
.fields
.create_specs()
431 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
432 self
.bigendian
= Signal(reset_less
=True)
434 for fname
, value
in self
.fields
.common_fields
.items():
435 signame
= get_pname(fname
, name
)
436 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=signame
)
437 setattr(self
, fname
, sig
)
439 # create signals for all field forms
440 self
.form_names
= forms
= self
.fields
.instrs
.keys()
443 fields
= self
.fields
.instrs
[form
]
445 Fields
= namedtuple("Fields", fk
)
447 for k
, value
in fields
.items():
448 fname
= "%s_%s" % (form
, k
)
449 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=fname
)
452 setattr(self
, "Form%s" % form
, instr
)
453 self
.sigforms
[form
] = instr
457 def elaborate(self
, platform
):
458 m
= PowerDecoder
.elaborate(self
, platform
)
460 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
461 raw_le
= self
.raw_opcode_in
463 for i
in range(0, self
.width
, 8):
464 l
.append(raw_le
[i
:i
+8])
467 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
469 # add all signal from commonly-used fields
470 for fname
, value
in self
.fields
.common_fields
.items():
471 sig
= getattr(self
, fname
)
472 comb
+= sig
.eq(value
[0:-1])
474 # link signals for all field forms
475 forms
= self
.form_names
477 sf
= self
.sigforms
[form
]
478 fields
= self
.fields
.instrs
[form
]
479 for k
, value
in fields
.items():
481 comb
+= sig
.eq(value
[0:-1])
486 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
489 ####################################################
490 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
492 def create_pdecode(name
=None, col_subset
=None, row_subset
=None):
493 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
495 subsetting of the PowerOp decoding is possible by setting col_subset
498 # minor 19 has extra patterns
500 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
501 opint
=True, bitsel
=(1, 11), suffix
=None,
503 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
504 opint
=True, bitsel
=(1, 6), suffix
=None,
510 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
511 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
512 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
513 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
514 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
515 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
516 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
517 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
520 # top level: extra merged with major
522 opcodes
= get_csv("major.csv")
523 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
524 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
525 opcodes
= get_csv("extra.csv")
526 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
527 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
529 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
530 row_subset
=row_subset
)
533 if __name__
== '__main__':
538 def rowsubsetfn(opcode
, row
):
539 print ("row_subset", opcode
, row
)
540 return row
['unit'] == 'ALU'
542 pdecode
= create_pdecode(name
="rowsub",
543 col_subset
={'function_unit', 'in1_sel'},
544 row_subset
=rowsubsetfn
)
545 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
546 with
open("row_subset_decoder.il", "w") as f
:
551 pdecode
= create_pdecode(name
="fusubset", col_subset
={'function_unit'})
552 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
553 with
open("col_subset_decoder.il", "w") as f
:
558 pdecode
= create_pdecode()
559 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
560 with
open("decoder.il", "w") as f
: