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
89 from collections
import namedtuple
90 from nmigen
import Module
, Elaboratable
, Signal
, Cat
, Mux
91 from nmigen
.cli
import rtlil
92 from soc
.decoder
.power_enums
import (Function
, Form
, MicrOp
,
93 In1Sel
, In2Sel
, In3Sel
, OutSel
,
94 RC
, LdstLen
, LDSTMode
, CryIn
, get_csv
,
95 single_bit_flags
, CRInSel
,
96 CROutSel
, get_signal_name
,
97 default_values
, insns
, asmidx
)
98 from soc
.decoder
.power_fields
import DecodeFields
99 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
102 # key data structure in which the POWER decoder is specified,
103 # in a hierarchical fashion
104 Subdecoder
= namedtuple("Subdecoder",
105 ["pattern", # the major pattern to search for (e.g. major opcode)
106 "opcodes", # a dictionary of minor patterns to find
107 "opint", # true => the pattern must not be in "10----11" format
108 # the bits (as a range) against which "pattern" matches
110 "suffix", # shift the opcode down before decoding
111 "subdecoders" # list of further subdecoders for *additional* matches,
112 # *ONLY* after "pattern" has *ALSO* been matched against.
115 power_op_types
= {'function_unit': Function
,
116 'internal_op': MicrOp
,
131 power_op_csvmap
= {'function_unit': 'unit',
133 'internal_op' : 'internal op',
140 'ldst_len' : 'ldst len',
146 def get_pname(field
, pname
):
149 return "%s_%s" % (pname
, field
)
153 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
155 this is an internal data structure, set up by reading CSV files
156 (which uses _eq to initialise each instance, not eq)
158 the "public" API (as far as actual usage as a useful decoder is concerned)
159 is Decode2ToExecute1Type
161 the "subset" allows for only certain columns to be decoded
164 def __init__(self
, incl_asm
=True, name
=None, subset
=None):
166 for field
, ptype
in power_op_types
.items():
167 if subset
and field
not in subset
:
169 fname
= get_pname(field
, name
)
170 setattr(self
, field
, Signal(ptype
, reset_less
=True, name
=fname
))
171 for bit
in single_bit_flags
:
172 field
= get_signal_name(bit
)
173 if subset
and field
not in subset
:
175 fname
= get_pname(field
, name
)
176 setattr(self
, field
, Signal(reset_less
=True, name
=fname
))
178 def _eq(self
, row
=None):
181 # TODO: this conversion process from a dict to an object
182 # should really be done using e.g. namedtuple and then
184 if False: # debugging
185 if row
['CR in'] == '1':
189 if row
['CR out'] == '0':
194 ldst_mode
= row
['upd']
195 if ldst_mode
.isdigit():
196 row
['upd'] = int(ldst_mode
)
198 for field
, ptype
in power_op_types
.items():
199 if not hasattr(self
, field
):
201 if field
not in power_op_csvmap
:
203 csvname
= power_op_csvmap
[field
]
205 if csvname
== 'upd' and isinstance(val
, int): # LDSTMode different
209 res
.append(getattr(self
, field
).eq(val
))
212 asmcode
= row
['comment']
213 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
214 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
215 for bit
in single_bit_flags
:
216 field
= get_signal_name(bit
)
217 if not hasattr(self
, field
):
219 sig
= getattr(self
, field
)
220 res
.append(sig
.eq(int(row
.get(bit
, 0))))
223 def _get_eq(self
, res
, field
, otherop
):
224 copyfrom
= getattr(otherop
, field
, None)
225 copyto
= getattr(self
, field
, None)
226 if copyfrom
is not None and copyto
is not None:
227 res
.append(copyto
.eq(copyfrom
))
229 def eq(self
, otherop
):
231 for field
in power_op_types
.keys():
232 self
._get
_eq
(res
, field
, otherop
)
233 for bit
in single_bit_flags
:
234 self
._get
_eq
(res
, get_signal_name(bit
), otherop
)
239 for field
in power_op_types
.keys():
240 if hasattr(self
, field
):
241 res
.append(getattr(self
, field
))
242 if hasattr(self
, "asmcode"):
243 res
.append(self
.asmcode
)
244 for field
in single_bit_flags
:
245 field
= get_signal_name(field
)
246 if hasattr(self
, field
):
247 res
.append(getattr(self
, field
))
251 class PowerDecoder(Elaboratable
):
252 """PowerDecoder - decodes an incoming opcode into the type of operation
255 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
257 self
.col_subset
= col_subset
258 self
.row_subsetfn
= row_subset
259 if not isinstance(dec
, list):
262 self
.opcode_in
= Signal(width
, reset_less
=True)
264 self
.op
= PowerOp(name
=name
, subset
=col_subset
)
266 if d
.suffix
is not None and d
.suffix
>= width
:
270 def suffix_mask(self
, d
):
271 return ((1 << d
.suffix
) - 1)
273 def divide_opcodes(self
, d
):
275 mask
= self
.suffix_mask(d
)
276 print("mask", hex(mask
))
277 for row
in d
.opcodes
:
278 opcode
= row
['opcode']
279 if d
.opint
and '-' not in opcode
:
280 opcode
= int(opcode
, 0)
282 opcode
= opcode
>> d
.suffix
283 if key
not in divided
:
287 divided
[key
].append(r
)
290 def elaborate(self
, platform
):
294 # note: default opcode is "illegal" as this is a combinatorial block
295 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
297 # go through the list of CSV decoders first
299 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
301 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
303 opcodes
= self
.divide_opcodes(d
)
304 opc_in
= Signal(d
.suffix
, reset_less
=True)
305 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
306 # begin the dynamic Switch statement here
307 with m
.Switch(opc_in
):
308 for key
, row
in opcodes
.items():
309 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
310 sd
= Subdecoder(pattern
=None, opcodes
=row
,
311 bitsel
=bitsel
, suffix
=None,
312 opint
=False, subdecoders
=[])
313 subdecoder
= PowerDecoder(width
=32, dec
=sd
,
315 col_subset
=self
.col_subset
,
316 row_subset
=self
.row_subsetfn
)
317 mname
= get_pname("dec_sub%d" % key
, self
.pname
)
318 setattr(m
.submodules
, mname
, subdecoder
)
319 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
320 # add in the dynamic Case statement here
322 comb
+= self
.op
.eq(subdecoder
.op
)
324 # TODO: arguments, here (all of them) need to be a list.
325 # a for-loop around the *list* of decoder args.
326 with m
.Switch(opcode_switch
):
327 self
.handle_subdecoders(m
, d
)
328 for row
in d
.opcodes
:
329 opcode
= row
['opcode']
330 if d
.opint
and '-' not in opcode
:
331 opcode
= int(opcode
, 0)
334 # add in the dynamic Case statement here
336 comb
+= self
.op
._eq
(row
)
339 def handle_subdecoders(self
, m
, d
):
340 for dec
in d
.subdecoders
:
341 subdecoder
= PowerDecoder(self
.width
, dec
,
343 col_subset
=self
.col_subset
,
344 row_subset
=self
.row_subsetfn
)
345 if isinstance(dec
, list): # XXX HACK: take first pattern
347 mname
= get_pname("dec%d" % dec
.pattern
, self
.pname
)
348 setattr(m
.submodules
, mname
, subdecoder
)
349 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
350 with m
.Case(dec
.pattern
):
351 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
354 return [self
.opcode_in
] + self
.op
.ports()
357 class TopPowerDecoder(PowerDecoder
):
360 top-level hierarchical decoder for POWER ISA
361 bigendian dynamically switches between big and little endian decoding
362 (reverses byte order). See V3.0B p44 1.11.2
365 def __init__(self
, width
, dec
, name
=None, col_subset
=None, row_subset
=None):
366 PowerDecoder
.__init
__(self
, width
, dec
, name
, col_subset
, row_subset
)
367 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
368 self
.fields
.create_specs()
369 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
370 self
.bigendian
= Signal(reset_less
=True)
372 for fname
, value
in self
.fields
.common_fields
.items():
373 signame
= get_pname(fname
, name
)
374 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=signame
)
375 setattr(self
, fname
, sig
)
377 # create signals for all field forms
378 self
.form_names
= forms
= self
.fields
.instrs
.keys()
381 fields
= self
.fields
.instrs
[form
]
383 Fields
= namedtuple("Fields", fk
)
385 for k
, value
in fields
.items():
386 fname
= "%s_%s" % (form
, k
)
387 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=fname
)
390 setattr(self
, "Form%s" % form
, instr
)
391 self
.sigforms
[form
] = instr
393 def elaborate(self
, platform
):
394 m
= PowerDecoder
.elaborate(self
, platform
)
396 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
397 raw_le
= self
.raw_opcode_in
399 for i
in range(0, self
.width
, 8):
400 l
.append(raw_le
[i
:i
+8])
403 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
405 # add all signal from commonly-used fields
406 for fname
, value
in self
.fields
.common_fields
.items():
407 sig
= getattr(self
, fname
)
408 comb
+= sig
.eq(value
[0:-1])
410 # link signals for all field forms
411 forms
= self
.form_names
413 sf
= self
.sigforms
[form
]
414 fields
= self
.fields
.instrs
[form
]
415 for k
, value
in fields
.items():
417 comb
+= sig
.eq(value
[0:-1])
422 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
425 ####################################################
426 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
428 def create_pdecode(name
=None, col_subset
=None, row_subset
=None):
429 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
431 subsetting of the PowerOp decoding is possible by setting col_subset
434 # minor 19 has extra patterns
436 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
437 opint
=True, bitsel
=(1, 11), suffix
=None,
439 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
440 opint
=True, bitsel
=(1, 6), suffix
=None,
446 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
447 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
448 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
449 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
450 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
451 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
452 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
453 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
456 # top level: extra merged with major
458 opcodes
= get_csv("major.csv")
459 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
460 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
461 opcodes
= get_csv("extra.csv")
462 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
463 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
465 return TopPowerDecoder(32, dec
, name
=name
, col_subset
=col_subset
,
466 row_subset
=row_subset
)
469 if __name__
== '__main__':
470 pdecode
= create_pdecode()
471 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
472 with
open("decoder.il", "w") as f
:
475 pdecode
= create_pdecode(name
="fusubset", col_subset
={'function_unit'})
476 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
477 with
open("col_subset_decoder.il", "w") as f
: