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 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
85 from collections
import namedtuple
86 from nmigen
import Module
, Elaboratable
, Signal
, Cat
, Mux
87 from nmigen
.cli
import rtlil
88 from soc
.decoder
.power_enums
import (Function
, Form
, MicrOp
,
89 In1Sel
, In2Sel
, In3Sel
, OutSel
,
90 RC
, LdstLen
, LDSTMode
, CryIn
, get_csv
,
91 single_bit_flags
, CRInSel
,
92 CROutSel
, get_signal_name
,
93 default_values
, insns
, asmidx
)
94 from soc
.decoder
.power_fields
import DecodeFields
95 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
98 # key data structure in which the POWER decoder is specified,
99 # in a hierarchical fashion
100 Subdecoder
= namedtuple("Subdecoder",
101 ["pattern", # the major pattern to search for (e.g. major opcode)
102 "opcodes", # a dictionary of minor patterns to find
103 "opint", # true => the pattern must not be in "10----11" format
104 # the bits (as a range) against which "pattern" matches
106 "suffix", # shift the opcode down before decoding
107 "subdecoders" # list of further subdecoders for *additional* matches,
108 # *ONLY* after "pattern" has *ALSO* been matched against.
113 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
115 this is an internal data structure, set up by reading CSV files
116 (which uses _eq to initialise each instance, not eq)
118 the "public" API (as far as actual usage as a useful decoder is concerned)
119 is Decode2ToExecute1Type
122 def __init__(self
, incl_asm
=True):
123 self
.function_unit
= Signal(Function
, reset_less
=True)
124 self
.internal_op
= Signal(MicrOp
, reset_less
=True)
125 self
.form
= Signal(Form
, reset_less
=True)
126 if incl_asm
: # for simulator only
127 self
.asmcode
= Signal(8, reset_less
=True)
128 self
.in1_sel
= Signal(In1Sel
, reset_less
=True)
129 self
.in2_sel
= Signal(In2Sel
, reset_less
=True)
130 self
.in3_sel
= Signal(In3Sel
, reset_less
=True)
131 self
.out_sel
= Signal(OutSel
, reset_less
=True)
132 self
.cr_in
= Signal(CRInSel
, reset_less
=True)
133 self
.cr_out
= Signal(CROutSel
, reset_less
=True)
134 self
.ldst_len
= Signal(LdstLen
, reset_less
=True)
135 self
.upd
= Signal(LDSTMode
, reset_less
=True)
136 self
.rc_sel
= Signal(RC
, reset_less
=True)
137 self
.cry_in
= Signal(CryIn
, reset_less
=True)
138 for bit
in single_bit_flags
:
139 name
= get_signal_name(bit
)
140 setattr(self
, name
, Signal(reset_less
=True, name
=name
))
142 def _eq(self
, row
=None):
145 # TODO: this conversion process from a dict to an object
146 # should really be done using e.g. namedtuple and then
148 if False: # debugging
149 if row
['CR in'] == '1':
153 if row
['CR out'] == '0':
158 ldst_mode
= row
['upd']
159 if ldst_mode
.isdigit():
160 ldst_mode
= LDSTMode(int(ldst_mode
))
162 ldst_mode
= LDSTMode
[ldst_mode
]
163 res
= [self
.function_unit
.eq(Function
[row
['unit']]),
164 self
.form
.eq(Form
[row
['form']]),
165 self
.internal_op
.eq(MicrOp
[row
['internal op']]),
166 self
.in1_sel
.eq(In1Sel
[row
['in1']]),
167 self
.in2_sel
.eq(In2Sel
[row
['in2']]),
168 self
.in3_sel
.eq(In3Sel
[row
['in3']]),
169 self
.out_sel
.eq(OutSel
[row
['out']]),
170 self
.cr_in
.eq(CRInSel
[row
['CR in']]),
171 self
.cr_out
.eq(CROutSel
[row
['CR out']]),
172 self
.ldst_len
.eq(LdstLen
[row
['ldst len']]),
173 self
.upd
.eq(ldst_mode
),
174 self
.rc_sel
.eq(RC
[row
['rc']]),
175 self
.cry_in
.eq(CryIn
[row
['cry in']]),
179 asmcode
= row
['comment']
180 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
181 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
182 for bit
in single_bit_flags
:
183 sig
= getattr(self
, get_signal_name(bit
))
184 res
.append(sig
.eq(int(row
.get(bit
, 0))))
187 def eq(self
, otherop
):
188 res
= [self
.function_unit
.eq(otherop
.function_unit
),
189 self
.form
.eq(otherop
.form
),
190 self
.internal_op
.eq(otherop
.internal_op
),
191 self
.in1_sel
.eq(otherop
.in1_sel
),
192 self
.in2_sel
.eq(otherop
.in2_sel
),
193 self
.in3_sel
.eq(otherop
.in3_sel
),
194 self
.out_sel
.eq(otherop
.out_sel
),
195 self
.cr_in
.eq(otherop
.cr_in
),
196 self
.cr_out
.eq(otherop
.cr_out
),
197 self
.rc_sel
.eq(otherop
.rc_sel
),
198 self
.ldst_len
.eq(otherop
.ldst_len
),
199 self
.upd
.eq(otherop
.upd
),
200 self
.cry_in
.eq(otherop
.cry_in
)]
201 for bit
in single_bit_flags
:
202 sig
= getattr(self
, get_signal_name(bit
))
203 res
.append(sig
.eq(getattr(otherop
, get_signal_name(bit
))))
204 if hasattr(self
, "asmcode"):
205 res
.append(self
.asmcode
.eq(otherop
.asmcode
))
209 regular
= [self
.function_unit
,
220 if hasattr(self
, "asmcode"):
221 regular
.append(self
.asmcode
)
222 single_bit_ports
= [getattr(self
, get_signal_name(x
))
223 for x
in single_bit_flags
]
224 return regular
+ single_bit_ports
227 class PowerDecoder(Elaboratable
):
228 """PowerDecoder - decodes an incoming opcode into the type of operation
231 def __init__(self
, width
, dec
):
232 if not isinstance(dec
, list):
235 self
.opcode_in
= Signal(width
, reset_less
=True)
239 if d
.suffix
is not None and d
.suffix
>= width
:
243 def suffix_mask(self
, d
):
244 return ((1 << d
.suffix
) - 1)
246 def divide_opcodes(self
, d
):
248 mask
= self
.suffix_mask(d
)
249 print("mask", hex(mask
))
250 for row
in d
.opcodes
:
251 opcode
= row
['opcode']
252 if d
.opint
and '-' not in opcode
:
253 opcode
= int(opcode
, 0)
255 opcode
= opcode
>> d
.suffix
256 if key
not in divided
:
260 divided
[key
].append(r
)
263 def elaborate(self
, platform
):
267 # note: default opcode is "illegal" as this is a combinatorial block
268 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
270 # go through the list of CSV decoders first
272 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
274 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
276 opcodes
= self
.divide_opcodes(d
)
277 opc_in
= Signal(d
.suffix
, reset_less
=True)
278 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
279 # begin the dynamic Switch statement here
280 with m
.Switch(opc_in
):
281 for key
, row
in opcodes
.items():
282 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
283 sd
= Subdecoder(pattern
=None, opcodes
=row
,
284 bitsel
=bitsel
, suffix
=None,
285 opint
=False, subdecoders
=[])
286 subdecoder
= PowerDecoder(width
=32, dec
=sd
)
287 setattr(m
.submodules
, "dec_sub%d" % key
, subdecoder
)
288 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
289 # add in the dynamic Case statement here
291 comb
+= self
.op
.eq(subdecoder
.op
)
293 # TODO: arguments, here (all of them) need to be a list.
294 # a for-loop around the *list* of decoder args.
295 with m
.Switch(opcode_switch
):
296 self
.handle_subdecoders(m
, d
)
297 for row
in d
.opcodes
:
298 opcode
= row
['opcode']
299 if d
.opint
and '-' not in opcode
:
300 opcode
= int(opcode
, 0)
303 # add in the dynamic Case statement here
305 comb
+= self
.op
._eq
(row
)
308 def handle_subdecoders(self
, m
, d
):
309 for dec
in d
.subdecoders
:
310 subdecoder
= PowerDecoder(self
.width
, dec
)
311 if isinstance(dec
, list): # XXX HACK: take first pattern
313 setattr(m
.submodules
, "dec%d" % dec
.pattern
, subdecoder
)
314 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
315 with m
.Case(dec
.pattern
):
316 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
319 return [self
.opcode_in
] + self
.op
.ports()
322 class TopPowerDecoder(PowerDecoder
):
325 top-level hierarchical decoder for POWER ISA
326 bigendian dynamically switches between big and little endian decoding
327 (reverses byte order). See V3.0B p44 1.11.2
330 def __init__(self
, width
, dec
):
331 PowerDecoder
.__init
__(self
, width
, dec
)
332 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
333 self
.fields
.create_specs()
334 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
335 self
.bigendian
= Signal(reset_less
=True)
337 for name
, value
in self
.fields
.common_fields
.items():
338 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
339 setattr(self
, name
, sig
)
341 # create signals for all field forms
342 self
.form_names
= forms
= self
.fields
.instrs
.keys()
345 fields
= self
.fields
.instrs
[form
]
347 Fields
= namedtuple("Fields", fk
)
349 for k
, value
in fields
.items():
350 name
= "%s_%s" % (form
, k
)
351 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
354 setattr(self
, "Form%s" % form
, instr
)
355 self
.sigforms
[form
] = instr
357 def elaborate(self
, platform
):
358 m
= PowerDecoder
.elaborate(self
, platform
)
360 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
361 raw_le
= self
.raw_opcode_in
363 for i
in range(0, self
.width
, 8):
364 l
.append(raw_le
[i
:i
+8])
367 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
369 # add all signal from commonly-used fields
370 for name
, value
in self
.fields
.common_fields
.items():
371 sig
= getattr(self
, name
)
372 comb
+= sig
.eq(value
[0:-1])
374 # link signals for all field forms
375 forms
= self
.form_names
377 sf
= self
.sigforms
[form
]
378 fields
= self
.fields
.instrs
[form
]
379 for k
, value
in fields
.items():
381 comb
+= sig
.eq(value
[0:-1])
386 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
389 ####################################################
390 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
392 def create_pdecode():
393 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
396 # minor 19 has extra patterns
398 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
399 opint
=True, bitsel
=(1, 11), suffix
=None, subdecoders
=[]))
400 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
401 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]))
406 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
407 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
408 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
409 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
410 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
411 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
412 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
413 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
416 # top level: extra merged with major
418 opcodes
= get_csv("major.csv")
419 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
420 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
421 opcodes
= get_csv("extra.csv")
422 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
423 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
425 return TopPowerDecoder(32, dec
)
428 if __name__
== '__main__':
429 pdecode
= create_pdecode()
430 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
431 with
open("decoder.il", "w") as f
: