1 """Cascading Power ISA Decoder
3 This module uses CSV tables in a hierarchical/peer cascading fashion,
4 to create a multi-level instruction decoder by recognising appropriate
5 patterns. The output is a wide, flattened (1-level) series of bitfields,
6 suitable for a simple RISC engine.
8 This is based on Anton Blanchard's excellent microwatt work:
9 https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
11 The basic principle is that the python code does the heavy lifting
12 (reading the CSV files, constructing the hierarchy), creating the HDL
13 AST with for-loops generating switch-case statements.
15 Where "normal" HDL would do this, in laborious excruciating detail:
17 switch (opcode & major_mask_bits):
18 case opcode_2: decode_opcode_2()
20 switch (opcode & minor_19_mask_bits)
21 case minor_opcode_19_operation_X:
22 case minor_opcode_19_operation_y:
24 we take *full* advantage of the decoupling between python and the
25 nmigen AST data structure, to do this:
27 with m.Switch(opcode & self.mask):
28 for case_bitmask in subcases:
29 with m.If(opcode & case_bitmask): {do_something}
31 this includes specifying the information sufficient to perform subdecoding.
35 the full hierarchical tree for decoding POWER9 is specified here
39 takes a *list* of CSV files with an associated bit-range that it
40 is requested to match against the "opcode" row of the CSV file.
41 This pattern can be either an integer, a binary number, *or* a
42 wildcard nmigen Case pattern of the form "001--1-100".
46 these are *additional* cases with further decoding. The "pattern"
47 argument is specified as one of the Case statements (a peer of the
48 opcode row in the CSV file), and thus further fields of the opcode
49 may be decoded giving increasing levels of detail.
53 [ (extra.csv: bit-fields entire 32-bit range
55 000000---------------01000000000 -> ILLEGAL instruction
56 01100000000000000000000000000000 -> SIM_CONFIG instruction
57 ................................ ->
59 (major.csv: first 6 bits ONLY
61 001100 -> ALU,OP_ADD (add)
62 001101 -> ALU,OP_ADD (another type of add)
66 001011 this must match *MAJOR*.CSV
67 [ (minor_19.csv: bits 21 through 30 inclusive:
69 0b0000000000 -> ALU,OP_MCRF
72 (minor_19_00000.csv: bits 21 through 25 inclusive:
74 0b00010 -> ALU,add_pcis
82 from collections
import namedtuple
83 from nmigen
import Module
, Elaboratable
, Signal
, Cat
, Mux
84 from nmigen
.cli
import rtlil
85 from soc
.decoder
.power_enums
import (Function
, Form
, MicrOp
,
86 In1Sel
, In2Sel
, In3Sel
, OutSel
,
87 RC
, LdstLen
, LDSTMode
, CryIn
, get_csv
,
88 single_bit_flags
, CRInSel
,
89 CROutSel
, get_signal_name
,
90 default_values
, insns
, asmidx
)
91 from soc
.decoder
.power_fields
import DecodeFields
92 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
95 # key data structure in which the POWER decoder is specified,
96 # in a hierarchical fashion
97 Subdecoder
= namedtuple("Subdecoder",
98 ["pattern", # the major pattern to search for (e.g. major opcode)
99 "opcodes", # a dictionary of minor patterns to find
100 "opint", # true => the pattern must not be in "10----11" format
101 # the bits (as a range) against which "pattern" matches
103 "suffix", # shift the opcode down before decoding
104 "subdecoders" # list of further subdecoders for *additional* matches,
105 # *ONLY* after "pattern" has *ALSO* been matched against.
110 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
112 this is an internal data structure, set up by reading CSV files
113 (which uses _eq to initialise each instance, not eq)
115 the "public" API (as far as actual usage as a useful decoder is concerned)
116 is Decode2ToExecute1Type
119 def __init__(self
, incl_asm
=True):
120 self
.function_unit
= Signal(Function
, reset_less
=True)
121 self
.internal_op
= Signal(MicrOp
, reset_less
=True)
122 self
.form
= Signal(Form
, reset_less
=True)
123 if incl_asm
: # for simulator only
124 self
.asmcode
= Signal(8, reset_less
=True)
125 self
.in1_sel
= Signal(In1Sel
, reset_less
=True)
126 self
.in2_sel
= Signal(In2Sel
, reset_less
=True)
127 self
.in3_sel
= Signal(In3Sel
, reset_less
=True)
128 self
.out_sel
= Signal(OutSel
, reset_less
=True)
129 self
.cr_in
= Signal(CRInSel
, reset_less
=True)
130 self
.cr_out
= Signal(CROutSel
, reset_less
=True)
131 self
.ldst_len
= Signal(LdstLen
, reset_less
=True)
132 self
.upd
= Signal(LDSTMode
, reset_less
=True)
133 self
.rc_sel
= Signal(RC
, reset_less
=True)
134 self
.cry_in
= Signal(CryIn
, reset_less
=True)
135 for bit
in single_bit_flags
:
136 name
= get_signal_name(bit
)
137 setattr(self
, name
, Signal(reset_less
=True, name
=name
))
139 def _eq(self
, row
=None):
142 # TODO: this conversion process from a dict to an object
143 # should really be done using e.g. namedtuple and then
145 if False: # debugging
146 if row
['CR in'] == '1':
150 if row
['CR out'] == '0':
155 ldst_mode
= row
['upd']
156 if ldst_mode
.isdigit():
157 ldst_mode
= LDSTMode(int(ldst_mode
))
159 ldst_mode
= LDSTMode
[ldst_mode
]
160 res
= [self
.function_unit
.eq(Function
[row
['unit']]),
161 self
.form
.eq(Form
[row
['form']]),
162 self
.internal_op
.eq(MicrOp
[row
['internal op']]),
163 self
.in1_sel
.eq(In1Sel
[row
['in1']]),
164 self
.in2_sel
.eq(In2Sel
[row
['in2']]),
165 self
.in3_sel
.eq(In3Sel
[row
['in3']]),
166 self
.out_sel
.eq(OutSel
[row
['out']]),
167 self
.cr_in
.eq(CRInSel
[row
['CR in']]),
168 self
.cr_out
.eq(CROutSel
[row
['CR out']]),
169 self
.ldst_len
.eq(LdstLen
[row
['ldst len']]),
170 self
.upd
.eq(ldst_mode
),
171 self
.rc_sel
.eq(RC
[row
['rc']]),
172 self
.cry_in
.eq(CryIn
[row
['cry in']]),
176 asmcode
= row
['comment']
177 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
178 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
179 for bit
in single_bit_flags
:
180 sig
= getattr(self
, get_signal_name(bit
))
181 res
.append(sig
.eq(int(row
.get(bit
, 0))))
184 def eq(self
, otherop
):
185 res
= [self
.function_unit
.eq(otherop
.function_unit
),
186 self
.form
.eq(otherop
.form
),
187 self
.internal_op
.eq(otherop
.internal_op
),
188 self
.in1_sel
.eq(otherop
.in1_sel
),
189 self
.in2_sel
.eq(otherop
.in2_sel
),
190 self
.in3_sel
.eq(otherop
.in3_sel
),
191 self
.out_sel
.eq(otherop
.out_sel
),
192 self
.cr_in
.eq(otherop
.cr_in
),
193 self
.cr_out
.eq(otherop
.cr_out
),
194 self
.rc_sel
.eq(otherop
.rc_sel
),
195 self
.ldst_len
.eq(otherop
.ldst_len
),
196 self
.upd
.eq(otherop
.upd
),
197 self
.cry_in
.eq(otherop
.cry_in
)]
198 for bit
in single_bit_flags
:
199 sig
= getattr(self
, get_signal_name(bit
))
200 res
.append(sig
.eq(getattr(otherop
, get_signal_name(bit
))))
201 if hasattr(self
, "asmcode"):
202 res
.append(self
.asmcode
.eq(otherop
.asmcode
))
206 regular
= [self
.function_unit
,
217 if hasattr(self
, "asmcode"):
218 regular
.append(self
.asmcode
)
219 single_bit_ports
= [getattr(self
, get_signal_name(x
))
220 for x
in single_bit_flags
]
221 return regular
+ single_bit_ports
224 class PowerDecoder(Elaboratable
):
225 """PowerDecoder - decodes an incoming opcode into the type of operation
228 def __init__(self
, width
, dec
):
229 if not isinstance(dec
, list):
232 self
.opcode_in
= Signal(width
, reset_less
=True)
236 if d
.suffix
is not None and d
.suffix
>= width
:
240 def suffix_mask(self
, d
):
241 return ((1 << d
.suffix
) - 1)
243 def divide_opcodes(self
, d
):
245 mask
= self
.suffix_mask(d
)
246 print("mask", hex(mask
))
247 for row
in d
.opcodes
:
248 opcode
= row
['opcode']
249 if d
.opint
and '-' not in opcode
:
250 opcode
= int(opcode
, 0)
252 opcode
= opcode
>> d
.suffix
253 if key
not in divided
:
257 divided
[key
].append(r
)
260 def elaborate(self
, platform
):
264 # note: default opcode is "illegal" as this is a combinatorial block
265 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
267 # go through the list of CSV decoders first
269 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
271 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
273 opcodes
= self
.divide_opcodes(d
)
274 opc_in
= Signal(d
.suffix
, reset_less
=True)
275 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
276 # begin the dynamic Switch statement here
277 with m
.Switch(opc_in
):
278 for key
, row
in opcodes
.items():
279 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
280 sd
= Subdecoder(pattern
=None, opcodes
=row
,
281 bitsel
=bitsel
, suffix
=None,
282 opint
=False, subdecoders
=[])
283 subdecoder
= PowerDecoder(width
=32, dec
=sd
)
284 setattr(m
.submodules
, "dec_sub%d" % key
, subdecoder
)
285 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
286 # add in the dynamic Case statement here
288 comb
+= self
.op
.eq(subdecoder
.op
)
290 # TODO: arguments, here (all of them) need to be a list.
291 # a for-loop around the *list* of decoder args.
292 with m
.Switch(opcode_switch
):
293 self
.handle_subdecoders(m
, d
)
294 for row
in d
.opcodes
:
295 opcode
= row
['opcode']
296 if d
.opint
and '-' not in opcode
:
297 opcode
= int(opcode
, 0)
300 # add in the dynamic Case statement here
302 comb
+= self
.op
._eq
(row
)
305 def handle_subdecoders(self
, m
, d
):
306 for dec
in d
.subdecoders
:
307 subdecoder
= PowerDecoder(self
.width
, dec
)
308 if isinstance(dec
, list): # XXX HACK: take first pattern
310 setattr(m
.submodules
, "dec%d" % dec
.pattern
, subdecoder
)
311 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
312 with m
.Case(dec
.pattern
):
313 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
316 return [self
.opcode_in
] + self
.op
.ports()
319 class TopPowerDecoder(PowerDecoder
):
322 top-level hierarchical decoder for POWER ISA
323 bigendian dynamically switches between big and little endian decoding
324 (reverses byte order). See V3.0B p44 1.11.2
327 def __init__(self
, width
, dec
):
328 PowerDecoder
.__init
__(self
, width
, dec
)
329 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
330 self
.fields
.create_specs()
331 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
332 self
.bigendian
= Signal(reset_less
=True)
334 for name
, value
in self
.fields
.common_fields
.items():
335 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
336 setattr(self
, name
, sig
)
338 # create signals for all field forms
339 self
.form_names
= forms
= self
.fields
.instrs
.keys()
342 fields
= self
.fields
.instrs
[form
]
344 Fields
= namedtuple("Fields", fk
)
346 for k
, value
in fields
.items():
347 name
= "%s_%s" % (form
, k
)
348 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
351 setattr(self
, "Form%s" % form
, instr
)
352 self
.sigforms
[form
] = instr
354 def elaborate(self
, platform
):
355 m
= PowerDecoder
.elaborate(self
, platform
)
357 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
358 raw_le
= self
.raw_opcode_in
360 for i
in range(0, self
.width
, 8):
361 l
.append(raw_le
[i
:i
+8])
364 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
366 # add all signal from commonly-used fields
367 for name
, value
in self
.fields
.common_fields
.items():
368 sig
= getattr(self
, name
)
369 comb
+= sig
.eq(value
[0:-1])
371 # link signals for all field forms
372 forms
= self
.form_names
374 sf
= self
.sigforms
[form
]
375 fields
= self
.fields
.instrs
[form
]
376 for k
, value
in fields
.items():
378 comb
+= sig
.eq(value
[0:-1])
383 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
386 ####################################################
387 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
389 def create_pdecode():
390 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
393 # minor 19 has extra patterns
395 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
396 opint
=True, bitsel
=(1, 11), suffix
=None, subdecoders
=[]))
397 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
398 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]))
403 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
404 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
405 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
406 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
407 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
408 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
409 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
410 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
413 # top level: extra merged with major
415 opcodes
= get_csv("major.csv")
416 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
417 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
418 opcodes
= get_csv("extra.csv")
419 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
420 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
422 return TopPowerDecoder(32, dec
)
425 if __name__
== '__main__':
426 pdecode
= create_pdecode()
427 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
428 with
open("decoder.il", "w") as f
: