2022-05-02 13:08:07 +01:00
|
|
|
## TargetController
|
|
|
|
|
|
|
|
|
|
The TargetController component possesses full control of the connected hardware (debug tool and target). Execution of
|
|
|
|
|
user-space device drivers takes place here. All interactions with the connected hardware go through the
|
|
|
|
|
TargetController. It runs on a dedicated thread (see `Applciation::startTargetController()`). The source code for the
|
|
|
|
|
TargetController component can be found in src/TargetController. The entry point is `TargetControllerComponent::run()`.
|
|
|
|
|
|
|
|
|
|
### Interfacing with the TargetController - The command-response mechanism
|
|
|
|
|
|
|
|
|
|
Other components within Bloom can interface with the TargetController via the provided command-response mechanism.
|
|
|
|
|
Simply put, when another component within Bloom needs to interact with the connected hardware, it will send a command to
|
|
|
|
|
the TargetController, and wait for a response. The TargetController will action the command and deliver the necessary
|
|
|
|
|
response.
|
|
|
|
|
|
|
|
|
|
All TargetController commands can be found in [src/TargetController/Commands](./Commands), and are derived from the
|
|
|
|
|
[`Bloom::TargetController::Commands::Command`](./Commands/Command.hpp) base class. Responses can be found in
|
|
|
|
|
[src/TargetController/Responses](./Responses), and are derived from the
|
|
|
|
|
[`Bloom::TargetController::Responses::Response`](./Responses/Response.hpp) base class.
|
|
|
|
|
|
|
|
|
|
**NOTE:** Components within Bloom do not typically concern themselves with the TargetController command-response
|
2022-12-26 21:27:19 +00:00
|
|
|
mechanism. Instead, they use the `TargetControllerService` class, which encapsulates the command-response mechanism and
|
2022-05-02 16:36:23 +01:00
|
|
|
provides a simplified means for interaction with the connected hardware. For more, see
|
2022-12-26 21:27:19 +00:00
|
|
|
[The TargetControllerService class](#the-TargetControllerService-class) section below.
|
2022-05-02 13:08:07 +01:00
|
|
|
|
|
|
|
|
Commands can be sent to the TargetController via the [`Bloom::TargetController::CommandManager`](./CommandManager.hpp)
|
|
|
|
|
class.
|
|
|
|
|
|
|
|
|
|
For example, to read memory from the connected target, we would send the
|
|
|
|
|
[`Bloom::TargetController::Commands::ReadTargetMemory`](./Commands/ReadTargetMemory.hpp) command:
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
auto tcCommandManager = TargetController::CommandManager();
|
|
|
|
|
|
|
|
|
|
auto readMemoryCommand = std::make_unique<TargetController::Commands::ReadTargetMemory>(
|
|
|
|
|
someMemoryType, // Flash, RAM, EEPROM, etc
|
|
|
|
|
someStartAddress,
|
|
|
|
|
someNumberOfBytes
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const auto readMemoryResponse = tcCommandManager.sendCommandAndWaitForResponse(
|
|
|
|
|
std::move(readMemoryCommand),
|
|
|
|
|
std::chrono::milliseconds(1000) // Response timeout
|
|
|
|
|
);
|
|
|
|
|
|
2022-05-02 16:36:23 +01:00
|
|
|
const auto& data = readMemoryResponse->data;
|
2022-05-02 13:08:07 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`readMemoryResponse` will be of type `std::unique_ptr<TargetController::Responses::TargetMemoryRead>`.
|
|
|
|
|
|
|
|
|
|
The `CommandManager::sendCommandAndWaitForResponse<CommandType>(std::unique_ptr<CommandType> command, std::chrono::milliseconds timeout)`
|
|
|
|
|
member function is a template function. It will issue the command to the TargetController and wait for a response, or
|
2022-05-02 16:36:23 +01:00
|
|
|
until a timeout has been reached. Because it is a template function, it is able to resolve the appropriate response
|
|
|
|
|
type at compile-time (see the `SuccessResponseType` alias in some command classes). If the TargetController responds
|
|
|
|
|
with an error, or the timeout is reached, `CommandManager::sendCommandAndWaitForResponse()` will throw an exception.
|
2022-05-02 13:08:07 +01:00
|
|
|
|
2022-12-26 21:27:19 +00:00
|
|
|
#### The TargetControllerService class
|
2022-05-02 13:08:07 +01:00
|
|
|
|
2022-12-26 21:27:19 +00:00
|
|
|
The `TargetControllerService` class encapsulates the TargetController's command-response mechanism and provides a
|
2022-05-02 13:08:07 +01:00
|
|
|
simplified means for other components to interact with the connected hardware. Iterating on the example above, to read
|
|
|
|
|
memory from the target:
|
|
|
|
|
|
|
|
|
|
```c++
|
2022-12-26 21:27:19 +00:00
|
|
|
auto tcService = Services::TargetControllerService();
|
2022-05-02 13:08:07 +01:00
|
|
|
|
2022-12-26 21:27:19 +00:00
|
|
|
const auto data = tcService.readMemory(
|
2022-05-02 13:08:07 +01:00
|
|
|
someMemoryType, // Flash, RAM, EEPROM, etc
|
|
|
|
|
someStartAddress,
|
|
|
|
|
someNumberOfBytes
|
|
|
|
|
);
|
|
|
|
|
```
|
|
|
|
|
|
2022-12-26 21:27:19 +00:00
|
|
|
The `TargetControllerService` class does not require any dependencies at construction. It can be constructed in
|
2022-05-02 16:36:23 +01:00
|
|
|
different threads and used freely to gain access to the connected hardware, from any component within Bloom.
|
2022-05-02 13:08:07 +01:00
|
|
|
|
2022-12-26 21:27:19 +00:00
|
|
|
All components within Bloom should use the `TargetControllerService` class to interact with the connected hardware. They
|
2022-05-02 16:36:23 +01:00
|
|
|
**should not** directly issue commands via the `Bloom::TargetController::CommandManager`, unless there is a very good
|
|
|
|
|
reason to do so.
|
|
|
|
|
|
|
|
|
|
### TargetController suspension
|
|
|
|
|
|
|
|
|
|
The TargetController possesses the ability to go into a suspended state. In this state, control of the connected
|
|
|
|
|
hardware is surrendered - Bloom will no longer be able to interact with the debug tool or target. The purpose of this
|
|
|
|
|
state is to allow other programs access to the hardware, without requiring the termination of the Bloom process. The
|
|
|
|
|
TargetController goes into a suspended state at the end of a debug session, if the user has enabled this via the
|
2022-07-23 15:37:22 +01:00
|
|
|
`releasePostDebugSession` debug tool parameter, in their project configuration file (bloom.yaml). See
|
2022-05-02 16:36:23 +01:00
|
|
|
`TargetControllerComponent::onDebugSessionFinishedEvent()` for more.
|
|
|
|
|
|
|
|
|
|
When in a suspended state, the TargetController will reject most commands. More specifically, any command that
|
|
|
|
|
requires access to the debug tool or target. Issuing any of these commands whilst the TargetController is suspended
|
|
|
|
|
will result in an error response.
|
|
|
|
|
|
2022-06-22 22:24:27 +01:00
|
|
|
In some cases, the TargetController may be forced to go into a suspended state. This could be in response to the user
|
|
|
|
|
disconnecting the debug tool, or from another program stealing control of the hardware. Actually, this is what led to
|
|
|
|
|
the introduction of TargetController suspension. See the corresponding
|
|
|
|
|
[GitHub issue](https://github.com/navnavnav/Bloom/issues/3) for more.
|
|
|
|
|
|
2022-05-02 16:36:23 +01:00
|
|
|
Upon suspension, the TargetController will trigger a `Bloom::Events::TargetControllerStateChanged` event. Other
|
|
|
|
|
components listen for this event to promptly perform the necessary actions in response to the state change. For example,
|
|
|
|
|
the [GDB debug server implementation](../DebugServer/Gdb/README.md) will terminate any active debug session:
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
void GdbRspDebugServer::onTargetControllerStateChanged(const Events::TargetControllerStateChanged& event) {
|
|
|
|
|
if (event.state == TargetControllerState::SUSPENDED && this->activeDebugSession.has_value()) {
|
|
|
|
|
Logger::warning("TargetController suspended unexpectedly - terminating debug session");
|
2022-08-13 03:06:30 +01:00
|
|
|
this->activeDebugSession.reset();
|
2022-05-02 16:36:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For more on TargetController suspension, see `TargetControllerComponent::suspend()` and
|
|
|
|
|
`TargetControllerComponent::resume()`.
|
|
|
|
|
|
2022-06-05 18:00:42 +01:00
|
|
|
### Programming mode
|
|
|
|
|
|
|
|
|
|
When a component needs to write to the target's program memory, it must enable programming mode on the target. This can
|
|
|
|
|
be done by issuing the `EnableProgrammingMode` command to the TargetController (see
|
2022-12-26 21:27:19 +00:00
|
|
|
`TargetControllerService::enableProgrammingMode()`). Once programming mode has been enabled, the TargetController will
|
2022-06-22 22:24:27 +01:00
|
|
|
reject any subsequent commands that involve debug operations (such as `ResumeTargetExecution`, `ReadTargetRegisters`,
|
2022-06-05 18:00:42 +01:00
|
|
|
etc), until programming mode has been disabled.
|
|
|
|
|
|
|
|
|
|
The TargetController will emit `ProgrammingModeEnabled` and `ProgrammingModeDisabled` events when it enables/disables
|
|
|
|
|
programming mode. Components should listen for these events to ensure that they disable any means for the user to trigger
|
|
|
|
|
debugging operations whilst programming mode is enabled. For example, the Insight component will disable much of its
|
|
|
|
|
GUI components when programming mode is enabled.
|
|
|
|
|
|
|
|
|
|
It shouldn't be too much of a problem if a component attempts to perform a debug operation on the target whilst
|
|
|
|
|
programming mode is enabled, as the TargetController will just respond with an error. But still, it would be best to
|
|
|
|
|
avoid doing this where possible.
|
|
|
|
|
|
2022-05-02 16:36:23 +01:00
|
|
|
---
|
2022-05-02 13:08:07 +01:00
|
|
|
|
|
|
|
|
TODO: Cover debug tool & target drivers.
|