blob: b349f05a16458edb124ed2a5a5cf9b0aa65cb5fe [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_zicsr_instructions.h"
#include <cstdint>
#include "absl/log/log.h"
#include "absl/strings/str_cat.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/generic/register.h"
#include "mpact/sim/generic/type_helpers.h"
#include "riscv/riscv_csr.h"
#include "riscv/riscv_register.h"
#include "riscv/riscv_state.h"
namespace mpact {
namespace sim {
namespace riscv {
using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error).
template <typename T>
inline T ReadCsr(RiscVCsrInterface *) {}
template <>
inline uint32_t ReadCsr<uint32_t>(RiscVCsrInterface *csr) {
return csr->AsUint32();
}
template <>
inline uint64_t ReadCsr<uint64_t>(RiscVCsrInterface *csr) {
return csr->AsUint64();
}
// Helper function to check that the CSR permission is valid. If not, throw
// an illegal instruction exception.
bool CheckCsrPermission(int csr_index, Instruction *instruction) {
auto required_mode = (csr_index >> 8) & 0x3;
auto *state = static_cast<RiscVState *>(instruction->state());
auto current_mode = state->privilege_mode();
// If the privilege mode is too low, throw an exception.
if (*current_mode < required_mode) {
state->Trap(/*is_interrupt*/ false, 0, *ExceptionCode::kIllegalInstruction,
instruction->address(), instruction);
return false;
}
// Check for any special case CSRs that have special access conditions.
// Check for access to satp.
if (current_mode == PrivilegeMode::kSupervisor &&
(csr_index == *RiscVCsrEnum::kSAtp) && (state->mstatus()->tvm())) {
state->Trap(/*is_interrupt*/ false, 0, *ExceptionCode::kIllegalInstruction,
instruction->address(), instruction);
return false;
}
return true;
}
// Templated helper functions.
// Read the CSR, write a new value back.
template <typename T>
static inline void RVZiCsrrw(Instruction *instruction) {
// Get a handle to the state instance.
auto state = static_cast<RiscVState *>(instruction->state());
// Get the csr index.
int csr_index = instruction->Source(1)->AsInt32(0);
if (!CheckCsrPermission(csr_index, instruction)) return;
// Read the csr.
auto result = state->csr_set()->GetCsr(csr_index);
if (!result.ok()) {
// Signal error if it failed.
LOG(ERROR) << absl::StrCat("Instruction at address 0x",
absl::Hex(instruction->address()),
" failed to read CSR 0x", absl::Hex(csr_index),
": ", result.status().message());
return;
}
// Get the new value.
T new_value = generic::GetInstructionSource<T>(instruction, 0);
auto *csr = result.value();
// Update the register.
auto *reg = static_cast<generic::RegisterDestinationOperand<T> *>(
instruction->Destination(0))
->GetRegister();
reg->data_buffer()->template Set<T>(0, ReadCsr<T>(csr));
// Write the new value to the csr.
csr->Write(new_value);
}
// Read the CSR, set the bits specified by the new value and write back.
template <typename T>
static inline void RVZiCsrrs(Instruction *instruction) {
// Get a handle to the state instance.
auto state = static_cast<RiscVState *>(instruction->state());
// Get the csr index.
int csr_index = instruction->Source(1)->AsInt32(0);
if (!CheckCsrPermission(csr_index, instruction)) return;
// Read the csr.
auto result = state->csr_set()->GetCsr(csr_index);
if (!result.ok()) {
// Signal error if it failed.
LOG(ERROR) << absl::StrCat("Instruction at address 0x",
absl::Hex(instruction->address()),
" failed to read CSR 0x", absl::Hex(csr_index),
": ", result.status().message());
return;
}
// Get the new value.
T new_value = generic::GetInstructionSource<T>(instruction, 0);
auto *csr = result.value();
// Update the register.
auto *reg = static_cast<generic::RegisterDestinationOperand<T> *>(
instruction->Destination(0))
->GetRegister();
reg->data_buffer()->template Set<T>(0, ReadCsr<T>(csr));
// Write the new value to the csr.
csr->SetBits(new_value);
}
// Read the CSR, clear the bits specified by the new value and write back.
template <typename T>
static inline void RVZiCsrrc(Instruction *instruction) {
// Get a handle to the state instance.
auto state = static_cast<RiscVState *>(instruction->state());
// Get the csr index.
int csr_index = instruction->Source(1)->AsInt32(0);
if (!CheckCsrPermission(csr_index, instruction)) return;
// Read the csr.
auto result = state->csr_set()->GetCsr(csr_index);
if (!result.ok()) {
// Signal error if it failed.
LOG(ERROR) << absl::StrCat("Instruction at address 0x",
absl::Hex(instruction->address()),
" failed to read CSR 0x", absl::Hex(csr_index),
": ", result.status().message());
return;
}
// Get the new value.
T new_value = generic::GetInstructionSource<T>(instruction, 0);
auto *csr = result.value();
// Write the current value of the CSR to the destination register.
auto *reg = static_cast<generic::RegisterDestinationOperand<T> *>(
instruction->Destination(0))
->GetRegister();
auto csr_val = ReadCsr<T>(csr);
reg->data_buffer()->template Set<T>(0, csr_val);
// Write the new value to the csr.
csr->ClearBits(new_value);
}
// Do not read the CSR, just write the new value back.
template <typename T>
static inline void RVZiCsrrwNr(Instruction *instruction) {
// Get a handle to the state instance.
auto state = static_cast<RiscVState *>(instruction->state());
// Get the csr index.
int csr_index = instruction->Source(1)->AsInt32(0);
if (!CheckCsrPermission(csr_index, instruction)) return;
// Read the csr.
auto result = state->csr_set()->GetCsr(csr_index);
if (!result.ok()) {
LOG(ERROR) << absl::StrCat("Instruction at address 0x",
absl::Hex(instruction->address()),
" failed to write CSR 0x", absl::Hex(csr_index),
": ", result.status().message());
return;
}
auto *csr = result.value();
// Write the new value to the csr.
T new_value = generic::GetInstructionSource<T>(instruction, 0);
csr->Write(new_value);
}
// Do not write a value back to the CSR, just read it.
template <typename T>
static inline void RVZiCsrrNw(Instruction *instruction) {
// Get a handle to the state instance.
auto state = static_cast<RiscVState *>(instruction->state());
// Get the csr index.
int csr_index = instruction->Source(0)->AsInt32(0);
if (!CheckCsrPermission(csr_index, instruction)) return;
// Read the csr.
auto result = state->csr_set()->GetCsr(csr_index);
if (!result.ok()) {
LOG(ERROR) << absl::StrCat("Instruction at address 0x",
absl::Hex(instruction->address()),
" failed to read CSR 0x", absl::Hex(csr_index),
": ", result.status().message());
return;
}
// Get the CSR object.
auto *csr = result.value();
auto *reg = static_cast<generic::RegisterDestinationOperand<T> *>(
instruction->Destination(0))
->GetRegister();
reg->data_buffer()->template Set<T>(0, ReadCsr<T>(csr));
}
namespace RV32 {
using RegisterType = RV32Register;
using UintReg = RegisterType::ValueType;
// Read the CSR, write a new value back.
void RiscVZiCsrrw(Instruction *instruction) { RVZiCsrrw<UintReg>(instruction); }
// Read the CSR, set the bits specified by the new value and write back.
void RiscVZiCsrrs(Instruction *instruction) { RVZiCsrrs<UintReg>(instruction); }
// Read the CSR, clear the bits specified by the new value and write back.
void RiscVZiCsrrc(Instruction *instruction) { RVZiCsrrc<UintReg>(instruction); }
// Do not read the CSR, just write the new value back.
void RiscVZiCsrrwNr(Instruction *instruction) {
RVZiCsrrwNr<UintReg>(instruction);
}
// Do not write a value back to the CSR, just read it.
void RiscVZiCsrrNw(Instruction *instruction) {
RVZiCsrrNw<UintReg>(instruction);
}
} // namespace RV32
namespace RV64 {
using RegisterType = RV64Register;
using UintReg = RegisterType::ValueType;
// Read the CSR, write a new value back.
void RiscVZiCsrrw(Instruction *instruction) { RVZiCsrrw<UintReg>(instruction); }
// Read the CSR, set the bits specified by the new value and write back.
void RiscVZiCsrrs(Instruction *instruction) { RVZiCsrrs<UintReg>(instruction); }
// Read the CSR, clear the bits specified by the new value and write back.
void RiscVZiCsrrc(Instruction *instruction) { RVZiCsrrc<UintReg>(instruction); }
// Do not read the CSR, just write the new value back.
void RiscVZiCsrrwNr(Instruction *instruction) {
RVZiCsrrwNr<UintReg>(instruction);
}
// Do not write a value back to the CSR, just read it.
void RiscVZiCsrrNw(Instruction *instruction) {
RVZiCsrrNw<UintReg>(instruction);
}
} // namespace RV64
} // namespace riscv
} // namespace sim
} // namespace mpact