grr, autopep8 messing up
[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
115 class PowerOp:
116 """PowerOp: spec for execution. op type (ADD etc.) reg specs etc.
117
118 this is an internal data structure, set up by reading CSV files
119 (which uses _eq to initialise each instance, not eq)
120
121 the "public" API (as far as actual usage as a useful decoder is concerned)
122 is Decode2ToExecute1Type
123 """
124
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))
144
145 def _eq(self, row=None):
146 if row is None:
147 row = default_values
148 # TODO: this conversion process from a dict to an object
149 # should really be done using e.g. namedtuple and then
150 # call eq not _eq
151 if False: # debugging
152 if row['CR in'] == '1':
153 import pdb
154 pdb.set_trace()
155 print(row)
156 if row['CR out'] == '0':
157 import pdb
158 pdb.set_trace()
159 print(row)
160 print(row)
161 ldst_mode = row['upd']
162 if ldst_mode.isdigit():
163 ldst_mode = LDSTMode(int(ldst_mode))
164 else:
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']]),
179 ]
180 if False:
181 print(row.keys())
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))))
188 return res
189
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))
209 return res
210
211 def ports(self):
212 regular = [self.function_unit,
213 self.in1_sel,
214 self.in2_sel,
215 self.in3_sel,
216 self.out_sel,
217 self.cr_in,
218 self.cr_out,
219 self.ldst_len,
220 self.rc_sel,
221 self.internal_op,
222 self.form]
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
228
229
230 class PowerDecoder(Elaboratable):
231 """PowerDecoder - decodes an incoming opcode into the type of operation
232 """
233
234 def __init__(self, width, dec):
235 if not isinstance(dec, list):
236 dec = [dec]
237 self.dec = dec
238 self.opcode_in = Signal(width, reset_less=True)
239
240 self.op = PowerOp()
241 for d in dec:
242 if d.suffix is not None and d.suffix >= width:
243 d.suffix = None
244 self.width = width
245
246 def suffix_mask(self, d):
247 return ((1 << d.suffix) - 1)
248
249 def divide_opcodes(self, d):
250 divided = {}
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)
257 key = opcode & mask
258 opcode = opcode >> d.suffix
259 if key not in divided:
260 divided[key] = []
261 r = row.copy()
262 r['opcode'] = opcode
263 divided[key].append(r)
264 return divided
265
266 def elaborate(self, platform):
267 m = Module()
268 comb = m.d.comb
269
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
272
273 # go through the list of CSV decoders first
274 for d in self.dec:
275 opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
276 reset_less=True)
277 comb += opcode_switch.eq(self.opcode_in[d.bitsel[0]:d.bitsel[1]])
278 if d.suffix:
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
293 with m.Case(key):
294 comb += self.op.eq(subdecoder.op)
295 else:
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)
304 if not row['unit']:
305 continue
306 # add in the dynamic Case statement here
307 with m.Case(opcode):
308 comb += self.op._eq(row)
309 return m
310
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
315 dec = dec[0]
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)
320
321 def ports(self):
322 return [self.opcode_in] + self.op.ports()
323
324
325 class TopPowerDecoder(PowerDecoder):
326 """TopPowerDecoder
327
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
331 """
332
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)
339
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)
343
344 # create signals for all field forms
345 self.form_names = forms = self.fields.instrs.keys()
346 self.sigforms = {}
347 for form in forms:
348 fields = self.fields.instrs[form]
349 fk = fields.keys()
350 Fields = namedtuple("Fields", fk)
351 sf = {}
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)
355 sf[k] = sig
356 instr = Fields(**sf)
357 setattr(self, "Form%s" % form, instr)
358 self.sigforms[form] = instr
359
360 def elaborate(self, platform):
361 m = PowerDecoder.elaborate(self, platform)
362 comb = m.d.comb
363 # raw opcode in assumed to be in LE order: byte-reverse it to get BE
364 raw_le = self.raw_opcode_in
365 l = []
366 for i in range(0, self.width, 8):
367 l.append(raw_le[i:i+8])
368 l.reverse()
369 raw_be = Cat(*l)
370 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
371
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])
376
377 # link signals for all field forms
378 forms = self.form_names
379 for form in forms:
380 sf = self.sigforms[form]
381 fields = self.fields.instrs[form]
382 for k, value in fields.items():
383 sig = getattr(sf, k)
384 comb += sig.eq(value[0:-1])
385
386 return m
387
388 def ports(self):
389 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
390
391
392 ####################################################
393 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
394
395 def create_pdecode():
396 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
397 """
398
399 # minor 19 has extra patterns
400 m19 = []
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=[]))
405
406 # minor opcodes.
407 pminor = [
408 m19,
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=[]),
417 ]
418
419 # top level: extra merged with major
420 dec = []
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=[]))
427
428 return TopPowerDecoder(32, dec)
429
430
431 if __name__ == '__main__':
432 pdecode = create_pdecode()
433 vl = rtlil.convert(pdecode, ports=pdecode.ports())
434 with open("decoder.il", "w") as f:
435 f.write(vl)