From 7630baf51bf751c253713a054a3d4dc3317a1b6f Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 9 Dec 2023 18:22:32 +0000 Subject: [PATCH] Implemented `RiscVProgramInterface` in `WchLinkInterface` --- .../Commands/PreparePartialFlashPageWrite.hpp | 29 ++++++++++ .../Protocols/WchLink/WchLinkInterface.cpp | 53 +++++++++++++++++++ .../Protocols/WchLink/WchLinkInterface.hpp | 10 +++- src/DebugToolDrivers/WCH/WchLinkBase.hpp | 18 +++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp new file mode 100644 index 00000000..41dcb9b3 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp @@ -0,0 +1,29 @@ +#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 067830e3..d5238e31 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp @@ -7,6 +7,7 @@ #include "Commands/Control/DetachTarget.hpp" #include "Commands/SetClockSpeed.hpp" #include "Commands/DebugModuleInterfaceOperation.hpp" +#include "Commands/PreparePartialFlashPageWrite.hpp" #include "src/Helpers/BiMap.hpp" #include "src/Services/StringService.hpp" @@ -108,6 +109,58 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink } } + void WchLinkInterface::writeFlashMemory( + Targets::TargetMemoryAddress startAddress, + const Targets::TargetMemoryBuffer& buffer + ) { + const auto packetSize = Targets::TargetMemorySize{this->dataEndpointMaxPacketSize}; + const auto bufferSize = static_cast(buffer.size()); + const auto packetsRequired = static_cast( + std::ceil(static_cast(bufferSize) / static_cast(packetSize)) + ); + + for (std::uint32_t i = 0; i < packetsRequired; ++i) { + const auto segmentSize = static_cast(std::min(bufferSize - (i * packetSize), packetSize)); + const auto response = this->sendCommandAndWaitForResponse( + Commands::PreparePartialFlashPageWrite(startAddress + (packetSize * i), segmentSize) + ); + + if (response.payload.size() != 1) { + throw Exceptions::DeviceCommunicationFailure( + "Unexpected response payload size for PreparePartialFlashPageWrite command" + ); + } + + this->usbInterface.writeBulk( + WchLinkInterface::USB_DATA_ENDPOINT_OUT, + std::vector( + buffer.begin() + (packetSize * i), + buffer.begin() + (packetSize * i) + segmentSize + ) + ); + + 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"); + } + + /* + * I have no idea what any of these bytes mean. There's no documentation available 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"); + } + } + } + void WchLinkInterface::setClockSpeed(WchLinkTargetClockSpeed speed) { const auto speedIdsBySpeed = BiMap({ {WchLinkTargetClockSpeed::CLK_6000_KHZ, 0x01}, diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp index 3d18e400..355766b4 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp @@ -6,6 +6,7 @@ #include #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp" +#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp" #include "src/DebugToolDrivers/USB/UsbInterface.hpp" #include "src/DebugToolDrivers/USB/UsbDevice.hpp" @@ -22,7 +23,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink /** * The WchLinkInterface implements the WCH-Link protocol. */ - class WchLinkInterface: public TargetInterfaces::RiscV::RiscVDebugInterface + class WchLinkInterface + : public TargetInterfaces::RiscV::RiscVDebugInterface + , public TargetInterfaces::RiscV::RiscVProgramInterface { public: WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice); @@ -44,6 +47,11 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink Targets::RiscV::DebugModule::RegisterValue value ) override; + void writeFlashMemory( + Targets::TargetMemoryAddress startAddress, + const Targets::TargetMemoryBuffer& buffer + ) override; + private: static constexpr std::uint8_t USB_COMMAND_ENDPOINT_IN = 0x81; static constexpr std::uint8_t USB_COMMAND_ENDPOINT_OUT = 0x01; diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.hpp b/src/DebugToolDrivers/WCH/WchLinkBase.hpp index c0d280b8..8db93555 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.hpp @@ -36,6 +36,24 @@ namespace DebugToolDrivers::Wch return this->wchLinkInterface.get(); } + /** + * WCH-Link debug tools cannot write to flash memory via the RISC-V debug interface (RiscVDebugInterface). + * Flash memory writes via abstract commands fail silently. + * + * We have to send a vendor-specific command to the debug tool, in order to program the target. + * + * For this reason, we have to provide an implementation of the RiscVProgramInterface, so that the RISC-V + * target driver forwards any flash memory writes to this implementation (instead of relying on the debug + * interface). + * + * The WchLinkInterface implements both the RiscVDebugInterface and the RiscVProgramInterface. + * + * @return + */ + DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface() override { + return this->wchLinkInterface.get(); + } + protected: WchLinkVariant variant;