| // Copyright 2026 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_zvt_state.h" |
| |
| #include <cstdint> |
| #include <memory> |
| |
| #include "absl/base/nullability.h" |
| #include "absl/log/check.h" |
| #include "absl/log/log.h" |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "mpact/sim/generic/data_buffer.h" |
| #include "mpact/sim/generic/instruction.h" |
| #include "riscv/riscv_csr.h" |
| #include "riscv/riscv_state.h" |
| #include "riscv/riscv_vector_state.h" |
| #include "riscv/riscv_xstatus.h" |
| |
| namespace mpact::sim::riscv { |
| |
| namespace { |
| constexpr uint32_t kMstatusVsMask = 0x0600U; |
| } // namespace |
| |
| uint32_t RiscVMtypeCsr::AsUint32() { |
| auto* state = matrix_state_->riscv_state(); |
| if ((state->mstatus()->GetUint32() & kMstatusVsMask) == 0) { |
| state->Trap( |
| /*is_interrupt=*/false, 0, |
| static_cast<uint64_t>( |
| ::mpact::sim::riscv::ExceptionCode::kIllegalInstruction), |
| 0, nullptr); |
| return 0; |
| } |
| return matrix_state_->mtype(); |
| } |
| uint64_t RiscVMtypeCsr::AsUint64() { |
| auto* state = matrix_state_->riscv_state(); |
| if ((state->mstatus()->GetUint32() & kMstatusVsMask) == 0) { |
| state->Trap( |
| /*is_interrupt=*/false, 0, |
| static_cast<uint64_t>( |
| ::mpact::sim::riscv::ExceptionCode::kIllegalInstruction), |
| 0, nullptr); |
| return 0; |
| } |
| return matrix_state_->mtype(); |
| } |
| |
| void RiscVMtypeCsr::Write(uint32_t /*value*/) { |
| matrix_state_->riscv_state()->Trap( |
| /*is_interrupt=*/false, 0, |
| static_cast<uint64_t>( |
| ::mpact::sim::riscv::ExceptionCode::kIllegalInstruction), |
| 0, nullptr); |
| } |
| |
| void RiscVMtypeCsr::Write(uint64_t /*value*/) { |
| matrix_state_->riscv_state()->Trap( |
| /*is_interrupt=*/false, 0, |
| static_cast<uint64_t>( |
| ::mpact::sim::riscv::ExceptionCode::kIllegalInstruction), |
| 0, nullptr); |
| } |
| |
| absl::StatusOr<std::unique_ptr<RiscVZvtMatrixState>> |
| RiscVZvtMatrixState::Create( |
| ::mpact::sim::riscv::RiscVState* /* absl_nonnull */ state, |
| int tile_elements) { |
| if (tile_elements < 4) { |
| return absl::InvalidArgumentError("Tile element width must be >= 4"); |
| } |
| if ((tile_elements & (tile_elements - 1)) != 0) { |
| return absl::InvalidArgumentError( |
| absl::StrCat("TE must be a power of two: ", tile_elements)); |
| } |
| auto matrix = absl::WrapUnique(new RiscVZvtMatrixState(state, tile_elements)); |
| auto status = state->csr_set()->AddCsr(&matrix->mtype_csr_); |
| if (!status.ok()) return status; |
| return matrix; |
| } |
| |
| RiscVZvtMatrixState::RiscVZvtMatrixState( |
| ::mpact::sim::riscv::RiscVState* /* absl_nonnull */ state, |
| int tile_elements) |
| : state_(state), tile_elements_(tile_elements), mtype_csr_(this, state) { |
| // TE must be a power of two in [4, VLEN/4]. VLEN/4 = 32 for VLEN=128b. |
| tile_buffer_.reset(state_->db_factory()->Allocate<uint8_t>( |
| 16 * tile_elements * tile_elements)); |
| for (auto& byte : tile_buffer_->Get<uint8_t>()) { |
| byte = 0; |
| } |
| } |
| |
| RiscVZvtMatrixState::~RiscVZvtMatrixState() = default; |
| |
| ::mpact::sim::generic::DataBuffer* RiscVZvtMatrixState::GetScratchDb(int size) { |
| if (size >= scratch_dbs_.size()) scratch_dbs_.resize(size + 1); |
| if (!scratch_dbs_[size]) { |
| scratch_dbs_[size].reset(state_->db_factory()->Allocate<uint8_t>(size)); |
| } |
| return scratch_dbs_[size].get(); |
| } |
| |
| int RiscVZvtMatrixState::sew_bytes() const { |
| auto* rv_vector = state_->rv_vector(); |
| // selected_element_width() is in bytes; default to 1 (SEW=8) if no vector |
| // state is attached. |
| return rv_vector == nullptr ? 1 : rv_vector->selected_element_width(); |
| } |
| |
| // Transcribed verbatim from the Zvt v0.4 spec ("Tile Punning"). |
| absl::StatusOr<int> RiscVZvtMatrixState::GetPunningOffset( |
| int tile, int row, int col, int tile_element_width) const { |
| const int kTileElements = tile_elements_; |
| int ptile, minor_offset, major_offset; |
| switch (tile_element_width) { |
| case 8: { |
| ptile = tile; |
| minor_offset = (row % 4) * 4 + (col % 4); |
| major_offset = (row / 4) * (kTileElements / 4) + (col / 4); |
| break; |
| } |
| case 16: { |
| ptile = tile + ((row & 2) >> 1); |
| minor_offset = (row % 2) * 4 + (col % 2) * 2 + ((col / 2) % 2) * 8; |
| major_offset = (row / 4) * (kTileElements / 4) + (col / 4); |
| break; |
| } |
| case 32: { |
| ptile = tile + 2 * (row / (kTileElements / 2)) + ((col & 2) >> 1); |
| minor_offset = (row % 2) * 8 + (col % 2) * 4; |
| major_offset = |
| ((row / 2) % (kTileElements / 4)) * (kTileElements / 4) + (col / 4); |
| break; |
| } |
| case 64: { |
| ptile = tile + (row / (kTileElements / 4)); |
| minor_offset = (col % 2) * 8; |
| major_offset = |
| (row % (kTileElements / 4)) * (kTileElements / 4) + (col / 2); |
| break; |
| } |
| default: |
| return absl::InvalidArgumentError( |
| absl::StrCat("Invalid tile element width: ", tile_element_width)); |
| } |
| return (ptile * kTileElements * kTileElements) + (major_offset * 16) + |
| minor_offset; |
| } |
| |
| namespace { |
| int GetTileAlignment(int tile_element_width_bits) { |
| int ntiles = 16; |
| if (tile_element_width_bits == 16) { |
| ntiles = 8; |
| } else if (tile_element_width_bits == 32) { |
| ntiles = 4; |
| } else if (tile_element_width_bits == 64) { |
| ntiles = 8; |
| } |
| return 16 / ntiles; |
| } |
| } // namespace |
| |
| bool RiscVZvtMatrixState::IsValidTile(int tile) const { |
| return (tile % GetTileAlignment(tile_element_width_bits())) == 0; |
| } |
| |
| int RiscVZvtMatrixState::MaskTssTile(int tile, int tile_element_width) const { |
| return tile & ~(GetTileAlignment(tile_element_width) - 1); |
| } |
| |
| absl::StatusOr<RiscVZvtMatrixState*> GetMatrixState( |
| const ::mpact::sim::generic::Instruction* inst) { |
| auto* rv_state = static_cast<::mpact::sim::riscv::RiscVState*>(inst->state()); |
| auto result = rv_state->csr_set()->GetCsr(kRiscVMtypeCsrIndex); |
| if (!result.ok()) return result.status(); |
| return static_cast<RiscVMtypeCsr*>(*result)->matrix_state(); |
| } |
| |
| } // namespace mpact::sim::riscv |