| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| from typing import List, Optional, cast |
| |
| from .trace import Trace |
| |
| |
| class TraceFlag(Trace): |
| def __init__(self, group_name: str, flag_name: str, value: bool): |
| self.group_name = group_name |
| self.flag_name = flag_name |
| self.value = value |
| |
| def trace(self) -> str: |
| return '{}.{} = {}'.format(self.group_name, self.flag_name, int(self.value)) |
| |
| |
| class FlagReg: |
| FLAG_NAMES = ['C', 'M', 'L', 'Z'] |
| |
| def __init__(self, C: bool, M: bool, L: bool, Z: bool): |
| self.C = C |
| self.M = M |
| self.L = L |
| self.Z = Z |
| |
| self._new_val = None # type: Optional['FlagReg'] |
| |
| def set_flags(self, other: 'FlagReg') -> None: |
| self._new_val = other |
| |
| def get_by_name(self, flag_name: str) -> bool: |
| assert flag_name in FlagReg.FLAG_NAMES |
| return cast(bool, getattr(self, flag_name)) |
| |
| def get_by_idx(self, flag_idx: int) -> bool: |
| assert 0 <= flag_idx <= 3 |
| flag_name = FlagReg.FLAG_NAMES[flag_idx] |
| return self.get_by_name(flag_name) |
| |
| def changes(self, group_name: str) -> List[TraceFlag]: |
| if self._new_val is None: |
| return [] |
| return [TraceFlag(group_name, n, self._new_val.get_by_name(n)) |
| for n in FlagReg.FLAG_NAMES] |
| |
| def commit(self) -> None: |
| if self._new_val is not None: |
| for n in FlagReg.FLAG_NAMES: |
| setattr(self, n, getattr(self._new_val, n)) |
| self._new_val = None |
| |
| def abort(self) -> None: |
| self._new_val = None |
| |
| def read_unsigned(self) -> int: |
| '''Return a 4-bit number with the flags as ZLMC''' |
| return ((int(self.Z) << 3) | |
| (int(self.L) << 2) | |
| (int(self.M) << 1) | |
| (int(self.C) << 0)) |
| |
| def write_unsigned(self, value: int) -> None: |
| '''Set flags using bottom 4 bits of the unsigned number, value''' |
| assert 0 <= value |
| self.set_flags(FlagReg.from_bits(value)) |
| |
| @staticmethod |
| def mlz_for_result(C: bool, result: int) -> 'FlagReg': |
| '''Generate flags for the result of an operation. |
| |
| C is the value for the C flag. result is the wide-side result value |
| that is used to generate M, L and Z. |
| |
| ''' |
| M = bool((result >> 255) & 1) |
| L = bool(result & 1) |
| Z = bool(result == 0) |
| return FlagReg(C=C, M=M, L=L, Z=Z) |
| |
| @staticmethod |
| def from_bits(value: int) -> 'FlagReg': |
| assert 0 <= value |
| C = bool((value >> 0) & 1) |
| M = bool((value >> 1) & 1) |
| L = bool((value >> 2) & 1) |
| Z = bool((value >> 3) & 1) |
| return FlagReg(C=C, M=M, L=L, Z=Z) |
| |
| |
| class FlagGroups: |
| def __init__(self) -> None: |
| self._groups = {0: FlagReg(False, False, False, False), |
| 1: FlagReg(False, False, False, False)} |
| # Have any flags changed? |
| self._dirty = False |
| |
| def __getitem__(self, key: int) -> FlagReg: |
| assert 0 <= key <= 1 |
| return self._groups[key] |
| |
| def __setitem__(self, key: int, value: FlagReg) -> None: |
| assert 0 <= key <= 1 |
| self._dirty = True |
| self._groups[key].set_flags(value) |
| |
| def changes(self) -> List[TraceFlag]: |
| return self._groups[0].changes('FG0') + self._groups[1].changes('FG1') |
| |
| def commit(self) -> None: |
| if self._dirty: |
| self._groups[0].commit() |
| self._groups[1].commit() |
| self._dirty = False |
| |
| def abort(self) -> None: |
| if self._dirty: |
| self._groups[0].abort() |
| self._groups[1].abort() |
| self._dirty = False |
| |
| def read_unsigned(self) -> int: |
| '''Return the flag groups as an unsigned value (as seen by CSRs) |
| |
| Format is defined in FlagReg, with group 0 as LSB. |
| |
| ''' |
| return ((self._groups[1].read_unsigned() << 4) | |
| (self._groups[0].read_unsigned() << 0)) |
| |
| def write_unsigned(self, value: int) -> None: |
| '''Set the flag groups with an unsigned value, ignoring unused bits |
| |
| Format is defined in FlagReg, with group 0 as LSB. |
| |
| ''' |
| assert 0 <= value |
| mask4 = (1 << 4) - 1 |
| self._dirty = True |
| self._groups[0].write_unsigned((value >> 0) & mask4) |
| self._groups[1].write_unsigned((value >> 4) & mask4) |