blob: a82a1a52c0b3d477c4dcabcbae93a6607a0b45d9 [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.
// Architectural state for the RISC-V Vector Matrix Extension (Zvt / "VME",
// spec v0.4). This is layered on top of the existing RISC-V
// vector state: the matrix unit sources its A/B operands from vector registers
// and accumulates results into the new square matrix "tile" state modeled here.
//
// NOTE: mtype CSR field positions below follow the v0.4 manual.
// They are isolated as named constants so they can be adjusted if the
// encoding firms up.
#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_ZVT_STATE_H_
#define THIRD_PARTY_MPACT_RISCV_RISCV_ZVT_STATE_H_
#include <cstdint>
#include <cstring>
#include <memory>
#include <vector>
#include "absl/base/nullability.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/instruction.h"
#include "riscv/riscv_csr.h"
#include "riscv/riscv_state.h"
namespace mpact::sim::riscv {
// mtype CSR address (unprivileged, read-only). Spec 15.1.1.2.
inline constexpr uint64_t kRiscVMtypeCsrIndex = 0xC23;
// mtype field layout (see note above re: draft status).
// [1:0] mtwiden (0 => unconfigured; otherwise TWIDEN = 1 << (mtwiden-1))
// [7:5] tk (0..4; bounded by KMAX). K is the inner dimension of the
// dot product.
// [23:10] tm (0..TE). M is the number of rows of the
// accumulator tile.
inline constexpr uint32_t kMtypeMtwidenShift = 0;
inline constexpr uint32_t kMtypeMtwidenMask = 0x3;
inline constexpr uint32_t kMtypeTkShift = 5;
inline constexpr uint32_t kMtypeTkMask = 0x7;
inline constexpr uint32_t kMtypeTmShift = 10;
inline constexpr uint32_t kMtypeTmMask = 0x3FFF;
// Default tile-edge dimension (TE). The tile state is fixed at the
// implementation maximum; the runtime-usable extents are bounded by tk/tm/tn.
// Constraint (spec 15.1.1.1): TE a power of two, VLEN/4 >= TE >= 4. With
// VLEN = 128 bits (kRiscVV2VectorByteLength = 16 bytes), VLEN/4 = 32.
inline constexpr int kRiscVZvtDefaultTe = 32;
class RiscVZvtMatrixState;
// The read-only `mtype` CSR. The value lives in the owning matrix state; this
// CSR is a read-only view (it mirrors mtype the way RiscVVl/RiscVVtype mirror
// the vector state). Software cannot write it (write mask is 0 so `csrw mtype`
// is a no-op); mtype is mutated only by the matrix configuration instructions.
// The back-pointer also lets semantic functions recover the matrix state from
// the CSR set (see GetMatrixState()).
class RiscVMtypeCsr : public ::mpact::sim::riscv::RiscVSimpleCsr<uint32_t> {
public:
RiscVMtypeCsr(RiscVZvtMatrixState* matrix_state,
::mpact::sim::riscv::RiscVState* state)
: ::mpact::sim::riscv::RiscVSimpleCsr<uint32_t>(
"mtype", kRiscVMtypeCsrIndex, /*read_mask=*/0xFFFFFFFFu,
/*write_mask=*/0x0u, state),
matrix_state_(matrix_state) {}
// Overrides: read the live value from the matrix state.
uint32_t AsUint32() override;
uint64_t AsUint64() override;
// Software writes to mtype are illegal; they must trap.
void Write(uint32_t value) override;
void Write(uint64_t value) override;
[[nodiscard]] RiscVZvtMatrixState* matrix_state() const {
return matrix_state_;
}
private:
RiscVZvtMatrixState* matrix_state_;
};
// Matrix tile state. Owns the flat tile buffer (16 * TE * TE bytes) and the
// mtype CSR. All element access goes through GetPunningOffset()/GetElem()/
// SetElem() so the tile-aliasing ("punning") layout is implemented exactly
// once.
struct DataBufferDeleter {
void operator()(::mpact::sim::generic::DataBuffer* db) const {
if (db) db->DecRef();
}
};
using SafeDataBuffer =
std::unique_ptr<::mpact::sim::generic::DataBuffer, DataBufferDeleter>;
class RiscVZvtMatrixState {
public:
static absl::StatusOr<std::unique_ptr<RiscVZvtMatrixState>> Create(
::mpact::sim::riscv::RiscVState* /* absl_nonnull */ state,
int tile_elements = kRiscVZvtDefaultTe);
~RiscVZvtMatrixState();
// Tile-edge dimension and total buffer size (bytes).
[[nodiscard]] int tile_elements() const { return tile_elements_; }
[[nodiscard]] int tile_buffer_bytes() const {
return tile_buffer_->size<uint8_t>();
}
[[nodiscard]] ::mpact::sim::riscv::RiscVState* riscv_state() const {
return state_;
}
// Byte offset into tile_buffer_ for element (tile,row,col) at the given tile
// element width `tile_element_width` (in bits: 8/16/32/64). Transcribed
// verbatim from the Zvt v0.4 spec ("Tile Punning"). `tile` is the
// named tile index (0..15); `tile_element_width` selects the aliasing scheme.
absl::StatusOr<int> GetPunningOffset(int tile, int row, int col,
int tile_element_width) const;
template <typename T>
absl::StatusOr<T> GetElem(int tile, int row, int col,
int tile_element_width) const {
T value;
auto span = tile_buffer_->Get<uint8_t>();
absl::StatusOr<int> offset =
GetPunningOffset(tile, row, col, tile_element_width);
if (!offset.ok()) return offset.status();
std::memcpy(&value, &span[*offset], sizeof(T));
return value;
}
template <typename T>
absl::Status SetElem(int tile, int row, int col, int tile_element_width,
T value) {
auto span = tile_buffer_->Get<uint8_t>();
absl::StatusOr<int> offset =
GetPunningOffset(tile, row, col, tile_element_width);
if (!offset.ok()) return offset.status();
std::memcpy(&span[*offset], &value, sizeof(T));
return absl::OkStatus();
}
// mtype accessors. The mtype value is held here; the CSR is a read-only view.
[[nodiscard]] uint32_t mtype() const { return mtype_; }
void set_mtype(uint32_t value) { mtype_ = value; }
void ResetMtype() { set_mtype(0); }
// Tile index validation and masking (Zvt v0.4).
[[nodiscard]] bool IsValidTile(int tile) const;
[[nodiscard]] int MaskTssTile(int tile, int tile_element_width) const;
// Decoded mtype fields.
[[nodiscard]] int mtwiden() const {
return (mtype() >> kMtypeMtwidenShift) & kMtypeMtwidenMask;
}
// TWIDEN = 1 << (mtwiden - 1) when configured, else 0.
[[nodiscard]] int twiden() const {
return mtwiden() == 0 ? 0 : (1 << (mtwiden() - 1));
}
bool configured() const { return mtwiden() != 0; }
// K is the inner dimension of the dot product.
[[nodiscard]] int tile_k() const {
return (mtype() >> kMtypeTkShift) & kMtypeTkMask;
}
// M is the number of rows of the accumulator tile.
[[nodiscard]] int tile_m() const {
return (mtype() >> kMtypeTmShift) & kMtypeTmMask;
}
void set_tile_k(int value) { set_field(kMtypeTkShift, kMtypeTkMask, value); }
void set_tile_m(int value) { set_field(kMtypeTmShift, kMtypeTmMask, value); }
// Returns a pre-allocated scratchpad DataBuffer of exactly the requested
// size.
::mpact::sim::generic::DataBuffer* GetScratchDb(int size);
// SEW (bytes/bits) from the current vector configuration.
[[nodiscard]] int sew_bytes() const;
[[nodiscard]] int sew_bits() const { return sew_bytes() * 8; }
// TILE_ELEMENT_WIDTH (tile element width) in bits = SEW * TWIDEN.
[[nodiscard]] int tile_element_width_bits() const {
return sew_bits() * twiden();
}
// EFFECTIVE_TILE_EDGE: effective tile edge for the current
// TILE_ELEMENT_WIDTH. = TE for TILE_ELEMENT_WIDTH<64, TE/2 else.
[[nodiscard]] int effective_tile_edge() const {
return tile_element_width_bits() < 64 ? tile_elements_ : tile_elements_ / 2;
}
private:
explicit RiscVZvtMatrixState(
::mpact::sim::riscv::RiscVState* /* absl_nonnull */ state,
int tile_elements);
void set_field(uint32_t shift, uint32_t mask, int value) {
uint32_t v = mtype();
v &= ~(mask << shift);
v |= (static_cast<uint32_t>(value) & mask) << shift;
set_mtype(v);
}
::mpact::sim::riscv::RiscVState* state_;
int tile_elements_;
SafeDataBuffer tile_buffer_; // 16 * TE * TE bytes.
std::vector<SafeDataBuffer> scratch_dbs_;
uint32_t mtype_ = 0;
RiscVMtypeCsr mtype_csr_;
};
// Recovers the matrix state from an executing instruction by looking up the
// mtype CSR (cheap hash lookup; same idiom as the existing kCsr operand
// getter). Returns an error if the Zvt matrix state is not installed.
absl::StatusOr<RiscVZvtMatrixState*> GetMatrixState(
const ::mpact::sim::generic::Instruction* inst);
} // namespace mpact::sim::riscv
#endif // THIRD_PARTY_MPACT_RISCV_RISCV_ZVT_STATE_H_