0b9ffc5bcdb9012d765574b0a4ce3909bf4c98db
[soc.git] / src / soc / decoder / power_decoder.py
1 """Cascading Power ISA Decoder
2
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.
7
8 This is based on Anton Blanchard's excellent microwatt work:
9 https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
10
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.
14
15 Where "normal" HDL would do this, in laborious excruciating detail:
16
17 switch (opcode & major_mask_bits):
18 case opcode_2: decode_opcode_2()
19 case opcode_19:
20 switch (opcode & minor_19_mask_bits)
21 case minor_opcode_19_operation_X:
22 case minor_opcode_19_operation_y:
23
24 we take *full* advantage of the decoupling between python and the
25 nmigen AST data structure, to do this:
26
27 with m.Switch(opcode & self.mask):
28 for case_bitmask in subcases:
29 with m.If(opcode & case_bitmask): {do_something}
30
31 this includes specifying the information sufficient to perform subdecoding.
32
33 create_pdecode()
34
35 the full hierarchical tree for decoding POWER9 is specified here
36
37 PowerDecoder
38
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".
43
44 Subdecoders
45
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.
50
51 Top Level:
52
53 [ (extra.csv: bit-fields entire 32-bit range
54 opcode -> matches
55 000000---------------01000000000 -> ILLEGAL instruction
56 01100000000000000000000000000000 -> SIM_CONFIG instruction
57 ................................ ->
58 ),
59 (major.csv: first 6 bits ONLY
60 opcode -> matches
61 001100 -> ALU,OP_ADD (add)
62 001101 -> ALU,OP_ADD (another type of add)
63 ...... -> ...
64 ...... -> ...
65 subdecoders:
66 001011 this must match *MAJOR*.CSV
67 [ (minor_19.csv: bits 21 through 30 inclusive:
68 opcode -> matches
69 0b0000000000 -> ALU,OP_MCRF
70 ............ -> ....
71 ),
72 (minor_19_00000.csv: bits 21 through 25 inclusive:
73 opcode -> matches
74 0b00010 -> ALU,add_pcis
75 )
76 ]
77 ),
78 ]
79
80 """
81
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
93
94
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.
105 ])
106
107
108 class PowerOp:
109 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
110
111 this is an internal data structure, set up by reading CSV files
112 (which uses _eq to initialise each instance, not eq)
113
114 the "public" API (as far as actual usage as a useful decoder is concerned)
115 is Decode2ToExecute1Type
116 """
117
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(7, 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))
136
137 def _eq(self, row=None):
138 if row is None:
139 row = default_values
140 # TODO: this conversion process from a dict to an object
141 # should really be done using e.g. namedtuple and then
142 # call eq not _eq
143 if row['CR in'] == '1':
144 import pdb; pdb.set_trace()
145 print(row)
146 if row['CR out'] == '0':
147 import pdb; pdb.set_trace()
148 print(row)
149 print(row)
150 res = [self.function_unit.eq(Function[row['unit']]),
151 self.form.eq(Form[row['form']]),
152 self.internal_op.eq(InternalOp[row['internal op']]),
153 self.in1_sel.eq(In1Sel[row['in1']]),
154 self.in2_sel.eq(In2Sel[row['in2']]),
155 self.in3_sel.eq(In3Sel[row['in3']]),
156 self.out_sel.eq(OutSel[row['out']]),
157 self.cr_in.eq(CRInSel[row['CR in']]),
158 self.cr_out.eq(CROutSel[row['CR out']]),
159 self.ldst_len.eq(LdstLen[row['ldst len']]),
160 self.rc_sel.eq(RC[row['rc']]),
161 self.cry_in.eq(CryIn[row['cry in']]),
162 ]
163 print (row.keys())
164 if hasattr(self, "asmcode"):
165 res.append(self.asmcode.eq(asmidx[row['comment']]))
166 for bit in single_bit_flags:
167 sig = getattr(self, get_signal_name(bit))
168 res.append(sig.eq(int(row.get(bit, 0))))
169 return res
170
171 def eq(self, otherop):
172 res = [self.function_unit.eq(otherop.function_unit),
173 self.form.eq(otherop.form),
174 self.internal_op.eq(otherop.internal_op),
175 self.in1_sel.eq(otherop.in1_sel),
176 self.in2_sel.eq(otherop.in2_sel),
177 self.in3_sel.eq(otherop.in3_sel),
178 self.out_sel.eq(otherop.out_sel),
179 self.cr_in.eq(otherop.cr_in),
180 self.cr_out.eq(otherop.cr_out),
181 self.rc_sel.eq(otherop.rc_sel),
182 self.ldst_len.eq(otherop.ldst_len),
183 self.cry_in.eq(otherop.cry_in)]
184 for bit in single_bit_flags:
185 sig = getattr(self, get_signal_name(bit))
186 res.append(sig.eq(getattr(otherop, get_signal_name(bit))))
187 if hasattr(self, "asmcode"):
188 res.append(self.asmcode.eq(otherop.asmcode))
189 return res
190
191 def ports(self):
192 regular = [self.function_unit,
193 self.in1_sel,
194 self.in2_sel,
195 self.in3_sel,
196 self.out_sel,
197 self.cr_in,
198 self.cr_out,
199 self.ldst_len,
200 self.rc_sel,
201 self.internal_op,
202 self.form]
203 single_bit_ports = [getattr(self, get_signal_name(x))
204 for x in single_bit_flags]
205 return regular + single_bit_ports
206
207
208 class PowerDecoder(Elaboratable):
209 """PowerDecoder - decodes an incoming opcode into the type of operation
210 """
211
212 def __init__(self, width, dec):
213 if not isinstance(dec, list):
214 dec = [dec]
215 self.dec = dec
216 self.opcode_in = Signal(width, reset_less=True)
217
218 self.op = PowerOp()
219 for d in dec:
220 if d.suffix is not None and d.suffix >= width:
221 d.suffix = None
222 self.width = width
223
224 def suffix_mask(self, d):
225 return ((1 << d.suffix) - 1)
226
227 def divide_opcodes(self, d):
228 divided = {}
229 mask = self.suffix_mask(d)
230 print("mask", hex(mask))
231 for row in d.opcodes:
232 opcode = row['opcode']
233 if d.opint and '-' not in opcode:
234 opcode = int(opcode, 0)
235 key = opcode & mask
236 opcode = opcode >> d.suffix
237 if key not in divided:
238 divided[key] = []
239 r = row.copy()
240 r['opcode'] = opcode
241 divided[key].append(r)
242 return divided
243
244 def elaborate(self, platform):
245 m = Module()
246 comb = m.d.comb
247
248 # note: default opcode is "illegal" as this is a combinatorial block
249 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
250
251 # go through the list of CSV decoders first
252 for d in self.dec:
253 opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
254 reset_less=True)
255 comb += opcode_switch.eq(self.opcode_in[d.bitsel[0]:d.bitsel[1]])
256 if d.suffix:
257 opcodes = self.divide_opcodes(d)
258 opc_in = Signal(d.suffix, reset_less=True)
259 comb += opc_in.eq(opcode_switch[:d.suffix])
260 # begin the dynamic Switch statement here
261 with m.Switch(opc_in):
262 for key, row in opcodes.items():
263 bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
264 sd = Subdecoder(pattern=None, opcodes=row,
265 bitsel=bitsel, suffix=None,
266 opint=False, subdecoders=[])
267 subdecoder = PowerDecoder(width=32, dec=sd)
268 setattr(m.submodules, "dec_sub%d" % key, subdecoder)
269 comb += subdecoder.opcode_in.eq(self.opcode_in)
270 # add in the dynamic Case statement here
271 with m.Case(key):
272 comb += self.op.eq(subdecoder.op)
273 else:
274 # TODO: arguments, here (all of them) need to be a list.
275 # a for-loop around the *list* of decoder args.
276 with m.Switch(opcode_switch):
277 self.handle_subdecoders(m, d)
278 for row in d.opcodes:
279 opcode = row['opcode']
280 if d.opint and '-' not in opcode:
281 opcode = int(opcode, 0)
282 if not row['unit']:
283 continue
284 # add in the dynamic Case statement here
285 with m.Case(opcode):
286 comb += self.op._eq(row)
287 return m
288
289 def handle_subdecoders(self, m, d):
290 for dec in d.subdecoders:
291 subdecoder = PowerDecoder(self.width, dec)
292 if isinstance(dec, list): # XXX HACK: take first pattern
293 dec = dec[0]
294 setattr(m.submodules, "dec%d" % dec.pattern, subdecoder)
295 m.d.comb += subdecoder.opcode_in.eq(self.opcode_in)
296 with m.Case(dec.pattern):
297 m.d.comb += self.op.eq(subdecoder.op)
298
299 def ports(self):
300 return [self.opcode_in] + self.op.ports()
301
302
303 class TopPowerDecoder(PowerDecoder):
304 """TopPowerDecoder
305
306 top-level hierarchical decoder for POWER ISA
307 bigendian dynamically switches between big and little endian decoding
308 (reverses byte order). See V3.0B p44 1.11.2
309 """
310
311 def __init__(self, width, dec):
312 PowerDecoder.__init__(self, width, dec)
313 self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
314 self.fields.create_specs()
315 self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
316 self.bigendian = Signal(reset_less=True)
317
318 for name, value in self.fields.common_fields.items():
319 sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
320 setattr(self, name, sig)
321
322 # create signals for all field forms
323 self.form_names = forms = self.fields.instrs.keys()
324 self.sigforms = {}
325 for form in forms:
326 fields = self.fields.instrs[form]
327 fk = fields.keys()
328 Fields = namedtuple("Fields", fk)
329 sf = {}
330 for k, value in fields.items():
331 name = "%s_%s" % (form, k)
332 sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
333 sf[k] = sig
334 instr = Fields(**sf)
335 setattr(self, "Form%s" % form, instr)
336 self.sigforms[form] = instr
337
338 def elaborate(self, platform):
339 m = PowerDecoder.elaborate(self, platform)
340 comb = m.d.comb
341 raw_be = self.raw_opcode_in
342 l = []
343 for i in range(0, self.width, 8):
344 l.append(raw_be[i:i+8])
345 l.reverse()
346 raw_le = Cat(*l)
347 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
348
349 # add all signal from commonly-used fields
350 for name, value in self.fields.common_fields.items():
351 sig = getattr(self, name)
352 comb += sig.eq(value[0:-1])
353
354 # link signals for all field forms
355 forms = self.form_names
356 for form in forms:
357 sf = self.sigforms[form]
358 fields = self.fields.instrs[form]
359 for k, value in fields.items():
360 sig = getattr(sf, k)
361 comb += sig.eq(value[0:-1])
362
363 return m
364
365 def ports(self):
366 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
367
368
369 ####################################################
370 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
371
372 def create_pdecode():
373 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
374 """
375
376 # minor 19 has extra patterns
377 m19 = []
378 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
379 opint=True, bitsel=(1, 11), suffix=None, subdecoders=[]))
380 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
381 opint=True, bitsel=(1, 6), suffix=None, subdecoders=[]))
382
383 # minor opcodes.
384 pminor = [
385 m19,
386 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
387 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
388 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
389 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
390 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
391 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
392 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
393 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
394 ]
395
396 # top level: extra merged with major
397 dec = []
398 opcodes = get_csv("major.csv")
399 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
400 bitsel=(26, 32), suffix=None, subdecoders=pminor))
401 opcodes = get_csv("extra.csv")
402 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
403 bitsel=(0, 32), suffix=None, subdecoders=[]))
404
405 return TopPowerDecoder(32, dec)
406
407
408 if __name__ == '__main__':
409 pdecode = create_pdecode()
410 vl = rtlil.convert(pdecode, ports=pdecode.ports())
411 with open("decoder.il", "w") as f:
412 f.write(vl)