blob: d254072d77d7fc8dbc8385ff461073bac49d63dd [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
from .trace import Trace
class TraceWSR(Trace):
def __init__(self, wsr_name: str, new_value: int):
self.wsr_name = wsr_name
self.new_value = new_value
def trace(self) -> str:
return '{} = {:#x}'.format(self.wsr_name, self.new_value)
def rtl_trace(self) -> str:
return '> {}: {}'.format(self.wsr_name,
Trace.hex_value(self.new_value, 256))
class WSR:
'''Models a Wide Status Register'''
def __init__(self, name: str):
self.name = name
def read_unsigned(self) -> int:
'''Get the stored value as a 256-bit unsigned value'''
raise NotImplementedError()
def write_unsigned(self, value: int) -> None:
'''Set the stored value as a 256-bit unsigned value'''
raise NotImplementedError()
def read_signed(self) -> int:
'''Get the stored value as a 256-bit signed value'''
uval = self.read_unsigned()
return uval - (1 << 256 if uval >> 255 else 0)
def write_signed(self, value: int) -> None:
'''Set the stored value as a 256-bit signed value'''
assert -(1 << 255) <= value < (1 << 255)
uval = (1 << 256) + value if value < 0 else value
self.write_unsigned(uval)
def commit(self) -> None:
'''Commit pending changes'''
return
def abort(self) -> None:
'''Abort pending changes'''
return
def changes(self) -> List[TraceWSR]:
'''Return list of pending architectural changes'''
return []
class DumbWSR(WSR):
'''Models a WSR without special behaviour'''
def __init__(self, name: str):
super().__init__(name)
self._value = 0
self._next_value = None # type: Optional[int]
def read_unsigned(self) -> int:
return self._value
def write_unsigned(self, value: int) -> None:
assert 0 <= value < (1 << 256)
self._next_value = value
def commit(self) -> None:
if self._next_value is not None:
self._value = self._next_value
self._next_value = None
def abort(self) -> None:
self._next_value = None
def changes(self) -> List[TraceWSR]:
return ([TraceWSR(self.name, self._next_value)]
if self._next_value is not None
else [])
class RandWSR(WSR):
'''The magic RND WSR
RND is special as OTBN can stall on reads to it. A read from RND either
immediately returns data from a cache of a previous EDN request (triggered
by writing to the RND_PREFETCH CSR) or waits for data from the EDN. To model
this anything reading from RND must first call `request_value` which returns
True if the value is available.
'''
def __init__(self, name: str):
super().__init__(name)
self._random_value = None # type: Optional[int]
self._random_value_read = False
self.pending_request = False
def read_unsigned(self) -> int:
assert self._random_value is not None
self._random_value_read = True
return self._random_value
def read_u32(self) -> int:
'''Read a 32-bit unsigned result'''
return self.read_unsigned() & ((1 << 32) - 1)
def write_unsigned(self, value: int) -> None:
'''Writes to RND are ignored
Note this is different to `set_unsigned`. This is used by executing
instruction, see `set_unsigned` docstring for more details
'''
return
def commit(self) -> None:
if self._random_value_read:
self._random_value = None
self.pending_request = False
self._random_value_read = False
def request_value(self) -> bool:
'''Signals intent to read RND, returns True if a value is available'''
if self._random_value:
return True
self.pending_request = True
return False
def set_unsigned(self, value: int) -> None:
'''Sets a random value that can be read by a future `read_unsigned`
This is different to `write_unsigned`, that is used by an executing
instruction to write to RND. This is used by the simulation environment
to provide a value that is later read by `read_unsigned` and doesn't
relate to instruction execution (e.g. in an RTL simulation it monitors
the EDN bus and supplies the simulator with an RND value when a fresh
one is seen on the EDN bus).
'''
assert 0 <= value < (1 << 256)
self._random_value = value
class WSRFile:
'''A model of the WSR file'''
def __init__(self) -> None:
self.MOD = DumbWSR('MOD')
self.RND = RandWSR('RND')
self.ACC = DumbWSR('ACC')
self._by_idx = {
0: self.MOD,
1: self.RND,
# TODO: Implement 2: URND
3: self.ACC
}
def check_idx(self, idx: int) -> bool:
'''Return True if idx is a valid WSR index'''
return idx in self._by_idx
def read_at_idx(self, idx: int) -> int:
'''Read the WSR at idx as an unsigned 256-bit value
Assumes that idx is a valid index (call check_idx to ensure this).
'''
return self._by_idx[idx].read_unsigned()
def write_at_idx(self, idx: int, value: int) -> None:
'''Write the WSR at idx as an unsigned 256-bit value
Assumes that idx is a valid index (call check_idx to ensure this).
'''
return self._by_idx[idx].write_unsigned(value)
def commit(self) -> None:
self.MOD.commit()
self.RND.commit()
self.ACC.commit()
def abort(self) -> None:
self.MOD.abort()
self.RND.abort()
self.ACC.abort()
def changes(self) -> List[TraceWSR]:
return self.MOD.changes() + self.RND.changes() + self.ACC.changes()