Added support for writing to EEPROM via EDBG AVR8 Generic driver
This commit is contained in:
@@ -129,7 +129,16 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
EEPROM = 0x22,
|
EEPROM = 0x22,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The FLASH_PAGE memory type can be used to read &write full flash pages on the target.
|
* The EEPROM_ATOMIC memory type can be used to write to EEPROM memory with automatic pag erasing.
|
||||||
|
*
|
||||||
|
* It's only available for XMEGA and UPDI config variants.
|
||||||
|
*
|
||||||
|
* Only one EEPROM page can be written at a time.
|
||||||
|
*/
|
||||||
|
EEPROM_ATOMIC = 0xC4,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The FLASH_PAGE memory type can be used to read and write full flash pages on the target.
|
||||||
*
|
*
|
||||||
* Only available with the JTAG and debugWire config variants.
|
* Only available with the JTAG and debugWire config variants.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -734,7 +734,10 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetMemoryType::EEPROM: {
|
case TargetMemoryType::EEPROM: {
|
||||||
avr8MemoryType = Avr8MemoryType::EEPROM;
|
avr8MemoryType =
|
||||||
|
this->configVariant == Avr8ConfigVariant::XMEGA || this->configVariant == Avr8ConfigVariant::UPDI
|
||||||
|
? Avr8MemoryType::EEPROM_ATOMIC
|
||||||
|
: Avr8MemoryType::EEPROM;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
@@ -1239,6 +1242,10 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
throw DeviceInitializationFailure("Missing required parameter: SIGNATURE BASE ADDRESS");
|
throw DeviceInitializationFailure("Missing required parameter: SIGNATURE BASE ADDRESS");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this->targetParameters.eepromPageSize.has_value()) {
|
||||||
|
throw DeviceInitializationFailure("Missing required parameter: UPDI_EEPROM_PAGE_SIZE");
|
||||||
|
}
|
||||||
|
|
||||||
if (this->targetParameters.programMemoryUpdiStartAddress.has_value()) {
|
if (this->targetParameters.programMemoryUpdiStartAddress.has_value()) {
|
||||||
/*
|
/*
|
||||||
* The program memory base address field for UPDI sessions (DEVICE_UPDI_PROGMEM_BASE_ADDR) seems to be
|
* The program memory base address field for UPDI sessions (DEVICE_UPDI_PROGMEM_BASE_ADDR) seems to be
|
||||||
@@ -1537,6 +1544,30 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Targets::TargetMemorySize> EdbgAvr8Interface::maximumMemoryAccessSize(Avr8MemoryType memoryType) {
|
||||||
|
if (
|
||||||
|
memoryType == Avr8MemoryType::FLASH_PAGE
|
||||||
|
|| memoryType == Avr8MemoryType::APPL_FLASH
|
||||||
|
|| memoryType == Avr8MemoryType::BOOT_FLASH
|
||||||
|
|| (memoryType == Avr8MemoryType::SPM && this->configVariant == Avr8ConfigVariant::MEGAJTAG)
|
||||||
|
) {
|
||||||
|
// These flash memory types require single page access.
|
||||||
|
return this->targetParameters.flashPageSize.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memoryType == Avr8MemoryType::EEPROM_ATOMIC) {
|
||||||
|
// This EEPROM memory type requires single page access.
|
||||||
|
return this->targetParameters.eepromPageSize.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->maximumMemoryAccessSizePerRequest.has_value()) {
|
||||||
|
// There is a memory access size limit for this entire EdbgAvr8Interface instance
|
||||||
|
return this->maximumMemoryAccessSizePerRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
TargetMemoryBuffer EdbgAvr8Interface::readMemory(
|
TargetMemoryBuffer EdbgAvr8Interface::readMemory(
|
||||||
Avr8MemoryType type,
|
Avr8MemoryType type,
|
||||||
TargetMemoryAddress startAddress,
|
TargetMemoryAddress startAddress,
|
||||||
@@ -1611,48 +1642,15 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const auto maximumReadSize = this->maximumMemoryAccessSize(type);
|
||||||
type == Avr8MemoryType::FLASH_PAGE
|
if (maximumReadSize.has_value() && bytes > *maximumReadSize) {
|
||||||
|| type == Avr8MemoryType::APPL_FLASH
|
|
||||||
|| type == Avr8MemoryType::BOOT_FLASH
|
|
||||||
|| (type == Avr8MemoryType::SPM && this->configVariant == Avr8ConfigVariant::MEGAJTAG)
|
|
||||||
) {
|
|
||||||
// With the FLASH_PAGE, APPL_FLASH, BOOT_FLASH and SPM memory types, we can only read one page at a time.
|
|
||||||
const auto pageSize = this->targetParameters.flashPageSize.value();
|
|
||||||
|
|
||||||
if (bytes > pageSize) {
|
|
||||||
// bytes should always be a multiple of pageSize (given the code above)
|
|
||||||
assert(bytes % pageSize == 0);
|
|
||||||
int pagesRequired = static_cast<int>(bytes / pageSize);
|
|
||||||
auto memoryBuffer = Targets::TargetMemoryBuffer();
|
|
||||||
|
|
||||||
for (auto i = 0; i < pagesRequired; i++) {
|
|
||||||
auto pageBuffer = this->readMemory(
|
|
||||||
type,
|
|
||||||
startAddress + static_cast<TargetMemoryAddress>(pageSize * i),
|
|
||||||
pageSize
|
|
||||||
);
|
|
||||||
std::move(pageBuffer.begin(), pageBuffer.end(), std::back_inserter(memoryBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
return memoryBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enforce a maximum memory access request size.
|
|
||||||
*
|
|
||||||
* See the comment for EdbgAvr8Interface::setMaximumMemoryAccessSizePerRequest() for more on this.
|
|
||||||
*/
|
|
||||||
if (this->maximumMemoryAccessSizePerRequest.has_value() && bytes > this->maximumMemoryAccessSizePerRequest) {
|
|
||||||
auto maximumRequestSize = this->maximumMemoryAccessSizePerRequest.value();
|
|
||||||
auto totalReadsRequired = std::ceil(static_cast<float>(bytes) / static_cast<float>(maximumRequestSize));
|
|
||||||
auto output = Targets::TargetMemoryBuffer();
|
auto output = Targets::TargetMemoryBuffer();
|
||||||
output.reserve(bytes);
|
output.reserve(bytes);
|
||||||
|
|
||||||
for (float i = 1; i <= totalReadsRequired; i++) {
|
while (output.size() < bytes) {
|
||||||
const auto bytesToRead = static_cast<TargetMemorySize>(
|
const auto bytesToRead = std::min(
|
||||||
(bytes - output.size()) > maximumRequestSize ? maximumRequestSize : bytes - output.size()
|
static_cast<TargetMemorySize>(bytes - output.size()),
|
||||||
|
*maximumReadSize
|
||||||
);
|
);
|
||||||
|
|
||||||
auto data = this->readMemory(
|
auto data = this->readMemory(
|
||||||
@@ -1661,7 +1659,7 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
bytesToRead,
|
bytesToRead,
|
||||||
excludedAddresses
|
excludedAddresses
|
||||||
);
|
);
|
||||||
output.insert(output.end(), data.begin(), data.end());
|
std::move(data.begin(), data.end(), std::back_inserter(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
@@ -1754,33 +1752,29 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const auto maximumWriteSize = this->maximumMemoryAccessSize(type);
|
||||||
type == Avr8MemoryType::FLASH_PAGE
|
if (maximumWriteSize.has_value() && buffer.size() > *maximumWriteSize) {
|
||||||
|| type == Avr8MemoryType::APPL_FLASH
|
auto bytesWritten = TargetMemorySize(0);
|
||||||
|| type == Avr8MemoryType::BOOT_FLASH
|
|
||||||
) {
|
|
||||||
// With the FLASH_PAGE, APPL_FLASH and BOOT_FLASH memory types, we can only write one page at a time.
|
|
||||||
const auto pageSize = this->targetParameters.flashPageSize.value();
|
|
||||||
|
|
||||||
if (bytes > pageSize) {
|
while (bytesWritten < buffer.size()) {
|
||||||
assert(bytes % pageSize == 0);
|
const auto chunkSize = std::min(
|
||||||
int pagesRequired = static_cast<int>(bytes / pageSize);
|
static_cast<TargetMemorySize>(buffer.size() - bytesWritten),
|
||||||
|
*maximumWriteSize
|
||||||
|
);
|
||||||
|
|
||||||
for (auto i = 0; i < pagesRequired; i++) {
|
this->writeMemory(
|
||||||
const auto offset = static_cast<std::uint32_t>(pageSize * i);
|
type,
|
||||||
auto pageBuffer = TargetMemoryBuffer();
|
startAddress + bytesWritten,
|
||||||
pageBuffer.reserve(pageSize);
|
TargetMemoryBuffer(
|
||||||
std::move(
|
buffer.begin() + bytesWritten,
|
||||||
buffer.begin() + offset,
|
buffer.begin() + bytesWritten + chunkSize
|
||||||
buffer.begin() + offset + pageSize,
|
)
|
||||||
std::back_inserter(pageBuffer)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
this->writeMemory(type, startAddress + offset, pageBuffer);
|
bytesWritten += chunkSize;
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||||
|
|||||||
@@ -505,6 +505,16 @@ namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
|
|||||||
*/
|
*/
|
||||||
Targets::TargetMemorySize alignMemoryBytes(Avr8MemoryType memoryType, Targets::TargetMemorySize bytes);
|
Targets::TargetMemorySize alignMemoryBytes(Avr8MemoryType memoryType, Targets::TargetMemorySize bytes);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a maximum memory access size is imposed for a given Avr8MemoryType.
|
||||||
|
*
|
||||||
|
* @param memoryType
|
||||||
|
* The imposed maximum size, or std::nullopt if a maximum isn't required.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::optional<Targets::TargetMemorySize> maximumMemoryAccessSize(Avr8MemoryType memoryType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads memory on the target.
|
* Reads memory on the target.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -367,51 +367,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) {
|
void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) {
|
||||||
if (
|
if (memoryType == TargetMemoryType::FLASH) {
|
||||||
memoryType == TargetMemoryType::FLASH && this->programmingSession.has_value()
|
return this->writeFlashMemory(startAddress, buffer);
|
||||||
&& this->targetConfig->physicalInterface != PhysicalInterface::DEBUG_WIRE
|
|
||||||
) {
|
|
||||||
if (this->targetConfig->physicalInterface == PhysicalInterface::PDI) {
|
|
||||||
const auto startSection = this->getProgramMemorySectionFromAddress(startAddress);
|
|
||||||
const auto endSection = this->getProgramMemorySectionFromAddress(
|
|
||||||
static_cast<std::uint32_t>(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 programming");
|
|
||||||
this->avr8DebugInterface->eraseProgramMemory(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 programming");
|
|
||||||
this->avr8DebugInterface->eraseProgramMemory(ProgramMemorySection::BOOT);
|
|
||||||
this->programmingSession->bootSectionErased = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!this->programmingSession->chipErased) {
|
|
||||||
Logger::warning("Erasing entire chip, in preparation for programming");
|
|
||||||
this->avr8DebugInterface->eraseProgramMemory();
|
|
||||||
this->programmingSession->chipErased = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->avr8DebugInterface->writeMemory(memoryType, startAddress, buffer);
|
this->avr8DebugInterface->writeMemory(memoryType, startAddress, buffer);
|
||||||
@@ -774,6 +731,73 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
return this->id.value();
|
return this->id.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avr8::writeFlashMemory(TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer) {
|
||||||
|
if (!this->programmingSession.has_value()) {
|
||||||
|
throw Exception("Attempted FLASH memory write with no active programming session.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->targetConfig->physicalInterface == PhysicalInterface::PDI) {
|
||||||
|
/*
|
||||||
|
* For PDI targets, we can erase specific sections (APPLICATION and BOOTLOADER sections) of program memory.
|
||||||
|
*
|
||||||
|
* We'll only erase the section if we intend to write to it.
|
||||||
|
*/
|
||||||
|
const auto startSection = this->getProgramMemorySectionFromAddress(startAddress);
|
||||||
|
const auto endSection = this->getProgramMemorySectionFromAddress(
|
||||||
|
static_cast<std::uint32_t>(startAddress + buffer.size() - 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (startSection != endSection) {
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
* Get rid of this. Was placed here because I didn't have enough time to implement and test the
|
||||||
|
* writing to multiple sections in a single instance.
|
||||||
|
*/
|
||||||
|
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 programming");
|
||||||
|
this->avr8DebugInterface->eraseProgramMemory(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 programming");
|
||||||
|
this->avr8DebugInterface->eraseProgramMemory(ProgramMemorySection::BOOT);
|
||||||
|
this->programmingSession->bootSectionErased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->programmingSession->chipErased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debugWire targets do not need to be erased - this is done automatically when writing to FLASH.
|
||||||
|
if (
|
||||||
|
this->targetConfig->physicalInterface != PhysicalInterface::DEBUG_WIRE
|
||||||
|
&& !this->programmingSession->chipErased
|
||||||
|
) {
|
||||||
|
Logger::warning("Erasing entire chip, in preparation for programming");
|
||||||
|
this->avr8DebugInterface->eraseProgramMemory();
|
||||||
|
this->programmingSession->chipErased = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->avr8DebugInterface->writeMemory(TargetMemoryType::FLASH, startAddress, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void Avr8::updateDwenFuseBit(bool enable) {
|
void Avr8::updateDwenFuseBit(bool enable) {
|
||||||
if (this->avrIspInterface == nullptr) {
|
if (this->avrIspInterface == nullptr) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
|
|||||||
@@ -175,6 +175,14 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
|
|||||||
*/
|
*/
|
||||||
TargetSignature getId() override;
|
TargetSignature getId() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes to FLASH memory (with any necessary erasing).
|
||||||
|
*
|
||||||
|
* @param startAddress
|
||||||
|
* @param buffer
|
||||||
|
*/
|
||||||
|
void writeFlashMemory(TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the debugWire enable (DWEN) fuse bit on the AVR target.
|
* Updates the debugWire enable (DWEN) fuse bit on the AVR target.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user