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