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
, RC
, LdstLen
,
87 CryIn
, get_csv
, single_bit_flags
,
88 get_signal_name
, default_values
)
89 from soc
.decoder
.power_fields
import DecodeFields
90 from soc
.decoder
.power_fieldsn
import SigDecode
, SignalBitRange
93 # key data structure in which the POWER decoder is specified,
94 # in a hierarchical fashion
95 Subdecoder
= namedtuple("Subdecoder",
96 ["pattern", # the major pattern to search for (e.g. major opcode)
97 "opcodes", # a dictionary of minor patterns to find
98 "opint", # true => the pattern must not be in "10----11" format
99 "bitsel", # the bits (as a range) against which "pattern" matches
100 "suffix", # shift the opcode down before decoding
101 "subdecoders" # list of further subdecoders for *additional* matches,
102 # *ONLY* after "pattern" has *ALSO* been matched against.
107 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
109 this is an internal data structure, set up by reading CSV files
110 (which uses _eq to initialise each instance, not eq)
112 the "public" API (as far as actual usage as a useful decoder is concerned)
113 is Decode2ToExecute1Type
117 self
.function_unit
= Signal(Function
, reset_less
=True)
118 self
.internal_op
= Signal(InternalOp
, reset_less
=True)
119 self
.form
= Signal(Form
, reset_less
=True)
120 self
.in1_sel
= Signal(In1Sel
, reset_less
=True)
121 self
.in2_sel
= Signal(In2Sel
, reset_less
=True)
122 self
.in3_sel
= Signal(In3Sel
, reset_less
=True)
123 self
.out_sel
= Signal(OutSel
, reset_less
=True)
124 self
.ldst_len
= Signal(LdstLen
, reset_less
=True)
125 self
.rc_sel
= Signal(RC
, reset_less
=True)
126 self
.cry_in
= Signal(CryIn
, reset_less
=True)
127 for bit
in single_bit_flags
:
128 name
= get_signal_name(bit
)
129 setattr(self
, name
, Signal(reset_less
=True, name
=name
))
131 def _eq(self
, row
=None):
134 # TODO: this conversion process from a dict to an object
135 # should really be done using e.g. namedtuple and then
137 res
= [self
.function_unit
.eq(Function
[row
['unit']]),
138 self
.form
.eq(Form
[row
['form']]),
139 self
.internal_op
.eq(InternalOp
[row
['internal op']]),
140 self
.in1_sel
.eq(In1Sel
[row
['in1']]),
141 self
.in2_sel
.eq(In2Sel
[row
['in2']]),
142 self
.in3_sel
.eq(In3Sel
[row
['in3']]),
143 self
.out_sel
.eq(OutSel
[row
['out']]),
144 self
.ldst_len
.eq(LdstLen
[row
['ldst len']]),
145 self
.rc_sel
.eq(RC
[row
['rc']]),
146 self
.cry_in
.eq(CryIn
[row
['cry in']]),
148 for bit
in single_bit_flags
:
149 sig
= getattr(self
, get_signal_name(bit
))
150 res
.append(sig
.eq(int(row
.get(bit
, 0))))
153 def eq(self
, otherop
):
154 res
= [self
.function_unit
.eq(otherop
.function_unit
),
155 self
.form
.eq(otherop
.form
),
156 self
.internal_op
.eq(otherop
.internal_op
),
157 self
.in1_sel
.eq(otherop
.in1_sel
),
158 self
.in2_sel
.eq(otherop
.in2_sel
),
159 self
.in3_sel
.eq(otherop
.in3_sel
),
160 self
.out_sel
.eq(otherop
.out_sel
),
161 self
.rc_sel
.eq(otherop
.rc_sel
),
162 self
.ldst_len
.eq(otherop
.ldst_len
),
163 self
.cry_in
.eq(otherop
.cry_in
)]
164 for bit
in single_bit_flags
:
165 sig
= getattr(self
, get_signal_name(bit
))
166 res
.append(sig
.eq(getattr(otherop
, get_signal_name(bit
))))
170 regular
= [self
.function_unit
,
179 single_bit_ports
= [getattr(self
, get_signal_name(x
))
180 for x
in single_bit_flags
]
181 return regular
+ single_bit_ports
184 class PowerDecoder(Elaboratable
):
185 """PowerDecoder - decodes an incoming opcode into the type of operation
188 def __init__(self
, width
, dec
):
189 if not isinstance(dec
, list):
192 self
.opcode_in
= Signal(width
, reset_less
=True)
196 if d
.suffix
is not None and d
.suffix
>= width
:
200 def suffix_mask(self
, d
):
201 return ((1 << d
.suffix
) - 1)
203 def divide_opcodes(self
, d
):
205 mask
= self
.suffix_mask(d
)
206 print("mask", hex(mask
))
207 for row
in d
.opcodes
:
208 opcode
= row
['opcode']
209 if d
.opint
and '-' not in opcode
:
210 opcode
= int(opcode
, 0)
212 opcode
= opcode
>> d
.suffix
213 if key
not in divided
:
217 divided
[key
].append(r
)
220 def elaborate(self
, platform
):
224 # note: default opcode is "illegal" as this is a combinatorial block
225 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
227 # go through the list of CSV decoders first
229 opcode_switch
= Signal(d
.bitsel
[1] - d
.bitsel
[0],
231 comb
+= opcode_switch
.eq(self
.opcode_in
[d
.bitsel
[0]:d
.bitsel
[1]])
233 opcodes
= self
.divide_opcodes(d
)
234 opc_in
= Signal(d
.suffix
, reset_less
=True)
235 comb
+= opc_in
.eq(opcode_switch
[:d
.suffix
])
236 # begin the dynamic Switch statement here
237 with m
.Switch(opc_in
):
238 for key
, row
in opcodes
.items():
239 bitsel
= (d
.suffix
+d
.bitsel
[0], d
.bitsel
[1])
240 sd
= Subdecoder(pattern
=None, opcodes
=row
,
241 bitsel
=bitsel
, suffix
=None,
242 opint
=False, subdecoders
=[])
243 subdecoder
= PowerDecoder(width
=32, dec
=sd
)
244 setattr(m
.submodules
, "dec_sub%d" % key
, subdecoder
)
245 comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
246 # add in the dynamic Case statement here
248 comb
+= self
.op
.eq(subdecoder
.op
)
250 # TODO: arguments, here (all of them) need to be a list.
251 # a for-loop around the *list* of decoder args.
252 with m
.Switch(opcode_switch
):
253 self
.handle_subdecoders(m
, d
)
254 for row
in d
.opcodes
:
255 opcode
= row
['opcode']
256 if d
.opint
and '-' not in opcode
:
257 opcode
= int(opcode
, 0)
260 # add in the dynamic Case statement here
262 comb
+= self
.op
._eq
(row
)
265 def handle_subdecoders(self
, m
, d
):
266 for dec
in d
.subdecoders
:
267 subdecoder
= PowerDecoder(self
.width
, dec
)
268 if isinstance(dec
, list): # XXX HACK: take first pattern
270 setattr(m
.submodules
, "dec%d" % dec
.pattern
, subdecoder
)
271 m
.d
.comb
+= subdecoder
.opcode_in
.eq(self
.opcode_in
)
272 with m
.Case(dec
.pattern
):
273 m
.d
.comb
+= self
.op
.eq(subdecoder
.op
)
276 return [self
.opcode_in
] + self
.op
.ports()
279 class TopPowerDecoder(PowerDecoder
):
282 top-level hierarchical decoder for POWER ISA
283 bigendian dynamically switches between big and little endian decoding
284 (reverses byte order). See V3.0B p44 1.11.2
287 def __init__(self
, width
, dec
):
288 PowerDecoder
.__init
__(self
, width
, dec
)
289 self
.fields
= df
= DecodeFields(SignalBitRange
, [self
.opcode_in
])
290 self
.fields
.create_specs()
291 self
.raw_opcode_in
= Signal
.like(self
.opcode_in
, reset_less
=True)
292 self
.bigendian
= Signal(reset_less
=True)
294 for name
, value
in self
.fields
.common_fields
.items():
295 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
296 setattr(self
, name
, sig
)
298 # create signals for all field forms
299 self
.form_names
= forms
= self
.fields
.instrs
.keys()
302 fields
= self
.fields
.instrs
[form
]
304 Fields
= namedtuple("Fields", fk
)
306 for k
, value
in fields
.items():
307 name
= "%s_%s" % (form
, k
)
308 sig
= Signal(value
[0:-1].shape(), reset_less
=True, name
=name
)
311 setattr(self
, "Form%s" % form
, instr
)
312 self
.sigforms
[form
] = instr
314 def elaborate(self
, platform
):
315 m
= PowerDecoder
.elaborate(self
, platform
)
317 raw_be
= self
.raw_opcode_in
319 for i
in range(0, self
.width
, 8):
320 l
.append(raw_be
[i
:i
+8])
323 comb
+= self
.opcode_in
.eq(Mux(self
.bigendian
, raw_be
, raw_le
))
325 # add all signal from commonly-used fields
326 for name
, value
in self
.fields
.common_fields
.items():
327 sig
= getattr(self
, name
)
328 comb
+= sig
.eq(value
[0:-1])
330 # link signals for all field forms
331 forms
= self
.form_names
333 sf
= self
.sigforms
[form
]
334 fields
= self
.fields
.instrs
[form
]
335 for k
, value
in fields
.items():
337 comb
+= sig
.eq(value
[0:-1])
342 return [self
.raw_opcode_in
, self
.bigendian
] + PowerDecoder
.ports(self
)
345 ####################################################
346 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
348 def create_pdecode():
349 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
352 # minor 19 has extra patterns
354 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19.csv"),
355 opint
=True, bitsel
=(1, 11), suffix
=None, subdecoders
=[]))
356 m19
.append(Subdecoder(pattern
=19, opcodes
=get_csv("minor_19_00000.csv"),
357 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]))
362 Subdecoder(pattern
=30, opcodes
=get_csv("minor_30.csv"),
363 opint
=True, bitsel
=(1, 6), suffix
=None, subdecoders
=[]),
364 Subdecoder(pattern
=31, opcodes
=get_csv("minor_31.csv"),
365 opint
=True, bitsel
=(1, 11), suffix
=0b00101, subdecoders
=[]),
366 Subdecoder(pattern
=58, opcodes
=get_csv("minor_58.csv"),
367 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
368 Subdecoder(pattern
=62, opcodes
=get_csv("minor_62.csv"),
369 opint
=True, bitsel
=(0, 2), suffix
=None, subdecoders
=[]),
372 # top level: extra merged with major
374 opcodes
= get_csv("major.csv")
375 dec
.append(Subdecoder(pattern
=None, opint
=True, opcodes
=opcodes
,
376 bitsel
=(26, 32), suffix
=None, subdecoders
=pminor
))
377 opcodes
= get_csv("extra.csv")
378 dec
.append(Subdecoder(pattern
=None, opint
=False, opcodes
=opcodes
,
379 bitsel
=(0, 32), suffix
=None, subdecoders
=[]))
381 return TopPowerDecoder(32, dec
)
384 if __name__
== '__main__':
385 pdecode
= create_pdecode()
386 vl
= rtlil
.convert(pdecode
, ports
=pdecode
.ports())
387 with
open("decoder.il", "w") as f
: