From 07283a2dc72a71ff690c5e4b0473c9c5f9dd88d2 Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 16 Nov 2024 20:05:26 +0000 Subject: [PATCH] Flash programming support for WCH-LinkE tool --- src/DebugToolDrivers/CMakeLists.txt | 3 +- .../RiscV/RiscVProgramInterface.hpp | 27 +++-- .../Commands/EndProgrammingSession.hpp | 20 ++++ .../WchLink/Commands/EndRamCodeWrite.hpp | 20 ++++ .../Protocols/WchLink/Commands/EraseChip.hpp | 20 ++++ .../Commands/PreparePartialFlashPageWrite.hpp | 5 +- .../WchLink/Commands/SetFlashWriteRegion.hpp | 29 ++++++ .../Commands/StartProgrammingSession.hpp | 26 +++++ .../WchLink/Commands/StartRamCodeWrite.hpp | 20 ++++ .../Protocols/WchLink/Commands/WriteFlash.hpp | 20 ++++ .../Protocols/WchLink/FlashProgramOpcodes.hpp | 80 +++++++++++++++ .../Protocols/WchLink/WchLinkInterface.cpp | 98 ++++++++++++++++--- .../Protocols/WchLink/WchLinkInterface.hpp | 35 ++++--- .../WchLink/WchLinkProgrammingInterface.cpp | 82 ++++++++++++++++ .../WchLink/WchLinkProgrammingInterface.hpp | 57 +++++++++++ src/DebugToolDrivers/WCH/WchLinkBase.cpp | 10 +- src/DebugToolDrivers/WCH/WchLinkBase.hpp | 6 +- src/Targets/RiscV/RiscV.cpp | 82 ++++++++++++++-- src/Targets/RiscV/RiscV.hpp | 2 + src/Targets/RiscV/TargetDescriptionFile.cpp | 6 +- src/Targets/RiscV/TargetDescriptionFile.hpp | 2 + src/Targets/RiscV/Wch/WchRiscV.cpp | 27 +++++ src/Targets/RiscV/Wch/WchRiscV.hpp | 12 ++- src/Targets/TargetMemory.hpp | 2 + 24 files changed, 638 insertions(+), 53 deletions(-) create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndProgrammingSession.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndRamCodeWrite.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EraseChip.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/SetFlashWriteRegion.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartProgrammingSession.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartRamCodeWrite.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/WriteFlash.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/FlashProgramOpcodes.hpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.cpp create mode 100644 src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.hpp diff --git a/src/DebugToolDrivers/CMakeLists.txt b/src/DebugToolDrivers/CMakeLists.txt index 7f035ce4..f47deb99 100755 --- a/src/DebugToolDrivers/CMakeLists.txt +++ b/src/DebugToolDrivers/CMakeLists.txt @@ -43,8 +43,9 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp - # RISC-V debug tools + # WCH debug tools and interface implementations ${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkInterface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkProgrammingInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkToolConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp diff --git a/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp index 976b52cd..8e3e202a 100644 --- a/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp +++ b/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp @@ -1,23 +1,34 @@ #pragma once #include +#include #include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" namespace DebugToolDrivers::TargetInterfaces::RiscV { class RiscVProgramInterface { public: - /** - * Should write to the target's FLASH memory. - * - * @param startAddress - * @param buffer - */ - virtual void writeFlashMemory( + virtual std::optional alignmentSize( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, - const Targets::TargetMemoryBuffer& buffer + Targets::TargetMemorySize bufferSize + ) = 0; + + virtual void writeProgramMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemoryBufferSpan buffer + ) = 0; + + virtual void eraseProgramMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor ) = 0; }; } diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndProgrammingSession.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndProgrammingSession.hpp new file mode 100644 index 00000000..efc3d3ec --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndProgrammingSession.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + class EndProgrammingSession: public Command> + { + public: + EndProgrammingSession() + : Command(0x02) + { + this->payload = { + 0x08 + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndRamCodeWrite.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndRamCodeWrite.hpp new file mode 100644 index 00000000..4b349d28 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EndRamCodeWrite.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + class EndRamCodeWrite: public Command> + { + public: + EndRamCodeWrite() + : Command(0x02) + { + this->payload = { + 0x07 + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EraseChip.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EraseChip.hpp new file mode 100644 index 00000000..5a9f2102 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/EraseChip.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + class EraseChip: public Command> + { + public: + EraseChip() + : Command(0x02) + { + this->payload = { + 0x01 + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp index 41dcb9b3..9cbd21c0 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/PreparePartialFlashPageWrite.hpp @@ -11,10 +11,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands class PreparePartialFlashPageWrite: public Command> { public: - PreparePartialFlashPageWrite( - Targets::TargetMemoryAddress startAddress, - std::uint8_t bytes - ) + PreparePartialFlashPageWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes) : Command(0x0A) { this->payload = { diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/SetFlashWriteRegion.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/SetFlashWriteRegion.hpp new file mode 100644 index 00000000..a4016724 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/SetFlashWriteRegion.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 SetFlashWriteRegion: public Command> + { + public: + SetFlashWriteRegion(Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize bytes) + : Command(0x01) + { + this->payload = { + static_cast(startAddress >> 24), + static_cast(startAddress >> 16), + static_cast(startAddress >> 8), + static_cast(startAddress), + static_cast(bytes >> 24), + static_cast(bytes >> 16), + static_cast(bytes >> 8), + static_cast(bytes), + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartProgrammingSession.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartProgrammingSession.hpp new file mode 100644 index 00000000..42aa29c3 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartProgrammingSession.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + /* + * We're not actually using this ATM. Sending it Issuing this command before flashing the target seems to + * result in failures. See the comment in WchLinkInterface::writeFullPage() for more. + * + * TODO: Consider removing at a later date. + */ + class StartProgrammingSession: public Command> + { + public: + StartProgrammingSession() + : Command(0x02) + { + this->payload = { + 0x06 + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartRamCodeWrite.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartRamCodeWrite.hpp new file mode 100644 index 00000000..b70d1420 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/StartRamCodeWrite.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + class StartRamCodeWrite: public Command> + { + public: + StartRamCodeWrite() + : Command(0x02) + { + this->payload = { + 0x05 + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/WriteFlash.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/WriteFlash.hpp new file mode 100644 index 00000000..1ea495fd --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/WriteFlash.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands +{ + class WriteFlash: public Command> + { + public: + WriteFlash() + : Command(0x02) + { + this->payload = { + 0x02 + }; + } + }; +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/FlashProgramOpcodes.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/FlashProgramOpcodes.hpp new file mode 100644 index 00000000..0d97a14c --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/FlashProgramOpcodes.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +namespace DebugToolDrivers::Wch::Protocols::WchLink::FlashProgramOpcodes +{ + static constexpr auto FLASH_OP1 = std::to_array({ + 0x01, 0x11, 0x02, 0xCE, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, 0x67, 0x45, 0xB7, 0x27, + 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, 0x13, 0x07, 0xB7, 0x9A, + 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x9D, 0xC7, 0xB7, 0x27, 0x02, 0x40, + 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x38, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, 0x98, 0xCB, 0x98, 0x4B, + 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x63, 0x16, + 0x07, 0x10, 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, 0xA9, 0xCB, 0x93, 0x07, + 0xF6, 0x0F, 0xA1, 0x83, 0x2E, 0xC6, 0x2D, 0x68, 0x81, 0x76, 0x3E, 0xCA, 0xB7, 0x08, 0x02, 0x00, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x33, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0xFD, 0x16, 0x98, 0x4B, + 0x33, 0x67, 0x17, 0x01, 0x98, 0xCB, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, 0x07, 0x04, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x69, 0xE7, 0x98, 0x4B, 0x75, 0x8F, 0x98, 0xCB, 0x32, 0x47, + 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, 0x69, 0xFB, 0x93, 0x77, + 0x85, 0x00, 0xF1, 0xCF, 0x93, 0x07, 0xF6, 0x0F, 0x2E, 0xC6, 0xA1, 0x83, 0x3E, 0xCA, 0x37, 0x27, + 0x02, 0x40, 0x1C, 0x4B, 0xC1, 0x66, 0x2D, 0x68, 0xD5, 0x8F, 0x1C, 0xCB, 0xB7, 0x16, 0x00, 0x20, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x03, 0x08, 0x00, 0x13, 0x0E, 0x00, 0x04, 0xB7, 0x0E, 0x04, 0x00, + 0xB7, 0x38, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0x98, 0x4B, 0x33, 0x67, 0x67, 0x00, 0x98, 0xCB, + 0xD8, 0x47, 0x05, 0x8B, 0x75, 0xFF, 0x32, 0x47, 0x36, 0x8F, 0x3A, 0xC8, 0x72, 0xCC, 0x42, 0x47, + 0x03, 0x2F, 0x0F, 0x00, 0x91, 0x06, 0x23, 0x20, 0xE7, 0x01, 0x98, 0x4B, 0x33, 0x67, 0xD7, 0x01, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x21, 0xEB, 0x42, 0x47, 0x36, 0x8F, 0x11, 0x07, 0x3A, 0xC8, + 0x62, 0x47, 0x7D, 0x17, 0x3A, 0xCC, 0x61, 0xFF, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, + 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x15, 0xEB, 0xD8, 0x47, 0x41, 0x8B, 0x15, 0xCB, + 0xD8, 0x47, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x13, 0x67, 0x07, 0x01, 0xD8, 0xC7, 0x98, 0x4B, + 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x05, 0x61, 0x02, 0x90, 0x23, 0x20, 0xD8, 0x00, 0xF5, 0xB5, + 0x23, 0x20, 0x03, 0x01, 0x3D, 0xB7, 0x23, 0xA0, 0x08, 0x01, 0x65, 0xB7, 0x23, 0xA0, 0x08, 0x01, + 0xD1, 0xB7, 0x32, 0x47, 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, + 0x25, 0xF7, 0x98, 0x4B, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x75, 0x8F, 0x98, 0xCB, 0x41, 0x89, + 0x19, 0xE1, 0x01, 0x45, 0xC9, 0xB7, 0x2E, 0xC6, 0x0D, 0x06, 0x02, 0xCA, 0x09, 0x82, 0x32, 0xCC, + 0xB7, 0x17, 0x00, 0x20, 0x98, 0x43, 0x13, 0x86, 0x47, 0x00, 0xD2, 0x47, 0xB2, 0x46, 0x8A, 0x07, + 0xB6, 0x97, 0x9C, 0x43, 0x63, 0x18, 0xF7, 0x02, 0xD2, 0x47, 0x32, 0x47, 0x8A, 0x07, 0xBA, 0x97, + 0x98, 0x43, 0xF2, 0x47, 0xBA, 0x97, 0x3E, 0xCE, 0xD2, 0x47, 0x85, 0x07, 0x3E, 0xCA, 0xD2, 0x46, + 0x62, 0x47, 0xB2, 0x87, 0xE3, 0xE8, 0xE6, 0xFC, 0xB7, 0x27, 0x00, 0x20, 0x98, 0x4B, 0xF2, 0x47, + 0xE3, 0x09, 0xF7, 0xFA, 0x41, 0x45, 0x85, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }); + + static constexpr auto FLASH_OP2 = std::to_array({ + 0x11, 0x11, 0x22, 0xCC, 0x26, 0xCA, 0x02, 0xC8, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, + 0x67, 0x45, 0xB7, 0x27, 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, + 0x13, 0x07, 0xB7, 0x9A, 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x9D, 0xC7, + 0xB7, 0x27, 0x02, 0x40, 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x33, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, + 0x98, 0xCB, 0x98, 0x4B, 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, + 0x05, 0x8B, 0x63, 0x16, 0x07, 0x10, 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, + 0xA9, 0xCB, 0x93, 0x07, 0xF6, 0x03, 0x99, 0x83, 0x2E, 0xC0, 0x2D, 0x63, 0x81, 0x76, 0x3E, 0xC4, + 0xB7, 0x32, 0x00, 0x40, 0xB7, 0x27, 0x02, 0x40, 0x13, 0x03, 0xA3, 0xAA, 0xFD, 0x16, 0x98, 0x4B, + 0xB7, 0x03, 0x02, 0x00, 0x33, 0x67, 0x77, 0x00, 0x98, 0xCB, 0x02, 0x47, 0xD8, 0xCB, 0x98, 0x4B, + 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x69, 0xE7, 0x98, 0x4B, 0x75, 0x8F, + 0x98, 0xCB, 0x02, 0x47, 0x13, 0x07, 0x07, 0x04, 0x3A, 0xC0, 0x22, 0x47, 0x7D, 0x17, 0x3A, 0xC4, + 0x79, 0xF7, 0x93, 0x77, 0x85, 0x00, 0xF1, 0xCF, 0x93, 0x07, 0xF6, 0x03, 0x2E, 0xC0, 0x99, 0x83, + 0x37, 0x27, 0x02, 0x40, 0x3E, 0xC4, 0x1C, 0x4B, 0xC1, 0x66, 0x2D, 0x63, 0xD5, 0x8F, 0x1C, 0xCB, + 0x37, 0x07, 0x00, 0x20, 0x13, 0x07, 0x07, 0x20, 0xB7, 0x27, 0x02, 0x40, 0xB7, 0x03, 0x08, 0x00, + 0xB7, 0x32, 0x00, 0x40, 0x13, 0x03, 0xA3, 0xAA, 0x94, 0x4B, 0xB3, 0xE6, 0x76, 0x00, 0x94, 0xCB, + 0xD4, 0x47, 0x85, 0x8A, 0xF5, 0xFE, 0x82, 0x46, 0xBA, 0x84, 0x37, 0x04, 0x04, 0x00, 0x36, 0xC2, + 0xC1, 0x46, 0x36, 0xC6, 0x92, 0x46, 0x84, 0x40, 0x11, 0x07, 0x84, 0xC2, 0x94, 0x4B, 0xC1, 0x8E, + 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0xB1, 0xEA, 0x92, 0x46, 0xBA, 0x84, 0x91, 0x06, 0x36, 0xC2, + 0xB2, 0x46, 0xFD, 0x16, 0x36, 0xC6, 0xF9, 0xFE, 0x82, 0x46, 0xD4, 0xCB, 0x94, 0x4B, 0x93, 0xE6, + 0x06, 0x04, 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0x85, 0xEE, 0xD4, 0x47, 0xC1, 0x8A, 0x85, 0xCE, + 0xD8, 0x47, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x13, 0x67, 0x07, 0x01, 0xD8, 0xC7, 0x98, 0x4B, + 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x62, 0x44, 0xD2, 0x44, 0x71, 0x01, 0x02, 0x90, 0x23, 0x20, + 0xD3, 0x00, 0xF5, 0xB5, 0x23, 0xA0, 0x62, 0x00, 0x3D, 0xB7, 0x23, 0xA0, 0x62, 0x00, 0x55, 0xB7, + 0x23, 0xA0, 0x62, 0x00, 0xC1, 0xB7, 0x82, 0x46, 0x93, 0x86, 0x06, 0x04, 0x36, 0xC0, 0xA2, 0x46, + 0xFD, 0x16, 0x36, 0xC4, 0xB5, 0xF2, 0x98, 0x4B, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x75, 0x8F, + 0x98, 0xCB, 0x41, 0x89, 0x19, 0xE1, 0x01, 0x45, 0x7D, 0xBF, 0x2E, 0xC0, 0x0D, 0x06, 0x02, 0xC4, + 0x09, 0x82, 0xB7, 0x07, 0x00, 0x20, 0x32, 0xC6, 0x93, 0x87, 0x07, 0x20, 0x94, 0x43, 0x13, 0x87, + 0x47, 0x00, 0xA2, 0x47, 0x02, 0x46, 0x8A, 0x07, 0xB2, 0x97, 0x9C, 0x43, 0x63, 0x99, 0xF6, 0x02, + 0xA2, 0x47, 0x82, 0x46, 0x8A, 0x07, 0xB6, 0x97, 0x94, 0x43, 0xC2, 0x47, 0xB6, 0x97, 0x3E, 0xC8, + 0xA2, 0x47, 0x85, 0x07, 0x3E, 0xC4, 0x22, 0x46, 0xB2, 0x46, 0xBA, 0x87, 0xE3, 0x68, 0xD6, 0xFC, + 0xB7, 0x07, 0x00, 0x20, 0x03, 0xA7, 0x07, 0x61, 0xC2, 0x47, 0xE3, 0x06, 0xF7, 0xFA, 0x41, 0x45, + 0x9D, 0xB7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }); + + static_assert((FLASH_OP1.size() % 64) == 0); + static_assert((FLASH_OP2.size() % 64) == 0); +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp index 5ca27464..497a8003 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp @@ -9,6 +9,13 @@ #include "Commands/SetClockSpeed.hpp" #include "Commands/DebugModuleInterfaceOperation.hpp" #include "Commands/PreparePartialFlashPageWrite.hpp" +#include "Commands/StartProgrammingSession.hpp" +#include "Commands/EndProgrammingSession.hpp" +#include "Commands/SetFlashWriteRegion.hpp" +#include "Commands/StartRamCodeWrite.hpp" +#include "Commands/EndRamCodeWrite.hpp" +#include "Commands/WriteFlash.hpp" +#include "Commands/EraseChip.hpp" #include "src/Helpers/BiMap.hpp" #include "src/Services/StringService.hpp" @@ -136,18 +143,23 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"}; } - void WchLinkInterface::writeFlashMemory( + void WchLinkInterface::writePartialPage( Targets::TargetMemoryAddress startAddress, - const Targets::TargetMemoryBuffer& buffer + Targets::TargetMemoryBufferSpan buffer ) { - const auto packetSize = Targets::TargetMemorySize{this->dataEndpointMaxPacketSize}; + constexpr auto packetSize = std::uint8_t{64}; const auto bufferSize = static_cast(buffer.size()); const auto packetsRequired = static_cast( std::ceil(static_cast(bufferSize) / static_cast(packetSize)) ); for (auto i = std::uint32_t{0}; i < packetsRequired; ++i) { - const auto segmentSize = static_cast(std::min(bufferSize - (i * packetSize), packetSize)); + const auto segmentSize = static_cast( + std::min( + static_cast(bufferSize - (i * packetSize)), + packetSize + ) + ); const auto response = this->sendCommandAndWaitForResponse( Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize} ); @@ -160,33 +172,87 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink this->usbInterface.writeBulk( WchLinkInterface::USB_DATA_ENDPOINT_OUT, - std::vector{ - buffer.begin() + (packetSize * i), - buffer.begin() + (packetSize * i) + segmentSize - } + buffer.subspan(packetSize * i, segmentSize), + this->dataEndpointMaxPacketSize ); 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. I've not been a le 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. 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"}; } } } + void WchLinkInterface::writeFullPage( + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemoryBufferSpan buffer, + Targets::TargetMemorySize pageSize, + std::span flashProgramOpcodes + ) { + assert((buffer.size() % pageSize) == 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. + */ + this->sendCommandAndWaitForResponse(Commands::SetFlashWriteRegion{startAddress, bufferSize}); + this->sendCommandAndWaitForResponse(Commands::StartRamCodeWrite{}); + + this->usbInterface.writeBulk( + WchLinkInterface::USB_DATA_ENDPOINT_OUT, + flashProgramOpcodes, + this->dataEndpointMaxPacketSize + ); + + this->sendCommandAndWaitForResponse(Commands::EndRamCodeWrite{}); + this->sendCommandAndWaitForResponse(Commands::WriteFlash{}); + + auto bytesWritten = Targets::TargetMemorySize{0}; + while (bytesWritten < bufferSize) { + const auto length = std::min(bufferSize - bytesWritten, pageSize); + + this->usbInterface.writeBulk( + WchLinkInterface::USB_DATA_ENDPOINT_OUT, + buffer.subspan(bytesWritten, length), + this->dataEndpointMaxPacketSize + ); + + 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"}; + } + + if (rawResponse[3] != 0x02 && rawResponse[3] != 0x04) { + throw Exceptions::DeviceCommunicationFailure{ + "Flash page write failed - unexpected response (0x" + + Services::StringService::toHex(rawResponse[3]) + ")" + }; + } + + bytesWritten += length; + } + + this->sendCommandAndWaitForResponse(Commands::EndProgrammingSession{}); + + this->deactivate(); + this->sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo{}); + this->activate(); + } + + void WchLinkInterface::eraseChip() { + this->sendCommandAndWaitForResponse(Commands::EraseChip{}); + } + 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 cabe2548..3aeaeaa9 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp" @@ -23,17 +24,11 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink { /** - * Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface, a - * programming interface and a target identification interface. - * - * WCH debug tools cannot write to program memory via the target's RISC-V debug module, so we cannot program the - * target via the tool's RISC-V DTM interface. Instead, the WCH-Link protocol provides a dedicated command for - * writing to program memory, which is why this class implements the RISC-V programming interface. - * See WchLinkInterface::writeFlashMemory() for more. + * Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface, and a + * target identification interface. */ class WchLinkInterface : public ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTransportModuleInterface - , public TargetInterfaces::RiscV::RiscVProgramInterface , public TargetInterfaces::RiscV::RiscVIdentificationInterface { public: @@ -52,10 +47,16 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value ) override; - void writeFlashMemory( + void writePartialPage(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer); + + void writeFullPage( Targets::TargetMemoryAddress startAddress, - const Targets::TargetMemoryBuffer& buffer - ) override; + Targets::TargetMemoryBufferSpan buffer, + Targets::TargetMemorySize pageSize, + std::span flashProgramOpcodes + ); + + void eraseChip(); private: static constexpr std::uint8_t USB_COMMAND_ENDPOINT_IN = 0x81; @@ -90,15 +91,23 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink template auto sendCommandAndWaitForResponse(const CommandType& command) { - auto rawCommand = command.getRawCommand(); + const auto rawCommand = command.getRawCommand(); + /* + * Although the UsbInterface::writeBulk() will split the transfer, if it exceeds the max packet size, we + * still expect all commands to not exceed that size. + */ if (rawCommand.size() > this->commandEndpointMaxPacketSize) { throw Exceptions::DeviceCommunicationFailure{ "Raw command size exceeds maximum packet size for command endpoint" }; } - this->usbInterface.writeBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_OUT, std::move(rawCommand)); + this->usbInterface.writeBulk( + WchLinkInterface::USB_COMMAND_ENDPOINT_OUT, + rawCommand, + this->commandEndpointMaxPacketSize + ); const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_IN); diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.cpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.cpp new file mode 100644 index 00000000..f4fe0bbf --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.cpp @@ -0,0 +1,82 @@ +#include "WchLinkProgrammingInterface.hpp" + +#include "FlashProgramOpcodes.hpp" + +#include "src/Services/StringService.hpp" +#include "src/Targets/TargetDescription/Exceptions/InvalidTargetDescriptionDataException.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink +{ + using namespace ::DebugToolDrivers::Protocols::RiscVDebugSpec; + using namespace Exceptions; + + using DebugModule::DmiOperation; + + WchLinkProgrammingInterface::WchLinkProgrammingInterface( + WchLinkInterface& wchLinkInterface, + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile + ) + : wchLinkInterface(wchLinkInterface) + , targetDescriptionFile(targetDescriptionFile) + , flashProgramOpcodes( + WchLinkProgrammingInterface::getFlashProgramOpcodes( + this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value + ) + ) + , programmingPacketSize( + Services::StringService::toUint32( + this->targetDescriptionFile.getProperty("wch_link_interface", "programming_packet_size").value + ) + ) + {} + + std::optional WchLinkProgrammingInterface::alignmentSize( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemorySize bufferSize + ) { + return bufferSize > WchLinkProgrammingInterface::MAX_PARTIAL_PAGE_WRITE_SIZE + ? std::optional{this->programmingPacketSize} + : std::nullopt; + } + + void WchLinkProgrammingInterface::writeProgramMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemoryBufferSpan buffer + ) { + if (buffer.size() <= WchLinkProgrammingInterface::MAX_PARTIAL_PAGE_WRITE_SIZE) { + return this->wchLinkInterface.writePartialPage(startAddress, buffer); + } + + this->wchLinkInterface.writeFullPage( + startAddress, + buffer, + this->programmingPacketSize, + this->flashProgramOpcodes + ); + } + + void WchLinkProgrammingInterface::eraseProgramMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) { + this->wchLinkInterface.eraseChip(); + } + + std::span WchLinkProgrammingInterface::getFlashProgramOpcodes(const std::string& key) { + if (key == "op1") { + return FlashProgramOpcodes::FLASH_OP1; + } + + if (key == "op2") { + return FlashProgramOpcodes::FLASH_OP2; + } + + throw Targets::TargetDescription::Exceptions::InvalidTargetDescriptionDataException{ + "Invalid programming_opcode_key value (\"" + key + "\")" + }; + } +} diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.hpp new file mode 100644 index 00000000..360cde77 --- /dev/null +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkProgrammingInterface.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include "WchLinkInterface.hpp" +#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp" + +namespace DebugToolDrivers::Wch::Protocols::WchLink +{ + /** + * WCH debug tools cannot write to program memory via the target's RISC-V debug module, so we cannot program the + * target via the tool's RISC-V DTM interface. Instead, the WCH-Link protocol provides a dedicated command for + * writing to program memory, which is why this class implements the RISC-V programming interface. + * See WchLinkInterface::writeFlashMemory() for more. + */ + class WchLinkProgrammingInterface + : public TargetInterfaces::RiscV::RiscVProgramInterface + { + public: + WchLinkProgrammingInterface( + WchLinkInterface& wchLinkInterface, + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile + ); + + std::optional alignmentSize( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemorySize bufferSize + ) override; + + void writeProgramMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemoryBufferSpan buffer + ) override; + + void eraseProgramMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) override; + + private: + static constexpr Targets::TargetMemorySize MAX_PARTIAL_PAGE_WRITE_SIZE = 64; + + WchLinkInterface& wchLinkInterface; + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile; + + std::span flashProgramOpcodes; + Targets::TargetMemorySize programmingPacketSize; + + static std::span getFlashProgramOpcodes(const std::string& key); + }; +} diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.cpp b/src/DebugToolDrivers/WCH/WchLinkBase.cpp index 534f5eb0..0eaec7f9 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.cpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.cpp @@ -87,11 +87,17 @@ namespace DebugToolDrivers::Wch return this->wchRiscVTranslator.get(); } - Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVProgramInterface( + Protocols::WchLink::WchLinkProgrammingInterface* WchLinkBase::getRiscVProgramInterface( const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, const Targets::RiscV::RiscVTargetConfig& targetConfig ) { - return this->wchLinkInterface.get(); + if (!this->wchLinkProgrammingInterface) { + this->wchLinkProgrammingInterface = std::make_unique( + *(this->wchLinkInterface), + targetDescriptionFile + ); + } + return this->wchLinkProgrammingInterface.get(); } Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVIdentificationInterface( diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.hpp b/src/DebugToolDrivers/WCH/WchLinkBase.hpp index 2a173c24..31f6db7e 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.hpp @@ -12,8 +12,11 @@ #include "src/ProjectConfig.hpp" #include "Protocols/WchLink/WchLinkInterface.hpp" +#include "Protocols/WchLink/WchLinkProgrammingInterface.hpp" #include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp" +#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp" + #include "WchGeneric.hpp" #include "DeviceInfo.hpp" @@ -57,7 +60,7 @@ namespace DebugToolDrivers::Wch * * @return */ - Protocols::WchLink::WchLinkInterface* getRiscVProgramInterface( + Protocols::WchLink::WchLinkProgrammingInterface* getRiscVProgramInterface( const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, const Targets::RiscV::RiscVTargetConfig& targetConfig ) override; @@ -76,6 +79,7 @@ namespace DebugToolDrivers::Wch std::uint8_t wchLinkUsbInterfaceNumber; std::unique_ptr wchLinkUsbInterface = nullptr; std::unique_ptr wchLinkInterface = nullptr; + std::unique_ptr wchLinkProgrammingInterface = nullptr; std::unique_ptr<::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator> wchRiscVTranslator = nullptr; mutable std::optional cachedDeviceInfo = std::nullopt; diff --git a/src/Targets/RiscV/RiscV.cpp b/src/Targets/RiscV/RiscV.cpp index db5a4e1a..793b8556 100644 --- a/src/Targets/RiscV/RiscV.cpp +++ b/src/Targets/RiscV/RiscV.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "src/Helpers/Pair.hpp" #include "src/Services/StringService.hpp" @@ -40,6 +41,7 @@ namespace Targets::RiscV bool RiscV::supportsDebugTool(DebugTool* debugTool) { return debugTool->getRiscVDebugInterface(this->targetDescriptionFile, this->targetConfig) != nullptr + && debugTool->getRiscVProgramInterface(this->targetDescriptionFile, this->targetConfig) != nullptr && debugTool->getRiscVIdentificationInterface(this->targetDescriptionFile, this->targetConfig) != nullptr ; } @@ -247,8 +249,76 @@ namespace Targets::RiscV static_cast(startAddress + buffer.size()) - 1) ); - if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH && this->riscVProgramInterface) { - return this->riscVProgramInterface->writeFlashMemory(startAddress, buffer); + if ( + memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH + && this->isProgramMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + static_cast(buffer.size()) + ) + ) { + const auto alignmentSize = this->riscVProgramInterface->alignmentSize( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + static_cast(buffer.size()) + ); + + if (alignmentSize.has_value()) { + const auto bufferSize = static_cast(buffer.size()); + const auto alignedStartAddress = (startAddress / *alignmentSize) * *alignmentSize; + const auto alignedBufferSize = static_cast(std::ceil( + static_cast(bufferSize) / static_cast(*alignmentSize) + ) * *alignmentSize); + + if (alignedStartAddress != startAddress || alignedBufferSize != bufferSize) { + auto alignedBuffer = (alignedStartAddress < startAddress) + ? this->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + alignedStartAddress, + (startAddress - alignedStartAddress), + {} + ) + : TargetMemoryBuffer{}; + + alignedBuffer.resize(alignedBufferSize); + + std::copy( + buffer.begin(), + buffer.end(), + alignedBuffer.begin() + (startAddress - alignedStartAddress) + ); + + const auto dataBack = this->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress + bufferSize, + alignedBufferSize - bufferSize - (startAddress - alignedStartAddress), + {} + ); + std::copy( + dataBack.begin(), + dataBack.end(), + alignedBuffer.begin() + (startAddress - alignedStartAddress) + bufferSize + ); + + return this->riscVProgramInterface->writeProgramMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + alignedStartAddress, + alignedBuffer + ); + } + } + + return this->riscVProgramInterface->writeProgramMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + buffer + ); } return this->riscVDebugInterface->writeMemory( @@ -272,7 +342,7 @@ namespace Targets::RiscV const TargetAddressSpaceDescriptor& addressSpaceDescriptor, const TargetMemorySegmentDescriptor& memorySegmentDescriptor ) { - + this->riscVProgramInterface->eraseProgramMemory(addressSpaceDescriptor, memorySegmentDescriptor); } TargetExecutionState RiscV::getExecutionState() { @@ -333,15 +403,15 @@ namespace Targets::RiscV } void RiscV::enableProgrammingMode() { - + this->programmingMode = true; } void RiscV::disableProgrammingMode() { - + this->programmingMode = false; } bool RiscV::programmingModeEnabled() { - return false; + return this->programmingMode; } const TargetMemorySegmentDescriptor& RiscV::resolveRegisterMemorySegmentDescriptor( diff --git a/src/Targets/RiscV/RiscV.hpp b/src/Targets/RiscV/RiscV.hpp index b42f6130..41aa113b 100644 --- a/src/Targets/RiscV/RiscV.hpp +++ b/src/Targets/RiscV/RiscV.hpp @@ -130,6 +130,8 @@ namespace Targets::RiscV */ TargetAddressSpaceDescriptor sysAddressSpaceDescriptor; + bool programmingMode = false; + const TargetMemorySegmentDescriptor& resolveRegisterMemorySegmentDescriptor( const TargetRegisterDescriptor& regDescriptor, const TargetAddressSpaceDescriptor& addressSpaceDescriptor diff --git a/src/Targets/RiscV/TargetDescriptionFile.cpp b/src/Targets/RiscV/TargetDescriptionFile.cpp index fc75ff1a..d8996d7d 100644 --- a/src/Targets/RiscV/TargetDescriptionFile.cpp +++ b/src/Targets/RiscV/TargetDescriptionFile.cpp @@ -6,7 +6,11 @@ namespace Targets::RiscV : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) {} + const TargetDescription::AddressSpace& TargetDescriptionFile::getSystemAddressSpace() const { + return this->getAddressSpace("system"); + } + TargetAddressSpaceDescriptor TargetDescriptionFile::getSystemAddressSpaceDescriptor() const { - return this->targetAddressSpaceDescriptorFromAddressSpace(this->getAddressSpace("system")); + return this->targetAddressSpaceDescriptorFromAddressSpace(this->getSystemAddressSpace()); } } diff --git a/src/Targets/RiscV/TargetDescriptionFile.hpp b/src/Targets/RiscV/TargetDescriptionFile.hpp index 34ee26b6..df382d9f 100644 --- a/src/Targets/RiscV/TargetDescriptionFile.hpp +++ b/src/Targets/RiscV/TargetDescriptionFile.hpp @@ -14,6 +14,8 @@ namespace Targets::RiscV public: explicit TargetDescriptionFile(const std::string& xmlFilePath); + [[nodiscard]] const TargetDescription::AddressSpace& getSystemAddressSpace() const; + [[nodiscard]] TargetAddressSpaceDescriptor getSystemAddressSpaceDescriptor() const; }; } diff --git a/src/Targets/RiscV/Wch/WchRiscV.cpp b/src/Targets/RiscV/Wch/WchRiscV.cpp index ffa0dfb1..75c7fadf 100644 --- a/src/Targets/RiscV/Wch/WchRiscV.cpp +++ b/src/Targets/RiscV/Wch/WchRiscV.cpp @@ -14,6 +14,8 @@ namespace Targets::RiscV::Wch ) : RiscV(targetConfig, targetDescriptionFile) , targetDescriptionFile(std::move(targetDescriptionFile)) + , programMemorySegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_program_memory")) + , mappedProgramMemorySegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("mapped_progmem")) {} void WchRiscV::activate() { @@ -85,4 +87,29 @@ namespace Targets::RiscV::Wch return descriptor; } + + void WchRiscV::writeMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + const TargetMemoryBuffer& buffer + ) { + /* + * WCH targets have a memory segment that maps to either the program memory segment or the boot program + * memory segment. + * + * Reading directly from the mapped memory segment is fine, but we cannot write to it - the operation just + * fails silently. We handle this by altering the write operation so that we write to the appropriate, + * non-mapped segment. + */ + if (memorySegmentDescriptor == this->mappedProgramMemorySegmentDescriptor) { + const auto newAddress = startAddress - this->mappedProgramMemorySegmentDescriptor.addressRange.startAddress + + this->programMemorySegmentDescriptor.addressRange.startAddress; + assert(this->programMemorySegmentDescriptor.addressRange.contains(newAddress)); + + return RiscV::writeMemory(addressSpaceDescriptor, this->programMemorySegmentDescriptor, newAddress, buffer); + } + + return RiscV::writeMemory(addressSpaceDescriptor, memorySegmentDescriptor, startAddress, buffer); + } } diff --git a/src/Targets/RiscV/Wch/WchRiscV.hpp b/src/Targets/RiscV/Wch/WchRiscV.hpp index 753fce5d..c64a15e0 100644 --- a/src/Targets/RiscV/Wch/WchRiscV.hpp +++ b/src/Targets/RiscV/Wch/WchRiscV.hpp @@ -10,7 +10,7 @@ namespace Targets::RiscV::Wch { -class WchRiscV: public ::Targets::RiscV::RiscV + class WchRiscV: public ::Targets::RiscV::RiscV { public: WchRiscV(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile); @@ -19,8 +19,18 @@ class WchRiscV: public ::Targets::RiscV::RiscV void postActivate() override; TargetDescriptor targetDescriptor() override; + void writeMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + const TargetMemoryBuffer& buffer + ) override; + protected: TargetDescriptionFile targetDescriptionFile; std::optional> variant = std::nullopt; + + const TargetMemorySegmentDescriptor& programMemorySegmentDescriptor; + const TargetMemorySegmentDescriptor& mappedProgramMemorySegmentDescriptor; }; } diff --git a/src/Targets/TargetMemory.hpp b/src/Targets/TargetMemory.hpp index c5a2c53b..63e66e6a 100644 --- a/src/Targets/TargetMemory.hpp +++ b/src/Targets/TargetMemory.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ namespace Targets using TargetMemorySize = std::uint32_t; using TargetStackPointer = TargetMemoryAddress; using TargetMemoryBuffer = std::vector; + using TargetMemoryBufferSpan = std::span; using TargetAddressSpaceId = std::size_t; using TargetMemorySegmentId = std::size_t;