diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp index e5101790..c8415947 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp @@ -720,6 +720,24 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr return this->writeMemory(avr8MemoryType, startAddress, buffer); } + void EdbgAvr8Interface::eraseProgramMemorySection(ProgramMemorySection section) { + if (this->configVariant != Avr8ConfigVariant::XMEGA) { + throw Exception("AVR8 erase command not supported for non-XMEGA config variants."); + } + + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + EraseMemory( + section == ProgramMemorySection::BOOT + ? Avr8EraseMemoryMode::BOOT_SECTION + : Avr8EraseMemoryMode::APPLICATION_SECTION + ) + ); + + if (response.getResponseId() == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure("AVR8 erase memory command failed", response); + } + } + TargetState EdbgAvr8Interface::getTargetState() { /* * We are not informed when a target goes from a stopped state to a running state, so there is no need @@ -745,13 +763,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr } this->programmingModeEnabled = true; - - if (this->configVariant == Avr8ConfigVariant::XMEGA) { - Logger::warning( - "The entire application section of program memory will be erased, in preparation for programming" - ); - this->eraseMemory(Avr8EraseMemoryMode::APPLICATION_SECTION); - } } void EdbgAvr8Interface::disableProgrammingMode() { @@ -1754,15 +1765,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr throw Avr8CommandFailure("AVR8 Write memory command failed", response); } - } - - void EdbgAvr8Interface::eraseMemory(Avr8EraseMemoryMode mode) { - auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( - EraseMemory(mode) - ); - - if (response.getResponseId() == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 erase memory command failed", response); } } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.hpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.hpp index 16c1865b..92e5fc52 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.hpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.hpp @@ -242,6 +242,13 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr const Targets::TargetMemoryBuffer& buffer ) override; + /** + * Issues the "Erase" command to erase a particular section of program memory. + * + * @param section + */ + void eraseProgramMemorySection(Targets::Microchip::Avr::Avr8Bit::ProgramMemorySection section) override; + /** * Returns the current state of the target. * @@ -536,13 +543,6 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr */ void writeMemory(Avr8MemoryType type, std::uint32_t address, const Targets::TargetMemoryBuffer& buffer); - /** - * Erases a particular region of memory, depending on the value of mode. - * - * @param mode - */ - void eraseMemory(Avr8EraseMemoryMode mode); - /** * Fetches the current target state. * diff --git a/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp index aae1124f..37ced9d2 100644 --- a/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp +++ b/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp @@ -8,6 +8,7 @@ #include "src/Targets/Microchip/AVR/TargetSignature.hpp" #include "src/Targets/Microchip/AVR/AVR8/Family.hpp" #include "src/Targets/Microchip/AVR/AVR8/PhysicalInterface.hpp" +#include "src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp" #include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp" #include "src/Targets/TargetState.hpp" @@ -194,6 +195,13 @@ namespace Bloom::DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8 const Targets::TargetMemoryBuffer& buffer ) = 0; + /** + * Should erase a particular program memory section. + * + * @param section + */ + virtual void eraseProgramMemorySection(Targets::Microchip::Avr::Avr8Bit::ProgramMemorySection section) = 0; + /** * Should obtain the current target state. * diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp index f3bd04ac..0aaaf2d5 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp @@ -323,6 +323,47 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit } void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) { + if ( + memoryType == TargetMemoryType::FLASH && this->programmingSession.has_value() + && this->targetConfig->physicalInterface == PhysicalInterface::PDI + ) { + // For PDI targets, we must erase the appropriate section before the first write. + const auto startSection = this->getProgramMemorySectionFromAddress(startAddress); + const auto endSection = this->getProgramMemorySectionFromAddress( + static_cast(startAddress + buffer.size() - 1) + ); + + if (startSection != endSection) { + throw Exception( + "Requested program memory write spans more than one section (APPLICATION and BOOT) - aborting." + ); + } + + if ( + !this->programmingSession->applicationSectionErased + && ( + startSection == ProgramMemorySection::APPLICATION + || endSection == ProgramMemorySection::APPLICATION + ) + ) { + Logger::warning("Erasing program memory APPLICATION section, in preparation for writing."); + this->avr8DebugInterface->eraseProgramMemorySection(ProgramMemorySection::APPLICATION); + this->programmingSession->applicationSectionErased = true; + } + + if ( + !this->programmingSession->bootSectionErased + && ( + startSection == ProgramMemorySection::BOOT + || endSection == ProgramMemorySection::BOOT + ) + ) { + Logger::warning("Erasing program memory BOOT section, in preparation for writing."); + this->avr8DebugInterface->eraseProgramMemorySection(ProgramMemorySection::BOOT); + this->programmingSession->bootSectionErased = true; + } + } + this->avr8DebugInterface->writeMemory(memoryType, startAddress, buffer); } @@ -919,4 +960,10 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit throw exception; } } + + ProgramMemorySection Avr8::getProgramMemorySectionFromAddress(std::uint32_t address) { + return address >= this->targetParameters->bootSectionStartAddress.value() + ? ProgramMemorySection::BOOT + : ProgramMemorySection::APPLICATION; + } } diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp index 70632a50..e818cc93 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp +++ b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp @@ -14,6 +14,7 @@ #include "TargetParameters.hpp" #include "PadDescriptor.hpp" #include "ProgrammingSession.hpp" +#include "ProgramMemorySection.hpp" #include "src/Targets/TargetRegister.hpp" #include "TargetDescription/TargetDescriptionFile.hpp" @@ -182,5 +183,13 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit * True to set the fuse, false to clear it. */ void writeDwenFuseBit(bool setFuse); + + /** + * Resolves the program memory section from a program memory address. + * + * @param address + * @return + */ + ProgramMemorySection getProgramMemorySectionFromAddress(std::uint32_t address); }; } diff --git a/src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp b/src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp new file mode 100644 index 00000000..b0037a64 --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Bloom::Targets::Microchip::Avr::Avr8Bit +{ + enum class ProgramMemorySection: std::uint8_t + { + APPLICATION, + BOOT, + }; +}