Flash programming support for WCH-LinkE tool
This commit is contained in:
@@ -43,8 +43,9 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.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/WchLinkInterface.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkProgrammingInterface.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkToolConfig.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkToolConfig.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp
|
||||||
|
|||||||
@@ -1,23 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "src/Targets/TargetMemory.hpp"
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||||
|
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||||
|
|
||||||
namespace DebugToolDrivers::TargetInterfaces::RiscV
|
namespace DebugToolDrivers::TargetInterfaces::RiscV
|
||||||
{
|
{
|
||||||
class RiscVProgramInterface
|
class RiscVProgramInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
virtual std::optional<Targets::TargetMemorySize> alignmentSize(
|
||||||
* Should write to the target's FLASH memory.
|
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
*
|
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
* @param startAddress
|
|
||||||
* @param buffer
|
|
||||||
*/
|
|
||||||
virtual void writeFlashMemory(
|
|
||||||
Targets::TargetMemoryAddress startAddress,
|
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;
|
) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||||
|
{
|
||||||
|
class EndProgrammingSession: public Command<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EndProgrammingSession()
|
||||||
|
: Command(0x02)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x08
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||||
|
{
|
||||||
|
class EndRamCodeWrite: public Command<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EndRamCodeWrite()
|
||||||
|
: Command(0x02)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x07
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||||
|
{
|
||||||
|
class EraseChip: public Command<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EraseChip()
|
||||||
|
: Command(0x02)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x01
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,10 +11,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
|||||||
class PreparePartialFlashPageWrite: public Command<std::array<unsigned char, 5>>
|
class PreparePartialFlashPageWrite: public Command<std::array<unsigned char, 5>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PreparePartialFlashPageWrite(
|
PreparePartialFlashPageWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes)
|
||||||
Targets::TargetMemoryAddress startAddress,
|
|
||||||
std::uint8_t bytes
|
|
||||||
)
|
|
||||||
: Command(0x0A)
|
: Command(0x0A)
|
||||||
{
|
{
|
||||||
this->payload = {
|
this->payload = {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#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 SetFlashWriteRegion: public Command<std::array<unsigned char, 8>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SetFlashWriteRegion(Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize bytes)
|
||||||
|
: Command(0x01)
|
||||||
|
{
|
||||||
|
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 >> 24),
|
||||||
|
static_cast<unsigned char>(bytes >> 16),
|
||||||
|
static_cast<unsigned char>(bytes >> 8),
|
||||||
|
static_cast<unsigned char>(bytes),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#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<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StartProgrammingSession()
|
||||||
|
: Command(0x02)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x06
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||||
|
{
|
||||||
|
class StartRamCodeWrite: public Command<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StartRamCodeWrite()
|
||||||
|
: Command(0x02)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x05
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||||
|
{
|
||||||
|
class WriteFlash: public Command<std::array<unsigned char, 1>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WriteFlash()
|
||||||
|
: Command(0x02)
|
||||||
|
{
|
||||||
|
this->payload = {
|
||||||
|
0x02
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace DebugToolDrivers::Wch::Protocols::WchLink::FlashProgramOpcodes
|
||||||
|
{
|
||||||
|
static constexpr auto FLASH_OP1 = std::to_array<unsigned char>({
|
||||||
|
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<unsigned char>({
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -9,6 +9,13 @@
|
|||||||
#include "Commands/SetClockSpeed.hpp"
|
#include "Commands/SetClockSpeed.hpp"
|
||||||
#include "Commands/DebugModuleInterfaceOperation.hpp"
|
#include "Commands/DebugModuleInterfaceOperation.hpp"
|
||||||
#include "Commands/PreparePartialFlashPageWrite.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/Helpers/BiMap.hpp"
|
||||||
#include "src/Services/StringService.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
@@ -136,18 +143,23 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
|||||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"};
|
throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"};
|
||||||
}
|
}
|
||||||
|
|
||||||
void WchLinkInterface::writeFlashMemory(
|
void WchLinkInterface::writePartialPage(
|
||||||
Targets::TargetMemoryAddress startAddress,
|
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<Targets::TargetMemorySize>(buffer.size());
|
const auto bufferSize = static_cast<Targets::TargetMemorySize>(buffer.size());
|
||||||
const auto packetsRequired = static_cast<std::uint32_t>(
|
const auto packetsRequired = static_cast<std::uint32_t>(
|
||||||
std::ceil(static_cast<float>(bufferSize) / static_cast<float>(packetSize))
|
std::ceil(static_cast<float>(bufferSize) / static_cast<float>(packetSize))
|
||||||
);
|
);
|
||||||
|
|
||||||
for (auto i = std::uint32_t{0}; i < packetsRequired; ++i) {
|
for (auto i = std::uint32_t{0}; i < packetsRequired; ++i) {
|
||||||
const auto segmentSize = static_cast<std::uint8_t>(std::min(bufferSize - (i * packetSize), packetSize));
|
const auto segmentSize = static_cast<std::uint8_t>(
|
||||||
|
std::min(
|
||||||
|
static_cast<std::uint8_t>(bufferSize - (i * packetSize)),
|
||||||
|
packetSize
|
||||||
|
)
|
||||||
|
);
|
||||||
const auto response = this->sendCommandAndWaitForResponse(
|
const auto response = this->sendCommandAndWaitForResponse(
|
||||||
Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize}
|
Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize}
|
||||||
);
|
);
|
||||||
@@ -160,33 +172,87 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
|||||||
|
|
||||||
this->usbInterface.writeBulk(
|
this->usbInterface.writeBulk(
|
||||||
WchLinkInterface::USB_DATA_ENDPOINT_OUT,
|
WchLinkInterface::USB_DATA_ENDPOINT_OUT,
|
||||||
std::vector<unsigned char>{
|
buffer.subspan(packetSize * i, segmentSize),
|
||||||
buffer.begin() + (packetSize * i),
|
this->dataEndpointMaxPacketSize
|
||||||
buffer.begin() + (packetSize * i) + segmentSize
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
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 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.
|
* this. All I know is that these values indicate a successful write.
|
||||||
*/
|
*/
|
||||||
if (
|
if (rawResponse[0] != 0x41 || rawResponse[1] != 0x01 || rawResponse[2] != 0x01 || rawResponse[3] != 0x02) {
|
||||||
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 page write failed"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WchLinkInterface::writeFullPage(
|
||||||
|
Targets::TargetMemoryAddress startAddress,
|
||||||
|
Targets::TargetMemoryBufferSpan buffer,
|
||||||
|
Targets::TargetMemorySize pageSize,
|
||||||
|
std::span<const unsigned char> flashProgramOpcodes
|
||||||
|
) {
|
||||||
|
assert((buffer.size() % pageSize) == 0);
|
||||||
|
|
||||||
|
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
|
||||||
|
* 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) {
|
void WchLinkInterface::setClockSpeed(WchLinkTargetClockSpeed speed) {
|
||||||
const auto speedIdsBySpeed = BiMap<WchLinkTargetClockSpeed, std::uint8_t>{
|
const auto speedIdsBySpeed = BiMap<WchLinkTargetClockSpeed, std::uint8_t>{
|
||||||
{WchLinkTargetClockSpeed::CLK_6000_KHZ, 0x01},
|
{WchLinkTargetClockSpeed::CLK_6000_KHZ, 0x01},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp"
|
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp"
|
||||||
@@ -23,17 +24,11 @@
|
|||||||
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface, a
|
* Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface, and a
|
||||||
* programming interface and a target identification interface.
|
* 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.
|
|
||||||
*/
|
*/
|
||||||
class WchLinkInterface
|
class WchLinkInterface
|
||||||
: public ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTransportModuleInterface
|
: public ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTransportModuleInterface
|
||||||
, public TargetInterfaces::RiscV::RiscVProgramInterface
|
|
||||||
, public TargetInterfaces::RiscV::RiscVIdentificationInterface
|
, public TargetInterfaces::RiscV::RiscVIdentificationInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -52,10 +47,16 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
|||||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value
|
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
void writeFlashMemory(
|
void writePartialPage(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer);
|
||||||
|
|
||||||
|
void writeFullPage(
|
||||||
Targets::TargetMemoryAddress startAddress,
|
Targets::TargetMemoryAddress startAddress,
|
||||||
const Targets::TargetMemoryBuffer& buffer
|
Targets::TargetMemoryBufferSpan buffer,
|
||||||
) override;
|
Targets::TargetMemorySize pageSize,
|
||||||
|
std::span<const unsigned char> flashProgramOpcodes
|
||||||
|
);
|
||||||
|
|
||||||
|
void eraseChip();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::uint8_t USB_COMMAND_ENDPOINT_IN = 0x81;
|
static constexpr std::uint8_t USB_COMMAND_ENDPOINT_IN = 0x81;
|
||||||
@@ -90,15 +91,23 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
|||||||
|
|
||||||
template <class CommandType>
|
template <class CommandType>
|
||||||
auto sendCommandAndWaitForResponse(const CommandType& command) {
|
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) {
|
if (rawCommand.size() > this->commandEndpointMaxPacketSize) {
|
||||||
throw Exceptions::DeviceCommunicationFailure{
|
throw Exceptions::DeviceCommunicationFailure{
|
||||||
"Raw command size exceeds maximum packet size for command endpoint"
|
"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);
|
const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_IN);
|
||||||
|
|
||||||
|
|||||||
@@ -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<Targets::TargetMemorySize> 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<const unsigned char> 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 + "\")"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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<Targets::TargetMemorySize> 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<const unsigned char> flashProgramOpcodes;
|
||||||
|
Targets::TargetMemorySize programmingPacketSize;
|
||||||
|
|
||||||
|
static std::span<const unsigned char> getFlashProgramOpcodes(const std::string& key);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -87,11 +87,17 @@ namespace DebugToolDrivers::Wch
|
|||||||
return this->wchRiscVTranslator.get();
|
return this->wchRiscVTranslator.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVProgramInterface(
|
Protocols::WchLink::WchLinkProgrammingInterface* WchLinkBase::getRiscVProgramInterface(
|
||||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||||
) {
|
) {
|
||||||
return this->wchLinkInterface.get();
|
if (!this->wchLinkProgrammingInterface) {
|
||||||
|
this->wchLinkProgrammingInterface = std::make_unique<Protocols::WchLink::WchLinkProgrammingInterface>(
|
||||||
|
*(this->wchLinkInterface),
|
||||||
|
targetDescriptionFile
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this->wchLinkProgrammingInterface.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVIdentificationInterface(
|
Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVIdentificationInterface(
|
||||||
|
|||||||
@@ -12,8 +12,11 @@
|
|||||||
#include "src/ProjectConfig.hpp"
|
#include "src/ProjectConfig.hpp"
|
||||||
|
|
||||||
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||||
|
#include "Protocols/WchLink/WchLinkProgrammingInterface.hpp"
|
||||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp"
|
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp"
|
||||||
|
|
||||||
|
#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp"
|
||||||
|
|
||||||
#include "WchGeneric.hpp"
|
#include "WchGeneric.hpp"
|
||||||
#include "DeviceInfo.hpp"
|
#include "DeviceInfo.hpp"
|
||||||
|
|
||||||
@@ -57,7 +60,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Protocols::WchLink::WchLinkInterface* getRiscVProgramInterface(
|
Protocols::WchLink::WchLinkProgrammingInterface* getRiscVProgramInterface(
|
||||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||||
) override;
|
) override;
|
||||||
@@ -76,6 +79,7 @@ namespace DebugToolDrivers::Wch
|
|||||||
std::uint8_t wchLinkUsbInterfaceNumber;
|
std::uint8_t wchLinkUsbInterfaceNumber;
|
||||||
std::unique_ptr<Usb::UsbInterface> wchLinkUsbInterface = nullptr;
|
std::unique_ptr<Usb::UsbInterface> wchLinkUsbInterface = nullptr;
|
||||||
std::unique_ptr<Protocols::WchLink::WchLinkInterface> wchLinkInterface = nullptr;
|
std::unique_ptr<Protocols::WchLink::WchLinkInterface> wchLinkInterface = nullptr;
|
||||||
|
std::unique_ptr<Protocols::WchLink::WchLinkProgrammingInterface> wchLinkProgrammingInterface = nullptr;
|
||||||
std::unique_ptr<::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator> wchRiscVTranslator = nullptr;
|
std::unique_ptr<::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator> wchRiscVTranslator = nullptr;
|
||||||
|
|
||||||
mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt;
|
mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "src/Helpers/Pair.hpp"
|
#include "src/Helpers/Pair.hpp"
|
||||||
#include "src/Services/StringService.hpp"
|
#include "src/Services/StringService.hpp"
|
||||||
@@ -40,6 +41,7 @@ namespace Targets::RiscV
|
|||||||
bool RiscV::supportsDebugTool(DebugTool* debugTool) {
|
bool RiscV::supportsDebugTool(DebugTool* debugTool) {
|
||||||
return
|
return
|
||||||
debugTool->getRiscVDebugInterface(this->targetDescriptionFile, this->targetConfig) != nullptr
|
debugTool->getRiscVDebugInterface(this->targetDescriptionFile, this->targetConfig) != nullptr
|
||||||
|
&& debugTool->getRiscVProgramInterface(this->targetDescriptionFile, this->targetConfig) != nullptr
|
||||||
&& debugTool->getRiscVIdentificationInterface(this->targetDescriptionFile, this->targetConfig) != nullptr
|
&& debugTool->getRiscVIdentificationInterface(this->targetDescriptionFile, this->targetConfig) != nullptr
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -247,8 +249,76 @@ namespace Targets::RiscV
|
|||||||
static_cast<TargetMemoryAddress>(startAddress + buffer.size()) - 1)
|
static_cast<TargetMemoryAddress>(startAddress + buffer.size()) - 1)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH && this->riscVProgramInterface) {
|
if (
|
||||||
return this->riscVProgramInterface->writeFlashMemory(startAddress, buffer);
|
memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH
|
||||||
|
&& this->isProgramMemory(
|
||||||
|
addressSpaceDescriptor,
|
||||||
|
memorySegmentDescriptor,
|
||||||
|
startAddress,
|
||||||
|
static_cast<TargetMemorySize>(buffer.size())
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const auto alignmentSize = this->riscVProgramInterface->alignmentSize(
|
||||||
|
addressSpaceDescriptor,
|
||||||
|
memorySegmentDescriptor,
|
||||||
|
startAddress,
|
||||||
|
static_cast<TargetMemorySize>(buffer.size())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (alignmentSize.has_value()) {
|
||||||
|
const auto bufferSize = static_cast<TargetMemorySize>(buffer.size());
|
||||||
|
const auto alignedStartAddress = (startAddress / *alignmentSize) * *alignmentSize;
|
||||||
|
const auto alignedBufferSize = static_cast<TargetMemorySize>(std::ceil(
|
||||||
|
static_cast<double>(bufferSize) / static_cast<double>(*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(
|
return this->riscVDebugInterface->writeMemory(
|
||||||
@@ -272,7 +342,7 @@ namespace Targets::RiscV
|
|||||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||||
) {
|
) {
|
||||||
|
this->riscVProgramInterface->eraseProgramMemory(addressSpaceDescriptor, memorySegmentDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetExecutionState RiscV::getExecutionState() {
|
TargetExecutionState RiscV::getExecutionState() {
|
||||||
@@ -333,15 +403,15 @@ namespace Targets::RiscV
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RiscV::enableProgrammingMode() {
|
void RiscV::enableProgrammingMode() {
|
||||||
|
this->programmingMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RiscV::disableProgrammingMode() {
|
void RiscV::disableProgrammingMode() {
|
||||||
|
this->programmingMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RiscV::programmingModeEnabled() {
|
bool RiscV::programmingModeEnabled() {
|
||||||
return false;
|
return this->programmingMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TargetMemorySegmentDescriptor& RiscV::resolveRegisterMemorySegmentDescriptor(
|
const TargetMemorySegmentDescriptor& RiscV::resolveRegisterMemorySegmentDescriptor(
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ namespace Targets::RiscV
|
|||||||
*/
|
*/
|
||||||
TargetAddressSpaceDescriptor sysAddressSpaceDescriptor;
|
TargetAddressSpaceDescriptor sysAddressSpaceDescriptor;
|
||||||
|
|
||||||
|
bool programmingMode = false;
|
||||||
|
|
||||||
const TargetMemorySegmentDescriptor& resolveRegisterMemorySegmentDescriptor(
|
const TargetMemorySegmentDescriptor& resolveRegisterMemorySegmentDescriptor(
|
||||||
const TargetRegisterDescriptor& regDescriptor,
|
const TargetRegisterDescriptor& regDescriptor,
|
||||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ namespace Targets::RiscV
|
|||||||
: Targets::TargetDescription::TargetDescriptionFile(xmlFilePath)
|
: Targets::TargetDescription::TargetDescriptionFile(xmlFilePath)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
const TargetDescription::AddressSpace& TargetDescriptionFile::getSystemAddressSpace() const {
|
||||||
|
return this->getAddressSpace("system");
|
||||||
|
}
|
||||||
|
|
||||||
TargetAddressSpaceDescriptor TargetDescriptionFile::getSystemAddressSpaceDescriptor() const {
|
TargetAddressSpaceDescriptor TargetDescriptionFile::getSystemAddressSpaceDescriptor() const {
|
||||||
return this->targetAddressSpaceDescriptorFromAddressSpace(this->getAddressSpace("system"));
|
return this->targetAddressSpaceDescriptorFromAddressSpace(this->getSystemAddressSpace());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ namespace Targets::RiscV
|
|||||||
public:
|
public:
|
||||||
explicit TargetDescriptionFile(const std::string& xmlFilePath);
|
explicit TargetDescriptionFile(const std::string& xmlFilePath);
|
||||||
|
|
||||||
|
[[nodiscard]] const TargetDescription::AddressSpace& getSystemAddressSpace() const;
|
||||||
|
|
||||||
[[nodiscard]] TargetAddressSpaceDescriptor getSystemAddressSpaceDescriptor() const;
|
[[nodiscard]] TargetAddressSpaceDescriptor getSystemAddressSpaceDescriptor() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ namespace Targets::RiscV::Wch
|
|||||||
)
|
)
|
||||||
: RiscV(targetConfig, targetDescriptionFile)
|
: RiscV(targetConfig, targetDescriptionFile)
|
||||||
, targetDescriptionFile(std::move(targetDescriptionFile))
|
, targetDescriptionFile(std::move(targetDescriptionFile))
|
||||||
|
, programMemorySegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_program_memory"))
|
||||||
|
, mappedProgramMemorySegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("mapped_progmem"))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void WchRiscV::activate() {
|
void WchRiscV::activate() {
|
||||||
@@ -85,4 +87,29 @@ namespace Targets::RiscV::Wch
|
|||||||
|
|
||||||
return descriptor;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace Targets::RiscV::Wch
|
namespace Targets::RiscV::Wch
|
||||||
{
|
{
|
||||||
class WchRiscV: public ::Targets::RiscV::RiscV
|
class WchRiscV: public ::Targets::RiscV::RiscV
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WchRiscV(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
WchRiscV(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile);
|
||||||
@@ -19,8 +19,18 @@ class WchRiscV: public ::Targets::RiscV::RiscV
|
|||||||
void postActivate() override;
|
void postActivate() override;
|
||||||
TargetDescriptor targetDescriptor() override;
|
TargetDescriptor targetDescriptor() override;
|
||||||
|
|
||||||
|
void writeMemory(
|
||||||
|
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||||
|
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||||
|
TargetMemoryAddress startAddress,
|
||||||
|
const TargetMemoryBuffer& buffer
|
||||||
|
) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TargetDescriptionFile targetDescriptionFile;
|
TargetDescriptionFile targetDescriptionFile;
|
||||||
std::optional<std::reference_wrapper<const TargetDescription::Variant>> variant = std::nullopt;
|
std::optional<std::reference_wrapper<const TargetDescription::Variant>> variant = std::nullopt;
|
||||||
|
|
||||||
|
const TargetMemorySegmentDescriptor& programMemorySegmentDescriptor;
|
||||||
|
const TargetMemorySegmentDescriptor& mappedProgramMemorySegmentDescriptor;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -13,6 +14,7 @@ namespace Targets
|
|||||||
using TargetMemorySize = std::uint32_t;
|
using TargetMemorySize = std::uint32_t;
|
||||||
using TargetStackPointer = TargetMemoryAddress;
|
using TargetStackPointer = TargetMemoryAddress;
|
||||||
using TargetMemoryBuffer = std::vector<unsigned char>;
|
using TargetMemoryBuffer = std::vector<unsigned char>;
|
||||||
|
using TargetMemoryBufferSpan = std::span<const TargetMemoryBuffer::value_type>;
|
||||||
|
|
||||||
using TargetAddressSpaceId = std::size_t;
|
using TargetAddressSpaceId = std::size_t;
|
||||||
using TargetMemorySegmentId = std::size_t;
|
using TargetMemorySegmentId = std::size_t;
|
||||||
|
|||||||
Reference in New Issue
Block a user