blob: 07c94e94542ed065711e353b94ca6ba569dd84a9 [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_CODELABS_OTHER_RISCV_SIMPLE_STATE_H_
#define MPACT_SIM_CODELABS_OTHER_RISCV_SIMPLE_STATE_H_
#include <cstddef>
#include <cstdint>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "mpact/sim/generic/arch_state.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/generic/operand_interface.h"
#include "mpact/sim/generic/ref_count.h"
#include "mpact/sim/generic/type_helpers.h"
#include "mpact/sim/util/memory/flat_demand_memory.h"
#include "mpact/sim/util/memory/memory_interface.h"
#include "other/riscv_register.h"
namespace mpact {
namespace sim {
namespace riscv {
using ArchState = ::mpact::sim::generic::ArchState;
using DataBuffer = ::mpact::sim::generic::DataBuffer;
using Instruction = ::mpact::sim::generic::Instruction;
using ReferenceCount = ::mpact::sim::generic::ReferenceCount;
// A simple load context class for convenience.
struct LoadContext : public generic::ReferenceCount {
explicit LoadContext(DataBuffer *vdb) : value_db(vdb) {}
~LoadContext() override {
if (value_db != nullptr) value_db->DecRef();
}
// Override the base class method so that the data buffer can be DecRef'ed
// when the context object is recycled.
void OnRefCountIsZero() override {
if (value_db != nullptr) value_db->DecRef();
value_db = nullptr;
// Call the base class method.
generic::ReferenceCount::OnRefCountIsZero();
}
// Data buffers for the value loaded from memory (byte, half, word, etc.).
DataBuffer *value_db = nullptr;
};
// Supported values of Xlen.
enum class RiscVXlen : uint64_t {
RV32 = 0b01,
RVUnknown = 4,
};
// Class that extends ArchState with RiscV specific methods. These methods
// implement RiscV specific memory operations, memory/IO fencing, system
// calls and software breakpoints.
class RiscVState : public ArchState {
public:
static constexpr char kXregPrefix[] = "x";
static constexpr char kVregPrefix[] = "v";
static constexpr char kNextPcName[] = "next_pc";
static constexpr char kPcName[] = "pc";
RiscVState(absl::string_view id, RiscVXlen xlen,
util::MemoryInterface *memory,
util::AtomicMemoryOpInterface *atomic_memory);
RiscVState(absl::string_view id, RiscVXlen xlen,
util::MemoryInterface *memory)
: RiscVState(id, xlen, memory, nullptr) {}
RiscVState(absl::string_view id, RiscVXlen xlen)
: RiscVState(id, xlen, nullptr, nullptr) {}
~RiscVState() override;
// Deleted Constructors and operators.
RiscVState(const RiscVState &) = delete;
RiscVState(RiscVState &&) = delete;
RiscVState &operator=(const RiscVState &) = delete;
RiscVState &operator=(RiscVState &&) = delete;
// Return a pair consisting of pointer to the named register and a bool that
// is true if the register had to be created, and false if it was found
// in the register map (or if nullptr is returned).
template <typename RegisterType>
std::pair<RegisterType *, bool> GetRegister(absl::string_view name) {
// If the register already exists, return a pointer to the register.
auto ptr = registers()->find(std::string(name));
if (ptr != registers()->end())
return std::make_pair(static_cast<RegisterType *>(ptr->second), false);
// Create a new register and return a pointer to the object.
return std::make_pair(AddRegister<RegisterType>(name), true);
}
// Add register alias.
template <typename RegisterType>
absl::Status AddRegisterAlias(absl::string_view current_name,
absl::string_view new_name) {
auto ptr = registers()->find(std::string(current_name));
if (ptr == registers()->end()) {
return absl::NotFoundError(
absl::StrCat("Register '", current_name, "' does not exist."));
}
AddRegister(new_name, ptr->second);
return absl::OkStatus();
}
// Methods called by instruction semantic functions to load from memory.
void LoadMemory(const Instruction *inst, uint64_t address, DataBuffer *db,
Instruction *child_inst, ReferenceCount *context);
void LoadMemory(const Instruction *inst, DataBuffer *address_db,
DataBuffer *mask_db, int el_size, DataBuffer *db,
Instruction *child_inst, ReferenceCount *context);
// Methods called by instruction semantic functions to store to memory.
void StoreMemory(const Instruction *inst, uint64_t address, DataBuffer *db);
void StoreMemory(const Instruction *inst, DataBuffer *address_db,
DataBuffer *mask_db, int el_size, DataBuffer *db);
// Called by the fence instruction semantic function to signal a fence
// operation.
void Fence(const Instruction *inst, int fm, int predecessor, int successor);
// Synchronize instruction and data streams.
void FenceI(const Instruction *inst);
// System call.
void ECall(const Instruction *inst);
// Breakpoint.
void EBreak(const Instruction *inst);
// WFI
void WFI(const Instruction *inst);
// Trap.
void Trap(bool is_interrupt, uint64_t trap_value, uint64_t exception_code,
uint64_t epc, const Instruction *inst);
// Add ebreak handler.
void AddEbreakHandler(absl::AnyInvocable<bool(const Instruction *)> handler) {
on_ebreak_.emplace_back(std::move(handler));
}
// Accessors.
void set_memory(util::MemoryInterface *memory) { memory_ = memory; }
util::MemoryInterface *memory() const { return memory_; }
util::AtomicMemoryOpInterface *atomic_memory() const {
return atomic_memory_;
}
// Setters for handlers for ecall, and trap. The handler returns true
// if the instruction/event was handled, and false otherwise.
void set_on_ecall(absl::AnyInvocable<bool(const Instruction *)> callback) {
on_ecall_ = std::move(callback);
}
void set_on_wfi(absl::AnyInvocable<bool(const Instruction *)> callback) {
on_wfi_ = std::move(callback);
}
void set_on_trap(
absl::AnyInvocable<bool(bool /*is_interrupt*/, uint64_t /*trap_value*/,
uint64_t /*exception_code*/, uint64_t /*epc*/,
const Instruction *)>
callback) {
on_trap_ = std::move(callback);
}
int flen() const { return flen_; }
RiscVXlen xlen() const { return xlen_; }
// Getters for select CSRs.
private:
RiscVXlen xlen_;
// Program counter register.
generic::RegisterBase *pc_;
// Operands used to access pc values generically. Note, the pc value may read
// as the address of the next instruction during execution of an instruction,
// so the address of the instruction executing should be used instead.
generic::SourceOperandInterface *pc_src_operand_ = nullptr;
generic::DestinationOperandInterface *pc_dst_operand_ = nullptr;
int flen_ = 0;
util::FlatDemandMemory *owned_memory_ = nullptr;
util::MemoryInterface *memory_ = nullptr;
util::AtomicMemoryOpInterface *atomic_memory_ = nullptr;
std::vector<absl::AnyInvocable<bool(const Instruction *)>> on_ebreak_;
absl::AnyInvocable<bool(const Instruction *)> on_ecall_;
absl::AnyInvocable<bool(bool, uint64_t, uint64_t, uint64_t,
const Instruction *)>
on_trap_;
absl::AnyInvocable<bool(const Instruction *)> on_wfi_;
};
} // namespace riscv
} // namespace sim
} // namespace mpact
#endif // MPACT_SIM_CODELABS_OHTER_RISCV_SIMPLE_STATE_H_