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.
116 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
118 this is an internal data structure, set up by reading CSV files
119 (which uses _eq to initialise each instance, not eq)
121 the "public" API (as far as actual usage as a useful decoder is concerned)
122 is Decode2ToExecute1Type
125 def __init__(self
, incl_asm
=True):
126 self
.function_unit
= Signal(Function
, reset_less
=True)
127 self
.internal_op
= Signal(MicrOp
, reset_less
=True)
128 self
.form
= Signal(Form
, reset_less
=True)
129 if incl_asm
: # for simulator only
130 self
.asmcode
= Signal(8, reset_less
=True)
131 self
.in1_sel
= Signal(In1Sel
, reset_less
=True)
132 self
.in2_sel
= Signal(In2Sel
, reset_less
=True)
133 self
.in3_sel
= Signal(In3Sel
, reset_less
=True)
134 self
.out_sel
= Signal(OutSel
, reset_less
=True)
135 self
.cr_in
= Signal(CRInSel
, reset_less
=True)
136 self
.cr_out
= Signal(CROutSel
, reset_less
=True)
137 self
.ldst_len
= Signal(LdstLen
, reset_less
=True)
138 self
.upd
= Signal(LDSTMode
, reset_less
=True)
139 self
.rc_sel
= Signal(RC
, reset_less
=True)
140 self
.cry_in
= Signal(CryIn
, reset_less
=True)
141 for bit
in single_bit_flags
:
142 name
= get_signal_name(bit
)
143 setattr(self
, name
, Signal(reset_less
=True, name
=name
))
145 def _eq(self
, row
=None):
148 # TODO: this conversion process from a dict to an object
149 # should really be done using e.g. namedtuple and then
151 if False: # debugging
152 if row
['CR in'] == '1':
156 if row
['CR out'] == '0':
161 ldst_mode
= row
['upd']
162 if ldst_mode
.isdigit():
163 ldst_mode
= LDSTMode(int(ldst_mode
))
165 ldst_mode
= LDSTMode
[ldst_mode
]
166 res
= [self
.function_unit
.eq(Function
[row
['unit']]),
167 self
.form
.eq(Form
[row
['form']]),
168 self
.internal_op
.eq(MicrOp
[row
['internal op']]),
169 self
.in1_sel
.eq(In1Sel
[row
['in1']]),
170 self
.in2_sel
.eq(In2Sel
[row
['in2']]),
171 self
.in3_sel
.eq(In3Sel
[row
['in3']]),
172 self
.out_sel
.eq(OutSel
[row
['out']]),
173 self
.cr_in
.eq(CRInSel
[row
['CR in']]),
174 self
.cr_out
.eq(CROutSel
[row
['CR out']]),
175 self
.ldst_len
.eq(LdstLen
[row
['ldst len']]),
176 self
.upd
.eq(ldst_mode
),
177 self
.rc_sel
.eq(RC
[row
['rc']]),
178 self
.cry_in
.eq(CryIn
[row
['cry in']]),
182 asmcode
= row
['comment']
183 if hasattr(self
, "asmcode") and asmcode
in asmidx
:
184 res
.append(self
.asmcode
.eq(asmidx
[asmcode
]))
185 for bit
in single_bit_flags
:
186 sig
= getattr(self
, get_signal_name(bit
))
187 res
.append(sig
.eq(int(row
.get(bit
, 0))))
190 def eq(self
, otherop
):
191 res
= [self
.function_unit
.eq(otherop
.function_unit
),
192 self
.form
.eq(otherop
.form
),
193 self
.internal_op
.eq(otherop
.internal_op
),
194 self
.in1_sel
.eq(otherop
.in1_sel
),
195 self
.in2_sel
.eq(otherop
.in2_sel
),
196 self
.in3_sel
.eq(otherop
.in3_sel
),
197 self
.out_sel
.eq(otherop
.out_sel
),
198 self
.cr_in
.eq(otherop
.cr_in
),
199 self
.cr_out
.eq(otherop
.cr_out
),
200 self
.rc_sel
.eq(otherop
.rc_sel
),
201 self
.ldst_len
.eq(otherop
.ldst_len
),
202 self
.upd
.eq(otherop
.upd
),
203 self
.cry_in
.eq(otherop
.cry_in
)]
204 for bit
in single_bit_flags
:
205 sig
= getattr(self
, get_signal_name(bit
))
206 res
.append(sig
.eq(getattr(otherop
, get_signal_name(bit
))))
207 if hasattr(self
, "asmcode"):
208 res
.append(self
.asmcode
.eq(otherop
.asmcode
))
212 regular
= [self
.function_unit
,
223 if hasattr(self
, "asmcode"):
224 regular
.append(self
.asmcode
)
225 single_bit_ports
= [getattr(self
, get_signal_name(x
))
226 for x
in single_bit_flags
]
227 return regular
+ single_bit_ports
230 class PowerDecoder(Elaboratable
):
231 """PowerDecoder - decodes an incoming opcode into the type of operation
234 def __init__(self
, width
, dec
):
235 if not isinstance(dec
, list):
238 self
.opcode_in
= Signal(width
, reset_less
=True)
242 if d
.suffix
is not None and d
.suffix
>= width
:
246 def suffix_mask(self
, d
):
247 return ((1 << d
.suffix
) - 1)
249 def divide_opcodes(self
, d
):
251 mask
= self
.suffix_mask(d
)
252 print("mask", hex(mask
))
253 for row
in d
.opcodes
:
254 opcode
= row
['opcode']
255 if d
.opint
and '-' not in opcode
:
256 opcode
= int(opcode
, 0)
258 opcode
= opcode
>> d
.suffix
259 if key
not in divided
:
263 divided
[key
].append(r
)
266 def elaborate(self
, platform
):
270 # note: default opcode is "illegal" as this is a combinatorial block
271 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
273 # go through the list of CSV decoders first
275 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
277 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
279 opcodes
= self
.divide_opcodes(d
)
280 opc_in
= Signal(d
.suffix
, reset_less
=True)
281 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
282 # begin the dynamic Switch statement here
283 with m
.Switch(opc_in
):
284 for key
, row
in opcodes
.items():
285 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
286 sd
= Subdecoder(pattern
=None, opcodes
=row
,
287 bitsel
=bitsel
, suffix
=None,
288 opint
=False, subdecoders
=[])
289 subdecoder
= PowerDecoder(width
=32, dec
=sd
)
290 setattr(m
.submodules
, "dec_sub%d" % key
, subdecoder
)
291 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
292 # add in the dynamic Case statement here
294 comb
+= self
.op
.eq(subdecoder
.op
)
296 # TODO: arguments, here (all of them) need to be a list.
297 # a for-loop around the *list* of decoder args.
298 with m
.Switch(opcode_switch
):
299 self
.handle_subdecoders(m
, d
)
300 for row
in d
.opcodes
:
301 opcode
= row
['opcode']
302 if d
.opint
and '-' not in opcode
:
303 opcode
= int(opcode
, 0)
306 # add in the dynamic Case statement here
308 comb
+= self
.op
._eq
(row
)
311 def handle_subdecoders(self
, m
, d
):
312 for dec
in d
.subdecoders
:
313 subdecoder
= PowerDecoder(self
.width
, dec
)
314 if isinstance(dec
, list): # XXX HACK: take first pattern
316 setattr(m
.submodules
, "dec%d" % dec
.pattern
, subdecoder
)
317 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
318 with m
.Case(dec
.pattern
):
319 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
322 return [self
.opcode_in
] + self
.op
.ports()
325 class TopPowerDecoder(PowerDecoder
):
328 top-level hierarchical decoder for POWER ISA
329 bigendian dynamically switches between big and little endian decoding
330 (reverses byte order). See V3.0B p44 1.11.2
333 def __init__(self
, width
, dec
):
334 PowerDecoder
.__init
__(self
, width
, dec
)
335 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
336 self
.fields
.create_specs()
337 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
338 self
.bigendian
= Signal(reset_less
=True)
340 for name
, value
in self
.fields
.common_fields
.items():
341 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
342 setattr(self
, name
, sig
)
344 # create signals for all field forms
345 self
.form_names
= forms
= self
.fields
.instrs
.keys()
348 fields
= self
.fields
.instrs
[form
]
350 Fields
= namedtuple("Fields", fk
)
352 for k
, value
in fields
.items():
353 name
= "%s_%s" % (form
, k
)
354 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
357 setattr(self
, "Form%s" % form
, instr
)
358 self
.sigforms
[form
] = instr
360 def elaborate(self
, platform
):
361 m
= PowerDecoder
.elaborate(self
, platform
)
363 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
364 raw_le
= self
.raw_opcode_in
366 for i
in range(0, self
.width
, 8):
367 l
.append(raw_le
[i
:i
+8])
370 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
372 # add all signal from commonly-used fields
373 for name
, value
in self
.fields
.common_fields
.items():
374 sig
= getattr(self
, name
)
375 comb
+= sig
.eq(value
[0:-1])
377 # link signals for all field forms
378 forms
= self
.form_names
380 sf
= self
.sigforms
[form
]
381 fields
= self
.fields
.instrs
[form
]
382 for k
, value
in fields
.items():
384 comb
+= sig
.eq(value
[0:-1])
389 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
392 ####################################################
393 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
395 def create_pdecode():
396 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
399 # minor 19 has extra patterns
401 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
402 opint
=True, bitsel
=(1, 11), suffix
=None, subdecoders
=[]))
403 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
404 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]))
409 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
410 opint
=True, bitsel
=(1, 5), suffix
=None, subdecoders
=[]),
411 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
412 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
413 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
414 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
415 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
416 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
419 # top level: extra merged with major
421 opcodes
= get_csv("major.csv")
422 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
423 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
424 opcodes
= get_csv("extra.csv")
425 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
426 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
428 return TopPowerDecoder(32, dec
)
431 if __name__
== '__main__':
432 pdecode
= create_pdecode()
433 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
434 with
open("decoder.il", "w") as f
: