blob: 91479c41a0573637c132276fb4b1dff3a20b24af [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.
#ifndef MPACT_SIM_GENERIC_REGISTER_H_
#define MPACT_SIM_GENERIC_REGISTER_H_
// Contains the basic generic definitions for classes used to model
// register state in simulated architectures, as in the kind of registers
// used in instruction visible register files. Registers can be scalar,
// one dimensional vector, or two dimensional array with a base value type.
#include <any>
#include <cstdint>
#include <functional>
#include <map>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "mpact/sim/generic/arch_state.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/operand_interface.h"
#include "mpact/sim/generic/signed_type.h"
#include "mpact/sim/generic/state_item.h"
#include "mpact/sim/generic/state_item_base.h"
namespace mpact {
namespace sim {
namespace generic {
class ArchState;
class RegisterBase;
class SimpleResource;
// A register source operand with value type T used to access (read/write)
// register values from instruction semantic functions.
template <typename T>
class RegisterSourceOperand : public SourceOperandInterface {
public:
// Constructor. Note, default constructor deleted.
RegisterSourceOperand(RegisterBase *reg, const std::string op_name);
explicit RegisterSourceOperand(RegisterBase *reg);
RegisterSourceOperand() = delete;
// Accessor methods call into the register_ data_buffer to obtain the values.
// The index is in one dimension. For scalar registers it should always be
// zero, for vector registers it is the element index. For higher order
// register shapes it should be the linearized row-major order index.
// In order to properly cast signed values, an internal helper template
// is used to get the signed type equivalent of any unsigned register unit
// value type.
bool AsBool(int i) final;
int8_t AsInt8(int i) final;
uint8_t AsUint8(int i) final;
int16_t AsInt16(int i) final;
uint16_t AsUint16(int i) final;
int32_t AsInt32(int i) final;
uint32_t AsUint32(int i) final;
int64_t AsInt64(int i) final;
uint64_t AsUint64(int i) final;
// Returns the RegisterBase object wrapped in absl::any.
std::any GetObject() const override { return std::any(register_); }
// Non-inherited method to get the register object.
RegisterBase *GetRegister() const { return register_; }
// Returns the shape of the register.
std::vector<int> shape() const override;
std::string AsString() const override { return op_name_; }
private:
RegisterBase *register_;
std::string op_name_;
};
// Register destination operand type with element value type T. It is agnostic
// of the actual structure of the underlying register (scalar, vector, matrix).
template <typename T>
class RegisterDestinationOperand : public DestinationOperandInterface {
public:
// Constructor and Destructor
RegisterDestinationOperand(RegisterBase *reg, int latency);
RegisterDestinationOperand(RegisterBase *reg, int latency,
std::string op_name);
RegisterDestinationOperand() = delete;
// Initializes the DataBuffer instance so that when Submit is called, it can
// be entered into the correct delay line, with the correct latency, targeting
// the correct register.
void InitializeDataBuffer(DataBuffer *db) override;
// Allocates and returns an initialized DataBuffer instance that contains a
// copy of the current value of the register. This is useful when only part
// of the destination register will be modified.
DataBuffer *CopyDataBuffer() override;
// Allocates and returns an initialized DataBuffer instance.
DataBuffer *AllocateDataBuffer() final;
// Returns the latency associated with writes to this register operand.
int latency() const override { return latency_; }
// Returns the RegisterBase object wrapped in absl::any.
std::any GetObject() const override { return std::any(register_); }
// Non-inherited method to get the register object.
RegisterBase *GetRegister() const { return register_; }
// Returns the shape of the underlying register (the number of elements in
// each dimension). For instance {0} indicates a scalar quantity, whereas
// {128} indicates an 128 element vector quantity.
std::vector<int> shape() const override;
std::string AsString() const override { return op_name_; }
private:
RegisterBase *register_;
DataBufferFactory *db_factory_;
int latency_;
DataBufferDelayLine *delay_line_;
std::string op_name_;
};
// Base class for register types with the DataBufferDestination interface.
// No default constructor - should only be constructed/destructed from the
// derived classes.
class RegisterBase : public StateItemBase {
public:
// Type alias for the notification callback function type.
using UpdateCallbackFunction = std::function<void()>;
// Constructors are only called from derived classes and are in the
// protected section below.
~RegisterBase() override;
RegisterBase() = delete;
RegisterBase(const RegisterBase &) = delete;
RegisterBase &operator=(const RegisterBase &) = delete;
// DecRef's the current data buffer and replaces it with a new one.
void SetDataBuffer(DataBuffer *db) override;
// Returns a pointer to the DataBuffer that contains the current value of
// the register.
DataBuffer *data_buffer() const { return data_buffer_; }
protected:
RegisterBase(ArchState *state, absl::string_view name,
const std::vector<int> &shape, int unit_size);
private:
DataBuffer *data_buffer_;
std::vector<UpdateCallbackFunction> next_update_callbacks_;
};
// A register class that frees a SimpleResource instance when written to. This
// is intended to be used in modeling dynamic stalls/hold issue due to data
// dependencies on long latency operations with a protected pipeline.
class ReservedRegisterBase : public RegisterBase {
public:
ReservedRegisterBase() = delete;
ReservedRegisterBase(const ReservedRegisterBase &) = delete;
ReservedRegisterBase &operator=(const ReservedRegisterBase &) = delete;
// Override the SetDataBuffer to release the SimpleResource instance when
// called.
void SetDataBuffer(DataBuffer *db) override;
// Accessor.
SimpleResource *resource() const { return resource_; }
protected:
ReservedRegisterBase(ArchState *state, absl::string_view name,
const std::vector<int> &shape, int unit_size,
SimpleResource *resource);
private:
// SimpleResource instance associated with the register.
SimpleResource *resource_;
};
// Scalar register type with value type ElementType.
template <typename ElementType>
using Register =
StateItem<RegisterBase, ElementType, RegisterSourceOperand<ElementType>,
RegisterDestinationOperand<ElementType>>;
// N long vector register type with element value type ElementType.
template <typename ElementType, int N>
using VectorRegister =
StateItem<RegisterBase, ElementType, RegisterSourceOperand<ElementType>,
RegisterDestinationOperand<ElementType>, N>;
// MxN matrix register type with element value type ElementType.
template <typename ElementType, int M, int N>
using MatrixRegister =
StateItem<RegisterBase, ElementType, RegisterSourceOperand<ElementType>,
RegisterDestinationOperand<ElementType>, M, N>;
// Scalar register type with value type ElementType.
template <typename ElementType>
using ReservedRegister = StateItem<ReservedRegisterBase, ElementType,
RegisterSourceOperand<ElementType>,
RegisterDestinationOperand<ElementType>>;
// N long vector register type with element value type ElementType.
template <typename ElementType, int N>
using ReservedVectorRegister =
StateItem<ReservedRegisterBase, ElementType,
RegisterSourceOperand<ElementType>,
RegisterDestinationOperand<ElementType>, N>;
// MxN matrix register type with element value type ElementType.
template <typename ElementType, int M, int N>
using ReservedMatrixRegister =
StateItem<ReservedRegisterBase, ElementType,
RegisterSourceOperand<ElementType>,
RegisterDestinationOperand<ElementType>, M, N>;
template <typename T>
RegisterSourceOperand<T>::RegisterSourceOperand(RegisterBase *reg,
const std::string op_name)
: register_(reg), op_name_(op_name) {}
template <typename T>
RegisterSourceOperand<T>::RegisterSourceOperand(RegisterBase *reg)
: RegisterSourceOperand(reg, reg->name()) {}
template <typename T>
bool RegisterSourceOperand<T>::AsBool(int i) {
return static_cast<bool>(register_->data_buffer()->Get<T>(i));
}
template <typename T>
int8_t RegisterSourceOperand<T>::AsInt8(int i) {
return static_cast<int8_t>(
register_->data_buffer()->Get<typename internal::SignedType<T>::type>(i));
}
template <typename T>
uint8_t RegisterSourceOperand<T>::AsUint8(int i) {
return static_cast<uint8_t>(register_->data_buffer()->Get<T>(i));
}
template <typename T>
int16_t RegisterSourceOperand<T>::AsInt16(int i) {
return static_cast<int16_t>(
register_->data_buffer()->Get<typename internal::SignedType<T>::type>(i));
}
template <typename T>
uint16_t RegisterSourceOperand<T>::AsUint16(int i) {
return static_cast<uint16_t>(register_->data_buffer()->Get<T>(i));
}
template <typename T>
int32_t RegisterSourceOperand<T>::AsInt32(int i) {
return static_cast<int32_t>(
register_->data_buffer()->Get<typename internal::SignedType<T>::type>(i));
}
template <typename T>
uint32_t RegisterSourceOperand<T>::AsUint32(int i) {
return static_cast<uint32_t>(register_->data_buffer()->Get<T>(i));
}
template <typename T>
int64_t RegisterSourceOperand<T>::AsInt64(int i) {
return static_cast<int64_t>(
register_->data_buffer()->Get<typename internal::SignedType<T>::type>(i));
}
template <typename T>
uint64_t RegisterSourceOperand<T>::AsUint64(int i) {
return static_cast<uint64_t>(register_->data_buffer()->Get<T>(i));
}
template <typename T>
std::vector<int> RegisterSourceOperand<T>::shape() const {
return register_->shape();
}
template <typename T>
RegisterDestinationOperand<T>::RegisterDestinationOperand(RegisterBase *reg,
int latency,
std::string op_name)
: register_(reg),
db_factory_(reg->arch_state()->db_factory()),
latency_(latency),
delay_line_(reg->arch_state()->data_buffer_delay_line()),
op_name_(op_name) {}
template <typename T>
RegisterDestinationOperand<T>::RegisterDestinationOperand(RegisterBase *reg,
int latency)
: RegisterDestinationOperand(reg, latency, reg->name()) {}
template <typename T>
void RegisterDestinationOperand<T>::InitializeDataBuffer(DataBuffer *db) {
db->set_destination(register_);
db->set_latency(latency_);
db->set_delay_line(delay_line_);
}
template <typename T>
DataBuffer *RegisterDestinationOperand<T>::CopyDataBuffer() {
DataBuffer *db = db_factory_->MakeCopyOf(register_->data_buffer());
InitializeDataBuffer(db);
return db;
}
template <typename T>
DataBuffer *RegisterDestinationOperand<T>::AllocateDataBuffer() {
DataBuffer *db = db_factory_->Allocate(register_->size());
InitializeDataBuffer(db);
return db;
}
template <typename T>
std::vector<int> RegisterDestinationOperand<T>::shape() const {
return register_->shape();
}
} // namespace generic
} // namespace sim
} // namespace mpact
#endif // MPACT_SIM_GENERIC_REGISTER_H_