blob: bea652a16271ad2f881cd51e4e15ae786172572b [file] [log] [blame]
# 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)