diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp index a0795121..890f2061 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp @@ -24,6 +24,11 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands std::uint8_t commandId; PayloadContainerType payload; + Command(std::uint8_t commandId, PayloadContainerType&& payload) + : commandId(commandId) + , payload(std::move(payload)) + {}; + explicit Command(std::uint8_t commandId) : commandId(commandId) {}; diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashBlockWrite.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashBlockWrite.hpp new file mode 100644 index 00000000..57283d73 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashBlockWrite.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +#include "src/Targets/TargetMemory.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + class PreparePartialFlashBlockWrite: public Command> + { + public: + PreparePartialFlashBlockWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes) + : Command( + 0x0A, + { + static_cast(startAddress >> 24), + static_cast(startAddress >> 16), + static_cast(startAddress >> 8), + static_cast(startAddress), + static_cast(bytes), + } + ) + {} + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp deleted file mode 100644 index 9cbd21c0..00000000 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" - -#include "src/Targets/TargetMemory.hpp" - -namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands -{ - class PreparePartialFlashPageWrite: public Command> - { - public: - PreparePartialFlashPageWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes) - : Command(0x0A) - { - this->payload = { - static_cast(startAddress >> 24), - static_cast(startAddress >> 16), - static_cast(startAddress >> 8), - static_cast(startAddress), - static_cast(bytes), - }; - } - }; -} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp index eec4b1e7..9212038e 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp @@ -9,7 +9,7 @@ #include "Commands/Control/PostAttach.hpp" #include "Commands/SetClockSpeed.hpp" #include "Commands/DebugModuleInterfaceOperation.hpp" -#include "Commands/PreparePartialFlashPageWrite.hpp" +#include "Commands/PreparePartialFlashBlockWrite.hpp" #include "Commands/StartProgrammingSession.hpp" #include "Commands/EndProgrammingSession.hpp" #include "Commands/SetFlashWriteRegion.hpp" @@ -138,7 +138,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"}; } - void WchLinkInterface::writePartialPage( + void WchLinkInterface::writeFlashPartialBlock( Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer ) { @@ -156,12 +156,12 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink ) ); const auto response = this->sendCommandAndWaitForResponse( - Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize} + Commands::PreparePartialFlashBlockWrite{startAddress + (packetSize * i), segmentSize} ); if (response.payload.size() != 1) { throw Exceptions::DeviceCommunicationFailure{ - "Unexpected response payload size for PreparePartialFlashPageWrite command" + "Unexpected response payload size for PreparePartialFlashBlockWrite command" }; } @@ -173,32 +173,32 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_DATA_ENDPOINT_IN); if (rawResponse.size() != 4) { - throw Exceptions::DeviceCommunicationFailure{"Unexpected response size for partial flash page write"}; + throw Exceptions::DeviceCommunicationFailure{"Unexpected response size for partial flash block write"}; } /* - * I have no idea what any of these bytes mean. I've not been able to find any documentation for - * this. All I know is that these values indicate a successful write. + * I have no idea what any of these bytes mean. I've not been able to find any documentation for this. + * All I know is that these values indicate a successful write. */ if (rawResponse[0] != 0x41 || rawResponse[1] != 0x01 || rawResponse[2] != 0x01 || rawResponse[3] != 0x02) { - throw Exceptions::DeviceCommunicationFailure{"Partial flash page write failed"}; + throw Exceptions::DeviceCommunicationFailure{"Partial flash block write failed"}; } } } - void WchLinkInterface::writeFullPage( + void WchLinkInterface::writeFlashFullBlocks( Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer, - Targets::TargetMemorySize pageSize, + Targets::TargetMemorySize blockSize, std::span flashProgramOpcodes ) { - assert((buffer.size() % pageSize) == 0); + assert((buffer.size() % blockSize) == 0); const auto bufferSize = static_cast(buffer.size()); /* * We don't issue the StartProgrammingSession command here, as it seems to result in a failure when writing a - * flash page. We get a 0x05 in rawResponse[3], but I have no idea why. + * flash block. We get a 0x05 in rawResponse[3], but I have no idea why. */ this->sendCommandAndWaitForResponse(Commands::SetFlashWriteRegion{startAddress, bufferSize}); this->sendCommandAndWaitForResponse(Commands::StartRamCodeWrite{}); @@ -214,7 +214,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink auto bytesWritten = Targets::TargetMemorySize{0}; while (bytesWritten < bufferSize) { - const auto length = std::min(bufferSize - bytesWritten, pageSize); + const auto length = std::min(bufferSize - bytesWritten, blockSize); this->usbInterface.writeBulk( WchLinkInterface::USB_DATA_ENDPOINT_OUT, @@ -224,12 +224,12 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_DATA_ENDPOINT_IN); if (rawResponse.size() != 4) { - throw Exceptions::DeviceCommunicationFailure{"Unexpected response size for flash page write"}; + throw Exceptions::DeviceCommunicationFailure{"Unexpected response size for flash block write"}; } if (rawResponse[3] != 0x02 && rawResponse[3] != 0x04) { throw Exceptions::DeviceCommunicationFailure{ - "Flash page write failed - unexpected response (0x" + "Flash block write failed - unexpected response (0x" + Services::StringService::toHex(rawResponse[3]) + ")" }; } diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp index 2d66c027..51a452ce 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp @@ -43,11 +43,11 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value ) override; - void writePartialPage(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer); - void writeFullPage( + void writeFlashPartialBlock(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer); + void writeFlashFullBlocks( Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer, - Targets::TargetMemorySize pageSize, + Targets::TargetMemorySize blockSize, std::span flashProgramOpcodes ); void eraseChip(); diff --git a/src/DebugToolDrivers/WCH/WchLinkDebugInterface.cpp b/src/DebugToolDrivers/WCH/WchLinkDebugInterface.cpp index 2b70c0da..9446ca6b 100644 --- a/src/DebugToolDrivers/WCH/WchLinkDebugInterface.cpp +++ b/src/DebugToolDrivers/WCH/WchLinkDebugInterface.cpp @@ -54,9 +54,9 @@ namespace DebugToolDrivers::Wch this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value ) ) - , programmingPacketSize( + , programmingBlockSize( Services::StringService::toUint32( - this->targetDescriptionFile.getProperty("wch_link_interface", "programming_packet_size").value + this->targetDescriptionFile.getProperty("wch_link_interface", "programming_block_size").value ) ) {} @@ -202,9 +202,16 @@ namespace DebugToolDrivers::Wch Targets::TargetMemoryBufferSpan buffer ) { if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) { + /* + * WCH-Link tools cannot write to flash memory via the target's debug module. + * + * They do, however, offer a set of dedicated commands for writing to flash memory. We invoke them here. + * + * See WchLinkDebugInterface::writeFlashMemory() below, for more. + */ const auto bufferSize = static_cast(buffer.size()); - const auto alignmentSize = bufferSize > WchLinkDebugInterface::MAX_PARTIAL_PAGE_WRITE_SIZE - ? this->programmingPacketSize + const auto alignmentSize = bufferSize > WchLinkDebugInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE + ? this->programmingBlockSize : 1; if (alignmentSize > 1) { @@ -272,14 +279,29 @@ namespace DebugToolDrivers::Wch } void WchLinkDebugInterface::writeFlashMemory(TargetMemoryAddress startAddress, TargetMemoryBufferSpan buffer) { - if (buffer.size() <= WchLinkDebugInterface::MAX_PARTIAL_PAGE_WRITE_SIZE) { - return this->wchLinkInterface.writePartialPage(startAddress, buffer); + /* + * There are two commands we can choose from when writing to flash memory: + * + * - Partial block write + * Writes any number of bytes to flash, but limited to a maximum of 64 bytes per write. Larger writes + * must be split into multiple writes. + * - Full block write + * Writes an entire block to flash. Where the block size is target-specific (resides in the target's + * TDF). Requires alignment to the block size. Requires reattaching to the target at the end of the + * programming session. + * + * The full block write is much faster for writing large buffers (KiBs), such as when we're programming 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() <= WchLinkDebugInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE) { + return this->wchLinkInterface.writeFlashPartialBlock(startAddress, buffer); } - this->wchLinkInterface.writeFullPage( + this->wchLinkInterface.writeFlashFullBlocks( startAddress, buffer, - this->programmingPacketSize, + this->programmingBlockSize, this->flashProgramOpcodes ); diff --git a/src/DebugToolDrivers/WCH/WchLinkDebugInterface.hpp b/src/DebugToolDrivers/WCH/WchLinkDebugInterface.hpp index ca625963..f3f86332 100644 --- a/src/DebugToolDrivers/WCH/WchLinkDebugInterface.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkDebugInterface.hpp @@ -77,7 +77,7 @@ namespace DebugToolDrivers::Wch ) override; private: - static constexpr Targets::TargetMemorySize MAX_PARTIAL_PAGE_WRITE_SIZE = 64; + static constexpr Targets::TargetMemorySize MAX_PARTIAL_BLOCK_WRITE_SIZE = 64; const WchLinkToolConfig& toolConfig; const Targets::RiscV::RiscVTargetConfig& targetConfig; @@ -97,7 +97,7 @@ namespace DebugToolDrivers::Wch std::optional cachedTargetId; std::span flashProgramOpcodes; - Targets::TargetMemorySize programmingPacketSize; + Targets::TargetMemorySize programmingBlockSize; void writeFlashMemory(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer); void eraseFlashMemory();