reorganise PowerOp to be dynamic
[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 power_op_csvmap = {'function_unit': 'unit',
131 'form' : 'form',
132 'internal_op' : 'internal op',
133 'in1_sel' : 'in1',
134 'in2_sel' : 'in2',
135 'in3_sel' : 'in3',
136 'out_sel' : 'out',
137 'cr_in' : 'CR in',
138 'cr_out' : 'CR out',
139 'ldst_len' : 'ldst len',
140 'upd' : 'upd',
141 'rc_sel' : 'rc',
142 'cry_in' : 'cry in',
143 }
144
145 def get_pname(field, pname):
146 if pname is None:
147 return field
148 return "%s_%s" % (pname, field)
149
150
151 class PowerOp:
152 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
153
154 this is an internal data structure, set up by reading CSV files
155 (which uses _eq to initialise each instance, not eq)
156
157 the "public" API (as far as actual usage as a useful decoder is concerned)
158 is Decode2ToExecute1Type
159
160 the "subset" allows for only certain columns to be decoded
161 """
162
163 def __init__(self, incl_asm=True, name=None, subset=None):
164 self.subset = subset
165 for field, ptype in power_op_types.items():
166 if subset and field not in subset:
167 continue
168 fname = get_pname(field, name)
169 setattr(self, field, Signal(ptype, reset_less=True, name=fname))
170 for bit in single_bit_flags:
171 field = get_signal_name(bit)
172 fname = get_pname(field, name)
173 setattr(self, field, Signal(reset_less=True, name=fname))
174
175 def _eq(self, row=None):
176 if row is None:
177 row = default_values
178 # TODO: this conversion process from a dict to an object
179 # should really be done using e.g. namedtuple and then
180 # call eq not _eq
181 if False: # debugging
182 if row['CR in'] == '1':
183 import pdb
184 pdb.set_trace()
185 print(row)
186 if row['CR out'] == '0':
187 import pdb
188 pdb.set_trace()
189 print(row)
190 print(row)
191 ldst_mode = row['upd']
192 if ldst_mode.isdigit():
193 row['upd'] = int(ldst_mode)
194 res = []
195 for field, ptype in power_op_types.items():
196 if not hasattr(self, field):
197 continue
198 if field not in power_op_csvmap:
199 continue
200 csvname = power_op_csvmap[field]
201 val = row[csvname]
202 if csvname == 'upd' and isinstance(val, int): # LDSTMode different
203 val = ptype(val)
204 else:
205 val = ptype[val]
206 res.append(getattr(self, field).eq(val))
207 if False:
208 print(row.keys())
209 asmcode = row['comment']
210 if hasattr(self, "asmcode") and asmcode in asmidx:
211 res.append(self.asmcode.eq(asmidx[asmcode]))
212 for bit in single_bit_flags:
213 sig = getattr(self, get_signal_name(bit))
214 res.append(sig.eq(int(row.get(bit, 0))))
215 return res
216
217 def _get_eq(self, res, field, otherop):
218 copyfrom = getattr(otherop, field, None)
219 copyto = getattr(self, field, None)
220 if copyfrom is not None and copyto is not None:
221 res.append(copyto.eq(copyfrom))
222
223 def eq(self, otherop):
224 res = []
225 for field in power_op_types.keys():
226 self._get_eq(res, field, otherop)
227 for bit in single_bit_flags:
228 self._get_eq(res, get_signal_name(bit), otherop)
229 return res
230
231 def ports(self):
232 res = []
233 for field in power_op_types.keys():
234 if hasattr(self, field):
235 regular.append(getattr(self, field))
236 if hasattr(self, "asmcode"):
237 res.append(self.asmcode)
238 for field in single_bit_flags:
239 field = get_signal_name(field)
240 if hasattr(self, field):
241 res.append(getattr(self, field))
242 return res
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, name=None, col_subset=None):
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(name=name, subset=col_subset)
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)