Delta programming - where we only upload what's changed
This commit is contained in:
@@ -55,8 +55,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
debugSession.programmingSession.reset();
|
debugSession.programmingSession.reset();
|
||||||
|
|
||||||
Logger::warning("Program memory updated");
|
|
||||||
targetControllerService.disableProgrammingMode();
|
targetControllerService.disableProgrammingMode();
|
||||||
|
Logger::warning("Program memory updated");
|
||||||
|
|
||||||
Logger::warning("Resetting target");
|
Logger::warning("Resetting target");
|
||||||
targetControllerService.resetTarget();
|
targetControllerService.resetTarget();
|
||||||
|
|||||||
@@ -54,8 +54,6 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
try {
|
try {
|
||||||
targetControllerService.enableProgrammingMode();
|
targetControllerService.enableProgrammingMode();
|
||||||
|
|
||||||
Logger::warning("Erasing program memory, in preparation for programming");
|
|
||||||
|
|
||||||
// We don't erase a specific address range - we just erase the entire program memory.
|
// We don't erase a specific address range - we just erase the entire program memory.
|
||||||
targetControllerService.eraseMemory(
|
targetControllerService.eraseMemory(
|
||||||
gdbTargetDescriptor.programAddressSpaceDescriptor,
|
gdbTargetDescriptor.programAddressSpaceDescriptor,
|
||||||
|
|||||||
@@ -55,8 +55,10 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
|
|
||||||
if (!debugSession.programmingSession.has_value()) {
|
if (!debugSession.programmingSession.has_value()) {
|
||||||
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
|
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
|
||||||
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
auto& programmingSession = debugSession.programmingSession.value();
|
auto& programmingSession = debugSession.programmingSession.value();
|
||||||
const auto currentEndAddress = programmingSession.startAddress + programmingSession.buffer.size() - 1;
|
const auto currentEndAddress = programmingSession.startAddress + programmingSession.buffer.size() - 1;
|
||||||
const auto expectedStartAddress = (currentEndAddress + 1);
|
const auto expectedStartAddress = (currentEndAddress + 1);
|
||||||
@@ -79,7 +81,6 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|||||||
this->buffer.begin(),
|
this->buffer.begin(),
|
||||||
this->buffer.end()
|
this->buffer.end()
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
debugSession.connection.writePacket(OkResponsePacket{});
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ namespace DebugServer::Gdb::RiscVGdb::CommandPackets
|
|||||||
|
|
||||||
debugSession.programmingSession.reset();
|
debugSession.programmingSession.reset();
|
||||||
|
|
||||||
Logger::warning("Program memory updated");
|
|
||||||
targetControllerService.disableProgrammingMode();
|
targetControllerService.disableProgrammingMode();
|
||||||
|
Logger::warning("Program memory updated");
|
||||||
|
|
||||||
Logger::warning("Resetting target");
|
Logger::warning("Resetting target");
|
||||||
targetControllerService.resetTarget();
|
targetControllerService.resetTarget();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace DebugServer::Gdb::RiscVGdb::CommandPackets
|
|||||||
throw Exception{"Memory segment (\"" + segmentDescriptor.name + "\") not writable in programming mode"};
|
throw Exception{"Memory segment (\"" + segmentDescriptor.name + "\") not writable in programming mode"};
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::warning("Erasing \"" + segmentDescriptor.name + "\" segment, in preparation for programming");
|
Logger::debug("Erase segment key: `" + segmentDescriptor.key + "`");
|
||||||
|
|
||||||
targetControllerService.enableProgrammingMode();
|
targetControllerService.enableProgrammingMode();
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,10 @@ namespace DebugServer::Gdb::RiscVGdb::CommandPackets
|
|||||||
|
|
||||||
if (!debugSession.programmingSession.has_value()) {
|
if (!debugSession.programmingSession.has_value()) {
|
||||||
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
|
debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer};
|
||||||
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
auto& programmingSession = debugSession.programmingSession.value();
|
auto& programmingSession = debugSession.programmingSession.value();
|
||||||
const auto currentEndAddress = programmingSession.startAddress + programmingSession.buffer.size() - 1;
|
const auto currentEndAddress = programmingSession.startAddress + programmingSession.buffer.size() - 1;
|
||||||
const auto expectedStartAddress = (currentEndAddress + 1);
|
const auto expectedStartAddress = (currentEndAddress + 1);
|
||||||
@@ -79,7 +81,6 @@ namespace DebugServer::Gdb::RiscVGdb::CommandPackets
|
|||||||
this->buffer.begin(),
|
this->buffer.begin(),
|
||||||
this->buffer.end()
|
this->buffer.end()
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
debugSession.connection.writePacket(OkResponsePacket{});
|
debugSession.connection.writePacket(OkResponsePacket{});
|
||||||
|
|
||||||
|
|||||||
@@ -397,6 +397,15 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EdbgAvr8Interface::clearAllBreakpoints() {
|
||||||
|
this->clearAllSoftwareBreakpoints();
|
||||||
|
|
||||||
|
// Clear all hardware breakpoints
|
||||||
|
while (!this->hardwareBreakpointNumbersByAddress.empty()) {
|
||||||
|
this->clearHardwareBreakpoint(this->hardwareBreakpointNumbersByAddress.begin()->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TargetRegisterDescriptorAndValuePairs EdbgAvr8Interface::readRegisters(
|
TargetRegisterDescriptorAndValuePairs EdbgAvr8Interface::readRegisters(
|
||||||
const Targets::TargetRegisterDescriptors& descriptors
|
const Targets::TargetRegisterDescriptors& descriptors
|
||||||
) {
|
) {
|
||||||
@@ -868,27 +877,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* When clearing individual software breakpoints via EDBG tools, the breakpoints are not actually removed
|
|
||||||
* until this next program flow command.
|
|
||||||
*
|
|
||||||
* This wouldn't be a problem, if the tool handled the stale breakpoint removals properly, when programming
|
|
||||||
* the target (overwriting the program memory at which the software breakpoints reside). But the tool
|
|
||||||
* doesn't handle this properly - it seems to completely ignore the fact that the program memory has been
|
|
||||||
* updated, and so it will corrupt the new program by overwriting the program memory where the old
|
|
||||||
* software breakpoints used to reside. The memory is overwritten with the old instruction - the one that
|
|
||||||
* was captured at the time the software breakpoint was inserted. So we end up with a corrupted program.
|
|
||||||
*
|
|
||||||
* To avoid this issue, we send the 'clear all software breakpoints' command to the tool, just before
|
|
||||||
* entering programming mode. That command will clear all breakpoints immediately, preventing program
|
|
||||||
* memory corruption at the next flow control command.
|
|
||||||
*
|
|
||||||
* The TargetController will reinsert all breakpoints at the end of a programming session, so the breakpoints
|
|
||||||
* that we clear here will be restored.
|
|
||||||
*/
|
|
||||||
Logger::debug("Clearing all software breakpoints in preparation for programming mode");
|
|
||||||
this->clearAllSoftwareBreakpoints();
|
|
||||||
|
|
||||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||||
EnterProgrammingMode{}
|
EnterProgrammingMode{}
|
||||||
);
|
);
|
||||||
@@ -1312,15 +1300,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
this->pendingSoftwareBreakpointDeletions.clear();
|
this->pendingSoftwareBreakpointDeletions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EdbgAvr8Interface::clearAllBreakpoints() {
|
|
||||||
this->clearAllSoftwareBreakpoints();
|
|
||||||
|
|
||||||
// Clear all hardware breakpoints
|
|
||||||
while (!this->hardwareBreakpointNumbersByAddress.empty()) {
|
|
||||||
this->clearHardwareBreakpoint(this->hardwareBreakpointNumbersByAddress.begin()->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EdbgAvr8Interface::injectActiveBreakpoints(
|
void EdbgAvr8Interface::injectActiveBreakpoints(
|
||||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
|||||||
@@ -113,8 +113,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
|
|
||||||
Targets::Microchip::Avr8::TargetSignature getDeviceId() override;
|
Targets::Microchip::Avr8::TargetSignature getDeviceId() override;
|
||||||
|
|
||||||
virtual void setProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) override;
|
void setProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) override;
|
||||||
virtual void removeProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) override;
|
void removeProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) override;
|
||||||
|
void clearAllBreakpoints() override;
|
||||||
|
|
||||||
Targets::TargetRegisterDescriptorAndValuePairs readRegisters(
|
Targets::TargetRegisterDescriptorAndValuePairs readRegisters(
|
||||||
const Targets::TargetRegisterDescriptors& descriptors
|
const Targets::TargetRegisterDescriptors& descriptors
|
||||||
@@ -247,7 +248,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
|||||||
void setHardwareBreakpoint(Targets::TargetMemoryAddress address);
|
void setHardwareBreakpoint(Targets::TargetMemoryAddress address);
|
||||||
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address);
|
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address);
|
||||||
void clearAllSoftwareBreakpoints();
|
void clearAllSoftwareBreakpoints();
|
||||||
void clearAllBreakpoints();
|
|
||||||
void injectActiveBreakpoints(
|
void injectActiveBreakpoints(
|
||||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr8
|
|||||||
|
|
||||||
virtual void setProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) = 0;
|
virtual void setProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) = 0;
|
||||||
virtual void removeProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) = 0;
|
virtual void removeProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) = 0;
|
||||||
|
virtual void clearAllBreakpoints() = 0;
|
||||||
|
|
||||||
virtual Targets::TargetMemoryAddress getProgramCounter() = 0;
|
virtual Targets::TargetMemoryAddress getProgramCounter() = 0;
|
||||||
virtual void setProgramCounter(Targets::TargetMemoryAddress programCounter) = 0;
|
virtual void setProgramCounter(Targets::TargetMemoryAddress programCounter) = 0;
|
||||||
|
|||||||
@@ -21,8 +21,6 @@
|
|||||||
#include "src/Helpers/BiMap.hpp"
|
#include "src/Helpers/BiMap.hpp"
|
||||||
#include "src/Services/StringService.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
|
|
||||||
#include "src/Logger/Logger.hpp"
|
|
||||||
|
|
||||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||||
|
|
||||||
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ namespace DebugToolDrivers::Wch
|
|||||||
using ::Targets::TargetAddressSpaceDescriptor;
|
using ::Targets::TargetAddressSpaceDescriptor;
|
||||||
using ::Targets::TargetMemorySegmentDescriptor;
|
using ::Targets::TargetMemorySegmentDescriptor;
|
||||||
using ::Targets::TargetProgramBreakpoint;
|
using ::Targets::TargetProgramBreakpoint;
|
||||||
|
using ::Targets::BreakpointResources;
|
||||||
using ::Targets::TargetMemorySegmentType;
|
using ::Targets::TargetMemorySegmentType;
|
||||||
|
using ::Targets::TargetRegisterDescriptor;
|
||||||
using ::Targets::TargetRegisterDescriptors;
|
using ::Targets::TargetRegisterDescriptors;
|
||||||
using ::Targets::TargetRegisterDescriptorAndValuePairs;
|
using ::Targets::TargetRegisterDescriptorAndValuePairs;
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
, toolInfo(toolInfo)
|
, toolInfo(toolInfo)
|
||||||
, sysAddressSpaceDescriptor(this->targetDescriptionFile.getSystemAddressSpaceDescriptor())
|
, sysAddressSpaceDescriptor(this->targetDescriptionFile.getSystemAddressSpaceDescriptor())
|
||||||
, mainProgramSegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("main_program"))
|
, mainProgramSegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("main_program"))
|
||||||
|
, bootProgramSegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("boot_program"))
|
||||||
, flashProgramOpcodes(
|
, flashProgramOpcodes(
|
||||||
WchLinkDebugInterface::getFlashProgramOpcodes(
|
WchLinkDebugInterface::getFlashProgramOpcodes(
|
||||||
this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value
|
this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value
|
||||||
@@ -129,7 +132,6 @@ namespace DebugToolDrivers::Wch
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::deactivate() {
|
void WchLinkDebugInterface::deactivate() {
|
||||||
this->riscVTranslator.clearAllTriggers();
|
|
||||||
this->riscVTranslator.deactivate();
|
this->riscVTranslator.deactivate();
|
||||||
|
|
||||||
const auto response = this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::DetachTarget{});
|
const auto response = this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::DetachTarget{});
|
||||||
@@ -142,7 +144,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
return "0x" + Services::StringService::toHex(this->cachedVariantId.value());
|
return "0x" + Services::StringService::toHex(this->cachedVariantId.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
Targets::TargetExecutionState WchLinkDebugInterface::getExecutionState() {
|
TargetExecutionState WchLinkDebugInterface::getExecutionState() {
|
||||||
return this->riscVTranslator.getExecutionState();
|
return this->riscVTranslator.getExecutionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +164,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
this->riscVTranslator.reset();
|
this->riscVTranslator.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Targets::BreakpointResources WchLinkDebugInterface::getBreakpointResources() {
|
BreakpointResources WchLinkDebugInterface::getBreakpointResources() {
|
||||||
return {
|
return {
|
||||||
.hardwareBreakpoints = this->riscVTranslator.getTriggerCount(),
|
.hardwareBreakpoints = this->riscVTranslator.getTriggerCount(),
|
||||||
.softwareBreakpoints = 0xFFFFFFFF, // TODO: Use the program memory size to determine the limit.
|
.softwareBreakpoints = 0xFFFFFFFF, // TODO: Use the program memory size to determine the limit.
|
||||||
@@ -187,22 +189,22 @@ namespace DebugToolDrivers::Wch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Targets::TargetRegisterDescriptorAndValuePairs WchLinkDebugInterface::readCpuRegisters(
|
TargetRegisterDescriptorAndValuePairs WchLinkDebugInterface::readCpuRegisters(
|
||||||
const Targets::TargetRegisterDescriptors& descriptors
|
const TargetRegisterDescriptors& descriptors
|
||||||
) {
|
) {
|
||||||
return this->riscVTranslator.readCpuRegisters(descriptors);
|
return this->riscVTranslator.readCpuRegisters(descriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) {
|
void WchLinkDebugInterface::writeCpuRegisters(const TargetRegisterDescriptorAndValuePairs& registers) {
|
||||||
return this->riscVTranslator.writeCpuRegisters(registers);
|
return this->riscVTranslator.writeCpuRegisters(registers);
|
||||||
}
|
}
|
||||||
|
|
||||||
Targets::TargetMemoryBuffer WchLinkDebugInterface::readMemory(
|
TargetMemoryBuffer WchLinkDebugInterface::readMemory(
|
||||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
Targets::TargetMemoryAddress startAddress,
|
TargetMemoryAddress startAddress,
|
||||||
Targets::TargetMemorySize bytes,
|
TargetMemorySize bytes,
|
||||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges
|
const std::set<TargetMemoryAddressRange>& excludedAddressRanges
|
||||||
) {
|
) {
|
||||||
return this->riscVTranslator.readMemory(
|
return this->riscVTranslator.readMemory(
|
||||||
addressSpaceDescriptor,
|
addressSpaceDescriptor,
|
||||||
@@ -216,8 +218,8 @@ namespace DebugToolDrivers::Wch
|
|||||||
void WchLinkDebugInterface::writeMemory(
|
void WchLinkDebugInterface::writeMemory(
|
||||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
Targets::TargetMemoryAddress startAddress,
|
TargetMemoryAddress startAddress,
|
||||||
Targets::TargetMemoryBufferSpan buffer
|
TargetMemoryBufferSpan buffer
|
||||||
) {
|
) {
|
||||||
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) {
|
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) {
|
||||||
/*
|
/*
|
||||||
@@ -293,8 +295,8 @@ namespace DebugToolDrivers::Wch
|
|||||||
* The erase operation will silently fail, and the target will be left in a bad state, causing the subsequent
|
* The erase operation will silently fail, and the target will be left in a bad state, causing the subsequent
|
||||||
* full block write to corrupt the target's program memory
|
* full block write to corrupt the target's program memory
|
||||||
*
|
*
|
||||||
* - The reset is what causes the erase operation to fail. But I have no idea why. I've inspected the relevant
|
* - The reset is what causes the erase operation to fail, but I have no idea why. I've inspected the relevant
|
||||||
* registers, at the relevant times, but have found nothing significant that could explain this
|
* registers, at the relevant times, but found nothing significant that could explain this
|
||||||
*
|
*
|
||||||
* - Subsequent resets do not fix the issue, but another full block write does. My guess is that the first full
|
* - Subsequent resets do not fix the issue, but another full block write does. My guess is that the first full
|
||||||
* block write (which corrupted program memory) corrected the target state, cleaning the mess made by the
|
* block write (which corrupted program memory) corrected the target state, cleaning the mess made by the
|
||||||
@@ -304,11 +306,11 @@ namespace DebugToolDrivers::Wch
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::enableProgrammingMode() {
|
void WchLinkDebugInterface::enableProgrammingMode() {
|
||||||
|
// TODO: Move this to target driver. After v2.0.0.
|
||||||
|
this->clearAllBreakpoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::disableProgrammingMode() {
|
void WchLinkDebugInterface::disableProgrammingMode() {}
|
||||||
this->softwareBreakpointRegistry.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WchLinkDebugInterface::applyAccessRestrictions(TargetMemorySegmentDescriptor& memorySegmentDescriptor) {
|
void WchLinkDebugInterface::applyAccessRestrictions(TargetMemorySegmentDescriptor& memorySegmentDescriptor) {
|
||||||
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) {
|
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) {
|
||||||
@@ -320,7 +322,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::applyAccessRestrictions(Targets::TargetRegisterDescriptor& registerDescriptor) {
|
void WchLinkDebugInterface::applyAccessRestrictions(TargetRegisterDescriptor& registerDescriptor) {
|
||||||
// I don't believe any further access restrictions are required for registers. TODO: Review after v2.0.0.
|
// I don't believe any further access restrictions are required for registers. TODO: Review after v2.0.0.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,23 +331,6 @@ namespace DebugToolDrivers::Wch
|
|||||||
throw Exception{"Invalid software breakpoint size (" + std::to_string(breakpoint.size) + ")"};
|
throw Exception{"Invalid software breakpoint size (" + std::to_string(breakpoint.size) + ")"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto originalData = this->readMemory(
|
|
||||||
breakpoint.addressSpaceDescriptor,
|
|
||||||
breakpoint.memorySegmentDescriptor,
|
|
||||||
breakpoint.address,
|
|
||||||
breakpoint.size,
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
const auto softwareBreakpoint = ::Targets::RiscV::ProgramBreakpoint{
|
|
||||||
breakpoint,
|
|
||||||
static_cast<::Targets::RiscV::Opcodes::Opcode>(
|
|
||||||
breakpoint.size == 2
|
|
||||||
? (originalData[1] << 8) | originalData[0]
|
|
||||||
: (originalData[3] << 24) | (originalData[2] << 16) | (originalData[1] << 8) | originalData[0]
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr auto EBREAK_OPCODE = std::to_array<unsigned char>({
|
static constexpr auto EBREAK_OPCODE = std::to_array<unsigned char>({
|
||||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak),
|
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak),
|
||||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak >> 8),
|
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak >> 8),
|
||||||
@@ -359,15 +344,15 @@ namespace DebugToolDrivers::Wch
|
|||||||
});
|
});
|
||||||
|
|
||||||
this->writeMemory(
|
this->writeMemory(
|
||||||
softwareBreakpoint.addressSpaceDescriptor,
|
breakpoint.addressSpaceDescriptor,
|
||||||
softwareBreakpoint.memorySegmentDescriptor,
|
breakpoint.memorySegmentDescriptor,
|
||||||
softwareBreakpoint.address,
|
breakpoint.address,
|
||||||
softwareBreakpoint.size == 2
|
breakpoint.size == 2
|
||||||
? TargetMemoryBufferSpan{COMPRESSED_EBREAK_OPCODE}
|
? TargetMemoryBufferSpan{COMPRESSED_EBREAK_OPCODE}
|
||||||
: TargetMemoryBufferSpan{EBREAK_OPCODE}
|
: TargetMemoryBufferSpan{EBREAK_OPCODE}
|
||||||
);
|
);
|
||||||
|
|
||||||
this->softwareBreakpointRegistry.insert(softwareBreakpoint);
|
this->softwareBreakpointRegistry.insert(breakpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::clearSoftwareBreakpoint(const TargetProgramBreakpoint& breakpoint) {
|
void WchLinkDebugInterface::clearSoftwareBreakpoint(const TargetProgramBreakpoint& breakpoint) {
|
||||||
@@ -375,37 +360,26 @@ namespace DebugToolDrivers::Wch
|
|||||||
throw Exception{"Invalid software breakpoint size (" + std::to_string(breakpoint.size) + ")"};
|
throw Exception{"Invalid software breakpoint size (" + std::to_string(breakpoint.size) + ")"};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto softwareBreakpointOpt = this->softwareBreakpointRegistry.find(breakpoint);
|
|
||||||
if (!softwareBreakpointOpt.has_value()) {
|
|
||||||
throw TargetOperationFailure{
|
|
||||||
"Unknown software breakpoint (byte address: 0x" + Services::StringService::toHex(breakpoint.address)
|
|
||||||
+ ")"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& softwareBreakpoint = softwareBreakpointOpt->get();
|
|
||||||
if (!softwareBreakpoint.originalInstruction.has_value()) {
|
|
||||||
throw InternalFatalErrorException{"Missing original opcode"};
|
|
||||||
}
|
|
||||||
|
|
||||||
this->writeMemory(
|
this->writeMemory(
|
||||||
softwareBreakpoint.addressSpaceDescriptor,
|
breakpoint.addressSpaceDescriptor,
|
||||||
softwareBreakpoint.memorySegmentDescriptor,
|
breakpoint.memorySegmentDescriptor,
|
||||||
softwareBreakpoint.address,
|
breakpoint.address,
|
||||||
softwareBreakpoint.size == 2
|
TargetMemoryBufferSpan{
|
||||||
? TargetMemoryBuffer{
|
breakpoint.originalData.begin(),
|
||||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction)),
|
breakpoint.originalData.begin() + breakpoint.size
|
||||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 8)
|
|
||||||
}
|
|
||||||
: TargetMemoryBuffer{
|
|
||||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction)),
|
|
||||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 8),
|
|
||||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 16),
|
|
||||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 24)
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this->softwareBreakpointRegistry.remove(softwareBreakpoint);
|
this->softwareBreakpointRegistry.remove(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WchLinkDebugInterface::clearAllBreakpoints() {
|
||||||
|
this->riscVTranslator.clearAllTriggers();
|
||||||
|
for (const auto [addressSpaceId, breakpointsByAddress] : this->softwareBreakpointRegistry) {
|
||||||
|
for (const auto& [address, breakpoint] : breakpointsByAddress) {
|
||||||
|
this->clearSoftwareBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkDebugInterface::writeProgramMemoryPartialBlock(
|
void WchLinkDebugInterface::writeProgramMemoryPartialBlock(
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include "src/Targets/ProgramBreakpointRegistry.hpp"
|
#include "src/Targets/ProgramBreakpointRegistry.hpp"
|
||||||
|
|
||||||
#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp"
|
#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp"
|
||||||
#include "src/Targets/RiscV/ProgramBreakpoint.hpp"
|
|
||||||
|
|
||||||
namespace DebugToolDrivers::Wch
|
namespace DebugToolDrivers::Wch
|
||||||
{
|
{
|
||||||
@@ -96,6 +95,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
|
|
||||||
const Targets::TargetAddressSpaceDescriptor sysAddressSpaceDescriptor;
|
const Targets::TargetAddressSpaceDescriptor sysAddressSpaceDescriptor;
|
||||||
const Targets::TargetMemorySegmentDescriptor& mainProgramSegmentDescriptor;
|
const Targets::TargetMemorySegmentDescriptor& mainProgramSegmentDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& bootProgramSegmentDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 'target activation' command returns a payload of 5 bytes.
|
* The 'target activation' command returns a payload of 5 bytes.
|
||||||
@@ -107,13 +107,14 @@ namespace DebugToolDrivers::Wch
|
|||||||
std::optional<WchTargetVariantId> cachedVariantId;
|
std::optional<WchTargetVariantId> cachedVariantId;
|
||||||
std::optional<WchTargetId> cachedTargetId;
|
std::optional<WchTargetId> cachedTargetId;
|
||||||
|
|
||||||
Targets::ProgramBreakpointRegistryGeneric<Targets::RiscV::ProgramBreakpoint> softwareBreakpointRegistry;
|
Targets::ProgramBreakpointRegistry softwareBreakpointRegistry;
|
||||||
|
|
||||||
std::span<const unsigned char> flashProgramOpcodes;
|
std::span<const unsigned char> flashProgramOpcodes;
|
||||||
Targets::TargetMemorySize programmingBlockSize;
|
Targets::TargetMemorySize programmingBlockSize;
|
||||||
|
|
||||||
void setSoftwareBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint);
|
void setSoftwareBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint);
|
||||||
void clearSoftwareBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint);
|
void clearSoftwareBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint);
|
||||||
|
void clearAllBreakpoints();
|
||||||
|
|
||||||
void writeProgramMemoryPartialBlock(
|
void writeProgramMemoryPartialBlock(
|
||||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
|||||||
@@ -213,6 +213,10 @@ TargetConfig::TargetConfig(const YAML::Node& targetNode) {
|
|||||||
this->programMemoryCache = targetNode["program_memory_cache"].as<bool>(this->programMemoryCache);
|
this->programMemoryCache = targetNode["program_memory_cache"].as<bool>(this->programMemoryCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetNode["delta_programming"]) {
|
||||||
|
this->deltaProgramming = targetNode["delta_programming"].as<bool>(this->deltaProgramming);
|
||||||
|
}
|
||||||
|
|
||||||
if (targetNode["reserve_stepping_breakpoint"]) {
|
if (targetNode["reserve_stepping_breakpoint"]) {
|
||||||
this->reserveSteppingBreakpoint = targetNode["reserve_stepping_breakpoint"].as<bool>(false);
|
this->reserveSteppingBreakpoint = targetNode["reserve_stepping_breakpoint"].as<bool>(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ struct TargetConfig
|
|||||||
*/
|
*/
|
||||||
bool programMemoryCache = true;
|
bool programMemoryCache = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether Bloom will employ "delta programming" during programming sessions.
|
||||||
|
*
|
||||||
|
* Not all targets support delta programming.
|
||||||
|
*/
|
||||||
|
bool deltaProgramming = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if Bloom will reserve a single hardware breakpoint for stepping operations.
|
* Determines if Bloom will reserve a single hardware breakpoint for stepping operations.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ namespace Services
|
|||||||
addressSpaceDescriptor,
|
addressSpaceDescriptor,
|
||||||
memorySegmentDescriptor,
|
memorySegmentDescriptor,
|
||||||
startAddress,
|
startAddress,
|
||||||
buffer
|
TargetMemoryBuffer{buffer.begin(), buffer.end()}
|
||||||
),
|
),
|
||||||
this->defaultTimeout,
|
this->defaultTimeout,
|
||||||
this->activeAtomicSessionId
|
this->activeAtomicSessionId
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ namespace Services
|
|||||||
|
|
||||||
std::optional<TargetController::AtomicSessionIdType> activeAtomicSessionId = std::nullopt;
|
std::optional<TargetController::AtomicSessionIdType> activeAtomicSessionId = std::nullopt;
|
||||||
|
|
||||||
std::chrono::milliseconds defaultTimeout = std::chrono::milliseconds{30000};
|
std::chrono::milliseconds defaultTimeout = std::chrono::milliseconds{90000};
|
||||||
|
|
||||||
TargetController::AtomicSessionIdType startAtomicSession();
|
TargetController::AtomicSessionIdType startAtomicSession();
|
||||||
void endAtomicSession(TargetController::AtomicSessionIdType sessionId);
|
void endAtomicSession(TargetController::AtomicSessionIdType sessionId);
|
||||||
|
|||||||
@@ -18,18 +18,18 @@ namespace TargetController::Commands
|
|||||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
||||||
Targets::TargetMemoryAddress startAddress;
|
Targets::TargetMemoryAddress startAddress;
|
||||||
Targets::TargetMemoryBufferSpan buffer;
|
Targets::TargetMemoryBuffer buffer;
|
||||||
|
|
||||||
WriteTargetMemory(
|
WriteTargetMemory(
|
||||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
Targets::TargetMemoryAddress startAddress,
|
Targets::TargetMemoryAddress startAddress,
|
||||||
Targets::TargetMemoryBufferSpan buffer
|
Targets::TargetMemoryBuffer&& buffer
|
||||||
)
|
)
|
||||||
: addressSpaceDescriptor(addressSpaceDescriptor)
|
: addressSpaceDescriptor(addressSpaceDescriptor)
|
||||||
, memorySegmentDescriptor(memorySegmentDescriptor)
|
, memorySegmentDescriptor(memorySegmentDescriptor)
|
||||||
, startAddress(startAddress)
|
, startAddress(startAddress)
|
||||||
, buffer(buffer)
|
, buffer(std::move(buffer))
|
||||||
{};
|
{};
|
||||||
|
|
||||||
[[nodiscard]] CommandType getType() const override {
|
[[nodiscard]] CommandType getType() const override {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "src/Services/PathService.hpp"
|
#include "src/Services/PathService.hpp"
|
||||||
#include "src/Services/ProcessService.hpp"
|
#include "src/Services/ProcessService.hpp"
|
||||||
#include "src/Services/StringService.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
|
#include "src/Services/AlignmentService.hpp"
|
||||||
#include "src/Logger/Logger.hpp"
|
#include "src/Logger/Logger.hpp"
|
||||||
|
|
||||||
#include "Exceptions/TargetOperationFailure.hpp"
|
#include "Exceptions/TargetOperationFailure.hpp"
|
||||||
@@ -287,9 +288,34 @@ namespace TargetController
|
|||||||
|
|
||||||
this->acquireHardware();
|
this->acquireHardware();
|
||||||
|
|
||||||
if (this->targetState->executionState != TargetExecutionState::RUNNING) {
|
this->targetState = std::make_unique<TargetState>(
|
||||||
// this->target->run();
|
TargetExecutionState::UNKNOWN,
|
||||||
// this->targetState->executionState = TargetExecutionState::RUNNING;
|
TargetMode::DEBUGGING,
|
||||||
|
std::nullopt
|
||||||
|
);
|
||||||
|
this->refreshExecutionState();
|
||||||
|
|
||||||
|
if (this->environmentConfig.targetConfig.hardwareBreakpoints) {
|
||||||
|
const auto& breakpointResources = this->targetDescriptor->breakpointResources;
|
||||||
|
Logger::info("Available hardware breakpoints: " + std::to_string(breakpointResources.hardwareBreakpoints));
|
||||||
|
Logger::info(
|
||||||
|
"Reserved hardware breakpoints: " + std::to_string(breakpointResources.reservedHardwareBreakpoints)
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Logger::warning("Hardware breakpoints have been disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this->targetState->executionState == TargetExecutionState::STOPPED
|
||||||
|
&& this->environmentConfig.targetConfig.resumeOnStartup
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
this->resumeTarget();
|
||||||
|
|
||||||
|
} catch (const Exceptions::TargetOperationFailure& exception) {
|
||||||
|
Logger::error("Failed to resume target execution on startup - error: " + exception.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state = TargetControllerState::ACTIVE;
|
this->state = TargetControllerState::ACTIVE;
|
||||||
@@ -569,35 +595,7 @@ namespace TargetController
|
|||||||
|
|
||||||
this->target->postActivate();
|
this->target->postActivate();
|
||||||
|
|
||||||
this->targetState = std::make_unique<TargetState>(
|
this->deltaProgrammingInterface = this->target->deltaProgrammingInterface();
|
||||||
TargetExecutionState::UNKNOWN,
|
|
||||||
TargetMode::DEBUGGING,
|
|
||||||
std::nullopt
|
|
||||||
);
|
|
||||||
this->refreshExecutionState();
|
|
||||||
|
|
||||||
if (this->environmentConfig.targetConfig.hardwareBreakpoints) {
|
|
||||||
const auto& breakpointResources = this->targetDescriptor->breakpointResources;
|
|
||||||
Logger::info("Available hardware breakpoints: " + std::to_string(breakpointResources.hardwareBreakpoints));
|
|
||||||
Logger::info(
|
|
||||||
"Reserved hardware breakpoints: " + std::to_string(breakpointResources.reservedHardwareBreakpoints)
|
|
||||||
);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Logger::warning("Hardware breakpoints have been disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this->targetState->executionState == TargetExecutionState::STOPPED
|
|
||||||
&& this->environmentConfig.targetConfig.resumeOnStartup
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
this->resumeTarget();
|
|
||||||
|
|
||||||
} catch (const Exceptions::TargetOperationFailure& exception) {
|
|
||||||
Logger::error("Failed to resume target execution on startup - error: " + exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TargetControllerComponent::releaseHardware() {
|
void TargetControllerComponent::releaseHardware() {
|
||||||
@@ -752,7 +750,8 @@ namespace TargetController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache.fetch(startAddress, bytes);
|
const auto cachedData = cache.fetch(startAddress, bytes);
|
||||||
|
return TargetMemoryBuffer{cachedData.begin(), cachedData.end()};
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->target->readMemory(
|
return this->target->readMemory(
|
||||||
@@ -783,7 +782,13 @@ namespace TargetController
|
|||||||
|
|
||||||
this->target->writeMemory(addressSpaceDescriptor, memorySegmentDescriptor, startAddress, buffer);
|
this->target->writeMemory(addressSpaceDescriptor, memorySegmentDescriptor, startAddress, buffer);
|
||||||
|
|
||||||
if (isProgramMemory && this->environmentConfig.targetConfig.programMemoryCache) {
|
if (
|
||||||
|
isProgramMemory
|
||||||
|
&& (
|
||||||
|
this->environmentConfig.targetConfig.programMemoryCache
|
||||||
|
|| this->environmentConfig.targetConfig.deltaProgramming
|
||||||
|
)
|
||||||
|
) {
|
||||||
this->getProgramMemoryCache(memorySegmentDescriptor).insert(startAddress, buffer);
|
this->getProgramMemoryCache(memorySegmentDescriptor).insert(startAddress, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,7 +816,10 @@ namespace TargetController
|
|||||||
throw Exception{"Cannot erase program memory - programming mode not enabled."};
|
throw Exception{"Cannot erase program memory - programming mode not enabled."};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->environmentConfig.targetConfig.programMemoryCache) {
|
if (
|
||||||
|
this->environmentConfig.targetConfig.programMemoryCache
|
||||||
|
|| this->environmentConfig.targetConfig.deltaProgramming
|
||||||
|
) {
|
||||||
Logger::debug("Clearing program memory cache");
|
Logger::debug("Clearing program memory cache");
|
||||||
this->getProgramMemoryCache(memorySegmentDescriptor).clear();
|
this->getProgramMemoryCache(memorySegmentDescriptor).clear();
|
||||||
}
|
}
|
||||||
@@ -847,7 +855,10 @@ namespace TargetController
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
breakpoint.type == TargetProgramBreakpoint::Type::SOFTWARE
|
breakpoint.type == TargetProgramBreakpoint::Type::SOFTWARE
|
||||||
&& this->environmentConfig.targetConfig.programMemoryCache
|
&& (
|
||||||
|
this->environmentConfig.targetConfig.programMemoryCache
|
||||||
|
|| this->environmentConfig.targetConfig.deltaProgramming
|
||||||
|
)
|
||||||
&& this->target->isProgramMemory(
|
&& this->target->isProgramMemory(
|
||||||
breakpoint.addressSpaceDescriptor,
|
breakpoint.addressSpaceDescriptor,
|
||||||
breakpoint.memorySegmentDescriptor,
|
breakpoint.memorySegmentDescriptor,
|
||||||
@@ -881,39 +892,43 @@ namespace TargetController
|
|||||||
|
|
||||||
Logger::debug("Removing breakpoint at byte address 0x" + StringService::toHex(breakpoint.address));
|
Logger::debug("Removing breakpoint at byte address 0x" + StringService::toHex(breakpoint.address));
|
||||||
|
|
||||||
if (!registry.contains(breakpoint)) {
|
|
||||||
|
const auto registeredBreakpointOpt = registry.find(breakpoint);
|
||||||
|
if (!registeredBreakpointOpt.has_value()) {
|
||||||
Logger::debug("Breakpoint not found in registry - ignoring removal request");
|
Logger::debug("Breakpoint not found in registry - ignoring removal request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->target->removeProgramBreakpoint(breakpoint);
|
const auto& registeredBreakpoint = registeredBreakpointOpt->get();
|
||||||
registry.remove(breakpoint);
|
this->target->removeProgramBreakpoint(registeredBreakpoint);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
breakpoint.type == TargetProgramBreakpoint::Type::SOFTWARE
|
registeredBreakpoint.type == TargetProgramBreakpoint::Type::SOFTWARE
|
||||||
&& this->environmentConfig.targetConfig.programMemoryCache
|
&& (
|
||||||
|
this->environmentConfig.targetConfig.programMemoryCache
|
||||||
|
|| this->environmentConfig.targetConfig.deltaProgramming
|
||||||
|
)
|
||||||
&& this->target->isProgramMemory(
|
&& this->target->isProgramMemory(
|
||||||
breakpoint.addressSpaceDescriptor,
|
registeredBreakpoint.addressSpaceDescriptor,
|
||||||
breakpoint.memorySegmentDescriptor,
|
registeredBreakpoint.memorySegmentDescriptor,
|
||||||
breakpoint.address,
|
registeredBreakpoint.address,
|
||||||
breakpoint.size
|
registeredBreakpoint.size
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
auto& cache = this->getProgramMemoryCache(breakpoint.memorySegmentDescriptor);
|
auto& cache = this->getProgramMemoryCache(registeredBreakpoint.memorySegmentDescriptor);
|
||||||
if (cache.contains(breakpoint.address, breakpoint.size)) {
|
if (cache.contains(registeredBreakpoint.address, registeredBreakpoint.size)) {
|
||||||
// Update program memory cache
|
// Update program memory cache with the original instruction
|
||||||
cache.insert(
|
cache.insert(
|
||||||
breakpoint.address,
|
registeredBreakpoint.address,
|
||||||
this->target->readMemory(
|
TargetMemoryBufferSpan{
|
||||||
breakpoint.addressSpaceDescriptor,
|
registeredBreakpoint.originalData.begin(),
|
||||||
breakpoint.memorySegmentDescriptor,
|
registeredBreakpoint.originalData.begin() + registeredBreakpoint.size
|
||||||
breakpoint.address,
|
}
|
||||||
breakpoint.size,
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registry.remove(registeredBreakpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TargetControllerComponent::clearAllBreakpoints() {
|
void TargetControllerComponent::clearAllBreakpoints() {
|
||||||
@@ -935,12 +950,21 @@ namespace TargetController
|
|||||||
this->target->enableProgrammingMode();
|
this->target->enableProgrammingMode();
|
||||||
Logger::warning("Programming mode enabled");
|
Logger::warning("Programming mode enabled");
|
||||||
|
|
||||||
|
if (this->environmentConfig.targetConfig.deltaProgramming && this->deltaProgrammingInterface != nullptr) {
|
||||||
|
this->deltaProgrammingSession = DeltaProgramming::Session{};
|
||||||
|
}
|
||||||
|
|
||||||
auto newState = *(this->targetState);
|
auto newState = *(this->targetState);
|
||||||
newState.mode = TargetMode::PROGRAMMING;
|
newState.mode = TargetMode::PROGRAMMING;
|
||||||
this->updateTargetState(newState);
|
this->updateTargetState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TargetControllerComponent::disableProgrammingMode() {
|
void TargetControllerComponent::disableProgrammingMode() {
|
||||||
|
if (this->deltaProgrammingSession.has_value()) {
|
||||||
|
this->commitDeltaProgrammingSession(*(this->deltaProgrammingSession));
|
||||||
|
this->deltaProgrammingSession = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
Logger::debug("Disabling programming mode");
|
Logger::debug("Disabling programming mode");
|
||||||
this->target->disableProgrammingMode();
|
this->target->disableProgrammingMode();
|
||||||
Logger::info("Programming mode disabled");
|
Logger::info("Programming mode disabled");
|
||||||
@@ -948,14 +972,41 @@ namespace TargetController
|
|||||||
Logger::info("Restoring breakpoints");
|
Logger::info("Restoring breakpoints");
|
||||||
this->target->stop();
|
this->target->stop();
|
||||||
|
|
||||||
for (const auto& [addressSpaceId, breakpointsByAddress] : this->softwareBreakpointRegistry) {
|
static const auto refreshOriginalData = [this] (TargetProgramBreakpoint& breakpoint) {
|
||||||
for (const auto& [address, breakpoint] : breakpointsByAddress) {
|
const auto originalData = this->readTargetMemory(
|
||||||
|
breakpoint.addressSpaceDescriptor,
|
||||||
|
breakpoint.memorySegmentDescriptor,
|
||||||
|
breakpoint.address,
|
||||||
|
breakpoint.size,
|
||||||
|
{},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
std::copy(originalData.begin(), originalData.end(), breakpoint.originalData.begin());
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& [addressSpaceId, breakpointsByAddress] : this->softwareBreakpointRegistry) {
|
||||||
|
for (auto& [address, breakpoint] : breakpointsByAddress) {
|
||||||
|
refreshOriginalData(breakpoint);
|
||||||
this->target->setProgramBreakpoint(breakpoint);
|
this->target->setProgramBreakpoint(breakpoint);
|
||||||
|
|
||||||
|
auto& cache = this->getProgramMemoryCache(breakpoint.memorySegmentDescriptor);
|
||||||
|
cache.insert(
|
||||||
|
breakpoint.address,
|
||||||
|
this->target->readMemory(
|
||||||
|
breakpoint.addressSpaceDescriptor,
|
||||||
|
breakpoint.memorySegmentDescriptor,
|
||||||
|
breakpoint.address,
|
||||||
|
breakpoint.size,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [addressSpaceId, breakpointsByAddress] : this->hardwareBreakpointRegistry) {
|
for (auto& [addressSpaceId, breakpointsByAddress] : this->hardwareBreakpointRegistry) {
|
||||||
for (const auto& [address, breakpoint] : breakpointsByAddress) {
|
for (auto& [address, breakpoint] : breakpointsByAddress) {
|
||||||
|
refreshOriginalData(breakpoint);
|
||||||
this->target->setProgramBreakpoint(breakpoint);
|
this->target->setProgramBreakpoint(breakpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -981,6 +1032,163 @@ namespace TargetController
|
|||||||
return cacheIt->second;
|
return cacheIt->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TargetControllerComponent::commitDeltaProgrammingSession(const DeltaProgramming::Session& session) {
|
||||||
|
using Services::AlignmentService;
|
||||||
|
using Services::StringService;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a single write operation cannot be committed, we must abandon the whole session.
|
||||||
|
*
|
||||||
|
* We prepare CommitOperation objects and then commit all of them at the end, allowing for the abandoning of
|
||||||
|
* the session, if necessary.
|
||||||
|
*/
|
||||||
|
struct CommitOperation
|
||||||
|
{
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
||||||
|
std::vector<DeltaProgramming::Session::WriteOperation::Region> deltaSegments;
|
||||||
|
};
|
||||||
|
auto commitOperations = std::vector<CommitOperation>{};
|
||||||
|
|
||||||
|
for (const auto& [segmentId, writeOperation] : session.writeOperationsBySegmentId) {
|
||||||
|
auto& segmentCache = this->getProgramMemoryCache(writeOperation.memorySegmentDescriptor);
|
||||||
|
|
||||||
|
// Can the program memory cache facilitate diffing with all regions in this write operation?
|
||||||
|
for (const auto& region : writeOperation.regions) {
|
||||||
|
if (!segmentCache.contains(region.addressRange.startAddress, region.addressRange.size())) {
|
||||||
|
Logger::info("Abandoning delta programming session - insufficient data in program memory cache");
|
||||||
|
return this->abandonDeltaProgrammingSession(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto alignTo = this->deltaProgrammingInterface->deltaBlockSize(
|
||||||
|
writeOperation.addressSpaceDescriptor,
|
||||||
|
writeOperation.memorySegmentDescriptor
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that the segment cache has sufficient data to facilitate delta segment alignment
|
||||||
|
*
|
||||||
|
* If the cache doesn't contain the necessary data for alignment, we just fill it with 0xFF instead of
|
||||||
|
* obtaining the data with a read operation. This saves us some time.
|
||||||
|
*/
|
||||||
|
for (const auto& region : writeOperation.regions) {
|
||||||
|
const auto alignedAddress = AlignmentService::alignMemoryAddress(
|
||||||
|
region.addressRange.startAddress,
|
||||||
|
alignTo
|
||||||
|
);
|
||||||
|
const auto alignedSize = AlignmentService::alignMemorySize(
|
||||||
|
region.addressRange.size() + (region.addressRange.startAddress - alignedAddress),
|
||||||
|
alignTo
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!segmentCache.contains(alignedAddress, alignedSize)) {
|
||||||
|
if (region.addressRange.startAddress != alignedAddress) {
|
||||||
|
segmentCache.fill(alignedAddress, region.addressRange.startAddress - alignedAddress, 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.addressRange.size() != alignedSize) {
|
||||||
|
segmentCache.fill(
|
||||||
|
region.addressRange.endAddress + 1,
|
||||||
|
alignedSize - region.addressRange.size(),
|
||||||
|
0xFF
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When constructing the delta segments, any software breakpoints currently installed in this memory
|
||||||
|
* segment can interfere with the diffing of the new program and the program memory cache.
|
||||||
|
*
|
||||||
|
* For this reason, we make a copy of the program memory cache and strip any software breakpoints from it,
|
||||||
|
* before constructing the delta segments.
|
||||||
|
*/
|
||||||
|
auto cacheData = segmentCache.data;
|
||||||
|
for (const auto& [addressSpaceId, breakpointsByAddress] : this->softwareBreakpointRegistry) {
|
||||||
|
for (const auto& [address, breakpoint] : breakpointsByAddress) {
|
||||||
|
if (breakpoint.memorySegmentDescriptor != writeOperation.memorySegmentDescriptor) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy(
|
||||||
|
breakpoint.originalData.begin(),
|
||||||
|
breakpoint.originalData.begin() + breakpoint.size,
|
||||||
|
cacheData.begin() + (breakpoint.address
|
||||||
|
- breakpoint.memorySegmentDescriptor.addressRange.startAddress)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operation = CommitOperation{
|
||||||
|
.addressSpaceDescriptor = writeOperation.addressSpaceDescriptor,
|
||||||
|
.memorySegmentDescriptor = writeOperation.memorySegmentDescriptor,
|
||||||
|
.deltaSegments = writeOperation.deltaSegments(cacheData, alignTo)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (operation.deltaSegments.empty()) {
|
||||||
|
Logger::warning("Abandoning delta programming session - zero delta segments");
|
||||||
|
return this->abandonDeltaProgrammingSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this->deltaProgrammingInterface->shouldAbandonSession(
|
||||||
|
operation.addressSpaceDescriptor,
|
||||||
|
operation.memorySegmentDescriptor,
|
||||||
|
operation.deltaSegments
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Logger::info("Abandoning delta programming session - upon target driver request");
|
||||||
|
return this->abandonDeltaProgrammingSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
commitOperations.emplace_back(std::move(operation));
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::info("Committing delta programming session");
|
||||||
|
|
||||||
|
for (const auto& operation : commitOperations) {
|
||||||
|
auto& segmentCache = this->getProgramMemoryCache(operation.memorySegmentDescriptor);
|
||||||
|
|
||||||
|
Logger::info(
|
||||||
|
std::to_string(operation.deltaSegments.size()) + " delta segment(s) to be flushed to `"
|
||||||
|
+ operation.memorySegmentDescriptor.key + "`"
|
||||||
|
);
|
||||||
|
for (const auto& deltaSegment : operation.deltaSegments) {
|
||||||
|
Logger::info(
|
||||||
|
"Flushing delta segment 0x" + StringService::toHex(deltaSegment.addressRange.startAddress)
|
||||||
|
+ " -> 0x" + StringService::toHex(deltaSegment.addressRange.endAddress) + " - "
|
||||||
|
+ std::to_string(deltaSegment.buffer.size()) + " byte(s)"
|
||||||
|
);
|
||||||
|
this->target->writeMemory(
|
||||||
|
operation.addressSpaceDescriptor,
|
||||||
|
operation.memorySegmentDescriptor,
|
||||||
|
deltaSegment.addressRange.startAddress,
|
||||||
|
deltaSegment.buffer
|
||||||
|
);
|
||||||
|
|
||||||
|
segmentCache.insert(deltaSegment.addressRange.startAddress, deltaSegment.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TargetControllerComponent::abandonDeltaProgrammingSession(const DeltaProgramming::Session& session) {
|
||||||
|
for (const auto& [segmentId, eraseOperation] : session.eraseOperationsBySegmentId) {
|
||||||
|
this->eraseTargetMemory(eraseOperation.addressSpaceDescriptor, eraseOperation.memorySegmentDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [segmentId, writeOperation] : session.writeOperationsBySegmentId) {
|
||||||
|
for (const auto& region : writeOperation.regions) {
|
||||||
|
this->writeTargetMemory(
|
||||||
|
writeOperation.addressSpaceDescriptor,
|
||||||
|
writeOperation.memorySegmentDescriptor,
|
||||||
|
region.addressRange.startAddress,
|
||||||
|
region.buffer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TargetControllerComponent::onShutdownTargetControllerEvent(const Events::ShutdownTargetController&) {
|
void TargetControllerComponent::onShutdownTargetControllerEvent(const Events::ShutdownTargetController&) {
|
||||||
this->shutdown();
|
this->shutdown();
|
||||||
}
|
}
|
||||||
@@ -1105,6 +1313,31 @@ namespace TargetController
|
|||||||
throw Exception{"Invalid address range"};
|
throw Exception{"Invalid address range"};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this->targetState->mode == TargetMode::PROGRAMMING
|
||||||
|
&& this->deltaProgrammingSession.has_value()
|
||||||
|
&& this->target->isProgramMemory(
|
||||||
|
command.addressSpaceDescriptor,
|
||||||
|
command.memorySegmentDescriptor,
|
||||||
|
command.startAddress,
|
||||||
|
static_cast<TargetMemorySize>(command.buffer.size())
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Logger::debug(
|
||||||
|
"Pushing program memory write operation, at 0x" + Services::StringService::toHex(command.startAddress)
|
||||||
|
+ ", " + std::to_string(command.buffer.size()) + " byte(s), to active delta programming session"
|
||||||
|
);
|
||||||
|
|
||||||
|
this->deltaProgrammingSession->pushWriteOperation(
|
||||||
|
command.addressSpaceDescriptor,
|
||||||
|
command.memorySegmentDescriptor,
|
||||||
|
command.startAddress,
|
||||||
|
std::move(command.buffer)
|
||||||
|
);
|
||||||
|
|
||||||
|
return std::make_unique<Response>();
|
||||||
|
}
|
||||||
|
|
||||||
this->writeTargetMemory(
|
this->writeTargetMemory(
|
||||||
command.addressSpaceDescriptor,
|
command.addressSpaceDescriptor,
|
||||||
command.memorySegmentDescriptor,
|
command.memorySegmentDescriptor,
|
||||||
@@ -1116,6 +1349,26 @@ namespace TargetController
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Response> TargetControllerComponent::handleEraseTargetMemory(EraseTargetMemory& command) {
|
std::unique_ptr<Response> TargetControllerComponent::handleEraseTargetMemory(EraseTargetMemory& command) {
|
||||||
|
if (
|
||||||
|
this->targetState->mode == TargetMode::PROGRAMMING
|
||||||
|
&& this->deltaProgrammingSession.has_value()
|
||||||
|
&& this->target->isProgramMemory(
|
||||||
|
command.addressSpaceDescriptor,
|
||||||
|
command.memorySegmentDescriptor,
|
||||||
|
command.memorySegmentDescriptor.addressRange.startAddress,
|
||||||
|
command.memorySegmentDescriptor.addressRange.size()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Logger::debug("Pushing program memory erase operation to active delta programming session");
|
||||||
|
|
||||||
|
this->deltaProgrammingSession->pushEraseOperation(
|
||||||
|
command.addressSpaceDescriptor,
|
||||||
|
command.memorySegmentDescriptor
|
||||||
|
);
|
||||||
|
|
||||||
|
return std::make_unique<Response>();
|
||||||
|
}
|
||||||
|
|
||||||
this->eraseTargetMemory(command.addressSpaceDescriptor, command.memorySegmentDescriptor);
|
this->eraseTargetMemory(command.addressSpaceDescriptor, command.memorySegmentDescriptor);
|
||||||
return std::make_unique<Response>();
|
return std::make_unique<Response>();
|
||||||
}
|
}
|
||||||
@@ -1128,15 +1381,32 @@ namespace TargetController
|
|||||||
std::unique_ptr<ProgramBreakpoint> TargetControllerComponent::handleSetProgramBreakpointBreakpointAnyType(
|
std::unique_ptr<ProgramBreakpoint> TargetControllerComponent::handleSetProgramBreakpointBreakpointAnyType(
|
||||||
SetProgramBreakpointAnyType& command
|
SetProgramBreakpointAnyType& command
|
||||||
) {
|
) {
|
||||||
const auto breakpoint = TargetProgramBreakpoint{
|
if (command.size > TargetProgramBreakpoint::MAX_SIZE) {
|
||||||
|
throw Exception{"Invalid breakpoint size"};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto breakpoint = TargetProgramBreakpoint{
|
||||||
.addressSpaceDescriptor = command.addressSpaceDescriptor,
|
.addressSpaceDescriptor = command.addressSpaceDescriptor,
|
||||||
.memorySegmentDescriptor = command.memorySegmentDescriptor,
|
.memorySegmentDescriptor = command.memorySegmentDescriptor,
|
||||||
.address = command.address,
|
.address = command.address,
|
||||||
.size = command.size,
|
.size = command.size,
|
||||||
.type = this->environmentConfig.targetConfig.hardwareBreakpoints && this->availableHardwareBreakpoints() > 0
|
.type = this->environmentConfig.targetConfig.hardwareBreakpoints && this->availableHardwareBreakpoints() > 0
|
||||||
? TargetProgramBreakpoint::Type::HARDWARE
|
? TargetProgramBreakpoint::Type::HARDWARE
|
||||||
: TargetProgramBreakpoint::Type::SOFTWARE
|
: TargetProgramBreakpoint::Type::SOFTWARE,
|
||||||
|
.originalData = {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const auto originalData = this->readTargetMemory(
|
||||||
|
command.addressSpaceDescriptor,
|
||||||
|
command.memorySegmentDescriptor,
|
||||||
|
command.address,
|
||||||
|
command.size,
|
||||||
|
{},
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
std::copy(originalData.begin(), originalData.end(), breakpoint.originalData.begin());
|
||||||
|
|
||||||
this->setProgramBreakpoint(breakpoint);
|
this->setProgramBreakpoint(breakpoint);
|
||||||
return std::make_unique<ProgramBreakpoint>(breakpoint);
|
return std::make_unique<ProgramBreakpoint>(breakpoint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@
|
|||||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||||
#include "src/Targets/ProgramBreakpointRegistry.hpp"
|
#include "src/Targets/ProgramBreakpointRegistry.hpp"
|
||||||
#include "src/Targets/TargetMemoryCache.hpp"
|
#include "src/Targets/TargetMemoryCache.hpp"
|
||||||
|
#include "src/Targets/DeltaProgramming/DeltaProgrammingInterface.hpp"
|
||||||
|
#include "src/Targets/DeltaProgramming/Session.hpp"
|
||||||
|
|
||||||
#include "src/EventManager/EventManager.hpp"
|
#include "src/EventManager/EventManager.hpp"
|
||||||
#include "src/EventManager/EventListener.hpp"
|
#include "src/EventManager/EventListener.hpp"
|
||||||
@@ -142,6 +144,8 @@ namespace TargetController
|
|||||||
std::unique_ptr<DebugTool> debugTool = nullptr;
|
std::unique_ptr<DebugTool> debugTool = nullptr;
|
||||||
std::unique_ptr<Targets::Target> target = nullptr;
|
std::unique_ptr<Targets::Target> target = nullptr;
|
||||||
|
|
||||||
|
Targets::DeltaProgramming::DeltaProgrammingInterface* deltaProgrammingInterface = nullptr;
|
||||||
|
|
||||||
std::map<
|
std::map<
|
||||||
Commands::CommandType,
|
Commands::CommandType,
|
||||||
std::function<std::unique_ptr<Responses::Response>(Commands::Command&)>
|
std::function<std::unique_ptr<Responses::Response>(Commands::Command&)>
|
||||||
@@ -170,6 +174,11 @@ namespace TargetController
|
|||||||
*/
|
*/
|
||||||
std::map<Targets::TargetMemorySegmentId, Targets::TargetMemoryCache> programMemoryCachesBySegmentId;
|
std::map<Targets::TargetMemorySegmentId, Targets::TargetMemoryCache> programMemoryCachesBySegmentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active delta programming session
|
||||||
|
*/
|
||||||
|
std::optional<Targets::DeltaProgramming::Session> deltaProgrammingSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a handler function for a particular command type.
|
* Registers a handler function for a particular command type.
|
||||||
* Only one handler function can be registered per command type.
|
* Only one handler function can be registered per command type.
|
||||||
@@ -329,6 +338,9 @@ namespace TargetController
|
|||||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void commitDeltaProgrammingSession(const Targets::DeltaProgramming::Session& session);
|
||||||
|
void abandonDeltaProgrammingSession(const Targets::DeltaProgramming::Session& session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes a shutdown.
|
* Invokes a shutdown.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/TargetMemoryCache.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/TargetMemoryCache.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/TargetPhysicalInterface.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/TargetPhysicalInterface.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/DynamicRegisterValue.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/DynamicRegisterValue.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/DeltaProgramming/Session.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/TargetDescription/TargetDescriptionFile.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/TargetDescription/TargetDescriptionFile.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Avr8/Avr8.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Avr8/Avr8.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Avr8/Avr8TargetConfig.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Avr8/Avr8TargetConfig.cpp
|
||||||
|
|||||||
36
src/Targets/DeltaProgramming/DeltaProgrammingInterface.hpp
Normal file
36
src/Targets/DeltaProgramming/DeltaProgrammingInterface.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Session.hpp"
|
||||||
|
|
||||||
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||||
|
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||||
|
|
||||||
|
namespace Targets::DeltaProgramming
|
||||||
|
{
|
||||||
|
class DeltaProgrammingInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeltaProgrammingInterface() = default;
|
||||||
|
virtual ~DeltaProgrammingInterface() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TargetController can align delta segments to a given block size, using the program memory cache.
|
||||||
|
* This can significantly reduce the number of read operations that would take place for page alignment,
|
||||||
|
* improving programming speed.
|
||||||
|
*
|
||||||
|
* This member function should return the necessary block size for the given memory segment. If alignment is
|
||||||
|
* not required, it should return a value of 1.
|
||||||
|
*/
|
||||||
|
virtual TargetMemorySize deltaBlockSize(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
virtual bool shouldAbandonSession(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
const std::vector<Session::WriteOperation::Region>& deltaSegments
|
||||||
|
) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
198
src/Targets/DeltaProgramming/Session.cpp
Normal file
198
src/Targets/DeltaProgramming/Session.cpp
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
#include "Session.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "src/Services/AlignmentService.hpp"
|
||||||
|
|
||||||
|
namespace Targets::DeltaProgramming
|
||||||
|
{
|
||||||
|
using Targets::TargetAddressSpaceDescriptor;
|
||||||
|
using Targets::TargetMemorySegmentDescriptor;
|
||||||
|
using Targets::TargetMemoryAddress;
|
||||||
|
using Targets::TargetMemoryAddressRange;
|
||||||
|
using Targets::TargetMemoryBuffer;
|
||||||
|
|
||||||
|
void Session::WriteOperation::Region::mergeWith(const Region& other) {
|
||||||
|
assert(this->addressRange.intersectsWith(other.addressRange));
|
||||||
|
assert(this->addressRange.startAddress <= other.addressRange.startAddress);
|
||||||
|
|
||||||
|
const auto intersectingSize = other.addressRange.intersectingSize(this->addressRange);
|
||||||
|
if (intersectingSize < addressRange.size()) {
|
||||||
|
this->buffer.resize(this->buffer.size() + other.buffer.size() - intersectingSize);
|
||||||
|
this->addressRange.endAddress = static_cast<Targets::TargetMemoryAddress>(
|
||||||
|
this->addressRange.startAddress + this->buffer.size() - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy(
|
||||||
|
other.buffer.begin(),
|
||||||
|
other.buffer.end(),
|
||||||
|
this->buffer.begin() + (other.addressRange.startAddress - this->addressRange.startAddress)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Session::WriteOperation::Region> Session::WriteOperation::deltaSegments(
|
||||||
|
Targets::TargetMemoryBufferSpan cacheData,
|
||||||
|
Targets::TargetMemorySize blockSize
|
||||||
|
) const {
|
||||||
|
using Services::AlignmentService;
|
||||||
|
|
||||||
|
// First, we merge any overlapping regions and sort all regions by start address. This simplifies things
|
||||||
|
auto mergedRegions = std::vector<Region>{};
|
||||||
|
|
||||||
|
for (const auto& region : this->regions) {
|
||||||
|
for (auto& existingRegion : mergedRegions) {
|
||||||
|
if (!existingRegion.addressRange.intersectsWith(region.addressRange)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.addressRange.startAddress >= existingRegion.addressRange.startAddress) {
|
||||||
|
existingRegion.mergeWith(region);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
auto regionClone = region;
|
||||||
|
regionClone.mergeWith(existingRegion);
|
||||||
|
std::swap(existingRegion, regionClone);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto CONTINUE_OUTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedRegions.emplace_back(region);
|
||||||
|
|
||||||
|
CONTINUE_OUTER:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(
|
||||||
|
mergedRegions.begin(),
|
||||||
|
mergedRegions.end(),
|
||||||
|
[] (const Region& regionA, const Region& regionB) {
|
||||||
|
return regionA.addressRange.startAddress < regionB.addressRange.startAddress;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
auto output = std::vector<Region>{};
|
||||||
|
auto deltaSegment = std::optional<Region>{};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given that delta segments must be aligned to a given block size, not all bytes in a delta segment will
|
||||||
|
* differ from the corresponding bytes in the cached data.
|
||||||
|
*
|
||||||
|
* We enforce alignment of all delta segments from the point of creation, and maintain alignment during any
|
||||||
|
* subsequent changes. This simplifies things.
|
||||||
|
*
|
||||||
|
* We use the cached data to align the segments. Initially, we populate the aligned segments with the cached
|
||||||
|
* data, and then gradually overwrite the bytes that differ.
|
||||||
|
*/
|
||||||
|
for (const auto& region : mergedRegions) {
|
||||||
|
for (auto i = std::size_t{0}; i < region.buffer.size(); ++i) {
|
||||||
|
const auto address = static_cast<TargetMemoryAddress>(region.addressRange.startAddress + i);
|
||||||
|
const auto cacheIndex = static_cast<std::size_t>(
|
||||||
|
address - this->memorySegmentDescriptor.addressRange.startAddress
|
||||||
|
);
|
||||||
|
const auto regionByte = region.buffer[i];
|
||||||
|
const auto cachedByte = cacheData[cacheIndex];
|
||||||
|
|
||||||
|
if (regionByte == cachedByte) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaSegment.has_value() && address > (deltaSegment->addressRange.endAddress + blockSize)) {
|
||||||
|
/*
|
||||||
|
* This region byte is not within the boundary of the current delta segment, or in the neighbouring
|
||||||
|
* block, so commit and create a new one.
|
||||||
|
*/
|
||||||
|
output.emplace_back(std::move(*deltaSegment));
|
||||||
|
deltaSegment = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!deltaSegment.has_value()) {
|
||||||
|
const auto alignedAddress = AlignmentService::alignMemoryAddress(address, blockSize);
|
||||||
|
const auto alignedSize = AlignmentService::alignMemorySize((address - alignedAddress) + 1, blockSize);
|
||||||
|
|
||||||
|
const auto cacheOffset = cacheData.begin() + static_cast<long>(
|
||||||
|
alignedAddress - this->memorySegmentDescriptor.addressRange.startAddress
|
||||||
|
);
|
||||||
|
deltaSegment = Region{
|
||||||
|
.addressRange = TargetMemoryAddressRange{alignedAddress, alignedAddress + alignedSize - 1},
|
||||||
|
.buffer = {cacheOffset, cacheOffset + alignedSize}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address > deltaSegment->addressRange.endAddress) {
|
||||||
|
/*
|
||||||
|
* This region byte is in the neighbouring block of the current delta segment.
|
||||||
|
*
|
||||||
|
* Instead of committing the segment and creating a new one, we just extend it by another block,
|
||||||
|
* to accommodate the byte. This reduces the number of segments (and therefore, the number of
|
||||||
|
* memory writes).
|
||||||
|
*/
|
||||||
|
const auto cacheOffset = cacheData.begin() + static_cast<long>(
|
||||||
|
(deltaSegment->addressRange.endAddress
|
||||||
|
- this->memorySegmentDescriptor.addressRange.startAddress) + 1
|
||||||
|
);
|
||||||
|
deltaSegment->buffer.insert(deltaSegment->buffer.end(), cacheOffset, cacheOffset + blockSize);
|
||||||
|
deltaSegment->addressRange.endAddress += blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
deltaSegment->buffer[address - deltaSegment->addressRange.startAddress] = regionByte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deltaSegment.has_value()) {
|
||||||
|
output.emplace_back(std::move(*deltaSegment));
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::pushEraseOperation(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
) {
|
||||||
|
if (this->eraseOperationsBySegmentId.contains(memorySegmentDescriptor.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->eraseOperationsBySegmentId.emplace(
|
||||||
|
memorySegmentDescriptor.id,
|
||||||
|
EraseOperation{
|
||||||
|
.addressSpaceDescriptor = addressSpaceDescriptor,
|
||||||
|
.memorySegmentDescriptor = memorySegmentDescriptor,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Session::pushWriteOperation(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
TargetMemoryAddress startAddress,
|
||||||
|
Targets::TargetMemoryBuffer&& buffer
|
||||||
|
) {
|
||||||
|
assert(!buffer.empty());
|
||||||
|
|
||||||
|
auto operationIt = this->writeOperationsBySegmentId.find(memorySegmentDescriptor.id);
|
||||||
|
if (operationIt == this->writeOperationsBySegmentId.end()) {
|
||||||
|
operationIt = this->writeOperationsBySegmentId.emplace(
|
||||||
|
memorySegmentDescriptor.id,
|
||||||
|
WriteOperation{
|
||||||
|
.addressSpaceDescriptor = addressSpaceDescriptor,
|
||||||
|
.memorySegmentDescriptor = memorySegmentDescriptor,
|
||||||
|
.regions = {},
|
||||||
|
}
|
||||||
|
).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
operationIt->second.regions.emplace_back(
|
||||||
|
WriteOperation::Region{
|
||||||
|
.addressRange = TargetMemoryAddressRange{
|
||||||
|
startAddress,
|
||||||
|
static_cast<TargetMemoryAddress>(startAddress + buffer.size() - 1)
|
||||||
|
},
|
||||||
|
.buffer = std::move(buffer)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/Targets/DeltaProgramming/Session.hpp
Normal file
57
src/Targets/DeltaProgramming/Session.hpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||||
|
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||||
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
#include "src/Targets/TargetMemoryAddressRange.hpp"
|
||||||
|
|
||||||
|
namespace Targets::DeltaProgramming
|
||||||
|
{
|
||||||
|
struct Session
|
||||||
|
{
|
||||||
|
struct EraseOperation
|
||||||
|
{
|
||||||
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WriteOperation
|
||||||
|
{
|
||||||
|
struct Region
|
||||||
|
{
|
||||||
|
Targets::TargetMemoryAddressRange addressRange;
|
||||||
|
Targets::TargetMemoryBuffer buffer;
|
||||||
|
|
||||||
|
void mergeWith(const Region& other);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor;
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
||||||
|
std::vector<Region> regions;
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<Region> deltaSegments(
|
||||||
|
Targets::TargetMemoryBufferSpan cacheData,
|
||||||
|
Targets::TargetMemorySize blockSize
|
||||||
|
) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<Targets::TargetMemorySegmentId, EraseOperation> eraseOperationsBySegmentId;
|
||||||
|
std::unordered_map<Targets::TargetMemorySegmentId, WriteOperation> writeOperationsBySegmentId;
|
||||||
|
|
||||||
|
void pushEraseOperation(
|
||||||
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
);
|
||||||
|
|
||||||
|
void pushWriteOperation(
|
||||||
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
Targets::TargetMemoryAddress startAddress,
|
||||||
|
Targets::TargetMemoryBuffer&& buffer
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -656,6 +656,7 @@ namespace Targets::Microchip::Avr8
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->avr8DebugInterface->clearAllBreakpoints();
|
||||||
this->avr8DebugInterface->enableProgrammingMode();
|
this->avr8DebugInterface->enableProgrammingMode();
|
||||||
this->activeProgrammingSession = ProgrammingSession();
|
this->activeProgrammingSession = ProgrammingSession();
|
||||||
}
|
}
|
||||||
@@ -687,6 +688,32 @@ namespace Targets::Microchip::Avr8
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeltaProgramming::DeltaProgrammingInterface* Avr8::deltaProgrammingInterface() {
|
||||||
|
if (
|
||||||
|
this->targetConfig.physicalInterface == TargetPhysicalInterface::DEBUG_WIRE
|
||||||
|
|| this->targetConfig.physicalInterface == TargetPhysicalInterface::UPDI
|
||||||
|
) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetMemorySize Avr8::deltaBlockSize(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
) {
|
||||||
|
return memorySegmentDescriptor.pageSize.value_or(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Avr8::shouldAbandonSession(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
const std::vector<DeltaProgramming::Session::WriteOperation::Region>& deltaSegments
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<TargetPadId, GpioPadDescriptor> Avr8::generateGpioPadDescriptorMapping(
|
std::map<TargetPadId, GpioPadDescriptor> Avr8::generateGpioPadDescriptorMapping(
|
||||||
const std::vector<TargetPeripheralDescriptor>& portPeripheralDescriptors
|
const std::vector<TargetPeripheralDescriptor>& portPeripheralDescriptors
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "src/Targets/TargetBitFieldDescriptor.hpp"
|
#include "src/Targets/TargetBitFieldDescriptor.hpp"
|
||||||
#include "src/Targets/TargetPadDescriptor.hpp"
|
#include "src/Targets/TargetPadDescriptor.hpp"
|
||||||
#include "src/Targets/TargetBreakpoint.hpp"
|
#include "src/Targets/TargetBreakpoint.hpp"
|
||||||
|
#include "src/Targets/DeltaProgramming/DeltaProgrammingInterface.hpp"
|
||||||
|
|
||||||
#include "TargetDescriptionFile.hpp"
|
#include "TargetDescriptionFile.hpp"
|
||||||
|
|
||||||
@@ -29,7 +30,9 @@
|
|||||||
|
|
||||||
namespace Targets::Microchip::Avr8
|
namespace Targets::Microchip::Avr8
|
||||||
{
|
{
|
||||||
class Avr8: public Target
|
class Avr8
|
||||||
|
: public Target
|
||||||
|
, public DeltaProgramming::DeltaProgrammingInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Avr8(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
explicit Avr8(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
||||||
@@ -111,6 +114,17 @@ namespace Targets::Microchip::Avr8
|
|||||||
std::string passthroughCommandHelpText() override;
|
std::string passthroughCommandHelpText() override;
|
||||||
std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) override;
|
std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) override;
|
||||||
|
|
||||||
|
DeltaProgramming::DeltaProgrammingInterface* deltaProgrammingInterface() override;
|
||||||
|
TargetMemorySize deltaBlockSize(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
) override;
|
||||||
|
bool shouldAbandonSession(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
const std::vector<DeltaProgramming::Session::WriteOperation::Region>& deltaSegments
|
||||||
|
) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* targetPowerManagementInterface = nullptr;
|
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* targetPowerManagementInterface = nullptr;
|
||||||
DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* avr8DebugInterface = nullptr;
|
DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* avr8DebugInterface = nullptr;
|
||||||
|
|||||||
@@ -95,6 +95,14 @@ namespace Targets
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decltype(ProgramBreakpointRegistryGeneric::mapping)::iterator begin() noexcept {
|
||||||
|
return this->mapping.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(ProgramBreakpointRegistryGeneric::mapping)::iterator end() noexcept {
|
||||||
|
return this->mapping.end();
|
||||||
|
}
|
||||||
|
|
||||||
decltype(ProgramBreakpointRegistryGeneric::mapping)::const_iterator begin() const noexcept {
|
decltype(ProgramBreakpointRegistryGeneric::mapping)::const_iterator begin() const noexcept {
|
||||||
return this->mapping.begin();
|
return this->mapping.begin();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "src/Targets/TargetBreakpoint.hpp"
|
|
||||||
#include "Opcodes/Opcode.hpp"
|
|
||||||
|
|
||||||
namespace Targets::RiscV
|
|
||||||
{
|
|
||||||
struct ProgramBreakpoint: TargetProgramBreakpoint
|
|
||||||
{
|
|
||||||
std::optional<Opcodes::Opcode> originalInstruction = std::nullopt;
|
|
||||||
|
|
||||||
explicit ProgramBreakpoint(const TargetProgramBreakpoint& breakpoint)
|
|
||||||
: TargetProgramBreakpoint(breakpoint)
|
|
||||||
{}
|
|
||||||
|
|
||||||
explicit ProgramBreakpoint(const TargetProgramBreakpoint& breakpoint, Opcodes::Opcode originalInstruction)
|
|
||||||
: TargetProgramBreakpoint(breakpoint)
|
|
||||||
, originalInstruction(originalInstruction)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -207,7 +208,8 @@ namespace Targets::RiscV::Wch
|
|||||||
.memorySegmentDescriptor = this->selectedProgramSegmentDescriptor,
|
.memorySegmentDescriptor = this->selectedProgramSegmentDescriptor,
|
||||||
.address = this->deAliasMappedAddress(breakpoint.address, this->selectedProgramSegmentDescriptor),
|
.address = this->deAliasMappedAddress(breakpoint.address, this->selectedProgramSegmentDescriptor),
|
||||||
.size = breakpoint.size,
|
.size = breakpoint.size,
|
||||||
.type = breakpoint.type
|
.type = breakpoint.type,
|
||||||
|
.originalData = breakpoint.originalData
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -233,7 +235,8 @@ namespace Targets::RiscV::Wch
|
|||||||
.memorySegmentDescriptor = this->selectedProgramSegmentDescriptor,
|
.memorySegmentDescriptor = this->selectedProgramSegmentDescriptor,
|
||||||
.address = this->deAliasMappedAddress(breakpoint.address, this->selectedProgramSegmentDescriptor),
|
.address = this->deAliasMappedAddress(breakpoint.address, this->selectedProgramSegmentDescriptor),
|
||||||
.size = breakpoint.size,
|
.size = breakpoint.size,
|
||||||
.type = breakpoint.type
|
.type = breakpoint.type,
|
||||||
|
.originalData = breakpoint.originalData
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -613,6 +616,48 @@ namespace Targets::RiscV::Wch
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeltaProgramming::DeltaProgrammingInterface* WchRiscV::deltaProgrammingInterface() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetMemorySize WchRiscV::deltaBlockSize(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
) {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WchRiscV::shouldAbandonSession(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
const std::vector<DeltaProgramming::Session::WriteOperation::Region>& deltaSegments
|
||||||
|
) {
|
||||||
|
/*
|
||||||
|
* Delta programming isn't always faster on WCH RISC-V targets with WCH-Link debug tools.
|
||||||
|
*
|
||||||
|
* This is because the debug tool can write to program memory with two methods - one better suited for small
|
||||||
|
* operations and the other for larger ones. If there are many small delta segments, we'd end up using the
|
||||||
|
* slower method many times, resulting in a negative impact on programming speed.
|
||||||
|
*
|
||||||
|
* For this reason, we abandon delta sessions if they consist of too many delta segments.
|
||||||
|
*
|
||||||
|
* See WchLinkDebugInterface::writeMemory() for more.
|
||||||
|
*
|
||||||
|
* TODO: Consider moving this to the WCH-Link driver, seeing as it's specific to that debug tool. In fact, I
|
||||||
|
* think the entire implementation of the DeltaProgrammingInterface should reside in the tool driver, as
|
||||||
|
* opposed to the target driver. Something to look at after v2.0.0.
|
||||||
|
*/
|
||||||
|
return
|
||||||
|
deltaSegments.size() > 5
|
||||||
|
|| std::count_if(
|
||||||
|
deltaSegments.begin(),
|
||||||
|
deltaSegments.end(),
|
||||||
|
[] (const DeltaProgramming::Session::WriteOperation::Region& segment) {
|
||||||
|
return segment.buffer.size() > 192;
|
||||||
|
}
|
||||||
|
) > 2;
|
||||||
|
}
|
||||||
|
|
||||||
const TargetMemorySegmentDescriptor& WchRiscV::resolveAliasedMemorySegment() {
|
const TargetMemorySegmentDescriptor& WchRiscV::resolveAliasedMemorySegment() {
|
||||||
/*
|
/*
|
||||||
* To determine the aliased segment, we probe the boundary of the boot segment via the mapped segment.
|
* To determine the aliased segment, we probe the boundary of the boot segment via the mapped segment.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "src/Targets/RiscV/RiscV.hpp"
|
#include "src/Targets/RiscV/RiscV.hpp"
|
||||||
#include "src/Targets/TargetPeripheralDescriptor.hpp"
|
#include "src/Targets/TargetPeripheralDescriptor.hpp"
|
||||||
#include "src/Targets/TargetPadDescriptor.hpp"
|
#include "src/Targets/TargetPadDescriptor.hpp"
|
||||||
|
#include "src/Targets/DeltaProgramming/DeltaProgrammingInterface.hpp"
|
||||||
|
|
||||||
#include "WchRiscVTargetConfig.hpp"
|
#include "WchRiscVTargetConfig.hpp"
|
||||||
#include "TargetDescriptionFile.hpp"
|
#include "TargetDescriptionFile.hpp"
|
||||||
@@ -21,7 +22,7 @@ namespace Targets::RiscV::Wch
|
|||||||
{
|
{
|
||||||
class WchRiscV
|
class WchRiscV
|
||||||
: public ::Targets::RiscV::RiscV
|
: public ::Targets::RiscV::RiscV
|
||||||
, public DeltaProgrammingInterface
|
, public DeltaProgramming::DeltaProgrammingInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WchRiscV(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
WchRiscV(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
||||||
@@ -59,6 +60,17 @@ namespace Targets::RiscV::Wch
|
|||||||
std::string passthroughCommandHelpText() override;
|
std::string passthroughCommandHelpText() override;
|
||||||
std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) override;
|
std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) override;
|
||||||
|
|
||||||
|
DeltaProgramming::DeltaProgrammingInterface* deltaProgrammingInterface() override;
|
||||||
|
TargetMemorySize deltaBlockSize(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
|
) override;
|
||||||
|
bool shouldAbandonSession(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
const std::vector<DeltaProgramming::Session::WriteOperation::Region>& deltaSegments
|
||||||
|
) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WchRiscVTargetConfig targetConfig;
|
WchRiscVTargetConfig targetConfig;
|
||||||
TargetDescriptionFile targetDescriptionFile;
|
TargetDescriptionFile targetDescriptionFile;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "PassthroughCommand.hpp"
|
#include "PassthroughCommand.hpp"
|
||||||
#include "PassthroughResponse.hpp"
|
#include "PassthroughResponse.hpp"
|
||||||
|
|
||||||
|
#include "DeltaProgramming/DeltaProgrammingInterface.hpp"
|
||||||
|
|
||||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||||
|
|
||||||
namespace Targets
|
namespace Targets
|
||||||
@@ -152,11 +154,19 @@ namespace Targets
|
|||||||
virtual TargetGpioPadDescriptorAndStatePairs getGpioPadStates(const TargetPadDescriptors& padDescriptors) = 0;
|
virtual TargetGpioPadDescriptorAndStatePairs getGpioPadStates(const TargetPadDescriptors& padDescriptors) = 0;
|
||||||
virtual void setGpioPadState(const TargetPadDescriptor& padDescriptor, const TargetGpioPadState& state) = 0;
|
virtual void setGpioPadState(const TargetPadDescriptor& padDescriptor, const TargetGpioPadState& state) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When enabling programming mode, the target driver is expected to clear all program breakpoints currently
|
||||||
|
* installed on the target.
|
||||||
|
*
|
||||||
|
* Before disabling program mode, the target controller will reinstall the necessary breakpoints.
|
||||||
|
*/
|
||||||
virtual void enableProgrammingMode() = 0;
|
virtual void enableProgrammingMode() = 0;
|
||||||
virtual void disableProgrammingMode() = 0;
|
virtual void disableProgrammingMode() = 0;
|
||||||
virtual bool programmingModeEnabled() = 0;
|
virtual bool programmingModeEnabled() = 0;
|
||||||
|
|
||||||
virtual std::string passthroughCommandHelpText() = 0;
|
virtual std::string passthroughCommandHelpText() = 0;
|
||||||
virtual std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) = 0;
|
virtual std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) = 0;
|
||||||
|
|
||||||
|
virtual DeltaProgramming::DeltaProgrammingInterface* deltaProgrammingInterface() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <array>
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "TargetMemory.hpp"
|
#include "TargetMemory.hpp"
|
||||||
#include "TargetAddressSpaceDescriptor.hpp"
|
#include "TargetAddressSpaceDescriptor.hpp"
|
||||||
@@ -18,6 +17,8 @@ namespace Targets
|
|||||||
|
|
||||||
struct TargetProgramBreakpoint
|
struct TargetProgramBreakpoint
|
||||||
{
|
{
|
||||||
|
static constexpr auto MAX_SIZE = TargetMemorySize{4};
|
||||||
|
|
||||||
enum class Type: std::uint8_t
|
enum class Type: std::uint8_t
|
||||||
{
|
{
|
||||||
HARDWARE,
|
HARDWARE,
|
||||||
@@ -29,6 +30,7 @@ namespace Targets
|
|||||||
TargetMemoryAddress address;
|
TargetMemoryAddress address;
|
||||||
TargetMemorySize size;
|
TargetMemorySize size;
|
||||||
Type type;
|
Type type;
|
||||||
|
std::array<unsigned char, TargetProgramBreakpoint::MAX_SIZE> originalData;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BreakpointResources
|
struct BreakpointResources
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "TargetMemoryCache.hpp"
|
#include "TargetMemoryCache.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "src/Exceptions/Exception.hpp"
|
#include "src/Exceptions/Exception.hpp"
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ namespace Targets
|
|||||||
, data(TargetMemoryBuffer(memorySegmentDescriptor.size(), 0x00))
|
, data(TargetMemoryBuffer(memorySegmentDescriptor.size(), 0x00))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
TargetMemoryBuffer TargetMemoryCache::fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const {
|
TargetMemoryBufferSpan TargetMemoryCache::fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const {
|
||||||
const auto startIndex = startAddress - this->memorySegmentDescriptor.addressRange.startAddress;
|
const auto startIndex = startAddress - this->memorySegmentDescriptor.addressRange.startAddress;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -21,7 +22,7 @@ namespace Targets
|
|||||||
throw Exceptions::Exception{"Invalid cache access"};
|
throw Exceptions::Exception{"Invalid cache access"};
|
||||||
}
|
}
|
||||||
|
|
||||||
return TargetMemoryBuffer{this->data.begin() + startIndex, this->data.begin() + startIndex + bytes};
|
return TargetMemoryBufferSpan{this->data.begin() + startIndex, this->data.begin() + startIndex + bytes};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TargetMemoryCache::contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const {
|
bool TargetMemoryCache::contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const {
|
||||||
@@ -34,12 +35,30 @@ namespace Targets
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TargetMemoryCache::insert(TargetMemoryAddress startAddress, TargetMemoryBufferSpan data) {
|
void TargetMemoryCache::insert(TargetMemoryAddress startAddress, TargetMemoryBufferSpan data) {
|
||||||
const auto startIndex = startAddress - this->memorySegmentDescriptor.addressRange.startAddress;
|
std::copy(
|
||||||
|
data.begin(),
|
||||||
|
data.end(),
|
||||||
|
this->data.begin() + (startAddress - this->memorySegmentDescriptor.addressRange.startAddress)
|
||||||
|
);
|
||||||
|
|
||||||
std::copy(data.begin(), data.end(), this->data.begin() + startIndex);
|
this->trackSegment(startAddress, static_cast<TargetMemoryAddress>(startAddress + data.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
const auto endAddress = static_cast<Targets::TargetMemoryAddress>(startAddress + data.size() - 1);
|
void TargetMemoryCache::fill(TargetMemoryAddress startAddress, TargetMemorySize size, unsigned char value) {
|
||||||
|
assert(this->data.size() >= (startAddress - this->memorySegmentDescriptor.addressRange.startAddress) + size);
|
||||||
|
|
||||||
|
for (auto i = std::size_t{0}; i < size; ++i) {
|
||||||
|
this->data[i + (startAddress - this->memorySegmentDescriptor.addressRange.startAddress)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->trackSegment(startAddress, static_cast<TargetMemoryAddress>(startAddress + size - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TargetMemoryCache::clear() {
|
||||||
|
this->populatedSegments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TargetMemoryCache::trackSegment(TargetMemoryAddress startAddress, TargetMemoryAddress endAddress) {
|
||||||
const auto intersectingStartSegmentIt = this->intersectingSegment(startAddress);
|
const auto intersectingStartSegmentIt = this->intersectingSegment(startAddress);
|
||||||
const auto intersectingEndSegmentIt = this->intersectingSegment(endAddress);
|
const auto intersectingEndSegmentIt = this->intersectingSegment(endAddress);
|
||||||
|
|
||||||
@@ -95,10 +114,6 @@ namespace Targets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TargetMemoryCache::clear() {
|
|
||||||
this->populatedSegments.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetMemoryCache::SegmentIt TargetMemoryCache::intersectingSegment(TargetMemoryAddress address) const {
|
TargetMemoryCache::SegmentIt TargetMemoryCache::intersectingSegment(TargetMemoryAddress address) const {
|
||||||
if (this->populatedSegments.empty()) {
|
if (this->populatedSegments.empty()) {
|
||||||
return this->populatedSegments.end();
|
return this->populatedSegments.end();
|
||||||
|
|||||||
@@ -11,47 +11,20 @@ namespace Targets
|
|||||||
class TargetMemoryCache
|
class TargetMemoryCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit TargetMemoryCache(const TargetMemorySegmentDescriptor& memorySegmentDescriptor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches data from the cache.
|
|
||||||
*
|
|
||||||
* TODO: Change return type to TargetMemoryBufferSpan
|
|
||||||
*
|
|
||||||
* @param startAddress
|
|
||||||
* @param bytes
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
[[nodiscard]] TargetMemoryBuffer fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the cache currently holds data within the given address range.
|
|
||||||
*
|
|
||||||
* @param startAddress
|
|
||||||
* @param bytes
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
[[nodiscard]] bool contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts data into the cache and performs any necessary bookkeeping.
|
|
||||||
*
|
|
||||||
* @param startAddress
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
void insert(TargetMemoryAddress startAddress, TargetMemoryBufferSpan data);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the cache.
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
|
||||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor;
|
||||||
TargetMemoryBuffer data;
|
TargetMemoryBuffer data;
|
||||||
|
|
||||||
|
explicit TargetMemoryCache(const TargetMemorySegmentDescriptor& memorySegmentDescriptor);
|
||||||
|
|
||||||
|
[[nodiscard]] TargetMemoryBufferSpan fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const;
|
||||||
|
[[nodiscard]] bool contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const;
|
||||||
|
|
||||||
|
void insert(TargetMemoryAddress startAddress, TargetMemoryBufferSpan data);
|
||||||
|
void fill(TargetMemoryAddress startAddress, TargetMemorySize size, unsigned char value);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
/**
|
/**
|
||||||
* A populated segment is just an address range in the cache that we know we've populated.
|
* A populated segment is just an address range in the cache that we know we've populated.
|
||||||
*
|
*
|
||||||
@@ -60,6 +33,14 @@ namespace Targets
|
|||||||
*/
|
*/
|
||||||
std::map<TargetMemoryAddress, TargetMemoryAddress> populatedSegments = {};
|
std::map<TargetMemoryAddress, TargetMemoryAddress> populatedSegments = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks a newly populated segment.
|
||||||
|
*
|
||||||
|
* @param startAddress
|
||||||
|
* @param endAddress
|
||||||
|
*/
|
||||||
|
void trackSegment(TargetMemoryAddress startAddress, TargetMemoryAddress endAddress);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the segment that intersects with the given address. Segments cannot overlap, so only one segment can
|
* Finds the segment that intersects with the given address. Segments cannot overlap, so only one segment can
|
||||||
* intersect with the given address, at any given time.
|
* intersect with the given address, at any given time.
|
||||||
|
|||||||
Reference in New Issue
Block a user