identifying locations where big/little endian is in place, adding args
[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(8, 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 False: # debugging
144 if row['CR in'] == '1':
145 import pdb; pdb.set_trace()
146 print(row)
147 if row['CR out'] == '0':
148 import pdb; pdb.set_trace()
149 print(row)
150 print(row)
151 res = [self.function_unit.eq(Function[row['unit']]),
152 self.form.eq(Form[row['form']]),
153 self.internal_op.eq(InternalOp[row['internal op']]),
154 self.in1_sel.eq(In1Sel[row['in1']]),
155 self.in2_sel.eq(In2Sel[row['in2']]),
156 self.in3_sel.eq(In3Sel[row['in3']]),
157 self.out_sel.eq(OutSel[row['out']]),
158 self.cr_in.eq(CRInSel[row['CR in']]),
159 self.cr_out.eq(CROutSel[row['CR out']]),
160 self.ldst_len.eq(LdstLen[row['ldst len']]),
161 self.rc_sel.eq(RC[row['rc']]),
162 self.cry_in.eq(CryIn[row['cry in']]),
163 ]
164 if False:
165 print (row.keys())
166 asmcode = row['comment']
167 if hasattr(self, "asmcode") and asmcode in asmidx:
168 res.append(self.asmcode.eq(asmidx[asmcode]))
169 for bit in single_bit_flags:
170 sig = getattr(self, get_signal_name(bit))
171 res.append(sig.eq(int(row.get(bit, 0))))
172 return res
173
174 def eq(self, otherop):
175 res = [self.function_unit.eq(otherop.function_unit),
176 self.form.eq(otherop.form),
177 self.internal_op.eq(otherop.internal_op),
178 self.in1_sel.eq(otherop.in1_sel),
179 self.in2_sel.eq(otherop.in2_sel),
180 self.in3_sel.eq(otherop.in3_sel),
181 self.out_sel.eq(otherop.out_sel),
182 self.cr_in.eq(otherop.cr_in),
183 self.cr_out.eq(otherop.cr_out),
184 self.rc_sel.eq(otherop.rc_sel),
185 self.ldst_len.eq(otherop.ldst_len),
186 self.cry_in.eq(otherop.cry_in)]
187 for bit in single_bit_flags:
188 sig = getattr(self, get_signal_name(bit))
189 res.append(sig.eq(getattr(otherop, get_signal_name(bit))))
190 if hasattr(self, "asmcode"):
191 res.append(self.asmcode.eq(otherop.asmcode))
192 return res
193
194 def ports(self):
195 regular = [self.function_unit,
196 self.in1_sel,
197 self.in2_sel,
198 self.in3_sel,
199 self.out_sel,
200 self.cr_in,
201 self.cr_out,
202 self.ldst_len,
203 self.rc_sel,
204 self.internal_op,
205 self.form]
206 if hasattr(self, "asmcode"):
207 regular.append(self.asmcode)
208 single_bit_ports = [getattr(self, get_signal_name(x))
209 for x in single_bit_flags]
210 return regular + single_bit_ports
211
212
213 class PowerDecoder(Elaboratable):
214 """PowerDecoder - decodes an incoming opcode into the type of operation
215 """
216
217 def __init__(self, width, dec):
218 if not isinstance(dec, list):
219 dec = [dec]
220 self.dec = dec
221 self.opcode_in = Signal(width, reset_less=True)
222
223 self.op = PowerOp()
224 for d in dec:
225 if d.suffix is not None and d.suffix >= width:
226 d.suffix = None
227 self.width = width
228
229 def suffix_mask(self, d):
230 return ((1 << d.suffix) - 1)
231
232 def divide_opcodes(self, d):
233 divided = {}
234 mask = self.suffix_mask(d)
235 print("mask", hex(mask))
236 for row in d.opcodes:
237 opcode = row['opcode']
238 if d.opint and '-' not in opcode:
239 opcode = int(opcode, 0)
240 key = opcode & mask
241 opcode = opcode >> d.suffix
242 if key not in divided:
243 divided[key] = []
244 r = row.copy()
245 r['opcode'] = opcode
246 divided[key].append(r)
247 return divided
248
249 def elaborate(self, platform):
250 m = Module()
251 comb = m.d.comb
252
253 # note: default opcode is "illegal" as this is a combinatorial block
254 # this only works because OP_ILLEGAL=0 and the default (unset) is 0
255
256 # go through the list of CSV decoders first
257 for d in self.dec:
258 opcode_switch = Signal(d.bitsel[1] - d.bitsel[0],
259 reset_less=True)
260 comb += opcode_switch.eq(self.opcode_in[d.bitsel[0]:d.bitsel[1]])
261 if d.suffix:
262 opcodes = self.divide_opcodes(d)
263 opc_in = Signal(d.suffix, reset_less=True)
264 comb += opc_in.eq(opcode_switch[:d.suffix])
265 # begin the dynamic Switch statement here
266 with m.Switch(opc_in):
267 for key, row in opcodes.items():
268 bitsel = (d.suffix+d.bitsel[0], d.bitsel[1])
269 sd = Subdecoder(pattern=None, opcodes=row,
270 bitsel=bitsel, suffix=None,
271 opint=False, subdecoders=[])
272 subdecoder = PowerDecoder(width=32, dec=sd)
273 setattr(m.submodules, "dec_sub%d" % key, subdecoder)
274 comb += subdecoder.opcode_in.eq(self.opcode_in)
275 # add in the dynamic Case statement here
276 with m.Case(key):
277 comb += self.op.eq(subdecoder.op)
278 else:
279 # TODO: arguments, here (all of them) need to be a list.
280 # a for-loop around the *list* of decoder args.
281 with m.Switch(opcode_switch):
282 self.handle_subdecoders(m, d)
283 for row in d.opcodes:
284 opcode = row['opcode']
285 if d.opint and '-' not in opcode:
286 opcode = int(opcode, 0)
287 if not row['unit']:
288 continue
289 # add in the dynamic Case statement here
290 with m.Case(opcode):
291 comb += self.op._eq(row)
292 return m
293
294 def handle_subdecoders(self, m, d):
295 for dec in d.subdecoders:
296 subdecoder = PowerDecoder(self.width, dec)
297 if isinstance(dec, list): # XXX HACK: take first pattern
298 dec = dec[0]
299 setattr(m.submodules, "dec%d" % dec.pattern, subdecoder)
300 m.d.comb += subdecoder.opcode_in.eq(self.opcode_in)
301 with m.Case(dec.pattern):
302 m.d.comb += self.op.eq(subdecoder.op)
303
304 def ports(self):
305 return [self.opcode_in] + self.op.ports()
306
307
308 class TopPowerDecoder(PowerDecoder):
309 """TopPowerDecoder
310
311 top-level hierarchical decoder for POWER ISA
312 bigendian dynamically switches between big and little endian decoding
313 (reverses byte order). See V3.0B p44 1.11.2
314 """
315
316 def __init__(self, width, dec):
317 PowerDecoder.__init__(self, width, dec)
318 self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
319 self.fields.create_specs()
320 self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
321 self.bigendian = Signal(reset_less=True)
322
323 for name, value in self.fields.common_fields.items():
324 sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
325 setattr(self, name, sig)
326
327 # create signals for all field forms
328 self.form_names = forms = self.fields.instrs.keys()
329 self.sigforms = {}
330 for form in forms:
331 fields = self.fields.instrs[form]
332 fk = fields.keys()
333 Fields = namedtuple("Fields", fk)
334 sf = {}
335 for k, value in fields.items():
336 name = "%s_%s" % (form, k)
337 sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
338 sf[k] = sig
339 instr = Fields(**sf)
340 setattr(self, "Form%s" % form, instr)
341 self.sigforms[form] = instr
342
343 def elaborate(self, platform):
344 m = PowerDecoder.elaborate(self, platform)
345 comb = m.d.comb
346 # raw opcode in, byte-reverse it
347 raw_be = self.raw_opcode_in
348 l = []
349 for i in range(0, self.width, 8):
350 l.append(raw_be[i:i+8])
351 l.reverse()
352 raw_le = Cat(*l)
353 comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
354
355 # add all signal from commonly-used fields
356 for name, value in self.fields.common_fields.items():
357 sig = getattr(self, name)
358 comb += sig.eq(value[0:-1])
359
360 # link signals for all field forms
361 forms = self.form_names
362 for form in forms:
363 sf = self.sigforms[form]
364 fields = self.fields.instrs[form]
365 for k, value in fields.items():
366 sig = getattr(sf, k)
367 comb += sig.eq(value[0:-1])
368
369 return m
370
371 def ports(self):
372 return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
373
374
375 ####################################################
376 # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
377
378 def create_pdecode():
379 """create_pdecode - creates a cascading hierarchical POWER ISA decoder
380 """
381
382 # minor 19 has extra patterns
383 m19 = []
384 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
385 opint=True, bitsel=(1, 11), suffix=None, subdecoders=[]))
386 m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
387 opint=True, bitsel=(1, 6), suffix=None, subdecoders=[]))
388
389 # minor opcodes.
390 pminor = [
391 m19,
392 Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
393 opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]),
394 Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"),
395 opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]),
396 Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"),
397 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
398 Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"),
399 opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]),
400 ]
401
402 # top level: extra merged with major
403 dec = []
404 opcodes = get_csv("major.csv")
405 dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
406 bitsel=(26, 32), suffix=None, subdecoders=pminor))
407 opcodes = get_csv("extra.csv")
408 dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
409 bitsel=(0, 32), suffix=None, subdecoders=[]))
410
411 return TopPowerDecoder(32, dec)
412
413
414 if __name__ == '__main__':
415 pdecode = create_pdecode()
416 vl = rtlil.convert(pdecode, ports=pdecode.ports())
417 with open("decoder.il", "w") as f:
418 f.write(vl)