- Implemented program memory erasure routine in WchRiscV target driver

- Moved away from relying on WCH-Link debug tool command for erasing program memory, due to a bug that I couldn't fix
- Small tweaks to programming method selection in WCH-Link driver
- Refactored flash peripheral registers in WchRiscV target driver
This commit is contained in:
Nav
2025-01-29 23:48:32 +00:00
parent 55b8bf17fe
commit 7466850478
10 changed files with 166 additions and 33 deletions

View File

@@ -241,6 +241,12 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
this->sendCommandAndWaitForResponse(Commands::EndProgrammingSession{});
}
/*
* We don't actually use this anywhere, as the WCH-Link's erase function is buggy. For more, see the comment in
* the WchLinkDebugInterface::eraseMemory() member function.
*
* TODO: Consider removing this after v2.0.0
*/
void WchLinkInterface::eraseProgramMemory() {
this->sendCommandAndWaitForResponse(Commands::EraseProgramMemory{});
}

View File

@@ -234,13 +234,12 @@ namespace DebugToolDrivers::Wch
* the target. But the partial block write is faster and more suitable for writing buffers that are
* smaller than 64 bytes, such as when we're inserting software breakpoints.
*/
if (
buffer.size() <= WchLinkInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE
|| !this->fullBlockWriteCompatible(addressSpaceDescriptor, memorySegmentDescriptor, startAddress)
buffer.size() > (WchLinkInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE * 3)
&& this->fullBlockWriteCompatible(addressSpaceDescriptor, memorySegmentDescriptor, startAddress)
) {
Logger::debug("Using partial block write method");
return this->writeProgramMemoryPartialBlock(
Logger::debug("Using full block write method");
return this->writeProgramMemoryFullBlock(
addressSpaceDescriptor,
memorySegmentDescriptor,
startAddress,
@@ -248,8 +247,8 @@ namespace DebugToolDrivers::Wch
);
}
Logger::debug("Using full block write method");
return this->writeProgramMemoryFullBlock(
Logger::debug("Using partial block write method");
return this->writeProgramMemoryPartialBlock(
addressSpaceDescriptor,
memorySegmentDescriptor,
startAddress,
@@ -269,21 +268,40 @@ namespace DebugToolDrivers::Wch
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
) {
if (memorySegmentDescriptor == this->mainProgramSegmentDescriptor) {
return this->wchLinkInterface.eraseProgramMemory();
}
Logger::debug("Ignoring erase operation on `" + memorySegmentDescriptor.key + "` segment - not supported");
/*
* WCH-Link tools provide an erase function that erases the entire user section (main program segment).
* However, this function doesn't always work. Sometimes, it silently fails and leaves the target in a bad
* state, causing further issues (like program memory corruption).
*
* I spent a long time trying to fix this, but all attempts failed. I then decided to cease all use of this
* function and just implement the erase procedure myself, in the target driver.
*
* See the WchRiscV::eraseMainFlashSegment() member function for more.
*
* For future reference, my notes on this issue:
*
* - I discovered the issue with a WCH-LinkE, FW version 2.9, connected to a CH32V003. I'm not sure if other
* targets are affected
*
* - The issue occurs rarely - a very specific sequence of events must take place for the issue to occur:
* 1. Perform exactly one partial block write, to the main program segment, at address 0x08000100, with
* exactly 64 bytes of data
* 2. Reset the target
* 3. Erase the target via the erase function provided by the WCH-Link tool
* 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
*
* - 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
*
* - 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
* failed erase operation
*/
throw TargetOperationFailure{"Not supported"};
}
void WchLinkDebugInterface::enableProgrammingMode() {
/*
* Nothing to do here
*
* We cannot prepare the WCH-Link tool for a programming session here, as the preparation process differs
* across the two types of flash write commands (full and partial block write). We don't know which command
* we'll be utilising at this point.
*/
}
void WchLinkDebugInterface::disableProgrammingMode() {