This commit is contained in:
Nav
2024-11-26 21:01:25 +00:00
parent 899cbc92c4
commit d613c9909b
7 changed files with 82 additions and 54 deletions

View File

@@ -24,6 +24,11 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
std::uint8_t commandId; std::uint8_t commandId;
PayloadContainerType payload; PayloadContainerType payload;
Command(std::uint8_t commandId, PayloadContainerType&& payload)
: commandId(commandId)
, payload(std::move(payload))
{};
explicit Command(std::uint8_t commandId) explicit Command(std::uint8_t commandId)
: commandId(commandId) : commandId(commandId)
{}; {};

View File

@@ -0,0 +1,27 @@
#pragma once
#include <cstdint>
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
{
class PreparePartialFlashBlockWrite: public Command<std::array<unsigned char, 5>>
{
public:
PreparePartialFlashBlockWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes)
: Command(
0x0A,
{
static_cast<unsigned char>(startAddress >> 24),
static_cast<unsigned char>(startAddress >> 16),
static_cast<unsigned char>(startAddress >> 8),
static_cast<unsigned char>(startAddress),
static_cast<unsigned char>(bytes),
}
)
{}
};
}

View File

@@ -1,26 +0,0 @@
#pragma once
#include <cstdint>
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
{
class PreparePartialFlashPageWrite: public Command<std::array<unsigned char, 5>>
{
public:
PreparePartialFlashPageWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes)
: Command(0x0A)
{
this->payload = {
static_cast<unsigned char>(startAddress >> 24),
static_cast<unsigned char>(startAddress >> 16),
static_cast<unsigned char>(startAddress >> 8),
static_cast<unsigned char>(startAddress),
static_cast<unsigned char>(bytes),
};
}
};
}

View File

@@ -9,7 +9,7 @@
#include "Commands/Control/PostAttach.hpp" #include "Commands/Control/PostAttach.hpp"
#include "Commands/SetClockSpeed.hpp" #include "Commands/SetClockSpeed.hpp"
#include "Commands/DebugModuleInterfaceOperation.hpp" #include "Commands/DebugModuleInterfaceOperation.hpp"
#include "Commands/PreparePartialFlashPageWrite.hpp" #include "Commands/PreparePartialFlashBlockWrite.hpp"
#include "Commands/StartProgrammingSession.hpp" #include "Commands/StartProgrammingSession.hpp"
#include "Commands/EndProgrammingSession.hpp" #include "Commands/EndProgrammingSession.hpp"
#include "Commands/SetFlashWriteRegion.hpp" #include "Commands/SetFlashWriteRegion.hpp"
@@ -138,7 +138,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"}; throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"};
} }
void WchLinkInterface::writePartialPage( void WchLinkInterface::writeFlashPartialBlock(
Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryAddress startAddress,
Targets::TargetMemoryBufferSpan buffer Targets::TargetMemoryBufferSpan buffer
) { ) {
@@ -156,12 +156,12 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
) )
); );
const auto response = this->sendCommandAndWaitForResponse( const auto response = this->sendCommandAndWaitForResponse(
Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize} Commands::PreparePartialFlashBlockWrite{startAddress + (packetSize * i), segmentSize}
); );
if (response.payload.size() != 1) { if (response.payload.size() != 1) {
throw Exceptions::DeviceCommunicationFailure{ 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); const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_DATA_ENDPOINT_IN);
if (rawResponse.size() != 4) { 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 * I have no idea what any of these bytes mean. I've not been able to find any documentation for this.
* this. All I know is that these values indicate a successful write. * All I know is that these values indicate a successful write.
*/ */
if (rawResponse[0] != 0x41 || rawResponse[1] != 0x01 || rawResponse[2] != 0x01 || rawResponse[3] != 0x02) { 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::TargetMemoryAddress startAddress,
Targets::TargetMemoryBufferSpan buffer, Targets::TargetMemoryBufferSpan buffer,
Targets::TargetMemorySize pageSize, Targets::TargetMemorySize blockSize,
std::span<const unsigned char> flashProgramOpcodes std::span<const unsigned char> flashProgramOpcodes
) { ) {
assert((buffer.size() % pageSize) == 0); assert((buffer.size() % blockSize) == 0);
const auto bufferSize = static_cast<Targets::TargetMemorySize>(buffer.size()); const auto bufferSize = static_cast<Targets::TargetMemorySize>(buffer.size());
/* /*
* We don't issue the StartProgrammingSession command here, as it seems to result in a failure when writing a * 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::SetFlashWriteRegion{startAddress, bufferSize});
this->sendCommandAndWaitForResponse(Commands::StartRamCodeWrite{}); this->sendCommandAndWaitForResponse(Commands::StartRamCodeWrite{});
@@ -214,7 +214,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
auto bytesWritten = Targets::TargetMemorySize{0}; auto bytesWritten = Targets::TargetMemorySize{0};
while (bytesWritten < bufferSize) { while (bytesWritten < bufferSize) {
const auto length = std::min(bufferSize - bytesWritten, pageSize); const auto length = std::min(bufferSize - bytesWritten, blockSize);
this->usbInterface.writeBulk( this->usbInterface.writeBulk(
WchLinkInterface::USB_DATA_ENDPOINT_OUT, 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); const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_DATA_ENDPOINT_IN);
if (rawResponse.size() != 4) { 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) { if (rawResponse[3] != 0x02 && rawResponse[3] != 0x04) {
throw Exceptions::DeviceCommunicationFailure{ throw Exceptions::DeviceCommunicationFailure{
"Flash page write failed - unexpected response (0x" "Flash block write failed - unexpected response (0x"
+ Services::StringService::toHex(rawResponse[3]) + ")" + Services::StringService::toHex(rawResponse[3]) + ")"
}; };
} }

View File

@@ -43,11 +43,11 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value
) override; ) override;
void writePartialPage(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer); void writeFlashPartialBlock(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer);
void writeFullPage( void writeFlashFullBlocks(
Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryAddress startAddress,
Targets::TargetMemoryBufferSpan buffer, Targets::TargetMemoryBufferSpan buffer,
Targets::TargetMemorySize pageSize, Targets::TargetMemorySize blockSize,
std::span<const unsigned char> flashProgramOpcodes std::span<const unsigned char> flashProgramOpcodes
); );
void eraseChip(); void eraseChip();

View File

@@ -54,9 +54,9 @@ namespace DebugToolDrivers::Wch
this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value
) )
) )
, programmingPacketSize( , programmingBlockSize(
Services::StringService::toUint32( 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 Targets::TargetMemoryBufferSpan buffer
) { ) {
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) { 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<TargetMemorySize>(buffer.size()); const auto bufferSize = static_cast<TargetMemorySize>(buffer.size());
const auto alignmentSize = bufferSize > WchLinkDebugInterface::MAX_PARTIAL_PAGE_WRITE_SIZE const auto alignmentSize = bufferSize > WchLinkDebugInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE
? this->programmingPacketSize ? this->programmingBlockSize
: 1; : 1;
if (alignmentSize > 1) { if (alignmentSize > 1) {
@@ -272,14 +279,29 @@ namespace DebugToolDrivers::Wch
} }
void WchLinkDebugInterface::writeFlashMemory(TargetMemoryAddress startAddress, TargetMemoryBufferSpan buffer) { 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, startAddress,
buffer, buffer,
this->programmingPacketSize, this->programmingBlockSize,
this->flashProgramOpcodes this->flashProgramOpcodes
); );

View File

@@ -77,7 +77,7 @@ namespace DebugToolDrivers::Wch
) override; ) override;
private: 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 WchLinkToolConfig& toolConfig;
const Targets::RiscV::RiscVTargetConfig& targetConfig; const Targets::RiscV::RiscVTargetConfig& targetConfig;
@@ -97,7 +97,7 @@ namespace DebugToolDrivers::Wch
std::optional<WchTargetId> cachedTargetId; std::optional<WchTargetId> cachedTargetId;
std::span<const unsigned char> flashProgramOpcodes; std::span<const unsigned char> flashProgramOpcodes;
Targets::TargetMemorySize programmingPacketSize; Targets::TargetMemorySize programmingBlockSize;
void writeFlashMemory(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer); void writeFlashMemory(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer);
void eraseFlashMemory(); void eraseFlashMemory();