| // 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 |
| // |
| // 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_UTIL_RENODE_RENODE_CLI_TOP_H_ |
| #define MPACT_SIM_UTIL_RENODE_RENODE_CLI_TOP_H_ |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <string> |
| |
| #include "absl/functional/any_invocable.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/synchronization/mutex.h" |
| #include "mpact/sim/generic/core_debug_interface.h" |
| #include "mpact/sim/generic/data_buffer.h" |
| #include "mpact/sim/generic/instruction.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace util { |
| namespace renode { |
| |
| using ::mpact::sim::generic::AccessType; |
| using ::mpact::sim::generic::CoreDebugInterface; |
| using ::mpact::sim::generic::DataBuffer; |
| using ::mpact::sim::generic::Instruction; |
| using HaltReason = ::mpact::sim::generic::CoreDebugInterface::HaltReason; |
| using HaltReasonValueType = |
| ::mpact::sim::generic::CoreDebugInterface::HaltReasonValueType; |
| using RunStatus = ::mpact::sim::generic::CoreDebugInterface::RunStatus; |
| |
| // This class arbitrates and merges commands from ReNode and the socket command |
| // line interface and forwards them to the top simulator control interface. |
| class RenodeCLITop { |
| public: |
| RenodeCLITop(CoreDebugInterface* top, bool wait_for_cli); |
| virtual ~RenodeCLITop() = default; |
| |
| // Set the connected status of the command line interface. |
| virtual void SetConnected(bool connected); |
| |
| // Methods that handle requests from Renode. |
| |
| // Step the simulator. |
| virtual absl::StatusOr<int> RenodeStep(int num); |
| // Get the reason for the last halt. |
| virtual absl::StatusOr<HaltReasonValueType> RenodeGetLastHaltReason(); |
| // Register access by register name. |
| virtual absl::StatusOr<uint64_t> RenodeReadRegister(const std::string& name); |
| virtual absl::Status RenodeWriteRegister(const std::string& name, |
| uint64_t value); |
| // Read and Write memory methods bypass any semihosting. |
| virtual absl::StatusOr<size_t> RenodeReadMemory(uint64_t address, void* buf, |
| size_t length); |
| virtual absl::StatusOr<size_t> RenodeWriteMemory(uint64_t address, |
| const void* buf, |
| size_t length); |
| |
| // Methods that handle requests from Command Line Interface. |
| |
| // Halt the simulator. |
| virtual absl::Status CLIHalt(); |
| // Step the simulator. |
| virtual absl::StatusOr<int> CLIStep(int num); |
| // Allow the simulator to free run. |
| virtual absl::Status CLIRun(); |
| // Wait for free run to complete. |
| virtual absl::Status CLIWait(); |
| virtual absl::StatusOr<RunStatus> CLIGetRunStatus(); |
| virtual void CLIRequestHalt(HaltReason halt_reason, const Instruction* inst); |
| virtual void CLIRequestHalt(HaltReasonValueType halt_reason, |
| const Instruction* inst); |
| virtual absl::StatusOr<HaltReasonValueType> CLIGetLastHaltReason(); |
| // Register access by register name. |
| virtual absl::StatusOr<uint64_t> CLIReadRegister(const std::string& name); |
| virtual absl::Status CLIWriteRegister(const std::string& name, |
| uint64_t value); |
| virtual absl::StatusOr<DataBuffer*> CLIGetRegisterDataBuffer( |
| const std::string& name); |
| // Read and Write memory methods bypass any semihosting. |
| virtual absl::StatusOr<size_t> CLIReadMemory(uint64_t address, void* buf, |
| size_t length); |
| virtual absl::StatusOr<size_t> CLIWriteMemory(uint64_t address, |
| const void* buf, size_t length); |
| |
| // Breakpoint and watchpoint management. |
| virtual bool CLIHasBreakpoint(uint64_t address); |
| virtual absl::Status CLISetSwBreakpoint(uint64_t address); |
| virtual absl::Status CLIClearSwBreakpoint(uint64_t address); |
| virtual absl::Status CLIClearAllSwBreakpoints(); |
| virtual absl::StatusOr<Instruction*> CLIGetInstruction(uint64_t address); |
| virtual absl::StatusOr<std::string> CLIGetDisassembly(uint64_t address); |
| |
| protected: |
| // Perform the action after having obtained the lock that depends on the CLI |
| // being in control. |
| template <typename T> |
| T DoWhenInControl(absl::AnyInvocable<T(void)> action); |
| // Accessor. |
| CoreDebugInterface* top() const { return top_; } |
| |
| private: |
| bool IsCLIInControl() const; |
| CoreDebugInterface* top_ = nullptr; |
| // Mutex that determines which of ReNode and the command line interface has |
| // control over the simulator control interface. |
| absl::Mutex run_control_mutex_; |
| // The status of the command line interface is used to determine which of |
| // ReNode and the CLI has control. When the cli_status_ is kRunning, control |
| // is transferred to ReNode. Any other status implies that the CLI has |
| // control. |
| RunStatus cli_status_ = RunStatus::kHalted; |
| // The following counters are used to track the number of steps requested |
| // and taken by ReNode and CLI. In particular, ReNode will call and request |
| // a number of steps appropriate for the simulators run quantum. Within that |
| // number of steps, the command line interface can advance with step/run up |
| // to the total number of steps requested. At that point, the ReNode interface |
| // must get control so the step function can return control to ReNode. On the |
| // next step call from ReNode, the command line interface can continue in |
| // control. |
| uint64_t cli_steps_taken_ = 0; |
| uint64_t cli_steps_to_take_ = 0; |
| uint64_t renode_steps_taken_ = 0; |
| uint64_t renode_steps_to_take_ = 0; |
| bool cli_connected_ = false; |
| bool wait_for_cli_; |
| bool program_done_ = false; |
| }; |
| |
| // Templated definition of DoWhenInControl |
| template <typename T> |
| inline T RenodeCLITop::DoWhenInControl(absl::AnyInvocable<T(void)> action) { |
| auto cli_is_in_control = [this] { |
| return program_done_ || ((cli_status_ != RunStatus::kRunning) && |
| (renode_steps_to_take_ > renode_steps_taken_)); |
| }; |
| run_control_mutex_.LockWhen(absl::Condition(&cli_is_in_control)); |
| T result = action(); |
| run_control_mutex_.Unlock(); |
| return result; |
| } |
| |
| // Partial specialization for void return type. |
| template <> |
| inline void RenodeCLITop::DoWhenInControl<void>( |
| absl::AnyInvocable<void(void)> action) { |
| auto cli_is_in_control = [this] { |
| return program_done_ || ((cli_status_ != RunStatus::kRunning) && |
| (renode_steps_to_take_ > renode_steps_taken_)); |
| }; |
| run_control_mutex_.LockWhen(absl::Condition(&cli_is_in_control)); |
| action(); |
| run_control_mutex_.Unlock(); |
| } |
| |
| } // namespace renode |
| } // namespace util |
| } // namespace sim |
| } // namespace mpact |
| |
| #endif // MPACT_SIM_UTIL_RENODE_RENODE_CLI_TOP_H_ |