blob: 7108f8ed609078f499c825bb0a663cb308fa429f [file]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "riscv/riscv_xstatus.h"
#include <cstdint>
#include "absl/log/log.h"
#include "mpact/sim/generic/type_helpers.h"
#include "riscv/riscv_csr.h"
#include "riscv/riscv_misa.h"
#include "riscv/riscv_state.h"
namespace mpact {
namespace sim {
namespace riscv {
using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error).
// Helper functions to convert between 32 and 64 bit views of mstatus.
static inline uint64_t StretchMStatus32(uint32_t value) {
uint64_t value64 = static_cast<uint64_t>(value);
value64 = ((value64 & 0xc000'0000) << 32) | (value64 & 0x03ff'ffff);
return value64;
}
static inline uint32_t CompressMStatus64(uint64_t value) {
uint32_t value32 = ((value >> 32) & 0xc000'0000) | (value & 0x3fff'ffff);
return value32;
}
// Constructors.
RiscVMStatus::RiscVMStatus(uint32_t initial_value, ArchState *state,
RiscVMIsa *misa)
: RiscVMStatus(StretchMStatus32(initial_value), state, RiscVXlen::RV32,
misa) {}
RiscVMStatus::RiscVMStatus(uint64_t initial_value, ArchState *state,
RiscVMIsa *misa)
: RiscVMStatus(initial_value, state, RiscVXlen::RV64, misa) {}
RiscVMStatus::RiscVMStatus(uint64_t initial_value, ArchState *state,
RiscVXlen xlen, RiscVMIsa *misa)
: RiscVSimpleCsr<uint64_t>("mstatus", RiscVCsrEnum::kMStatus, initial_value,
kReadMask, kWriteMask, state),
misa_(misa) {
// All bits except for uxl and sxl, since that doesn't appear in the 32 bit
// view.
set_mask_from_32_ = 0xffff'fff0'ffff'ffffULL;
// Set UXL and SXL be the current xlen.
uint64_t xlen_val = xlen == RiscVXlen::RV64 ? 0b10 : 0b01;
// If misa doesn't show support for user or supervisor, clear corresponding
// bits from values and read/write masks.
if (misa_->HasSupervisorMode()) {
xlen_val &= 0b0011;
}
if (misa_->HasUserMode()) {
xlen_val &= 0b1100;
}
xlen_val <<= 32;
Set(GetUint64() | xlen_val);
read_mask_32_ = CompressMStatus64(read_mask());
write_mask_32_ = CompressMStatus64(write_mask());
}
// Override implementations.
uint32_t RiscVMStatus::AsUint32() {
// The value is stored as if xlen is 64, so adjust the layout.
return GetUint32() & read_mask_32_;
}
void RiscVMStatus::Write(uint32_t value) { Set(value & write_mask_32_); }
void RiscVMStatus::SetBits(uint32_t bits) {
uint32_t new_value = GetUint32() | (bits & write_mask_32_);
Set(new_value);
}
void RiscVMStatus::ClearBits(uint32_t bits) {
uint32_t new_value = GetUint32() & ~(bits & write_mask_32_);
Set(new_value);
}
uint32_t RiscVMStatus::GetUint32() {
// The value is stored as if xlen is 64, so adjust the layout.
return CompressMStatus64(GetUint64());
}
void RiscVMStatus::Set(uint32_t value) {
// Keep the sxl and uxl bits in the 64 bit value.
uint64_t new_value = (StretchMStatus32(value) & set_mask_from_32_) |
(GetUint64() & ~set_mask_from_32_);
Set(new_value);
}
void RiscVMStatus::Set(uint64_t value) {
this->RiscVSimpleCsr<uint64_t>::Set(value);
// If an interrupt enable might be set, check for interrupts.
if (value & 0xf) {
state()->CheckForInterrupt();
}
}
void RiscVMStatus::set_mpp(uint32_t value) {
// Don't allow values for unsupported privilege modes.
if ((value == *PrivilegeMode::kUser) && !misa_->HasUserMode()) return;
if ((value == *PrivilegeMode::kSupervisor) && !misa_->HasSupervisorMode())
return;
SetterHelper<11, 0b11>(value);
}
// RiscVSStatus methods.
RiscVSStatus::RiscVSStatus(RiscVMStatus *mstatus, RiscVState *state)
: RiscVSimpleCsr<uint64_t>("sstatus", RiscVCsrEnum::kSStatus, kReadMask,
kWriteMask, state),
mstatus_(mstatus) {
set_mask_from_32_ = 0xffff'fff0'ffff'ffffULL;
read_mask_32_ = CompressMStatus64(read_mask());
write_mask_32_ = CompressMStatus64(write_mask());
}
uint32_t RiscVSStatus::AsUint32() { return CompressMStatus64(AsUint64()); }
uint64_t RiscVSStatus::AsUint64() { return mstatus_->AsUint64(); }
void RiscVSStatus::Set(uint32_t value) {
uint64_t new_value = StretchMStatus32(value);
Set(new_value);
}
// Only update those bytes of mstatus that can be written into sstatus.
void RiscVSStatus::Set(uint64_t value) {
mstatus_->Set((mstatus_->GetUint64() & ~write_mask()) |
(value & write_mask()));
}
void RiscVSStatus::Write(uint32_t value) { Set(value & write_mask_32_); }
void RiscVSStatus::SetBits(uint32_t bits) {
uint32_t new_value = GetUint32() | (bits & write_mask_32_);
Set(new_value);
}
void RiscVSStatus::ClearBits(uint32_t bits) {
uint32_t new_value = GetUint32() & ~(bits & write_mask_32_);
Set(new_value);
}
uint32_t RiscVSStatus::GetUint32() { return CompressMStatus64(GetUint64()); }
uint64_t RiscVSStatus::GetUint64() { return mstatus_->GetUint64(); }
// RiscVUStatus methods.
RiscVUStatus::RiscVUStatus(RiscVMStatus *mstatus, RiscVState *state)
: RiscVSimpleCsr<uint64_t>("ustatus", RiscVCsrEnum::kUStatus, kReadMask,
kWriteMask, state),
mstatus_(mstatus) {
set_mask_from_32_ = 0xffff'fff0'ffff'ffffULL;
read_mask_32_ = CompressMStatus64(read_mask());
write_mask_32_ = CompressMStatus64(write_mask());
}
uint32_t RiscVUStatus::AsUint32() { return CompressMStatus64(AsUint64()); }
uint64_t RiscVUStatus::AsUint64() { return mstatus_->AsUint64(); }
void RiscVUStatus::Write(uint32_t value) { Set(value & write_mask_32_); }
void RiscVUStatus::SetBits(uint32_t bits) {
uint32_t new_value = GetUint32() | (bits & write_mask_32_);
Set(new_value);
}
void RiscVUStatus::ClearBits(uint32_t bits) {
uint32_t new_value = GetUint32() & ~(bits & write_mask_32_);
Set(new_value);
}
void RiscVUStatus::Set(uint32_t value) {
uint64_t new_value = StretchMStatus32(value);
Set(new_value);
}
// Only update those bytes of mstatus that can be written into ustatus.
void RiscVUStatus::Set(uint64_t value) {
mstatus_->Set((mstatus_->GetUint64() & ~write_mask()) |
(value & write_mask()));
}
uint32_t RiscVUStatus::GetUint32() { return CompressMStatus64(GetUint64()); }
uint64_t RiscVUStatus::GetUint64() { return mstatus_->GetUint64(); }
} // namespace riscv
} // namespace sim
} // namespace mpact