# SPDX-License-Identifier: LGPLv3+
# Funded by NLnet https://nlnet.nl/
+
+# XXX TODO: get this into openpower/consts.py instead.
+# create the layout from an auto-created Enum FPSCRb
""" Record for FPSCR as defined in
Power ISA v3.1B Book I section 4.2.2 page 136(162)
FPSCR fields in MSB0:
-| Bits | Mnemonic | Description |
-|-------|----------|-------------------------------------------------------------------------|
-| 0:28 | | Reserved |
-| 29:31 | DRN | Decimal Rounding Mode |
-| 32 | FX | Floating-Point Exception Summary |
-| 33 | FEX | Floating-Point Enabled Exception Summary |
-| 34 | VX | Floating-Point Invalid Operation Exception Summary |
-| 35 | OX | Floating-Point Overflow Exception |
-| 36 | UX | Floating-Point Underflow Exception |
-| 37 | ZX | Floating-Point Zero Divide Exception |
-| 38 | XX | Floating-Point Inexact Exception |
-| 39 | VXSNAN | Floating-Point Invalid Operation Exception (SNaN) |
-| 40 | VXISI | Floating-Point Invalid Operation Exception (∞ - ∞) |
-| 41 | VXIDI | Floating-Point Invalid Operation Exception (∞ ÷ ∞) |
-| 42 | VXZDZ | Floating-Point Invalid Operation Exception (0 ÷ 0) |
-| 43 | VXIMZ | Floating-Point Invalid Operation Exception (∞ × 0) |
-| 44 | VXVC | Floating-Point Invalid Operation Exception (Invalid Compare) |
-| 45 | FR | Floating-Point Fraction Rounded |
-| 46 | FI | Floating-Point Fraction Inexact |
-| 47:51 | FPRF | Floating-Point Result Flags |
-| 47 | C | Floating-Point Result Class Descriptor |
-| 48:51 | FPCC | Floating-Point Condition Code |
-| 48 | FL | Floating-Point Less Than or Negative |
-| 49 | FG | Floating-Point Greater Than or Positive |
-| 50 | FE | Floating-Point Equal or Zero |
-| 51 | FU | Floating-Point Unordered or NaN |
-| 52 | | Reserved |
-| 53 | VXSOFT | Floating-Point Invalid Operation Exception (Software-Defined Condition) |
-| 54 | VXSQRT | Floating-Point Invalid Operation Exception (Invalid Square Root) |
-| 55 | VXCVI | Floating-Point Invalid Operation Exception (Invalid Integer Convert) |
-| 56 | VE | Floating-Point Invalid Operation Exception Enable |
-| 57 | OE | Floating-Point Overflow Exception Enable |
-| 58 | UE | Floating-Point Underflow Exception Enable |
-| 59 | ZE | Floating-Point Zero Divide Exception Enable |
-| 60 | XE | Floating-Point Inexact Exception Enable |
-| 61 | NI | Floating-Point Non-IEEE Mode |
-| 62:63 | RN | Floating-Point Rounding Control |
+|Bits |Mnemonic | Description |
+|-----|---------|-------------------------------------------------------------|
+|0:28 | | Reserved |
+|29:31| DRN | Decimal Rounding Mode |
+|32 | FX | FP Exception Summary |
+|33 | FEX | FP Enabled Exception Summary |
+|34 | VX | FP Invalid Operation Exception Summary |
+|35 | OX | FP Overflow Exception |
+|36 | UX | FP Underflow Exception |
+|37 | ZX | FP Zero Divide Exception |
+|38 | XX | FP Inexact Exception |
+|39 | VXSNAN | FP Invalid Operation Exception (SNaN) |
+|40 | VXISI | FP Invalid Operation Exception (∞ - ∞) |
+|41 | VXIDI | FP Invalid Operation Exception (∞ ÷ ∞) |
+|42 | VXZDZ | FP Invalid Operation Exception (0 ÷ 0) |
+|43 | VXIMZ | FP Invalid Operation Exception (∞ × 0) |
+|44 | VXVC | FP Invalid Operation Exception (Invalid Compare) |
+|45 | FR | FP Fraction Rounded |
+|46 | FI | FP Fraction Inexact |
+|47:51| FPRF | FP Result Flags |
+|47 | C | FP Result Class Descriptor |
+|48:51| FPCC | FP Condition Code |
+|48 | FL | FP Less Than or Negative |
+|49 | FG | FP Greater Than or Positive |
+|50 | FE | FP Equal or Zero |
+|51 | FU | FP Unordered or NaN |
+|52 | | Reserved |
+|53 | VXSOFT | FP Invalid Operation Exception (Software-Defined Condition) |
+|54 | VXSQRT | FP Invalid Operation Exception (Invalid Square Root) |
+|55 | VXCVI | FP Invalid Operation Exception (Invalid Integer Convert) |
+|56 | VE | FP Invalid Operation Exception Enable |
+|57 | OE | FP Overflow Exception Enable |
+|58 | UE | FP Underflow Exception Enable |
+|59 | ZE | FP Zero Divide Exception Enable |
+|60 | XE | FP Inexact Exception Enable |
+|61 | NI | FP Non-IEEE Mode |
+|62:63| RN | FP Rounding Control |
"""
from nmigen import Record
-from typing import NoReturn
-from nmutil.plain_data import plain_data
-import linecache
+from copy import deepcopy
+from openpower.util import log
from openpower.decoder.selectable_int import (
FieldSelectableInt, SelectableInt)
-def _parse_line_fields(line):
- # type: (str) -> None | list[str]
- sline = line.strip()
- if not sline.startswith("|"):
- return None
- if not sline.endswith("|"):
- return None
- if sline == "|":
- return None
- return [v.strip() for v in sline[1:-2].split("|")]
-
-
-_BITS_FIELD = "Bits"
-_MNEMONIC_FIELD = "Mnemonic"
-FPSCR_WIDTH = 64
-
-
-@plain_data()
-class _MutableField:
- __slots__ = "name", "bits_msb0", "lineno", "include_in_record"
-
- def __init__(self, name, bits_msb0, lineno, include_in_record=True):
- # type: (str, int | range, int, bool) -> None
- self.name = name
- self.bits_msb0 = bits_msb0
- self.lineno = lineno
- self.include_in_record = include_in_record
-
- def bits_msb0_iter(self):
- # type: () -> range | tuple[int]
- if isinstance(self.bits_msb0, int):
- return self.bits_msb0,
- return self.bits_msb0
-
- def to_field(self):
- # type () -> FPSCRField
- return FPSCRField(name=self.name, bits_msb0=self.bits_msb0,
- include_in_record=self.include_in_record)
-
-
-@plain_data(frozen=True, unsafe_hash=True)
-class FPSCRField:
- __slots__ = "name", "bits_msb0", "include_in_record"
-
- def __init__(self, name, bits_msb0, include_in_record):
- # type: (str, int | range, bool) -> None
- self.name = name
- self.bits_msb0 = bits_msb0
- self.include_in_record = include_in_record
- """True if this field should be
- included in `FPSCRRecord`, since there are some overlapping fields and
- `Record` doesn't support that.
- """
-
- def bits_msb0_iter(self):
- # type: () -> range | tuple[int]
- if isinstance(self.bits_msb0, int):
- return self.bits_msb0,
- return self.bits_msb0
-
-
-def _parse_fields():
- # type: () -> tuple[FPSCRField, ...]
- lines = __doc__.splitlines()
- in_header_sep = False
- in_table_body = False
- header_fields = [] # type: list[str]
- header_lineno = 0
- fields = {} # type: dict[str, _MutableField]
- bit_fields = [None] * FPSCR_WIDTH # type: list[_MutableField | None]
- lineno = 0
-
- def raise_(msg, col=1, err_lineno=None):
- # type: (str, int, int | None) -> NoReturn
- nonlocal lineno
- if err_lineno is None:
- err_lineno = lineno
- for i in range(10000): # 10000 is random limit if we can't read
- if linecache.getline(__file__, i).strip().startswith('"'):
- break
- err_lineno += 1 # lines before doc comment start
- raise SyntaxError(msg, (
- __file__, err_lineno + 3, col, lines[err_lineno]))
-
- for lineno, line in enumerate(lines):
- line_fields = _parse_line_fields(line)
- if in_table_body:
- if line_fields is None:
- if len(fields) == 0:
- raise_("missing table body")
- break
- if len(line_fields) != len(header_fields):
- raise_("wrong number of fields")
- fields_dict = {k: v for k, v in zip(header_fields, line_fields)}
- name = fields_dict[_MNEMONIC_FIELD]
- if name == "" or name == " ":
- continue
- if not name.isidentifier():
- raise_(f"invalid field name {name!r}")
- if name in fields:
- raise_(f"duplicate field name {name}")
- bits_str = fields_dict[_BITS_FIELD]
- bits_fields_str = bits_str.split(":")
- if len(bits_fields_str) not in (1, 2) or not all(
- v.isascii() and v.isdigit() for v in bits_fields_str):
- raise_(f"`{_BITS_FIELD}` field must be "
- f"of the form `23` or `23:56`")
- bits_fields = [int(v, base=10) for v in bits_fields_str]
- if not all(0 <= v < FPSCR_WIDTH for v in bits_fields):
- raise_(f"`{_BITS_FIELD}` field value is beyond the "
- f"limits of FPSCR: must be `0 <= v < {FPSCR_WIDTH}`")
- if len(bits_fields) == 2:
- first, last = bits_fields
- if first > last:
- raise_(f"`{_BITS_FIELD}` field value is an improper "
- f"range: {first} > {last}")
- bits = range(first, last + 1)
- else:
- bits = bits_fields[0]
- field = _MutableField(name=name, bits_msb0=bits, lineno=lineno)
- fields[name] = field
- for bit in field.bits_msb0_iter():
- old_field = bit_fields[bit]
- if old_field is not None:
- # field is overwritten -- don't include in Record
- old_field.include_in_record = False
- bit_fields[bit] = field
- elif in_header_sep:
- if line_fields is None:
- raise_("missing header separator line")
- for v in line_fields:
- if v != "-" * len(v):
- raise_("header separator field isn't just hyphens")
- if len(line_fields) != len(header_fields):
- raise_("wrong number of fields")
- in_header_sep = False
- in_table_body = True
- else:
- if line_fields is None:
- continue
- if _BITS_FIELD not in line_fields:
- raise_(f"missing `{_BITS_FIELD}` field")
- if _MNEMONIC_FIELD not in line_fields:
- raise_(f"missing `{_MNEMONIC_FIELD}` field")
- if len(set(line_fields)) != len(line_fields):
- raise_("duplicate header field")
- header_fields = line_fields
- in_header_sep = True
- header_lineno = lineno
- if len(fields) == 0:
- raise_("missing table")
- # insert reserved fields and check for partially overwritten fields
- for bit in range(FPSCR_WIDTH):
- field = bit_fields[bit]
- if field is None:
- start = bit
- bit += 1
- while bit < FPSCR_WIDTH and bit_fields[bit] is None:
- bit += 1
- field = _MutableField(name=f"RESERVED_{start}_{bit - 1}",
- bits_msb0=range(start, bit),
- lineno=header_lineno)
- if len(field.bits_msb0) == 1:
- field.bits_msb0 = start
- field.name = f"RESERVED_{start}"
- for bit in field.bits_msb0_iter():
- bit_fields[bit] = field
- if field.name in fields:
- raise_(f"field {field.name}'s name conflicts with a "
- f"generated reserved field",
- err_lineno=fields[field.name].lineno)
- fields[field.name] = field
- elif not field.include_in_record:
- raise_(f"field {field.name} is partially overwritten -- "
- f"this is an error because FPSCRRecord will have "
- f"incorrect fields", err_lineno=field.lineno)
- else:
- bit += 1
- return tuple(f.to_field() for f in fields.values())
-
-
-FPSCR_FIELDS_MSB0 = _parse_fields() # type: tuple[FPSCRField, ...]
-""" All fields in FPSCR. """
-
-
-def _calc_record_layout_lsb0():
- # type: () -> list[tuple[str, int]]
- fields_lsb0 = [] # type: list[tuple[int, int, str]]
- for field in FPSCR_FIELDS_MSB0:
- if not field.include_in_record:
- continue
- start_msb0 = field.bits_msb0_iter()[0]
- field_len = len(field.bits_msb0_iter())
- start_lsb0 = FPSCR_WIDTH - 1 - start_msb0
- fields_lsb0.append((start_lsb0, field_len, field.name))
- fields_lsb0.sort()
- # _parse_fields already checks for partially overlapping fields and
- # inserts reserved fields ensuring the returned fields cover every bit
- # exactly one, therefore this is correct
- return [(name, f_len) for _, f_len, name in fields_lsb0]
-
-
class FPSCRRecord(Record):
- layout = _calc_record_layout_lsb0()
+ layout = [("RN", 2),
+ ("NI", 1),
+ ("XE", 1),
+ ("ZE", 1),
+ ("UE", 1),
+ ("OE", 1),
+ ("VE", 1),
+ ("VXCVI", 1),
+ ("VXSQRT", 1),
+ ("VXSOFT", 1),
+ ("rsvd1", 1),
+ ("FPRF", [
+ ("FPCC", [
+ ("FU", 1),
+ ("FE", 1),
+ ("FG", 1),
+ ("FL", 1),
+ ]),
+ ("C", 1),
+ ]),
+ ("FI", 1),
+ ("FR", 1),
+ ("VXVC", 1),
+ ("VXIMZ", 1),
+ ("VXZDZ", 1),
+ ("VXIDI", 1),
+ ("VXISI", 1),
+ ("VXSNAN", 1),
+ ("XX", 1),
+ ("ZX", 1),
+ ("UX", 1),
+ ("OX", 1),
+ ("VX", 1),
+ ("FEX", 1),
+ ("FX", 1),
+ ("DRN", 3),
+ ("rsvd2", 29),
+ ]
def __init__(self, name=None):
super().__init__(name=name, layout=FPSCRRecord.layout)
+class FPSCR_FPRF(FieldSelectableInt):
+ """ special FieldSelectableInt instance to handle assigning strings to
+ FPSCR.FPRF
+
+ Translation Table from:
+ PowerISA v3.1B Book I Section 4.2.2 Page 139(165)
+ Figure 47 Floating-Point Result Flags
+ """
+ TRANSLATION_TABLE = (
+ ("Quiet NaN", 0b10001),
+ ("QNaN", 0b10001),
+ ("- Infinity", 0b01001),
+ ("- Normalized Number", 0b01000),
+ ("- Normal Number", 0b01000),
+ ("- Denormalized Number", 0b11000),
+ ("- Zero", 0b10010),
+ ("+ Zero", 0b00010),
+ ("+ Denormalized Number", 0b10100),
+ ("+ Normalized Number", 0b00100),
+ ("+ Normal Number", 0b00100),
+ ("+ Infinity", 0b00101),
+ )
+ TRANSLATION_TABLE_DICT = {k.casefold(): v for k, v in TRANSLATION_TABLE}
+
+ def eq(self, b):
+ if isinstance(b, str):
+ b = FPSCR_FPRF.TRANSLATION_TABLE_DICT[b.casefold()]
+ super().eq(b)
+
+
class FPSCRState(SelectableInt):
- def __init__(self, value=0):
- SelectableInt.__init__(self, value, FPSCR_WIDTH)
+ def __init__(self, value=0, *, auto_update_summary_bits=True):
+ self.__do_update_summary_bits = False
+ SelectableInt.__init__(self, value, 64)
+ self.fsi = {}
+ offs = 0
+ # set up sub-fields from Record layout
self.fsi = {}
- for field in FPSCR_FIELDS_MSB0:
- bits_msb0 = tuple(field.bits_msb0_iter())
- self.fsi[field.name] = FieldSelectableInt(self, bits_msb0)
+ for field, width in reversed(FPSCRRecord.layout):
+ if field == "FPRF":
+ v = FPSCR_FPRF(self, tuple(range(47, 52)))
+ end = 52
+ else:
+ end = offs + width
+ fs = tuple(range(offs, end))
+ v = FieldSelectableInt(self, fs)
+ self.fsi[field] = v
+ offs = end
+ # extra fields, temporarily explicitly added. TODO nested layout above
+ extras = [
+ (47, "C"),
+ (range(48, 52), "FPCC"),
+ (48, "FL"),
+ (49, "FG"),
+ (50, "FE"),
+ (51, "FU"),
+ ]
+ for offs, field in extras:
+ if isinstance(offs, int):
+ fs = (offs,)
+ else:
+ fs = tuple(offs)
+ v = FieldSelectableInt(self, fs)
+ self.fsi[field] = v
+ if auto_update_summary_bits:
+ self.__update_summary_bits()
+
+ @property
+ def value(self):
+ return self.__value
+
+ @value.setter
+ def value(self, value):
+ self.__value = value
+ if self.__do_update_summary_bits:
+ self.__update_summary_bits()
+
+ def __update_summary_bits(self):
+ self.__do_update_summary_bits = False
+ try:
+ # update summary bits -- FX is manually handled by pseudo-code,
+ # so we don't update it here
+ self.VX = (self.VXSNAN |
+ self.VXISI |
+ self.VXIDI |
+ self.VXZDZ |
+ self.VXIMZ |
+ self.VXVC |
+ self.VXSOFT |
+ self.VXSQRT |
+ self.VXCVI)
+ self.FEX = ((self.VX & self.VE) |
+ (self.OX & self.OE) |
+ (self.UX & self.UE) |
+ (self.ZX & self.ZE) |
+ (self.XX & self.XE))
+ finally:
+ self.__do_update_summary_bits = True
@property
def DRN(self):
if __name__ == "__main__":
from pprint import pprint
- print("FPSCR_FIELDS_MSB0:")
- pprint(FPSCR_FIELDS_MSB0)
print("FPSCRRecord.layout:")
pprint(FPSCRRecord.layout)
print("FPSCRState.fsi:")
pprint(FPSCRState().fsi)
+
+ # quick test of setter/getters
+ fpscr = FPSCRState()
+ fpscr.FPCC = 0b0001
+ print(fpscr.FPCC, fpscr.FL, fpscr.FG, fpscr.FE, fpscr.FU)
+ fpscr.FG = 0b1
+ print(fpscr.FPCC, fpscr.FL, fpscr.FG, fpscr.FE, fpscr.FU)
+ fpscr.FPRF = 0b00011
+ print(fpscr.FPRF, fpscr.C)
+ fpscr[63] = 1
+ print(fpscr.RN)