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
, InternalOp
,
86 In1Sel
, In2Sel
, In3Sel
, OutSel
,
87 RC
, LdstLen
, 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 "bitsel", # the bits (as a range) against which "pattern" matches
102 "suffix", # shift the opcode down before decoding
103 "subdecoders" # list of further subdecoders for *additional* matches,
104 # *ONLY* after "pattern" has *ALSO* been matched against.
109 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
111 this is an internal data structure, set up by reading CSV files
112 (which uses _eq to initialise each instance, not eq)
114 the "public" API (as far as actual usage as a useful decoder is concerned)
115 is Decode2ToExecute1Type
118 def __init__(self
, incl_asm
=True):
119 self
.function_unit
= Signal(Function
, reset_less
=True)
120 self
.internal_op
= Signal(InternalOp
, reset_less
=True)
121 self
.form
= Signal(Form
, reset_less
=True)
122 if incl_asm
: # for simulator only
123 self
.asmcode
= Signal(8, reset_less
=True)
124 self
.in1_sel
= Signal(In1Sel
, reset_less
=True)
125 self
.in2_sel
= Signal(In2Sel
, reset_less
=True)
126 self
.in3_sel
= Signal(In3Sel
, reset_less
=True)
127 self
.out_sel
= Signal(OutSel
, reset_less
=True)
128 self
.cr_in
= Signal(CRInSel
, reset_less
=True)
129 self
.cr_out
= Signal(CROutSel
, reset_less
=True)
130 self
.ldst_len
= Signal(LdstLen
, reset_less
=True)
131 self
.rc_sel
= Signal(RC
, reset_less
=True)
132 self
.cry_in
= Signal(CryIn
, reset_less
=True)
133 for bit
in single_bit_flags
:
134 name
= get_signal_name(bit
)
135 setattr(self
, name
, Signal(reset_less
=True, name
=name
))
137 def _eq(self
, row
=None):
140 # TODO: this conversion process from a dict to an object
141 # should really be done using e.g. namedtuple and then
143 if False: # debugging
144 if row
['CR in'] == '1':
145 import pdb
; pdb
.set_trace()
147 if row
['CR out'] == '0':
148 import pdb
; pdb
.set_trace()
151 res
= [self
.function_unit
.eq(Function
[row
['unit']]),
152 self
.form
.eq(Form
[row
['form']]),
153 self
.internal_op
.eq(InternalOp
[row
['internal op']]),
154 self
.in1_sel
.eq(In1Sel
[row
['in1']]),
155 self
.in2_sel
.eq(In2Sel
[row
['in2']]),
156 self
.in3_sel
.eq(In3Sel
[row
['in3']]),
157 self
.out_sel
.eq(OutSel
[row
['out']]),
158 self
.cr_in
.eq(CRInSel
[row
['CR in']]),
159 self
.cr_out
.eq(CROutSel
[row
['CR out']]),
160 self
.ldst_len
.eq(LdstLen
[row
['ldst len']]),
161 self
.rc_sel
.eq(RC
[row
['rc']]),
162 self
.cry_in
.eq(CryIn
[row
['cry in']]),
166 asmcode
= row
['comment']
167 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
168 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
169 for bit
in single_bit_flags
:
170 sig
= getattr(self
, get_signal_name(bit
))
171 res
.append(sig
.eq(int(row
.get(bit
, 0))))
174 def eq(self
, otherop
):
175 res
= [self
.function_unit
.eq(otherop
.function_unit
),
176 self
.form
.eq(otherop
.form
),
177 self
.internal_op
.eq(otherop
.internal_op
),
178 self
.in1_sel
.eq(otherop
.in1_sel
),
179 self
.in2_sel
.eq(otherop
.in2_sel
),
180 self
.in3_sel
.eq(otherop
.in3_sel
),
181 self
.out_sel
.eq(otherop
.out_sel
),
182 self
.cr_in
.eq(otherop
.cr_in
),
183 self
.cr_out
.eq(otherop
.cr_out
),
184 self
.rc_sel
.eq(otherop
.rc_sel
),
185 self
.ldst_len
.eq(otherop
.ldst_len
),
186 self
.cry_in
.eq(otherop
.cry_in
)]
187 for bit
in single_bit_flags
:
188 sig
= getattr(self
, get_signal_name(bit
))
189 res
.append(sig
.eq(getattr(otherop
, get_signal_name(bit
))))
190 if hasattr(self
, "asmcode"):
191 res
.append(self
.asmcode
.eq(otherop
.asmcode
))
195 regular
= [self
.function_unit
,
206 if hasattr(self
, "asmcode"):
207 regular
.append(self
.asmcode
)
208 single_bit_ports
= [getattr(self
, get_signal_name(x
))
209 for x
in single_bit_flags
]
210 return regular
+ single_bit_ports
213 class PowerDecoder(Elaboratable
):
214 """PowerDecoder - decodes an incoming opcode into the type of operation
217 def __init__(self
, width
, dec
):
218 if not isinstance(dec
, list):
221 self
.opcode_in
= Signal(width
, reset_less
=True)
225 if d
.suffix
is not None and d
.suffix
>= width
:
229 def suffix_mask(self
, d
):
230 return ((1 << d
.suffix
) - 1)
232 def divide_opcodes(self
, d
):
234 mask
= self
.suffix_mask(d
)
235 print("mask", hex(mask
))
236 for row
in d
.opcodes
:
237 opcode
= row
['opcode']
238 if d
.opint
and '-' not in opcode
:
239 opcode
= int(opcode
, 0)
241 opcode
= opcode
>> d
.suffix
242 if key
not in divided
:
246 divided
[key
].append(r
)
249 def elaborate(self
, platform
):
253 # note: default opcode is "illegal" as this is a combinatorial block
254 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
256 # go through the list of CSV decoders first
258 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
260 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
262 opcodes
= self
.divide_opcodes(d
)
263 opc_in
= Signal(d
.suffix
, reset_less
=True)
264 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
265 # begin the dynamic Switch statement here
266 with m
.Switch(opc_in
):
267 for key
, row
in opcodes
.items():
268 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
269 sd
= Subdecoder(pattern
=None, opcodes
=row
,
270 bitsel
=bitsel
, suffix
=None,
271 opint
=False, subdecoders
=[])
272 subdecoder
= PowerDecoder(width
=32, dec
=sd
)
273 setattr(m
.submodules
, "dec_sub%d" % key
, subdecoder
)
274 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
275 # add in the dynamic Case statement here
277 comb
+= self
.op
.eq(subdecoder
.op
)
279 # TODO: arguments, here (all of them) need to be a list.
280 # a for-loop around the *list* of decoder args.
281 with m
.Switch(opcode_switch
):
282 self
.handle_subdecoders(m
, d
)
283 for row
in d
.opcodes
:
284 opcode
= row
['opcode']
285 if d
.opint
and '-' not in opcode
:
286 opcode
= int(opcode
, 0)
289 # add in the dynamic Case statement here
291 comb
+= self
.op
._eq
(row
)
294 def handle_subdecoders(self
, m
, d
):
295 for dec
in d
.subdecoders
:
296 subdecoder
= PowerDecoder(self
.width
, dec
)
297 if isinstance(dec
, list): # XXX HACK: take first pattern
299 setattr(m
.submodules
, "dec%d" % dec
.pattern
, subdecoder
)
300 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
301 with m
.Case(dec
.pattern
):
302 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
305 return [self
.opcode_in
] + self
.op
.ports()
308 class TopPowerDecoder(PowerDecoder
):
311 top-level hierarchical decoder for POWER ISA
312 bigendian dynamically switches between big and little endian decoding
313 (reverses byte order). See V3.0B p44 1.11.2
316 def __init__(self
, width
, dec
):
317 PowerDecoder
.__init
__(self
, width
, dec
)
318 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
319 self
.fields
.create_specs()
320 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
321 self
.bigendian
= Signal(reset_less
=True)
323 for name
, value
in self
.fields
.common_fields
.items():
324 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
325 setattr(self
, name
, sig
)
327 # create signals for all field forms
328 self
.form_names
= forms
= self
.fields
.instrs
.keys()
331 fields
= self
.fields
.instrs
[form
]
333 Fields
= namedtuple("Fields", fk
)
335 for k
, value
in fields
.items():
336 name
= "%s_%s" % (form
, k
)
337 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
340 setattr(self
, "Form%s" % form
, instr
)
341 self
.sigforms
[form
] = instr
343 def elaborate(self
, platform
):
344 m
= PowerDecoder
.elaborate(self
, platform
)
346 # raw opcode in, byte-reverse it
347 raw_be
= self
.raw_opcode_in
349 for i
in range(0, self
.width
, 8):
350 l
.append(raw_be
[i
:i
+8])
353 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
355 # add all signal from commonly-used fields
356 for name
, value
in self
.fields
.common_fields
.items():
357 sig
= getattr(self
, name
)
358 comb
+= sig
.eq(value
[0:-1])
360 # link signals for all field forms
361 forms
= self
.form_names
363 sf
= self
.sigforms
[form
]
364 fields
= self
.fields
.instrs
[form
]
365 for k
, value
in fields
.items():
367 comb
+= sig
.eq(value
[0:-1])
372 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
375 ####################################################
376 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
378 def create_pdecode():
379 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
382 # minor 19 has extra patterns
384 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
385 opint
=True, bitsel
=(1, 11), suffix
=None, subdecoders
=[]))
386 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
387 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]))
392 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
393 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
394 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
395 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
396 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
397 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
398 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
399 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
402 # top level: extra merged with major
404 opcodes
= get_csv("major.csv")
405 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
406 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
407 opcodes
= get_csv("extra.csv")
408 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
409 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
411 return TopPowerDecoder(32, dec
)
414 if __name__
== '__main__':
415 pdecode
= create_pdecode()
416 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
417 with
open("decoder.il", "w") as f
: