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