blob: e187face034de18e831614fdc73ccea68073e163 [file] [log] [blame]
/*
* Copyright 2024 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
*
* http://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_CHERIOT__CHERIOT_TOP_H_
#define MPACT_CHERIOT__CHERIOT_TOP_H_
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/synchronization/notification.h"
#include "cheriot/cheriot_debug_interface.h"
#include "cheriot/cheriot_register.h"
#include "cheriot/cheriot_state.h"
#include "mpact/sim/generic/action_point_manager_base.h"
#include "mpact/sim/generic/breakpoint_manager.h"
#include "mpact/sim/generic/component.h"
#include "mpact/sim/generic/core_debug_interface.h"
#include "mpact/sim/generic/counters.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/decode_cache.h"
#include "mpact/sim/generic/decoder_interface.h"
#include "mpact/sim/util/memory/cache.h"
#include "mpact/sim/util/memory/memory_interface.h"
#include "mpact/sim/util/memory/memory_watcher.h"
#include "mpact/sim/util/memory/tagged_memory_watcher.h"
#include "re2/re2.h"
#include "riscv//riscv_action_point_memory_interface.h"
namespace mpact {
namespace sim {
namespace cheriot {
using ::mpact::sim::generic::ActionPointManagerBase;
using ::mpact::sim::generic::BreakpointManager;
using ::mpact::sim::generic::DecoderInterface;
using ::mpact::sim::riscv::RiscVActionPointMemoryInterface;
using ::mpact::sim::util::Cache;
struct BranchTraceEntry {
uint32_t from;
uint32_t to;
uint32_t count;
};
// Top level class for the CherIoT simulator. This is the main interface for
// interacting and controlling execution of programs running on the simulator.
// This class brings together the decoder, the architecture state, and control.
class CheriotTop : public generic::Component, public CheriotDebugInterface {
public:
static constexpr int kBranchTraceSize = 16;
using RunStatus = generic::CoreDebugInterface::RunStatus;
using HaltReason = generic::CoreDebugInterface::HaltReason;
CheriotTop(std::string name, CheriotState *state, DecoderInterface *decoder);
~CheriotTop() override;
// Methods inherited from CoreDebugInterface.
absl::Status Halt() override;
absl::Status Halt(HaltReason halt_reason) override;
absl::Status Halt(HaltReasonValueType halt_reason) override;
absl::StatusOr<int> Step(int num) override;
absl::Status Run() override;
absl::Status Wait() override;
absl::StatusOr<RunStatus> GetRunStatus() override;
absl::StatusOr<HaltReasonValueType> GetLastHaltReason() override;
// Register access by register name.
absl::StatusOr<uint64_t> ReadRegister(const std::string &name) override;
absl::Status WriteRegister(const std::string &name, uint64_t value) override;
absl::StatusOr<generic::DataBuffer *> GetRegisterDataBuffer(
const std::string &name) override;
// Read and Write memory methods bypass any semihosting.
absl::StatusOr<size_t> ReadMemory(uint64_t address, void *buf,
size_t length) override;
absl::StatusOr<size_t> WriteMemory(uint64_t address, const void *buf,
size_t length) override;
absl::StatusOr<size_t> ReadTagMemory(uint64_t address, void *buf,
size_t length) override;
// Breakpoints.
bool HasBreakpoint(uint64_t address) override;
absl::Status SetSwBreakpoint(uint64_t address) override;
absl::Status ClearSwBreakpoint(uint64_t address) override;
absl::Status ClearAllSwBreakpoints() override;
// Action points.
absl::StatusOr<int> SetActionPoint(
uint64_t address,
absl::AnyInvocable<void(uint64_t, int)> action) override;
absl::Status ClearActionPoint(uint64_t address, int id) override;
absl::Status EnableAction(uint64_t address, int id) override;
absl::Status DisableAction(uint64_t address, int id) override;
// Watch points.
absl::Status SetDataWatchpoint(uint64_t address, size_t length,
AccessType access_type) override;
absl::Status ClearDataWatchpoint(uint64_t address,
AccessType access_type) override;
void SetBreakOnControlFlowChange(bool value) override;
// If successful, returns a pointer to the instruction at the given address.
// The instruction object is IncRef'ed, and the caller must DecRef the object
// when it is done with it.
absl::StatusOr<Instruction *> GetInstruction(uint64_t address) override;
absl::StatusOr<std::string> GetDisassembly(uint64_t address) override;
// Called when a halt is requested.
void RequestHalt(HaltReason halt_reason, const Instruction *inst);
void RequestHalt(HaltReasonValueType halt_reason, const Instruction *inst);
// Resize branch trace.
absl::Status ResizeBranchTrace(size_t size);
// Enable/disable the registered statistics counters.
void EnableStatistics();
void DisableStatistics();
// Accessors.
CheriotState *state() const { return state_; }
// The following are not const as callers may need to call non-const methods
// of the counter.
generic::SimpleCounter<uint64_t> *counter_num_instructions() {
return &counter_num_instructions_;
}
generic::SimpleCounter<uint64_t> *counter_num_cycles() {
return &counter_num_cycles_;
}
generic::SimpleCounter<uint64_t> *counter_pc() { return &counter_pc_; }
// Memory watchers used for data watch points.
util::TaggedMemoryWatcher *tagged_watcher() { return tagged_watcher_; }
util::MemoryWatcher *memory_watcher() { return memory_watcher_; }
const std::string &halt_string() const { return halt_string_; }
void set_halt_string(std::string halt_string) { halt_string_ = halt_string; }
private:
// Initialize the top.
void Initialize();
// Execute instruction. Returns true if the instruction was executed (or
// an exception was triggered).
bool ExecuteInstruction(Instruction *inst);
// Helper method to step past a breakpoint.
absl::Status StepPastBreakpoint();
// Set the pc value.
void SetPc(uint64_t value);
void ICacheFetch(uint64_t address);
// Branch tracing.
void AddToBranchTrace(uint64_t from, uint64_t to);
// The DB factory is used to manage data buffers for memory read/writes.
generic::DataBufferFactory db_factory_;
// Current status and last halt reasons.
RunStatus run_status_ = RunStatus::kHalted;
HaltReasonValueType halt_reason_ = *HaltReason::kNone;
// Halting flag. This is set to true when execution must halt.
bool halted_ = false;
absl::Notification *run_halted_ = nullptr;
// The local CherIoT state.
CheriotState *state_;
// Flag that indicates an instruction needs to be stepped over.
bool need_to_step_over_ = false;
// Action point memory interface.
RiscVActionPointMemoryInterface *rv_ap_memory_if_ = nullptr;
// Action point manager.
ActionPointManagerBase *rv_ap_manager_ = nullptr;
// Breakpoint manager.
BreakpointManager *rv_bp_manager_ = nullptr;
// Textual description of halt reason.
std::string halt_string_;
// The pc register instance.
CheriotRegister *pcc_;
// RiscV32 decoder instance.
generic::DecoderInterface *cheriot_decoder_ = nullptr;
// Decode cache, memory and memory watcher.
generic::DecodeCache *cheriot_decode_cache_ = nullptr;
util::AtomicMemoryOpInterface *atomic_memory_ = nullptr;
util::TaggedMemoryWatcher *tagged_watcher_ = nullptr;
util::MemoryWatcher *memory_watcher_ = nullptr;
// Branch trace info - uses a circular buffer. The size is defined by the
// constant kBranchTraceSize in the .cc file.
BranchTraceEntry *branch_trace_;
// Data buffer used to hold the branch trace info. This is used so that it
// can be returned to the debug command shell using the GetRegisterDataBuffer
// call.
DataBuffer *branch_trace_db_ = nullptr;
// Points to the most recently written entry in the circular buffer.
int branch_trace_head_ = 0;
int branch_trace_mask_ = kBranchTraceSize - 1;
int branch_trace_size_ = kBranchTraceSize;
// Counter for the number of instructions simulated.
std::vector<generic::SimpleCounter<uint64_t>> counter_opcode_;
generic::SimpleCounter<uint64_t> counter_num_instructions_;
generic::SimpleCounter<uint64_t> counter_num_cycles_;
// Counter used for profiling by connecting it to a profiler. This allows
// the pc to be written to the counter, and the profiling can be enabled/
// disabled with the other counters.
generic::SimpleCounter<uint64_t> counter_pc_;
absl::flat_hash_map<uint32_t, std::string> register_id_map_;
// RegEx for parsing capability register component name.
LazyRE2 cap_reg_re_;
// Flag for breaking on a control flow change.
bool break_on_control_flow_change_ = false;
// ICache.
Cache *icache_ = nullptr;
DataBuffer *inst_db_ = nullptr;
};
} // namespace cheriot
} // namespace sim
} // namespace mpact
#endif // MPACT_CHERIOT__CHERIOT_TOP_H_