Support for hardware breakpoints

This commit is contained in:
Nav
2023-09-20 23:37:54 +01:00
parent df5a141089
commit d7b59cac59
24 changed files with 480 additions and 68 deletions

View File

@@ -159,7 +159,7 @@ namespace DebugServer::Gdb::AvrGdb
*
* We need to figure out why, and determine whether the stop should be reported to GDB.
*/
if (this->activeDebugSession->externalBreakpointAddresses.contains(programAddress)) {
if (this->activeDebugSession->externalBreakpointsByAddress.contains(programAddress)) {
/*
* The target stopped due to an external breakpoint, set by GDB.
*

View File

@@ -56,7 +56,7 @@ namespace DebugServer::Gdb::CommandPackets
try {
Logger::debug("Removing breakpoint at address " + std::to_string(this->address));
debugSession.removeExternalBreakpoint(TargetBreakpoint(this->address), targetControllerService);
debugSession.removeExternalBreakpoint(this->address, targetControllerService);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {

View File

@@ -65,7 +65,7 @@ namespace DebugServer::Gdb::CommandPackets
return;
}
debugSession.setExternalBreakpoint(TargetBreakpoint(this->address), targetControllerService);
debugSession.setExternalBreakpoint(this->address, targetControllerService);
debugSession.connection.writePacket(OkResponsePacket());
} catch (const Exception& exception) {

View File

@@ -27,63 +27,83 @@ namespace DebugServer::Gdb
}
void DebugSession::setInternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
) {
if (this->internalBreakpointAddresses.contains(breakpoint.address)) {
if (this->internalBreakpointsByAddress.contains(address)) {
return;
}
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
targetControllerService.setBreakpoint(breakpoint);
const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address);
if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) {
// We already have an external breakpoint at this address
this->internalBreakpointsByAddress.insert(std::pair(address, externalBreakpointIt->second));
return;
}
this->internalBreakpointAddresses.insert(breakpoint.address);
this->internalBreakpointsByAddress.insert(
std::pair(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
)
);
}
void DebugSession::removeInternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
) {
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
const auto breakpointIt = this->internalBreakpointsByAddress.find(address);
if (breakpointIt == this->internalBreakpointsByAddress.end()) {
return;
}
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
targetControllerService.removeBreakpoint(breakpoint);
if (!this->externalBreakpointsByAddress.contains(address)) {
targetControllerService.removeBreakpoint(breakpointIt->second);
}
this->internalBreakpointAddresses.erase(breakpoint.address);
this->internalBreakpointsByAddress.erase(breakpointIt);
}
void DebugSession::setExternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
) {
if (this->externalBreakpointAddresses.contains(breakpoint.address)) {
if (this->externalBreakpointsByAddress.contains(address)) {
return;
}
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
targetControllerService.setBreakpoint(breakpoint);
const auto internalBreakpointIt = this->internalBreakpointsByAddress.find(address);
if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) {
// We already have an internal breakpoint at this address
this->externalBreakpointsByAddress.insert(std::pair(address, internalBreakpointIt->second));
return;
}
this->externalBreakpointAddresses.insert(breakpoint.address);
this->externalBreakpointsByAddress.insert(
std::pair(
address,
targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE)
)
);
}
void DebugSession::removeExternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
) {
if (!this->externalBreakpointAddresses.contains(breakpoint.address)) {
const auto breakpointIt = this->externalBreakpointsByAddress.find(address);
if (breakpointIt == this->externalBreakpointsByAddress.end()) {
return;
}
if (!this->internalBreakpointAddresses.contains(breakpoint.address)) {
targetControllerService.removeBreakpoint(breakpoint);
if (!this->internalBreakpointsByAddress.contains(address)) {
targetControllerService.removeBreakpoint(breakpointIt->second);
}
this->externalBreakpointAddresses.erase(breakpoint.address);
this->externalBreakpointsByAddress.erase(breakpointIt);
}
void DebugSession::startRangeSteppingSession(
@@ -91,7 +111,7 @@ namespace DebugServer::Gdb
Services::TargetControllerService& targetControllerService
) {
for (const auto& interceptAddress : session.interceptedAddresses) {
this->setInternalBreakpoint(Targets::TargetBreakpoint(interceptAddress), targetControllerService);
this->setInternalBreakpoint(interceptAddress, targetControllerService);
}
this->activeRangeSteppingSession = std::move(session);
@@ -104,7 +124,7 @@ namespace DebugServer::Gdb
// Clear all intercepting breakpoints
for (const auto& interceptAddress : this->activeRangeSteppingSession->interceptedAddresses) {
this->removeInternalBreakpoint(Targets::TargetBreakpoint(interceptAddress), targetControllerService);
this->removeInternalBreakpoint(interceptAddress, targetControllerService);
}
this->activeRangeSteppingSession.reset();

View File

@@ -2,7 +2,7 @@
#include <cstdint>
#include <optional>
#include <set>
#include <map>
#include "TargetDescriptor.hpp"
#include "GdbDebugServerConfig.hpp"
@@ -55,8 +55,8 @@ namespace DebugServer::Gdb
*
* We track internal and external breakpoints separately.
*/
std::set<Targets::TargetMemoryAddress> internalBreakpointAddresses;
std::set<Targets::TargetMemoryAddress> externalBreakpointAddresses;
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> internalBreakpointsByAddress;
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> externalBreakpointsByAddress;
/**
* When the GDB client is waiting for the target to halt, this is set to true so we know when to notify the
@@ -105,22 +105,22 @@ namespace DebugServer::Gdb
virtual ~DebugSession();
virtual void setInternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
);
virtual void removeInternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
);
virtual void setExternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
);
virtual void removeExternalBreakpoint(
const Targets::TargetBreakpoint& breakpoint,
Targets::TargetMemoryAddress address,
Services::TargetControllerService& targetControllerService
);

View File

@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <utility>
#include "Avr8GenericCommandFrame.hpp"
namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic
{
class ClearHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
{
public:
explicit ClearHardwareBreakpoint(std::uint8_t number)
: Avr8GenericCommandFrame()
{
/*
* The clear hardware breakpoint command consists of 3 bytes:
*
* 1. Command ID (0x41)
* 2. Version (0x00)
* 3. Breakpoint Number (1, 2, or 3)
*/
this->payload = {
0x41,
0x00,
number
};
}
};
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
#include <utility>
#include "Avr8GenericCommandFrame.hpp"
namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic
{
class SetHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 9>>
{
public:
explicit SetHardwareBreakpoint(std::uint32_t address, std::uint8_t number)
: Avr8GenericCommandFrame()
{
/*
* The set hardware breakpoint command consists of 9 bytes:
*
* 1. Command ID (0x40)
* 2. Version (0x00)
* 3. Breakpoint Type (0x01 for program break)
* 4. Breakpoint Number (1, 2, or 3)
* 5. Program address (4 bytes) - the EDBG Protocol document states that this should be the word address,
* but that seems to be incorrect. The tool expects a byte address here.
* 5. Mode (0x03 for program break)
*/
this->payload = {
0x40,
0x00,
0x01,
number,
static_cast<unsigned char>(address),
static_cast<unsigned char>(address >> 8),
static_cast<unsigned char>(address >> 16),
static_cast<unsigned char>(address >> 24),
0x03
};
}
};
}

View File

@@ -34,6 +34,8 @@
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/SetSoftwareBreakpoints.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ClearAllSoftwareBreakpoints.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ClearSoftwareBreakpoints.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/SetHardwareBreakpoint.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/ClearHardwareBreakpoint.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/EnterProgrammingMode.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/LeaveProgrammingMode.hpp"
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVR8Generic/EraseMemory.hpp"
@@ -58,6 +60,8 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
using CommandFrames::Avr8Generic::SetSoftwareBreakpoints;
using CommandFrames::Avr8Generic::ClearSoftwareBreakpoints;
using CommandFrames::Avr8Generic::ClearAllSoftwareBreakpoints;
using CommandFrames::Avr8Generic::SetHardwareBreakpoint;
using CommandFrames::Avr8Generic::ClearHardwareBreakpoint;
using CommandFrames::Avr8Generic::ReadMemory;
using CommandFrames::Avr8Generic::EnterProgrammingMode;
using CommandFrames::Avr8Generic::LeaveProgrammingMode;
@@ -346,7 +350,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
return responseFrame.extractSignature(this->targetConfig.physicalInterface);
}
void EdbgAvr8Interface::setBreakpoint(TargetMemoryAddress address) {
void EdbgAvr8Interface::setSoftwareBreakpoint(TargetMemoryAddress address) {
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
SetSoftwareBreakpoints({address})
);
@@ -356,7 +360,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
}
}
void EdbgAvr8Interface::clearBreakpoint(TargetMemoryAddress address) {
void EdbgAvr8Interface::clearSoftwareBreakpoint(TargetMemoryAddress address) {
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
ClearSoftwareBreakpoints({address})
);
@@ -366,6 +370,55 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
}
}
void EdbgAvr8Interface::setHardwareBreakpoint(TargetMemoryAddress address) {
static const auto getAvailableBreakpointNumbers = [this] () {
auto breakpointNumbers = std::set<std::uint8_t>({1, 2, 3});
for (const auto& [address, allocatedNumber] : this->hardwareBreakpointNumbersByAddress) {
breakpointNumbers.erase(allocatedNumber);
}
return breakpointNumbers;
};
const auto availableBreakpointNumbers = getAvailableBreakpointNumbers();
if (availableBreakpointNumbers.empty()) {
throw Exception("Maximum hardware breakpoints have been allocated");
}
const auto breakpointNumber = *(availableBreakpointNumbers.begin());
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
SetHardwareBreakpoint(address, breakpointNumber)
);
if (responseFrame.id == Avr8ResponseId::FAILED) {
throw Avr8CommandFailure("AVR8 Set hardware breakpoint command failed", responseFrame);
}
this->hardwareBreakpointNumbersByAddress.insert(std::pair(address, breakpointNumber));
}
void EdbgAvr8Interface::clearHardwareBreakpoint(TargetMemoryAddress address) {
const auto breakpointNumberIt = this->hardwareBreakpointNumbersByAddress.find(address);
if (breakpointNumberIt == this->hardwareBreakpointNumbersByAddress.end()) {
Logger::error("No hardware breakpoint at byte address 0x" + Services::StringService::toHex(address));
return;
}
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
ClearHardwareBreakpoint(breakpointNumberIt->second)
);
if (responseFrame.id == Avr8ResponseId::FAILED) {
throw Avr8CommandFailure("AVR8 Clear hardware breakpoint command failed", responseFrame);
}
this->hardwareBreakpointNumbersByAddress.erase(address);
}
void EdbgAvr8Interface::clearAllBreakpoints() {
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
ClearAllSoftwareBreakpoints()

View File

@@ -164,18 +164,36 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
* byte address.
*
* @param address
* The byte address to position the breakpoint.
* The byte address to place the breakpoint.
*/
void setBreakpoint(Targets::TargetMemoryAddress address) override;
void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
/**
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any breakpoint at the given
* byte address.
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any software breakpoint at the
* given byte address.
*
* @param address
* The byte address of the breakpoint to clear.
*/
void clearBreakpoint(Targets::TargetMemoryAddress address) override;
void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
/**
* Issues the "Hardware Breakpoint Set" command to the debug tool, setting a hardware breakpoint at the given
* byte address.
*
* @param address
* The byte address to place the breakpoint.
*/
void setHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
/**
* Issues the "Hardware Breakpoint Clear" command to the debug tool, clearing any hardware breakpoint at the
* given byte address.
*
* @param address
* The byte address of the breakpoint to clear.
*/
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
/**
* Issues the "Software Breakpoint Clear All" command to the debug tool, clearing all software breakpoints
@@ -345,6 +363,12 @@ namespace DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
bool programmingModeEnabled = false;
/**
* Every hardware breakpoint is assigned a "breakpoint number", which we need to keep track of in order to
* clear a hardware breakpoint.
*/
std::map<Targets::TargetMemoryAddress, std::uint8_t> hardwareBreakpointNumbersByAddress;
/**
* Sends the necessary target parameters to the debug tool.
*

View File

@@ -100,14 +100,28 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
*
* @param address
*/
virtual void setBreakpoint(Targets::TargetMemoryAddress address) = 0;
virtual void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
/**
* Should remove a software breakpoint at a given address.
*
* @param address
*/
virtual void clearBreakpoint(Targets::TargetMemoryAddress address) = 0;
virtual void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
/**
* Should set a hardware breakpoint at a given address.
*
* @param address
*/
virtual void setHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
/**
* Should remove a hardware breakpoint at a given address.
*
* @param address
*/
virtual void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
/**
* Should remove all software and hardware breakpoints on the target.

View File

@@ -134,6 +134,10 @@ TargetConfig::TargetConfig(const YAML::Node& targetNode) {
this->variantName = StringService::asciiToLower(targetNode["variantName"].as<std::string>());
}
if (targetNode["hardwareBreakpoints"]) {
this->hardwareBreakpoints = targetNode["hardwareBreakpoints"].as<bool>(this->hardwareBreakpoints);
}
this->targetNode = targetNode;
}

View File

@@ -51,6 +51,11 @@ struct TargetConfig
*/
std::optional<std::string> variantName;
/**
* Determines whether Bloom will make use of the target's hardware breakpoint resources (if available).
*/
bool hardwareBreakpoints = true;
/**
* For extracting any target specific configuration. See Avr8TargetConfig::Avr8TargetConfig() and
* Avr8::preActivationConfigure() for an example of this.

View File

@@ -218,12 +218,15 @@ namespace Services
);
}
void TargetControllerService::setBreakpoint(TargetBreakpoint breakpoint) const {
this->commandManager.sendCommandAndWaitForResponse(
std::make_unique<SetBreakpoint>(breakpoint),
Targets::TargetBreakpoint TargetControllerService::setBreakpoint(
Targets::TargetMemoryAddress address,
Targets::TargetBreakpoint::Type preferredType
) const {
return this->commandManager.sendCommandAndWaitForResponse(
std::make_unique<SetBreakpoint>(address, preferredType),
this->defaultTimeout,
this->activeAtomicSessionId
);
)->breakpoint;
}
void TargetControllerService::removeBreakpoint(TargetBreakpoint breakpoint) const {

View File

@@ -142,9 +142,16 @@ namespace Services
/**
* Requests the TargetController to set a breakpoint on the target.
*
* @param breakpoint
* @param address
* @param preferredType
*
* @return
* The installed breakpoint.
*/
void setBreakpoint(Targets::TargetBreakpoint breakpoint) const;
Targets::TargetBreakpoint setBreakpoint(
Targets::TargetMemoryAddress address,
Targets::TargetBreakpoint::Type preferredType = Targets::TargetBreakpoint::Type::HARDWARE
) const;
/**
* Requests the TargetController to remove a breakpoint from the target.

View File

@@ -1,22 +1,39 @@
#pragma once
#include "Command.hpp"
#include "src/TargetController/Responses/Breakpoint.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace TargetController::Commands
{
class SetBreakpoint: public Command
{
public:
using SuccessResponseType = Responses::Breakpoint;
static constexpr CommandType type = CommandType::SET_BREAKPOINT;
static const inline std::string name = "SetBreakpoint";
Targets::TargetBreakpoint breakpoint;
/**
* Byte address in program memory.
*/
Targets::TargetMemoryAddress address;
SetBreakpoint() = default;
explicit SetBreakpoint(const Targets::TargetBreakpoint& breakpoint)
: breakpoint(breakpoint)
/**
* The preferred breakpoint type (HARDWARE/SOFTWARE).
*
* There is no guarantee that the TC will be able to allocate resources for the preferred type.
* If the preferredType is set to HARDWARE, but the target doesn't have any available resources, or hardware
* breakpoints have been disabled (@see TargetConfig::hardwareBreakpoints), the TC will fall back to software
* breakpoints.
*/
Targets::TargetBreakpoint::Type preferredType;
SetBreakpoint(Targets::TargetMemoryAddress address, Targets::TargetBreakpoint::Type preferredType)
: address(address)
, preferredType(preferredType)
{};
[[nodiscard]] CommandType getType() const override {

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Response.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
namespace TargetController::Responses
{
class Breakpoint: public Response
{
public:
static constexpr ResponseType type = ResponseType::BREAKPOINT;
Targets::TargetBreakpoint breakpoint;
explicit Breakpoint(const Targets::TargetBreakpoint& breakpoint)
: breakpoint(breakpoint)
{}
[[nodiscard]] ResponseType getType() const override {
return Breakpoint::type;
}
};
}

View File

@@ -16,5 +16,6 @@ namespace TargetController::Responses
TARGET_PIN_STATES,
TARGET_STACK_POINTER,
TARGET_PROGRAM_COUNTER,
BREAKPOINT,
};
}

View File

@@ -7,6 +7,7 @@
#include "Responses/Error.hpp"
#include "src/Services/ProcessService.hpp"
#include "src/Services/StringService.hpp"
#include "src/Logger/Logger.hpp"
#include "src/Exceptions/InvalidConfig.hpp"
@@ -51,6 +52,7 @@ namespace TargetController
using Responses::TargetPinStates;
using Responses::TargetStackPointer;
using Responses::TargetProgramCounter;
using Responses::Breakpoint;
TargetControllerComponent::TargetControllerComponent(
const ProjectConfig& projectConfig,
@@ -504,6 +506,17 @@ namespace TargetController
Logger::info("Target ID: " + targetDescriptor.id);
Logger::info("Target name: " + targetDescriptor.name);
if (!this->environmentConfig.targetConfig.hardwareBreakpoints) {
Logger::warning("Hardware breakpoints have been disabled");
} else if (targetDescriptor.breakpointResources.maximumHardwareBreakpoints.has_value()) {
Logger::info(
"Available hardware breakpoints: " + std::to_string(
*(targetDescriptor.breakpointResources.maximumHardwareBreakpoints)
)
);
}
}
void TargetControllerComponent::releaseHardware() {
@@ -877,13 +890,76 @@ namespace TargetController
return std::make_unique<Response>();
}
std::unique_ptr<Response> TargetControllerComponent::handleSetBreakpoint(SetBreakpoint& command) {
this->target->setBreakpoint(command.breakpoint.address);
return std::make_unique<Response>();
std::unique_ptr<Breakpoint> TargetControllerComponent::handleSetBreakpoint(SetBreakpoint& command) {
using Targets::TargetBreakpoint;
using Services::StringService;
auto breakpoint = TargetBreakpoint(command.address, TargetBreakpoint::Type::SOFTWARE);
const auto& targetBreakpointResources = this->getTargetDescriptor().breakpointResources;
if (
command.preferredType == Targets::TargetBreakpoint::Type::HARDWARE
&& this->environmentConfig.targetConfig.hardwareBreakpoints
) {
static auto exhaustedResourcesWarning = false;
if (
!targetBreakpointResources.maximumHardwareBreakpoints.has_value()
|| this->hardwareBreakpointsByAddress.size() < *(targetBreakpointResources.maximumHardwareBreakpoints)
) {
exhaustedResourcesWarning = true;
Logger::debug(
"Installing hardware breakpoint at byte address 0x" + StringService::toHex(command.address)
);
this->target->setHardwareBreakpoint(command.address);
this->hardwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint));
breakpoint.type = TargetBreakpoint::Type::HARDWARE;
return std::make_unique<Breakpoint>(breakpoint);
}
if (exhaustedResourcesWarning) {
exhaustedResourcesWarning = false;
Logger::warning(
"Hardware breakpoint resources have been exhausted. Falling back to software breakpoints"
);
}
}
Logger::debug(
"Installing software breakpoint at byte address 0x" + StringService::toHex(command.address)
);
this->target->setSoftwareBreakpoint(command.address);
this->softwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint));
return std::make_unique<Breakpoint>(breakpoint);
}
std::unique_ptr<Response> TargetControllerComponent::handleRemoveBreakpoint(RemoveBreakpoint& command) {
this->target->removeBreakpoint(command.breakpoint.address);
using Services::StringService;
if (command.breakpoint.type == Targets::TargetBreakpoint::Type::HARDWARE) {
assert(this->environmentConfig.targetConfig.hardwareBreakpoints);
Logger::debug(
"Removing hardware breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address)
);
this->target->removeHardwareBreakpoint(command.breakpoint.address);
this->hardwareBreakpointsByAddress.erase(command.breakpoint.address);
} else {
Logger::debug(
"Removing software breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address)
);
this->target->removeSoftwareBreakpoint(command.breakpoint.address);
this->softwareBreakpointsByAddress.erase(command.breakpoint.address);
}
return std::make_unique<Response>();
}

View File

@@ -55,6 +55,7 @@
#include "Responses/TargetPinStates.hpp"
#include "Responses/TargetStackPointer.hpp"
#include "Responses/TargetProgramCounter.hpp"
#include "Responses/Breakpoint.hpp"
#include "src/DebugToolDrivers/DebugTools.hpp"
#include "src/Targets/Target.hpp"
@@ -161,6 +162,12 @@ namespace TargetController
*/
std::map<Targets::TargetMemoryType, Targets::TargetMemoryAddressRange> registerAddressRangeByMemoryType;
/**
* The TargetController keeps track of all installed breakpoints.
*/
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> softwareBreakpointsByAddress;
std::map<Targets::TargetMemoryAddress, Targets::TargetBreakpoint> hardwareBreakpointsByAddress;
/**
* Registers a handler function for a particular command type.
* Only one handler function can be registered per command type.
@@ -339,7 +346,7 @@ namespace TargetController
std::unique_ptr<Responses::Response> handleWriteTargetMemory(Commands::WriteTargetMemory& command);
std::unique_ptr<Responses::Response> handleEraseTargetMemory(Commands::EraseTargetMemory& command);
std::unique_ptr<Responses::Response> handleStepTargetExecution(Commands::StepTargetExecution& command);
std::unique_ptr<Responses::Response> handleSetBreakpoint(Commands::SetBreakpoint& command);
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);

View File

@@ -268,6 +268,7 @@ namespace Targets::Microchip::Avr::Avr8Bit
"Microchip",
this->targetMemoryDescriptorsByType,
this->targetRegisterDescriptorsById,
this->getBreakpointResources(),
{},
Targets::TargetMemoryType::FLASH
);
@@ -304,12 +305,20 @@ namespace Targets::Microchip::Avr::Avr8Bit
this->avr8DebugInterface->reset();
}
void Avr8::setBreakpoint(std::uint32_t address) {
this->avr8DebugInterface->setBreakpoint(address);
void Avr8::setSoftwareBreakpoint(TargetMemoryAddress address) {
this->avr8DebugInterface->setSoftwareBreakpoint(address);
}
void Avr8::removeBreakpoint(std::uint32_t address) {
this->avr8DebugInterface->clearBreakpoint(address);
void Avr8::removeSoftwareBreakpoint(TargetMemoryAddress address) {
this->avr8DebugInterface->clearSoftwareBreakpoint(address);
}
void Avr8::setHardwareBreakpoint(TargetMemoryAddress address) {
this->avr8DebugInterface->setHardwareBreakpoint(address);
}
void Avr8::removeHardwareBreakpoint(TargetMemoryAddress address) {
this->avr8DebugInterface->clearHardwareBreakpoint(address);
}
void Avr8::clearAllBreakpoints() {
@@ -687,6 +696,33 @@ namespace Targets::Microchip::Avr::Avr8Bit
}
}
BreakpointResources Avr8::getBreakpointResources() {
auto maxHardwareBreakpoints = 0;
switch (this->targetConfig.physicalInterface) {
case PhysicalInterface::JTAG: {
maxHardwareBreakpoints = this->family == Family::XMEGA ? 2 : 3;
break;
}
case PhysicalInterface::PDI: {
maxHardwareBreakpoints = 2;
break;
}
case PhysicalInterface::UPDI: {
maxHardwareBreakpoints = 1;
break;
}
default: {
break;
}
}
return BreakpointResources(
maxHardwareBreakpoints,
std::nullopt
);
}
bool Avr8::isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const {
const auto programmedValue = static_cast<unsigned char>(
this->fuseEnableStrategy == FuseEnableStrategy::SET

View File

@@ -19,6 +19,7 @@
#include "src/Targets/Microchip/AVR/Fuse.hpp"
#include "src/Targets/TargetRegister.hpp"
#include "src/Targets/TargetBreakpoint.hpp"
#include "TargetDescription/TargetDescriptionFile.hpp"
@@ -58,8 +59,11 @@ namespace Targets::Microchip::Avr::Avr8Bit
void step() override;
void reset() override;
void setBreakpoint(TargetProgramCounter address) override;
void removeBreakpoint(TargetProgramCounter address) override;
void setSoftwareBreakpoint(TargetProgramCounter address) override;
void removeSoftwareBreakpoint(TargetProgramCounter address) override;
void setHardwareBreakpoint(TargetProgramCounter address) override;
void removeHardwareBreakpoint(TargetProgramCounter address) override;
void clearAllBreakpoints() override;
void writeRegisters(TargetRegisters registers) override;
@@ -143,6 +147,8 @@ namespace Targets::Microchip::Avr::Avr8Bit
void loadTargetMemoryDescriptors();
BreakpointResources getBreakpointResources();
/**
* Checks if a particular fuse is enabled in the given fuse byte value. Takes the target's fuse enable strategy
* into account.

View File

@@ -111,18 +111,32 @@ namespace Targets
virtual void reset() = 0;
/**
* Should set a breakpoint on the target, at the given address.
* Should set a software breakpoint on the target, at the given address.
*
* @param address
*/
virtual void setBreakpoint(TargetMemoryAddress address) = 0;
virtual void setSoftwareBreakpoint(TargetMemoryAddress address) = 0;
/**
* Should remove a breakpoint at the given address.
* Should remove a software breakpoint at the given address.
*
* @param address
*/
virtual void removeBreakpoint(TargetMemoryAddress address) = 0;
virtual void removeSoftwareBreakpoint(TargetMemoryAddress address) = 0;
/**
* Should set a hardware breakpoint on the target, at the given address.
*
* @param address
*/
virtual void setHardwareBreakpoint(TargetMemoryAddress address) = 0;
/**
* Should remove a hardware breakpoint at the given address.
*
* @param address
*/
virtual void removeHardwareBreakpoint(TargetMemoryAddress address) = 0;
/**
* Should clear all breakpoints on the target.

View File

@@ -1,12 +1,13 @@
#pragma once
#include <cstdint>
#include <optional>
#include "TargetMemory.hpp"
namespace Targets
{
enum class TargetBreakCause: int
enum class TargetBreakCause: std::uint8_t
{
BREAKPOINT,
UNKNOWN,
@@ -14,12 +15,38 @@ namespace Targets
struct TargetBreakpoint
{
enum class Type: std::uint8_t
{
HARDWARE,
SOFTWARE,
};
/**
* Byte address of the breakpoint.
*/
TargetMemoryAddress address = 0;
Type type = Type::SOFTWARE;
TargetBreakpoint() = default;
explicit TargetBreakpoint(TargetMemoryAddress address): address(address) {};
explicit TargetBreakpoint(TargetMemoryAddress address, Type type = Type::SOFTWARE)
: address(address)
, type(type)
{};
};
struct BreakpointResources
{
std::optional<std::uint16_t> maximumHardwareBreakpoints;
std::optional<std::uint16_t> maximumSoftwareBreakpoints;
BreakpointResources(
std::optional<std::uint16_t> maximumHardwareBreakpoints,
std::optional<std::uint16_t> maximumSoftwareBreakpoints
)
: maximumHardwareBreakpoints(maximumHardwareBreakpoints)
, maximumSoftwareBreakpoints(maximumSoftwareBreakpoints)
{}
};
}

View File

@@ -10,6 +10,7 @@
#include "TargetMemory.hpp"
#include "TargetRegister.hpp"
#include "TargetVariant.hpp"
#include "TargetBreakpoint.hpp"
namespace Targets
{
@@ -20,6 +21,7 @@ namespace Targets
std::string vendorName;
std::map<TargetMemoryType, TargetMemoryDescriptor> memoryDescriptorsByType;
std::map<TargetRegisterDescriptorId, TargetRegisterDescriptor> registerDescriptorsById;
BreakpointResources breakpointResources;
std::vector<TargetVariant> variants;
TargetMemoryType programMemoryType;
@@ -30,6 +32,7 @@ namespace Targets
const std::string& vendorName,
const std::map<TargetMemoryType, TargetMemoryDescriptor>& memoryDescriptorsByType,
const std::map<TargetRegisterDescriptorId, TargetRegisterDescriptor>& registerDescriptorsById,
const BreakpointResources& breakpointResources,
const std::vector<TargetVariant>& variants,
TargetMemoryType programMemoryType
)
@@ -38,6 +41,7 @@ namespace Targets
, vendorName(vendorName)
, memoryDescriptorsByType(memoryDescriptorsByType)
, registerDescriptorsById(registerDescriptorsById)
, breakpointResources(breakpointResources)
, variants(variants)
, programMemoryType(programMemoryType)
{}