add FPSCRState and FPSCRRecord and a FPSCR smoke-test
[openpower-isa.git] / src / openpower / fpscr.py
1 # SPDX-License-Identifier: LGPLv3+
2 # Funded by NLnet https://nlnet.nl/
3 """ Record for FPSCR as defined in
4 Power ISA v3.1B Book I section 4.2.2 page 136(162)
5
6 FPSCR fields in MSB0:
7
8 | Bits | Mnemonic | Description |
9 |-------|----------|-------------------------------------------------------------------------|
10 | 0:28 |   | Reserved |
11 | 29:31 | DRN | Decimal Rounding Mode |
12 | 32 | FX | Floating-Point Exception Summary |
13 | 33 | FEX | Floating-Point Enabled Exception Summary |
14 | 34 | VX | Floating-Point Invalid Operation Exception Summary |
15 | 35 | OX | Floating-Point Overflow Exception |
16 | 36 | UX | Floating-Point Underflow Exception |
17 | 37 | ZX | Floating-Point Zero Divide Exception |
18 | 38 | XX | Floating-Point Inexact Exception |
19 | 39 | VXSNAN | Floating-Point Invalid Operation Exception (SNaN) |
20 | 40 | VXISI | Floating-Point Invalid Operation Exception (∞ - ∞) |
21 | 41 | VXIDI | Floating-Point Invalid Operation Exception (∞ ÷ ∞) |
22 | 42 | VXZDZ | Floating-Point Invalid Operation Exception (0 ÷ 0) |
23 | 43 | VXIMZ | Floating-Point Invalid Operation Exception (∞ × 0) |
24 | 44 | VXVC | Floating-Point Invalid Operation Exception (Invalid Compare) |
25 | 45 | FR | Floating-Point Fraction Rounded |
26 | 46 | FI | Floating-Point Fraction Inexact |
27 | 47:51 | FPRF | Floating-Point Result Flags |
28 | 47 | C | Floating-Point Result Class Descriptor |
29 | 48:51 | FPCC | Floating-Point Condition Code |
30 | 48 | FL | Floating-Point Less Than or Negative |
31 | 49 | FG | Floating-Point Greater Than or Positive |
32 | 50 | FE | Floating-Point Equal or Zero |
33 | 51 | FU | Floating-Point Unordered or NaN |
34 | 52 |   | Reserved |
35 | 53 | VXSOFT | Floating-Point Invalid Operation Exception (Software-Defined Condition) |
36 | 54 | VXSQRT | Floating-Point Invalid Operation Exception (Invalid Square Root) |
37 | 55 | VXCVI | Floating-Point Invalid Operation Exception (Invalid Integer Convert) |
38 | 56 | VE | Floating-Point Invalid Operation Exception Enable |
39 | 57 | OE | Floating-Point Overflow Exception Enable |
40 | 58 | UE | Floating-Point Underflow Exception Enable |
41 | 59 | ZE | Floating-Point Zero Divide Exception Enable |
42 | 60 | XE | Floating-Point Inexact Exception Enable |
43 | 61 | NI | Floating-Point Non-IEEE Mode |
44 | 62:63 | RN | Floating-Point Rounding Control |
45 """
46
47 from nmigen import Record
48 from typing import NoReturn
49 from nmutil.plain_data import plain_data
50 import linecache
51 from openpower.decoder.selectable_int import (
52 FieldSelectableInt, SelectableInt)
53
54
55 def _parse_line_fields(line):
56 # type: (str) -> None | list[str]
57 sline = line.strip()
58 if not sline.startswith("|"):
59 return None
60 if not sline.endswith("|"):
61 return None
62 if sline == "|":
63 return None
64 return [v.strip() for v in sline[1:-2].split("|")]
65
66
67 _BITS_FIELD = "Bits"
68 _MNEMONIC_FIELD = "Mnemonic"
69 FPSCR_WIDTH = 64
70
71
72 @plain_data()
73 class _MutableField:
74 __slots__ = "name", "bits_msb0", "lineno", "include_in_record"
75
76 def __init__(self, name, bits_msb0, lineno, include_in_record=True):
77 # type: (str, int | range, int, bool) -> None
78 self.name = name
79 self.bits_msb0 = bits_msb0
80 self.lineno = lineno
81 self.include_in_record = include_in_record
82
83 def bits_msb0_iter(self):
84 # type: () -> range | tuple[int]
85 if isinstance(self.bits_msb0, int):
86 return self.bits_msb0,
87 return self.bits_msb0
88
89 def to_field(self):
90 # type () -> FPSCRField
91 return FPSCRField(name=self.name, bits_msb0=self.bits_msb0,
92 include_in_record=self.include_in_record)
93
94
95 @plain_data(frozen=True, unsafe_hash=True)
96 class FPSCRField:
97 __slots__ = "name", "bits_msb0", "include_in_record"
98
99 def __init__(self, name, bits_msb0, include_in_record):
100 # type: (str, int | range, bool) -> None
101 self.name = name
102 self.bits_msb0 = bits_msb0
103 self.include_in_record = include_in_record
104 """True if this field should be
105 included in `FPSCRRecord`, since there are some overlapping fields and
106 `Record` doesn't support that.
107 """
108
109 def bits_msb0_iter(self):
110 # type: () -> range | tuple[int]
111 if isinstance(self.bits_msb0, int):
112 return self.bits_msb0,
113 return self.bits_msb0
114
115
116 def _parse_fields():
117 # type: () -> tuple[FPSCRField, ...]
118 lines = __doc__.splitlines()
119 in_header_sep = False
120 in_table_body = False
121 header_fields = [] # type: list[str]
122 header_lineno = 0
123 fields = {} # type: dict[str, _MutableField]
124 bit_fields = [None] * FPSCR_WIDTH # type: list[_MutableField | None]
125 lineno = 0
126
127 def raise_(msg, col=1, err_lineno=None):
128 # type: (str, int, int | None) -> NoReturn
129 nonlocal lineno
130 if err_lineno is None:
131 err_lineno = lineno
132 for i in range(10000): # 10000 is random limit if we can't read
133 if linecache.getline(__file__, i).strip().startswith('"'):
134 break
135 err_lineno += 1 # lines before doc comment start
136 raise SyntaxError(msg, (
137 __file__, err_lineno + 3, col, lines[err_lineno]))
138
139 for lineno, line in enumerate(lines):
140 line_fields = _parse_line_fields(line)
141 if in_table_body:
142 if line_fields is None:
143 if len(fields) == 0:
144 raise_("missing table body")
145 break
146 if len(line_fields) != len(header_fields):
147 raise_("wrong number of fields")
148 fields_dict = {k: v for k, v in zip(header_fields, line_fields)}
149 name = fields_dict[_MNEMONIC_FIELD]
150 if name == "" or name == " ":
151 continue
152 if not name.isidentifier():
153 raise_(f"invalid field name {name!r}")
154 if name in fields:
155 raise_(f"duplicate field name {name}")
156 bits_str = fields_dict[_BITS_FIELD]
157 bits_fields_str = bits_str.split(":")
158 if len(bits_fields_str) not in (1, 2) or not all(
159 v.isascii() and v.isdigit() for v in bits_fields_str):
160 raise_(f"`{_BITS_FIELD}` field must be "
161 f"of the form `23` or `23:56`")
162 bits_fields = [int(v, base=10) for v in bits_fields_str]
163 if not all(0 <= v < FPSCR_WIDTH for v in bits_fields):
164 raise_(f"`{_BITS_FIELD}` field value is beyond the "
165 f"limits of FPSCR: must be `0 <= v < {FPSCR_WIDTH}`")
166 if len(bits_fields) == 2:
167 first, last = bits_fields
168 if first > last:
169 raise_(f"`{_BITS_FIELD}` field value is an improper "
170 f"range: {first} > {last}")
171 bits = range(first, last + 1)
172 else:
173 bits = bits_fields[0]
174 field = _MutableField(name=name, bits_msb0=bits, lineno=lineno)
175 fields[name] = field
176 for bit in field.bits_msb0_iter():
177 old_field = bit_fields[bit]
178 if old_field is not None:
179 # field is overwritten -- don't include in Record
180 old_field.include_in_record = False
181 bit_fields[bit] = field
182 elif in_header_sep:
183 if line_fields is None:
184 raise_("missing header separator line")
185 for v in line_fields:
186 if v != "-" * len(v):
187 raise_("header separator field isn't just hyphens")
188 if len(line_fields) != len(header_fields):
189 raise_("wrong number of fields")
190 in_header_sep = False
191 in_table_body = True
192 else:
193 if line_fields is None:
194 continue
195 if _BITS_FIELD not in line_fields:
196 raise_(f"missing `{_BITS_FIELD}` field")
197 if _MNEMONIC_FIELD not in line_fields:
198 raise_(f"missing `{_MNEMONIC_FIELD}` field")
199 if len(set(line_fields)) != len(line_fields):
200 raise_("duplicate header field")
201 header_fields = line_fields
202 in_header_sep = True
203 header_lineno = lineno
204 if len(fields) == 0:
205 raise_("missing table")
206 # insert reserved fields and check for partially overwritten fields
207 for bit in range(FPSCR_WIDTH):
208 field = bit_fields[bit]
209 if field is None:
210 start = bit
211 bit += 1
212 while bit < FPSCR_WIDTH and bit_fields[bit] is None:
213 bit += 1
214 field = _MutableField(name=f"RESERVED_{start}_{bit - 1}",
215 bits_msb0=range(start, bit),
216 lineno=header_lineno)
217 if len(field.bits_msb0) == 1:
218 field.bits_msb0 = start
219 field.name = f"RESERVED_{start}"
220 for bit in field.bits_msb0_iter():
221 bit_fields[bit] = field
222 if field.name in fields:
223 raise_(f"field {field.name}'s name conflicts with a "
224 f"generated reserved field",
225 err_lineno=fields[field.name].lineno)
226 fields[field.name] = field
227 elif not field.include_in_record:
228 raise_(f"field {field.name} is partially overwritten -- "
229 f"this is an error because FPSCRRecord will have "
230 f"incorrect fields", err_lineno=field.lineno)
231 else:
232 bit += 1
233 return tuple(f.to_field() for f in fields.values())
234
235
236 FPSCR_FIELDS_MSB0 = _parse_fields() # type: tuple[FPSCRField, ...]
237 """ All fields in FPSCR. """
238
239
240 def _calc_record_layout_lsb0():
241 # type: () -> list[tuple[str, int]]
242 fields_lsb0 = [] # type: list[tuple[int, int, str]]
243 for field in FPSCR_FIELDS_MSB0:
244 if not field.include_in_record:
245 continue
246 start_msb0 = field.bits_msb0_iter()[0]
247 field_len = len(field.bits_msb0_iter())
248 start_lsb0 = FPSCR_WIDTH - 1 - start_msb0
249 fields_lsb0.append((start_lsb0, field_len, field.name))
250 fields_lsb0.sort()
251 # _parse_fields already checks for partially overlapping fields and
252 # inserts reserved fields ensuring the returned fields cover every bit
253 # exactly one, therefore this is correct
254 return [(name, f_len) for _, f_len, name in fields_lsb0]
255
256
257 class FPSCRRecord(Record):
258 layout = _calc_record_layout_lsb0()
259
260 def __init__(self, name=None):
261 super().__init__(name=name, layout=FPSCRRecord.layout)
262
263
264 class FPSCRState(SelectableInt):
265 def __init__(self, value=0):
266 SelectableInt.__init__(self, value, FPSCR_WIDTH)
267 self.fsi = {}
268 for field in FPSCR_FIELDS_MSB0:
269 bits_msb0 = tuple(field.bits_msb0_iter())
270 self.fsi[field.name] = FieldSelectableInt(self, bits_msb0)
271
272 @property
273 def DRN(self):
274 return self.fsi['DRN'].asint(msb0=True)
275
276 @DRN.setter
277 def DRN(self, value):
278 self.fsi['DRN'].eq(value)
279
280 @property
281 def FX(self):
282 return self.fsi['FX'].asint(msb0=True)
283
284 @FX.setter
285 def FX(self, value):
286 self.fsi['FX'].eq(value)
287
288 @property
289 def FEX(self):
290 return self.fsi['FEX'].asint(msb0=True)
291
292 @FEX.setter
293 def FEX(self, value):
294 self.fsi['FEX'].eq(value)
295
296 @property
297 def VX(self):
298 return self.fsi['VX'].asint(msb0=True)
299
300 @VX.setter
301 def VX(self, value):
302 self.fsi['VX'].eq(value)
303
304 @property
305 def OX(self):
306 return self.fsi['OX'].asint(msb0=True)
307
308 @OX.setter
309 def OX(self, value):
310 self.fsi['OX'].eq(value)
311
312 @property
313 def UX(self):
314 return self.fsi['UX'].asint(msb0=True)
315
316 @UX.setter
317 def UX(self, value):
318 self.fsi['UX'].eq(value)
319
320 @property
321 def ZX(self):
322 return self.fsi['ZX'].asint(msb0=True)
323
324 @ZX.setter
325 def ZX(self, value):
326 self.fsi['ZX'].eq(value)
327
328 @property
329 def XX(self):
330 return self.fsi['XX'].asint(msb0=True)
331
332 @XX.setter
333 def XX(self, value):
334 self.fsi['XX'].eq(value)
335
336 @property
337 def VXSNAN(self):
338 return self.fsi['VXSNAN'].asint(msb0=True)
339
340 @VXSNAN.setter
341 def VXSNAN(self, value):
342 self.fsi['VXSNAN'].eq(value)
343
344 @property
345 def VXISI(self):
346 return self.fsi['VXISI'].asint(msb0=True)
347
348 @VXISI.setter
349 def VXISI(self, value):
350 self.fsi['VXISI'].eq(value)
351
352 @property
353 def VXIDI(self):
354 return self.fsi['VXIDI'].asint(msb0=True)
355
356 @VXIDI.setter
357 def VXIDI(self, value):
358 self.fsi['VXIDI'].eq(value)
359
360 @property
361 def VXZDZ(self):
362 return self.fsi['VXZDZ'].asint(msb0=True)
363
364 @VXZDZ.setter
365 def VXZDZ(self, value):
366 self.fsi['VXZDZ'].eq(value)
367
368 @property
369 def VXIMZ(self):
370 return self.fsi['VXIMZ'].asint(msb0=True)
371
372 @VXIMZ.setter
373 def VXIMZ(self, value):
374 self.fsi['VXIMZ'].eq(value)
375
376 @property
377 def VXVC(self):
378 return self.fsi['VXVC'].asint(msb0=True)
379
380 @VXVC.setter
381 def VXVC(self, value):
382 self.fsi['VXVC'].eq(value)
383
384 @property
385 def FR(self):
386 return self.fsi['FR'].asint(msb0=True)
387
388 @FR.setter
389 def FR(self, value):
390 self.fsi['FR'].eq(value)
391
392 @property
393 def FI(self):
394 return self.fsi['FI'].asint(msb0=True)
395
396 @FI.setter
397 def FI(self, value):
398 self.fsi['FI'].eq(value)
399
400 @property
401 def FPRF(self):
402 return self.fsi['FPRF'].asint(msb0=True)
403
404 @FPRF.setter
405 def FPRF(self, value):
406 self.fsi['FPRF'].eq(value)
407
408 @property
409 def C(self):
410 return self.fsi['C'].asint(msb0=True)
411
412 @C.setter
413 def C(self, value):
414 self.fsi['C'].eq(value)
415
416 @property
417 def FPCC(self):
418 return self.fsi['FPCC'].asint(msb0=True)
419
420 @FPCC.setter
421 def FPCC(self, value):
422 self.fsi['FPCC'].eq(value)
423
424 @property
425 def FL(self):
426 return self.fsi['FL'].asint(msb0=True)
427
428 @FL.setter
429 def FL(self, value):
430 self.fsi['FL'].eq(value)
431
432 @property
433 def FG(self):
434 return self.fsi['FG'].asint(msb0=True)
435
436 @FG.setter
437 def FG(self, value):
438 self.fsi['FG'].eq(value)
439
440 @property
441 def FE(self):
442 return self.fsi['FE'].asint(msb0=True)
443
444 @FE.setter
445 def FE(self, value):
446 self.fsi['FE'].eq(value)
447
448 @property
449 def FU(self):
450 return self.fsi['FU'].asint(msb0=True)
451
452 @FU.setter
453 def FU(self, value):
454 self.fsi['FU'].eq(value)
455
456 @property
457 def VXSOFT(self):
458 return self.fsi['VXSOFT'].asint(msb0=True)
459
460 @VXSOFT.setter
461 def VXSOFT(self, value):
462 self.fsi['VXSOFT'].eq(value)
463
464 @property
465 def VXSQRT(self):
466 return self.fsi['VXSQRT'].asint(msb0=True)
467
468 @VXSQRT.setter
469 def VXSQRT(self, value):
470 self.fsi['VXSQRT'].eq(value)
471
472 @property
473 def VXCVI(self):
474 return self.fsi['VXCVI'].asint(msb0=True)
475
476 @VXCVI.setter
477 def VXCVI(self, value):
478 self.fsi['VXCVI'].eq(value)
479
480 @property
481 def VE(self):
482 return self.fsi['VE'].asint(msb0=True)
483
484 @VE.setter
485 def VE(self, value):
486 self.fsi['VE'].eq(value)
487
488 @property
489 def OE(self):
490 return self.fsi['OE'].asint(msb0=True)
491
492 @OE.setter
493 def OE(self, value):
494 self.fsi['OE'].eq(value)
495
496 @property
497 def UE(self):
498 return self.fsi['UE'].asint(msb0=True)
499
500 @UE.setter
501 def UE(self, value):
502 self.fsi['UE'].eq(value)
503
504 @property
505 def ZE(self):
506 return self.fsi['ZE'].asint(msb0=True)
507
508 @ZE.setter
509 def ZE(self, value):
510 self.fsi['ZE'].eq(value)
511
512 @property
513 def XE(self):
514 return self.fsi['XE'].asint(msb0=True)
515
516 @XE.setter
517 def XE(self, value):
518 self.fsi['XE'].eq(value)
519
520 @property
521 def NI(self):
522 return self.fsi['NI'].asint(msb0=True)
523
524 @NI.setter
525 def NI(self, value):
526 self.fsi['NI'].eq(value)
527
528 @property
529 def RN(self):
530 return self.fsi['RN'].asint(msb0=True)
531
532 @RN.setter
533 def RN(self, value):
534 self.fsi['RN'].eq(value)
535
536
537 if __name__ == "__main__":
538 from pprint import pprint
539 print("FPSCR_FIELDS_MSB0:")
540 pprint(FPSCR_FIELDS_MSB0)
541 print("FPSCRRecord.layout:")
542 pprint(FPSCRRecord.layout)
543 print("FPSCRState.fsi:")
544 pprint(FPSCRState().fsi)