Files
BloomPatched/src/TargetController/TargetControllerComponent.hpp

379 lines
15 KiB
C++
Raw Normal View History

2021-04-04 21:04:12 +01:00
#pragma once
#include <atomic>
2021-04-04 21:04:12 +01:00
#include <memory>
#include <queue>
#include <condition_variable>
#include <optional>
#include <chrono>
2021-04-04 21:04:12 +01:00
#include <map>
#include <string>
#include <functional>
#include <QJsonObject>
#include <QJsonArray>
#include "src/Helpers/Thread.hpp"
#include "src/Helpers/Synchronised.hpp"
#include "src/Helpers/ConditionVariableNotifier.hpp"
2022-05-02 13:38:03 +01:00
#include "TargetControllerState.hpp"
2023-06-01 22:13:07 +01:00
#include "AtomicSession.hpp"
2022-05-02 13:38:03 +01:00
// Commands
#include "Commands/Command.hpp"
2023-06-01 22:13:07 +01:00
#include "Commands/StartAtomicSession.hpp"
#include "Commands/EndAtomicSession.hpp"
2023-05-26 01:02:51 +01:00
#include "Commands/Shutdown.hpp"
#include "Commands/GetTargetDescriptor.hpp"
#include "Commands/GetTargetState.hpp"
#include "Commands/StopTargetExecution.hpp"
#include "Commands/ResumeTargetExecution.hpp"
#include "Commands/ResetTarget.hpp"
#include "Commands/ReadTargetRegisters.hpp"
#include "Commands/WriteTargetRegisters.hpp"
#include "Commands/ReadTargetMemory.hpp"
#include "Commands/WriteTargetMemory.hpp"
2022-12-11 23:25:15 +00:00
#include "Commands/EraseTargetMemory.hpp"
#include "Commands/StepTargetExecution.hpp"
#include "Commands/SetBreakpoint.hpp"
#include "Commands/RemoveBreakpoint.hpp"
#include "Commands/SetTargetProgramCounter.hpp"
#include "Commands/GetTargetPinStates.hpp"
#include "Commands/SetTargetPinState.hpp"
#include "Commands/GetTargetStackPointer.hpp"
2022-05-01 18:44:04 +01:00
#include "Commands/GetTargetProgramCounter.hpp"
2022-06-05 16:13:43 +01:00
#include "Commands/EnableProgrammingMode.hpp"
#include "Commands/DisableProgrammingMode.hpp"
// Responses
#include "Responses/Response.hpp"
2023-06-01 22:13:07 +01:00
#include "Responses/AtomicSessionId.hpp"
#include "Responses/TargetDescriptor.hpp"
#include "Responses/TargetState.hpp"
#include "Responses/TargetRegistersRead.hpp"
#include "Responses/TargetMemoryRead.hpp"
#include "Responses/TargetPinStates.hpp"
#include "Responses/TargetStackPointer.hpp"
2022-05-01 18:44:04 +01:00
#include "Responses/TargetProgramCounter.hpp"
2023-09-20 23:37:54 +01:00
#include "Responses/Breakpoint.hpp"
2021-04-04 21:04:12 +01:00
#include "src/DebugToolDrivers/DebugTools.hpp"
#include "src/Targets/BriefTargetDescriptor.hpp"
2021-04-04 21:04:12 +01:00
#include "src/Targets/Target.hpp"
#include "src/Targets/Targets.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Targets/TargetMemory.hpp"
2023-09-22 17:52:28 +01:00
#include "src/Targets/TargetMemoryCache.hpp"
2021-05-30 16:52:32 +01:00
2021-04-04 21:04:12 +01:00
#include "src/EventManager/EventManager.hpp"
2021-05-30 16:52:32 +01:00
#include "src/EventManager/EventListener.hpp"
2021-04-04 21:04:12 +01:00
#include "src/EventManager/Events/Events.hpp"
namespace TargetController
2021-04-04 21:04:12 +01:00
{
static_assert(std::atomic<TargetControllerState>::is_always_lock_free);
2021-04-04 21:04:12 +01:00
/**
* The TargetController possesses full control of the debugging target and the debug tool.
*
2021-10-10 23:18:06 +01:00
* The TargetController runs on a dedicated thread. Its sole purpose is to handle communication to & from the
2021-04-04 21:04:12 +01:00
* debug tool and target.
*
* The TargetController should be oblivious to any manufacture/device specific functionality. It should
* only ever interface with the base Target and DebugTool classes.
*/
class TargetControllerComponent: public Thread
2021-04-04 21:04:12 +01:00
{
public:
explicit TargetControllerComponent(
const ProjectConfig& projectConfig,
const EnvironmentConfig& environmentConfig
2022-04-15 22:06:38 +01:00
);
/**
* Entry point for the TargetController.
*/
void run();
2023-06-01 22:13:07 +01:00
static void registerCommand(
std::unique_ptr<Commands::Command> command,
const std::optional<AtomicSessionIdType>& atomicSessionId
);
static std::optional<std::unique_ptr<Responses::Response>> waitForResponse(
Commands::CommandIdType commandId,
std::optional<std::chrono::milliseconds> timeout = std::nullopt
);
2021-04-04 21:04:12 +01:00
private:
static inline Synchronised<std::queue<std::unique_ptr<Commands::Command>>> commandQueue;
2023-06-01 22:13:07 +01:00
/**
* We have a dedicated queue for atomic sessions.
*
* During an atomic session, all commands for the session are placed into this dedicated queue.
* The TargetController will only serve commands from this dedicated queue, until the atomic session ends.
*/
static inline Synchronised<std::queue<std::unique_ptr<Commands::Command>>> atomicSessionCommandQueue;
static inline Synchronised<
std::map<Commands::CommandIdType, std::unique_ptr<Responses::Response>>
> responsesByCommandId;
static inline ConditionVariableNotifier notifier = ConditionVariableNotifier();
static inline std::condition_variable responsesByCommandIdCv = std::condition_variable();
2022-04-17 23:56:57 +01:00
2023-05-26 00:23:07 +01:00
static inline std::atomic<TargetControllerState> state = TargetControllerState::INACTIVE;
2021-05-30 16:52:32 +01:00
ProjectConfig projectConfig;
2021-04-04 21:04:12 +01:00
EnvironmentConfig environmentConfig;
2023-06-01 22:13:07 +01:00
std::optional<AtomicSession> activeAtomicSession = std::nullopt;
2021-04-06 02:10:14 +01:00
/**
* The TargetController should be the sole owner of the target and debugTool. They are constructed and
* destroyed within the TargetController. Under no circumstance should ownership of these resources be
* transferred to any other component within Bloom.
*/
2021-04-04 21:04:12 +01:00
std::unique_ptr<Targets::Target> target = nullptr;
std::unique_ptr<DebugTool> debugTool = nullptr;
std::map<
Commands::CommandType,
std::function<std::unique_ptr<Responses::Response>(Commands::Command&)>
> commandHandlersByCommandType;
2021-04-04 21:04:12 +01:00
EventListenerPointer eventListener = std::make_shared<EventListener>("TargetControllerEventListener");
2021-04-06 02:10:14 +01:00
/**
* We keep record of the last known execution state of the target. When the connected target reports a
* different state to what's stored in lastTargetState, a state change (TargetExecutionStopped/TargetExecutionResumed)
* event is emitted.
*/
Targets::TargetState lastTargetState = Targets::TargetState::UNKNOWN;
2021-04-06 02:10:14 +01:00
2023-09-22 17:52:38 +01:00
/**
* Target descriptor cache.
*/
std::optional<const Targets::TargetDescriptor> targetDescriptor;
2021-04-04 21:04:12 +01:00
/**
* Target register descriptors mapped by the memory type on which the register is stored.
*/
std::map<Targets::TargetMemoryType, Targets::TargetRegisterDescriptors> registerDescriptorsByMemoryType;
/**
* Memory address ranges for target registers, mapped by the register memory type.
*/
std::map<Targets::TargetMemoryType, Targets::TargetMemoryAddressRange> registerAddressRangeByMemoryType;
2023-09-20 23:37:54 +01:00
/**
* The TargetController keeps track of all installed breakpoints.
*/
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> softwareBreakpointsByAddress;
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> hardwareBreakpointsByAddress;
2023-09-22 17:52:28 +01:00
/**
* The target's program memory cache.
*
* If program caching is enabled, all program memory reads will be serviced by the cache, if we have the data.
*
* We use std::unique_ptr here due to delayed construction (we construct this after activating the target
* and obtaining the target descriptor).
*/
std::unique_ptr<Targets::TargetMemoryCache> programMemoryCache = nullptr;
2022-05-01 19:23:58 +01:00
/**
* Registers a handler function for a particular command type.
* Only one handler function can be registered per command type.
*
* @tparam CommandType
* @param callback
*/
template<class CommandType>
void registerCommandHandler(std::function<std::unique_ptr<Responses::Response>(CommandType&)> callback) {
2022-12-17 14:46:08 +00:00
this->commandHandlersByCommandType.insert(
std::pair(
CommandType::type,
[callback] (Commands::Command& command) {
// Downcast the command to the expected type
return callback(dynamic_cast<CommandType&>(command));
}
)
);
2021-04-04 21:04:12 +01:00
}
2022-05-01 19:23:58 +01:00
/**
* Removes any registered handler for a given command type. After calling this function, any commands issued
* for the given command type will be rejected with a "No handler registered for this command." error, until a
* handler is registered again.
*
* @param commandType
*/
void deregisterCommandHandler(Commands::CommandType commandType);
2021-04-04 21:04:12 +01:00
/**
* Updates the state of the TargetController and emits a state changed event.
*
* @param state
* @param emitEvent
*/
void setThreadStateAndEmitEvent(ThreadState state) {
2023-05-25 23:16:30 +01:00
this->threadState = state;
2022-03-20 17:37:36 +00:00
EventManager::triggerEvent(
std::make_shared<Events::TargetControllerThreadStateChanged>(state)
2021-04-04 21:04:12 +01:00
);
}
2021-04-04 21:04:12 +01:00
/**
* Because the TargetController hogs the thread, this method must be called in a dedicated thread.
*/
void startup();
2023-05-26 00:23:07 +01:00
/**
* Exit point - must be called before the TargetController thread is terminated.
*
* Handles releasing the hardware among other clean-up related things.
*/
void shutdown();
/**
* Constructs a mapping of supported debug tool names to lambdas. The lambdas should *only* instantiate
* and return an instance to the derived DebugTool class. They should not attempt to establish
* a connection to the device.
*
* @return
*/
std::map<std::string, std::function<std::unique_ptr<DebugTool>()>> getSupportedDebugTools();
/**
* Constructs a Target instance from a BriefTargetDescriptor object.
*
* @param briefDescriptor
* @return
*/
std::unique_ptr<Targets::Target> constructTarget(const Targets::BriefTargetDescriptor& briefDescriptor);
2022-05-01 19:23:58 +01:00
/**
* Processes any pending commands in the queue.
*/
void processQueuedCommands();
2022-05-01 19:23:58 +01:00
/**
* Records a response for a given command ID. Notifies the TargetControllerComponent::responsesByCommandIdCv
* condition variable of the new response.
*
* @param commandId
* @param response
*/
void registerCommandResponse(Commands::CommandIdType commandId, std::unique_ptr<Responses::Response> response);
/**
* Establishes a connection with the debug tool and target. Prepares the hardware for a debug session.
*/
void acquireHardware();
/**
* Attempts to gracefully disconnect from the debug tool and the target. All control of the debug tool and
* target will cease.
*/
void releaseHardware();
2023-06-01 22:13:07 +01:00
void startAtomicSession();
void endActiveAtomicSession();
/**
* Extracts address ranges and groups target register descriptors.
*/
void loadRegisterDescriptors();
/**
* Resolves the descriptors of all target registers found within the given address range and memory type.
*
* @param startAddress
* @param endAddress
* @param memoryType
* @return
*/
Targets::TargetRegisterDescriptors getRegisterDescriptorsWithinAddressRange(
Targets::TargetMemoryAddress startAddress,
Targets::TargetMemoryAddress endAddress,
Targets::TargetMemoryType memoryType
);
2021-04-04 21:04:12 +01:00
/**
* Should fire any events queued on the target.
*/
void fireTargetEvents();
/**
* Triggers a target reset and emits a TargetReset event.
*/
void resetTarget();
2022-06-05 16:13:43 +01:00
/**
* Puts the target into programming mode and disables command handlers for debug commands (commands that serve
* debug operations such as SetBreakpoint, ResumeTargetExecution, etc).
*/
void enableProgrammingMode();
/**
* Pulls the target out of programming mode and enables command handlers for debug commands.
*/
void disableProgrammingMode();
2021-04-06 02:10:14 +01:00
/**
2022-05-01 19:23:58 +01:00
* Returns a cached instance of the target's TargetDescriptor.
2021-04-06 02:10:14 +01:00
*
2022-05-01 19:23:58 +01:00
* @return
2021-04-06 02:10:14 +01:00
*/
const Targets::TargetDescriptor& getTargetDescriptor();
2021-04-04 21:04:12 +01:00
/**
* Invokes a shutdown.
*
* @param event
2021-04-04 21:04:12 +01:00
*/
void onShutdownTargetControllerEvent(const Events::ShutdownTargetController& event);
2021-04-04 21:04:12 +01:00
/**
* Will simply kick off execution on the target.
*
* @param event
*/
void onDebugSessionFinishedEvent(const Events::DebugSessionFinished& event);
2022-05-02 13:38:03 +01:00
// Command handlers
2023-06-01 22:13:07 +01:00
std::unique_ptr<Responses::AtomicSessionId> handleStartAtomicSession(Commands::StartAtomicSession& command);
std::unique_ptr<Responses::Response> handleEndAtomicSession(Commands::EndAtomicSession& command);
2023-05-26 01:02:51 +01:00
std::unique_ptr<Responses::Response> handleShutdown(Commands::Shutdown& command);
std::unique_ptr<Responses::TargetDescriptor> handleGetTargetDescriptor(Commands::GetTargetDescriptor& command);
std::unique_ptr<Responses::TargetState> handleGetTargetState(Commands::GetTargetState& command);
std::unique_ptr<Responses::Response> handleStopTargetExecution(Commands::StopTargetExecution& command);
std::unique_ptr<Responses::Response> handleResumeTargetExecution(Commands::ResumeTargetExecution& command);
std::unique_ptr<Responses::Response> handleResetTarget(Commands::ResetTarget& command);
std::unique_ptr<Responses::TargetRegistersRead> handleReadTargetRegisters(
Commands::ReadTargetRegisters& command
);
std::unique_ptr<Responses::Response> handleWriteTargetRegisters(Commands::WriteTargetRegisters& command);
std::unique_ptr<Responses::TargetMemoryRead> handleReadTargetMemory(Commands::ReadTargetMemory& command);
std::unique_ptr<Responses::Response> handleWriteTargetMemory(Commands::WriteTargetMemory& command);
2022-12-11 23:25:15 +00:00
std::unique_ptr<Responses::Response> handleEraseTargetMemory(Commands::EraseTargetMemory& command);
std::unique_ptr<Responses::Response> handleStepTargetExecution(Commands::StepTargetExecution& command);
2023-09-20 23:37:54 +01:00
std::unique_ptr<Responses::Breakpoint> handleSetBreakpoint(Commands::SetBreakpoint& command);
std::unique_ptr<Responses::Response> handleRemoveBreakpoint(Commands::RemoveBreakpoint& command);
std::unique_ptr<Responses::Response> handleSetProgramCounter(Commands::SetTargetProgramCounter& command);
std::unique_ptr<Responses::TargetPinStates> handleGetTargetPinStates(Commands::GetTargetPinStates& command);
std::unique_ptr<Responses::Response> handleSetTargetPinState(Commands::SetTargetPinState& command);
std::unique_ptr<Responses::TargetStackPointer> handleGetTargetStackPointer(
Commands::GetTargetStackPointer& command
);
2022-05-01 18:44:04 +01:00
std::unique_ptr<Responses::TargetProgramCounter> handleGetTargetProgramCounter(
Commands::GetTargetProgramCounter& command
);
2022-06-05 16:13:43 +01:00
std::unique_ptr<Responses::Response> handleEnableProgrammingMode(Commands::EnableProgrammingMode& command);
std::unique_ptr<Responses::Response> handleDisableProgrammingMode(Commands::DisableProgrammingMode& command);
2021-04-04 21:04:12 +01:00
};
}