power_fields: introduce new-style fields classes
[openpower-isa.git] / src / openpower / decoder / power_fields.py
1 from collections import namedtuple
2
3 import operator as _operator
4 import functools as _functools
5
6 from openpower.decoder.power_enums import find_wiki_file as _find_wiki_file
7 from openpower.decoder.selectable_int import (
8 SelectableInt as _SelectableInt,
9 BitRange as _BitRange,
10 selectconcat as _selectconcat,
11 )
12
13
14 class RemapError(ValueError):
15 pass
16
17
18 class Descriptor:
19 def __init__(self, cls):
20 self.__cls = cls
21 return super().__init__()
22
23 def __get__(self, instance, owner):
24 if instance is None:
25 return self.__cls
26 return self.__cls(storage=instance.storage)
27
28 def __set__(self, instance, value):
29 if instance is None:
30 raise AttributeError("read-only attribute")
31 self.__cls(storage=instance.storage).assign(value)
32
33
34 @_functools.total_ordering
35 class Reference:
36 def __init__(self, storage, *args, **kwargs):
37 if not isinstance(storage, _SelectableInt):
38 raise ValueError(storage)
39
40 self.storage = storage
41
42 super().__init__()
43 self.__post_init__()
44
45 def __post_init__(self, *args, **kwargs):
46 _ = (args, kwargs)
47
48 def __binary_operator(self, op, other):
49 span = self.__class__.span
50 lhs = _selectconcat(*(self.storage[bit] for bit in span))
51
52 if isinstance(other, Field):
53 bits = len(other.__class__)
54 value = int(other)
55 rhs = _SelectableInt(value=value, bits=bits)
56 elif isinstance(other, int):
57 bits = len(self.__class__)
58 if other.bit_length() > bits:
59 raise OverflowError(other)
60 rhs = _SelectableInt(value=other, bits=bits)
61 elif isinstance(other, _SelectableInt):
62 rhs = other
63 else:
64 raise ValueError(other)
65
66 return op(lhs, rhs)
67
68 def __lt__(self, other):
69 return self.__binary_operator(_operator.lt, other)
70
71 def __eq__(self, other):
72 return self.__binary_operator(_operator.eq, other)
73
74 def __int__(self):
75 span = dict.fromkeys(self.__class__.span).keys()
76 return int(_selectconcat(*(self.storage[bit] for bit in span)))
77
78 def __index__(self):
79 return int(self).__index__()
80
81 @property
82 def storage(self):
83 return self.__storage
84
85 @storage.setter
86 def storage(self, storage):
87 if not isinstance(storage, _SelectableInt):
88 raise ValueError(storage)
89
90 self.__storage = storage
91
92 def assign(self, value):
93 if isinstance(value, int):
94 bits = len(self.__class__)
95 value = _SelectableInt(value=value, bits=bits)
96 if not isinstance(value, _SelectableInt):
97 raise ValueError(value)
98
99 span = frozenset(self.__class__.span)
100 for (src_bit, dst_bit) in enumerate(span):
101 self.storage[dst_bit] = value[src_bit]
102
103
104 class FieldMeta(type):
105 def __new__(metacls, clsname, bases, ns, items=()):
106 assert "__members__" not in ns
107
108 members = []
109 for item in items:
110 if not isinstance(item, int):
111 raise ValueError(item)
112 if item < 0:
113 raise ValueError(item)
114 members.append(item)
115
116 ns["__members__"] = tuple(members)
117
118 return super().__new__(metacls, clsname, bases, ns)
119
120 def __getitem__(cls, size):
121 clsname = f"{cls.__name__}[{size}]"
122 items = ((Field,) * size)
123 return ArrayMeta(clsname, (Array,), {}, items=items)
124
125 def __repr__(cls):
126 if not cls.__members__:
127 return cls.__name__
128 return f"{cls.__name__}{cls.__members__!r}"
129
130 def __iter__(cls):
131 yield from cls.__members__
132
133 def __len__(cls):
134 return len(cls.__members__)
135
136 @property
137 def span(cls):
138 return cls.__members__
139
140 def remap(cls, scheme):
141 if isinstance(scheme, type) and issubclass(scheme, Mapping):
142 scheme = range(len(scheme))
143 scheme = cls.__class__(cls.__name__, (cls,), {}, items=scheme)
144
145 if len(cls) == 0:
146 return scheme
147 elif len(cls) > len(scheme):
148 llen = f"len(scheme)"
149 rlen = f"len({cls.__name__})"
150 raise RemapError(f"{llen} != {rlen}")
151
152 items = map(lambda item: scheme.__members__[item], cls)
153
154 return cls.__class__(cls.__name__, (cls,), {}, items=items)
155
156
157 class Field(Reference, metaclass=FieldMeta):
158 def __len__(self):
159 return self.__class__.__len__()
160
161 def __repr__(self):
162 return f"[{len(self.__class__)}]0x{int(self):x}"
163
164 def __iter__(self):
165 for bit in self.__class__:
166 yield self.storage[bit]
167
168
169 class ArrayMeta(type):
170 def __new__(metacls, clsname, bases, ns, items=()):
171 assert "__members__" not in ns
172
173 members = []
174 for item in items:
175 if not (isinstance(item, type) and issubclass(item, Field)):
176 item = FieldMeta("Field", (Field,), {}, items=item)
177 members.append(item)
178
179 ns["__members__"] = tuple(members)
180
181 return super().__new__(metacls, clsname, bases, ns)
182
183 def __repr__(cls):
184 if not cls.__members__:
185 return cls.__name__
186 return f"{cls.__name__}{cls.__members__!r}"
187
188 def __iter__(cls):
189 yield from enumerate(cls.__members__)
190
191 def __len__(cls):
192 length = 0
193 for field in cls.__members__:
194 length += len(field)
195 return length
196
197 def remap(cls, scheme):
198 scheme_md = []
199 scheme_sd = []
200
201 for item in scheme:
202 if not isinstance(item, int):
203 scheme_md.append(item)
204 else:
205 scheme_sd.append(item)
206
207 if scheme_md and scheme_sd:
208 raise ValueError(scheme)
209
210 def remap_md(scheme):
211 scheme = cls.__class__(cls.__name__, (cls,), {}, items=scheme)
212 if len(cls) == 0:
213 if len(cls.__members__) != len(scheme.__members__):
214 llen = f"len(scheme.__members__)"
215 rlen = f"len({cls.__name__}.__members__)"
216 raise RemapError(f"{llen} != {rlen}")
217 return scheme
218 elif len(scheme) != len(cls):
219 llen = f"len(scheme)"
220 rlen = f"len({cls.__name__})"
221 raise RemapError(f"{llen} != {rlen}")
222
223 items = []
224 for (idx, field) in enumerate(cls):
225 try:
226 item = field.remap(scheme.__members__[idx])
227 except RemapError as error:
228 raise RemapError(f"[{idx}]: {error}")
229 items.append(item)
230
231 return cls.__class__(cls.__name__, (cls,), {}, items=items)
232
233 def remap_sd(scheme):
234 items = tuple(item.remap(scheme) for item in cls.__members__)
235 return cls.__class__(cls.__name__, (cls,), {}, items=items)
236
237 if scheme_md:
238 return remap_md(scheme_md)
239 else:
240 return remap_sd(scheme_sd)
241
242 @property
243 def span(cls):
244 for field in cls.__members__:
245 yield from field.span
246
247
248 class Array(Reference, metaclass=ArrayMeta):
249 def __init__(self, storage):
250 members = []
251 for (idx, cls) in self.__class__:
252 members.append(cls(storage))
253
254 self.__members = tuple(members)
255
256 return super().__init__(storage)
257
258 def __repr__(self):
259 items = tuple(f"[{idx}]={field!r}" for (idx, field) in self)
260 return f"[{', '.join(items)}]"
261
262 def __iter__(self):
263 yield from enumerate(self.__members)
264
265 def __getitem__(self, key):
266 return self.__members[key]
267
268 def __setitem__(self, key, value):
269 self.__members[key].assign(value)
270
271
272 class MappingMeta(type):
273 def __new__(metacls, clsname, bases, ns):
274 members = {}
275
276 for cls in bases:
277 if isinstance(cls, metacls):
278 members.update(cls.__members__)
279
280 for (name, cls) in ns.get("__annotations__", {}).items():
281 if not (isinstance(cls, type) and
282 issubclass(cls, (Mapping, Array, Field))):
283 raise ValueError(f"{clsname}.{name}: {cls!r}")
284
285 if name in ns:
286 try:
287 members[name] = cls.remap(ns[name])
288 except RemapError as error:
289 raise RemapError(f"{name}: {error}")
290 else:
291 if cls in (Array, Field):
292 raise ValueError(f"{clsname}.{name}: " + \
293 "base class without initializer")
294 members[name] = cls
295
296 ns["__members__"] = members
297 for (name, cls) in members.items():
298 ns[name] = Descriptor(cls)
299
300 return super().__new__(metacls, clsname, bases, ns)
301
302 def __repr__(cls):
303 return f"{cls.__name__}({cls.__members__!r})"
304
305 def __iter__(cls):
306 yield from cls.__members__.items()
307
308 def __len__(cls):
309 length = 0
310 for field in cls.__members__.values():
311 length = max(length, len(field))
312 return length
313
314 def remap(cls, scheme):
315 ns = {}
316 annotations = {}
317
318 for (name, field) in cls:
319 annotations[name] = field.remap(scheme)
320 ns["__annotations__"] = annotations
321
322 return cls.__class__(cls.__name__, (cls,), ns)
323
324 @property
325 def span(cls):
326 for field in cls.__members__.values():
327 yield from field.span
328
329
330 class Mapping(Reference, metaclass=MappingMeta):
331 def __init__(self, storage, **kwargs):
332 members = {}
333 for (name, cls) in self.__class__:
334 members[name] = cls(storage)
335
336 self.__members = members
337
338 return super().__init__(storage, **kwargs)
339
340 def __repr__(self):
341 items = tuple(f"{name}={field!r}" for (name, field) in self)
342 return f"{{{', '.join(items)}}}"
343
344 def __iter__(self):
345 yield from self.__members.items()
346
347 def __getitem__(self, key):
348 if isinstance(key, (int, slice, list, tuple, range)):
349 return self.storage[key]
350
351 return self.__members[key]
352
353
354 def decode_instructions(form):
355 res = {}
356 accum = []
357 for l in form:
358 if l.strip().startswith("Formats"):
359 l = l.strip().split(":")[-1]
360 l = l.replace(" ", "")
361 l = l.split(",")
362 for fmt in l:
363 if fmt not in res:
364 res[fmt] = [accum[0]]
365 else:
366 res[fmt].append(accum[0])
367 accum = []
368 else:
369 accum.append(l.strip())
370 return res
371
372
373 def decode_form_header(hdr):
374 res = {}
375 count = 0
376 hdr = hdr.strip()
377 for f in hdr.split("|"):
378 if not f:
379 continue
380 if f[0].isdigit():
381 idx = int(f.strip().split(' ')[0])
382 res[count] = idx
383 count += len(f) + 1
384 return res
385
386
387 def find_unique(d, key):
388 if key not in d:
389 return key
390 idx = 1
391 while "%s_%d" % (key, idx) in d:
392 idx += 1
393 return "%s_%d" % (key, idx)
394
395
396 def decode_line(header, line):
397 line = line.strip()
398 res = {}
399 count = 0
400 prev_fieldname = None
401 for f in line.split("|"):
402 if not f:
403 continue
404 end = count + len(f) + 1
405 fieldname = f.strip()
406 if not fieldname or fieldname.startswith('/'):
407 if prev_fieldname is not None:
408 res[prev_fieldname] = (res[prev_fieldname], header[count])
409 prev_fieldname = None
410 count = end
411 continue
412 bitstart = header[count]
413 if prev_fieldname is not None:
414 res[prev_fieldname] = (res[prev_fieldname], bitstart)
415 res[fieldname] = bitstart
416 count = end
417 prev_fieldname = fieldname
418 res[prev_fieldname] = (bitstart, 32)
419 return res
420
421
422 def decode_form(form):
423 header = decode_form_header(form[0])
424 res = []
425 for line in form[1:]:
426 dec = decode_line(header, line)
427 if dec:
428 res.append(dec)
429 fields = {}
430 falternate = {}
431 for l in res:
432 for k, (start, end) in l.items():
433 if k in fields:
434 if (start, end) == fields[k]:
435 continue # already in and matching for this Form
436 if k in falternate:
437 alternate = "%s_%d" % (k, falternate[k])
438 if (start, end) == fields[alternate]:
439 continue
440 falternate[k] = fidx = falternate.get(k, 0) + 1
441 fields["%s_%d" % (k, fidx)] = (start, end)
442 else:
443 fields[k] = (start, end)
444 return fields
445
446
447 class DecodeFields:
448
449 def __init__(self, bitkls=_BitRange, bitargs=(), fname=None,
450 name_on_wiki=None):
451 self.bitkls = bitkls
452 self.bitargs = bitargs
453 if fname is None:
454 assert name_on_wiki is None
455 fname = "fields.txt"
456 name_on_wiki = "fields.text"
457 self.fname = _find_wiki_file(name_on_wiki)
458
459 @property
460 def form_names(self):
461 return self.instrs.keys()
462
463 def create_specs(self):
464 self.forms, self.instrs = self.decode_fields()
465 forms = self.form_names
466 #print ("specs", self.forms, forms)
467 for form in forms:
468 fields = self.instrs[form]
469 fk = fields.keys()
470 Fields = namedtuple("Fields", fk)
471 instr = Fields(**fields)
472 setattr(self, "Form%s" % form, instr)
473 # now add in some commonly-used fields (should be done automatically)
474 # note that these should only be ones which are the same on all Forms
475 # note: these are from microwatt insn_helpers.vhdl
476 self.common_fields = {
477 "PO": self.Formall.PO,
478 "FRS": self.FormX.FRS,
479 "FRT": self.FormX.FRT,
480 "FRA": self.FormX.FRA,
481 "FRB": self.FormX.FRB,
482 "FRC": self.FormA.FRC,
483 "RS": self.FormX.RS,
484 "RT": self.FormX.RT,
485 "RA": self.FormX.RA,
486 "RB": self.FormX.RB,
487 "RC": self.FormVA.RC,
488 "SI": self.FormD.SI,
489 "UI": self.FormD.UI,
490 "L": self.FormD.L,
491 "SH32": self.FormM.SH,
492 "sh": self.FormMD.sh,
493 "MB32": self.FormM.MB,
494 "ME32": self.FormM.ME,
495 "LI": self.FormI.LI,
496 "LK": self.FormI.LK,
497 "AA": self.FormB.AA,
498 "Rc": self.FormX.Rc,
499 "OE": self.FormXO.OE,
500 "BD": self.FormB.BD,
501 "BF": self.FormX.BF,
502 "CR": self.FormXL.XO,
503 "BB": self.FormXL.BB,
504 "BA": self.FormXL.BA,
505 "BT": self.FormXL.BT,
506 "FXM": self.FormXFX.FXM,
507 "BO": self.FormXL.BO,
508 "BI": self.FormXL.BI,
509 "BH": self.FormXL.BH,
510 "D": self.FormD.D,
511 "DS": self.FormDS.DS,
512 "TO": self.FormX.TO,
513 "BC": self.FormA.BC,
514 "SH": self.FormX.SH,
515 "ME": self.FormM.ME,
516 "MB": self.FormM.MB,
517 "SPR": self.FormXFX.SPR}
518 for k, v in self.common_fields.items():
519 setattr(self, k, v)
520
521 def decode_fields(self):
522 with open(self.fname) as f:
523 txt = f.readlines()
524 #print ("decode", txt)
525 forms = {}
526 reading_data = False
527 for l in txt:
528 l = l.strip()
529 if len(l) == 0:
530 continue
531 if reading_data:
532 if l[0] == '#':
533 reading_data = False
534 else:
535 forms[heading].append(l)
536 if not reading_data:
537 assert l[0] == '#'
538 heading = l[1:].strip()
539 # if heading.startswith('1.6.28'): # skip instr fields for now
540 # break
541 heading = heading.split(' ')[-1]
542 reading_data = True
543 forms[heading] = []
544
545 res = {}
546 inst = {}
547
548 for hdr, form in forms.items():
549 if heading == 'Fields':
550 i = decode_instructions(form)
551 for form, field in i.items():
552 inst[form] = self.decode_instruction_fields(field)
553 # else:
554 # res[hdr] = decode_form(form)
555 return res, inst
556
557 def decode_instruction_fields(self, fields):
558 res = {}
559 for field in fields:
560 f, spec = field.strip().split(" ")
561 ss = spec[1:-1].split(",")
562 fs = f.split(",")
563 if len(fs) > 1:
564 individualfields = []
565 for f0, s0 in zip(fs, ss):
566 txt = "%s (%s)" % (f0, s0)
567 individualfields.append(txt)
568 if len(fs) > 1:
569 res.update(self.decode_instruction_fields(
570 individualfields))
571 d = self.bitkls(*self.bitargs)
572 idx = 0
573 for s in ss:
574 s = s.split(':')
575 if len(s) == 1:
576 d[idx] = int(s[0])
577 idx += 1
578 else:
579 start = int(s[0])
580 end = int(s[1])
581 while start <= end:
582 d[idx] = start
583 idx += 1
584 start += 1
585 f = f.replace(",", "_")
586 unique = find_unique(res, f)
587 res[unique] = d
588
589 return res
590
591
592 if __name__ == '__main__':
593 dec = DecodeFields()
594 dec.create_specs()
595 forms, instrs = dec.forms, dec.instrs
596 for form, fields in instrs.items():
597 print("Form", form)
598 for field, bits in fields.items():
599 print("\tfield", field, bits)