| // Copyright 2024 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 |
| // |
| // http://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 "cheriot/riscv_cheriot_fp_state.h" |
| |
| #include <cstdint> |
| |
| #include "absl/log/log.h" |
| #include "cheriot/cheriot_state.h" |
| #include "mpact/sim/generic/type_helpers.h" |
| #include "riscv//riscv_csr.h" |
| #include "riscv//riscv_fp_host.h" |
| #include "riscv//riscv_fp_info.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace cheriot { |
| |
| using ::mpact::sim::riscv::FPRoundingMode; |
| using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). |
| using ::mpact::sim::riscv::GetHostFloatingPointInterface; |
| using ::mpact::sim::riscv::RiscVCsrEnum; |
| |
| constexpr char kFcsrName[] = "fcsr"; |
| constexpr uint32_t kFcsrInitial = 0b000'0'0'0'0'0; |
| constexpr uint32_t kFcsrReadMask = 0b111'1'1111; |
| constexpr uint32_t kFcsrWriteMask = 0b111'1'1111; |
| |
| constexpr char kFflagsName[] = "fflags"; |
| constexpr uint32_t kFflagsInitial = 0b0'0000; |
| constexpr uint32_t kFflagsReadMask = 0b1'1111; |
| constexpr uint32_t kFflagsWriteMask = 0b1'1111; |
| |
| constexpr char kFrmName[] = "frm"; |
| constexpr uint32_t kFrmInitial = 0b000; |
| constexpr uint32_t kFrmReadMask = 0b111; |
| constexpr uint32_t kFrmWriteMask = 0b111; |
| |
| // Helper function to avoid some extra code below. |
| static inline void LogIfError(absl::Status status) { |
| if (status.ok()) return; |
| LOG(ERROR) << status.message(); |
| } |
| |
| RiscVCheriotFPState::RiscVCheriotFPState(CheriotState *rv_state) |
| : rv_state_(rv_state) { |
| fcsr_ = new RiscVFcsr(this); |
| frm_ = new RiscVFrm(this); |
| fflags_ = new RiscVFflags(this); |
| host_fp_interface_ = GetHostFloatingPointInterface(); |
| |
| LogIfError(rv_state_->csr_set()->AddCsr(fcsr_)); |
| LogIfError(rv_state_->csr_set()->AddCsr(frm_)); |
| LogIfError(rv_state_->csr_set()->AddCsr(fflags_)); |
| } |
| |
| RiscVCheriotFPState::~RiscVCheriotFPState() { |
| delete fcsr_; |
| delete frm_; |
| delete fflags_; |
| delete host_fp_interface_; |
| } |
| |
| FPRoundingMode RiscVCheriotFPState::GetRoundingMode() const { |
| return rounding_mode_; |
| } |
| |
| void RiscVCheriotFPState::SetRoundingMode(FPRoundingMode mode) { |
| if (rounding_mode_ != mode) { |
| switch (mode) { |
| case FPRoundingMode::kRoundToNearestTiesToMax: |
| case FPRoundingMode::kRoundToNearest: |
| break; |
| case FPRoundingMode::kRoundTowardsZero: |
| break; |
| case FPRoundingMode::kRoundDown: |
| break; |
| case FPRoundingMode::kRoundUp: |
| break; |
| default: |
| rounding_mode_valid_ = false; |
| LOG(ERROR) << "Illegal rounding mode: " << static_cast<int>(mode); |
| return; |
| } |
| rounding_mode_ = mode; |
| frm_->Write(*mode); |
| rounding_mode_valid_ = true; |
| } |
| } |
| |
| // The RiscV fp csr. |
| RiscVFcsr::RiscVFcsr(RiscVCheriotFPState *fp_state) |
| : RiscVSimpleCsr<uint32_t>(kFcsrName, RiscVCsrEnum::kFCsr, kFcsrInitial, |
| kFcsrReadMask, kFcsrWriteMask, |
| fp_state->rv_state()), |
| fp_state_(fp_state) {} |
| |
| // The status value is computed from the most recent x86 status value. |
| uint32_t RiscVFcsr::AsUint32() { |
| uint32_t status_value = fp_state_->host_fp_interface()->GetRiscVFcsr(); |
| auto csr_value = GetUint32(); |
| auto value = ((csr_value & ~0x1f) | (status_value & 0x1f)) & read_mask(); |
| return value; |
| } |
| |
| uint64_t RiscVFcsr::AsUint64() { return AsUint32(); } |
| |
| // The status value is converted to x86 and stored for use in next fp |
| // instruction. |
| void RiscVFcsr::Write(uint32_t value) { |
| auto wr_value = value & write_mask(); |
| Set(wr_value); |
| fp_state_->host_fp_interface()->SetRiscVFcsr(wr_value); |
| fp_state_->SetRoundingMode( |
| static_cast<FPRoundingMode>((wr_value >> 5) & 0b111)); |
| } |
| |
| void RiscVFcsr::Write(uint64_t value) { Write(static_cast<uint32_t>(value)); } |
| |
| // RiscVFflags translates reads and writes into reads and writes of fcsr. |
| RiscVFflags::RiscVFflags(RiscVCheriotFPState *fp_state) |
| : RiscVSimpleCsr<uint32_t>(kFflagsName, RiscVCsrEnum::kFFlags, |
| kFflagsInitial, kFflagsReadMask, |
| kFflagsWriteMask, fp_state->rv_state()), |
| fp_state_(fp_state) {} |
| |
| uint32_t RiscVFflags::AsUint32() { |
| uint32_t value = fp_state_->fcsr()->AsUint32(); |
| value &= 0b1'1111; |
| return value; |
| } |
| |
| void RiscVFflags::Write(uint32_t value) { |
| uint32_t current = fp_state_->fcsr()->AsUint32(); |
| uint32_t new_value = (current & ~write_mask()) | (value & write_mask()); |
| if (new_value == current) return; |
| fp_state_->fcsr()->Write(new_value); |
| } |
| |
| uint32_t RiscVFflags::GetUint32() { return AsUint32(); } |
| |
| void RiscVFflags::Set(uint32_t value) { Write(value); } |
| |
| // RiscV rm (rounding mode) csr translates reads and writes into reads |
| // and writes of fcsr. |
| RiscVFrm::RiscVFrm(RiscVCheriotFPState *fp_state) |
| : RiscVSimpleCsr<uint32_t>(kFrmName, RiscVCsrEnum::kFrm, kFrmInitial, |
| kFrmReadMask, kFrmWriteMask, |
| fp_state->rv_state()), |
| fp_state_(fp_state) {} |
| |
| uint32_t RiscVFrm::AsUint32() { |
| uint32_t value = fp_state_->fcsr()->AsUint32(); |
| uint32_t rm = (value >> 5) & read_mask(); |
| return rm; |
| } |
| |
| void RiscVFrm::Write(uint32_t value) { |
| uint32_t wr_value = value & write_mask(); |
| uint32_t fcsr = fp_state_->fcsr()->AsUint32(); |
| uint32_t new_fcsr = ((fcsr & 0b1'1111) | (wr_value << 5)); |
| fp_state_->fcsr()->Write(new_fcsr); |
| } |
| |
| uint32_t RiscVFrm::GetUint32() { return AsUint32(); } |
| |
| void RiscVFrm::Set(uint32_t value) { Write(value); } |
| |
| } // namespace cheriot |
| } // namespace sim |
| } // namespace mpact |