blob: d1327a0c7af610b19abc967341ea07166b721247 [file] [edit]
// 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