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
44 takes a *list* of CSV files with an associated bit-range that it
45 is requested to match against the "opcode" row of the CSV file.
46 This pattern can be either an integer, a binary number, *or* a
47 wildcard nmigen Case pattern of the form "001--1-100".
51 these are *additional* cases with further decoding. The "pattern"
52 argument is specified as one of the Case statements (a peer of the
53 opcode row in the CSV file), and thus further fields of the opcode
54 may be decoded giving increasing levels of detail.
58 [ (extra.csv: bit-fields entire 32-bit range
60 000000---------------01000000000 -> ILLEGAL instruction
61 01100000000000000000000000000000 -> SIM_CONFIG instruction
62 ................................ ->
64 (major.csv: first 6 bits ONLY
66 001100 -> ALU,OP_ADD (add)
67 001101 -> ALU,OP_ADD (another type of add)
71 001011 this must match *MAJOR*.CSV
72 [ (minor_19.csv: bits 21 through 30 inclusive:
74 0b0000000000 -> ALU,OP_MCRF
77 (minor_19_00000.csv: bits 21 through 25 inclusive:
79 0b00010 -> ALU,add_pcis
88 from collections
import namedtuple
89 from nmigen
import Module
, Elaboratable
, Signal
, Cat
, Mux
90 from nmigen
.cli
import rtlil
91 from soc
.decoder
.power_enums
import (Function
, Form
, MicrOp
,
92 In1Sel
, In2Sel
, In3Sel
, OutSel
,
93 RC
, LdstLen
, LDSTMode
, CryIn
, get_csv
,
94 single_bit_flags
, CRInSel
,
95 CROutSel
, get_signal_name
,
96 default_values
, insns
, asmidx
)
97 from soc
.decoder
.power_fields
import DecodeFields
98 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
101 # key data structure in which the POWER decoder is specified,
102 # in a hierarchical fashion
103 Subdecoder
= namedtuple("Subdecoder",
104 ["pattern", # the major pattern to search for (e.g. major opcode)
105 "opcodes", # a dictionary of minor patterns to find
106 "opint", # true => the pattern must not be in "10----11" format
107 # the bits (as a range) against which "pattern" matches
109 "suffix", # shift the opcode down before decoding
110 "subdecoders" # list of further subdecoders for *additional* matches,
111 # *ONLY* after "pattern" has *ALSO* been matched against.
114 power_op_types
= {'function_unit': Function
,
115 'internal_op': MicrOp
,
131 def get_pname(field
, pname
):
134 return "%s_%s" % (pname
, field
)
138 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
140 this is an internal data structure, set up by reading CSV files
141 (which uses _eq to initialise each instance, not eq)
143 the "public" API (as far as actual usage as a useful decoder is concerned)
144 is Decode2ToExecute1Type
146 the "subset" allows for only certain columns to be decoded
149 def __init__(self
, incl_asm
=True, name
=None, subset
=None):
150 for field
, ptype
in power_op_types
.items():
151 if subset
and field
not in subset
:
153 fname
= get_pname(field
, name
)
154 setattr(self
, field
, Signal(ptype
, reset_less
=True, name
=fname
))
155 for bit
in single_bit_flags
:
156 field
= get_signal_name(bit
)
157 fname
= get_pname(field
, name
)
158 setattr(self
, field
, Signal(reset_less
=True, name
=fname
))
160 def _eq(self
, row
=None):
163 # TODO: this conversion process from a dict to an object
164 # should really be done using e.g. namedtuple and then
166 if False: # debugging
167 if row
['CR in'] == '1':
171 if row
['CR out'] == '0':
176 ldst_mode
= row
['upd']
177 if ldst_mode
.isdigit():
178 ldst_mode
= LDSTMode(int(ldst_mode
))
180 ldst_mode
= LDSTMode
[ldst_mode
]
181 res
= [self
.function_unit
.eq(Function
[row
['unit']]),
182 self
.form
.eq(Form
[row
['form']]),
183 self
.internal_op
.eq(MicrOp
[row
['internal op']]),
184 self
.in1_sel
.eq(In1Sel
[row
['in1']]),
185 self
.in2_sel
.eq(In2Sel
[row
['in2']]),
186 self
.in3_sel
.eq(In3Sel
[row
['in3']]),
187 self
.out_sel
.eq(OutSel
[row
['out']]),
188 self
.cr_in
.eq(CRInSel
[row
['CR in']]),
189 self
.cr_out
.eq(CROutSel
[row
['CR out']]),
190 self
.ldst_len
.eq(LdstLen
[row
['ldst len']]),
191 self
.upd
.eq(ldst_mode
),
192 self
.rc_sel
.eq(RC
[row
['rc']]),
193 self
.cry_in
.eq(CryIn
[row
['cry in']]),
197 asmcode
= row
['comment']
198 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
199 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
200 for bit
in single_bit_flags
:
201 sig
= getattr(self
, get_signal_name(bit
))
202 res
.append(sig
.eq(int(row
.get(bit
, 0))))
205 def eq(self
, otherop
):
206 res
= [self
.function_unit
.eq(otherop
.function_unit
),
207 self
.form
.eq(otherop
.form
),
208 self
.internal_op
.eq(otherop
.internal_op
),
209 self
.in1_sel
.eq(otherop
.in1_sel
),
210 self
.in2_sel
.eq(otherop
.in2_sel
),
211 self
.in3_sel
.eq(otherop
.in3_sel
),
212 self
.out_sel
.eq(otherop
.out_sel
),
213 self
.cr_in
.eq(otherop
.cr_in
),
214 self
.cr_out
.eq(otherop
.cr_out
),
215 self
.rc_sel
.eq(otherop
.rc_sel
),
216 self
.ldst_len
.eq(otherop
.ldst_len
),
217 self
.upd
.eq(otherop
.upd
),
218 self
.cry_in
.eq(otherop
.cry_in
)]
219 for bit
in single_bit_flags
:
220 sig
= getattr(self
, get_signal_name(bit
))
221 res
.append(sig
.eq(getattr(otherop
, get_signal_name(bit
))))
222 if hasattr(self
, "asmcode"):
223 res
.append(self
.asmcode
.eq(otherop
.asmcode
))
227 regular
= [self
.function_unit
,
238 if hasattr(self
, "asmcode"):
239 regular
.append(self
.asmcode
)
240 single_bit_ports
= [getattr(self
, get_signal_name(x
))
241 for x
in single_bit_flags
]
242 return regular
+ single_bit_ports
245 class PowerDecoder(Elaboratable
):
246 """PowerDecoder - decodes an incoming opcode into the type of operation
249 def __init__(self
, width
, dec
):
250 if not isinstance(dec
, list):
253 self
.opcode_in
= Signal(width
, reset_less
=True)
257 if d
.suffix
is not None and d
.suffix
>= width
:
261 def suffix_mask(self
, d
):
262 return ((1 << d
.suffix
) - 1)
264 def divide_opcodes(self
, d
):
266 mask
= self
.suffix_mask(d
)
267 print("mask", hex(mask
))
268 for row
in d
.opcodes
:
269 opcode
= row
['opcode']
270 if d
.opint
and '-' not in opcode
:
271 opcode
= int(opcode
, 0)
273 opcode
= opcode
>> d
.suffix
274 if key
not in divided
:
278 divided
[key
].append(r
)
281 def elaborate(self
, platform
):
285 # note: default opcode is "illegal" as this is a combinatorial block
286 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
288 # go through the list of CSV decoders first
290 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
292 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
294 opcodes
= self
.divide_opcodes(d
)
295 opc_in
= Signal(d
.suffix
, reset_less
=True)
296 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
297 # begin the dynamic Switch statement here
298 with m
.Switch(opc_in
):
299 for key
, row
in opcodes
.items():
300 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
301 sd
= Subdecoder(pattern
=None, opcodes
=row
,
302 bitsel
=bitsel
, suffix
=None,
303 opint
=False, subdecoders
=[])
304 subdecoder
= PowerDecoder(width
=32, dec
=sd
)
305 setattr(m
.submodules
, "dec_sub%d" % key
, subdecoder
)
306 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
307 # add in the dynamic Case statement here
309 comb
+= self
.op
.eq(subdecoder
.op
)
311 # TODO: arguments, here (all of them) need to be a list.
312 # a for-loop around the *list* of decoder args.
313 with m
.Switch(opcode_switch
):
314 self
.handle_subdecoders(m
, d
)
315 for row
in d
.opcodes
:
316 opcode
= row
['opcode']
317 if d
.opint
and '-' not in opcode
:
318 opcode
= int(opcode
, 0)
321 # add in the dynamic Case statement here
323 comb
+= self
.op
._eq
(row
)
326 def handle_subdecoders(self
, m
, d
):
327 for dec
in d
.subdecoders
:
328 subdecoder
= PowerDecoder(self
.width
, dec
)
329 if isinstance(dec
, list): # XXX HACK: take first pattern
331 setattr(m
.submodules
, "dec%d" % dec
.pattern
, subdecoder
)
332 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
333 with m
.Case(dec
.pattern
):
334 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
337 return [self
.opcode_in
] + self
.op
.ports()
340 class TopPowerDecoder(PowerDecoder
):
343 top-level hierarchical decoder for POWER ISA
344 bigendian dynamically switches between big and little endian decoding
345 (reverses byte order). See V3.0B p44 1.11.2
348 def __init__(self
, width
, dec
):
349 PowerDecoder
.__init
__(self
, width
, dec
)
350 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
351 self
.fields
.create_specs()
352 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
353 self
.bigendian
= Signal(reset_less
=True)
355 for name
, value
in self
.fields
.common_fields
.items():
356 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
357 setattr(self
, name
, sig
)
359 # create signals for all field forms
360 self
.form_names
= forms
= self
.fields
.instrs
.keys()
363 fields
= self
.fields
.instrs
[form
]
365 Fields
= namedtuple("Fields", fk
)
367 for k
, value
in fields
.items():
368 name
= "%s_%s" % (form
, k
)
369 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
372 setattr(self
, "Form%s" % form
, instr
)
373 self
.sigforms
[form
] = instr
375 def elaborate(self
, platform
):
376 m
= PowerDecoder
.elaborate(self
, platform
)
378 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
379 raw_le
= self
.raw_opcode_in
381 for i
in range(0, self
.width
, 8):
382 l
.append(raw_le
[i
:i
+8])
385 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
387 # add all signal from commonly-used fields
388 for name
, value
in self
.fields
.common_fields
.items():
389 sig
= getattr(self
, name
)
390 comb
+= sig
.eq(value
[0:-1])
392 # link signals for all field forms
393 forms
= self
.form_names
395 sf
= self
.sigforms
[form
]
396 fields
= self
.fields
.instrs
[form
]
397 for k
, value
in fields
.items():
399 comb
+= sig
.eq(value
[0:-1])
404 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
407 ####################################################
408 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
410 def create_pdecode():
411 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
414 # minor 19 has extra patterns
416 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
417 opint
=True, bitsel
=(1, 11), suffix
=None, subdecoders
=[]))
418 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
419 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]))
424 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
425 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
426 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
427 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
428 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
429 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
430 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
431 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
434 # top level: extra merged with major
436 opcodes
= get_csv("major.csv")
437 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
438 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
439 opcodes
= get_csv("extra.csv")
440 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
441 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
443 return TopPowerDecoder(32, dec
)
446 if __name__
== '__main__':
447 pdecode
= create_pdecode()
448 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
449 with
open("decoder.il", "w") as f
: