Consistent casing in directory names
This commit is contained in:
21
src/DebugToolDrivers/Wch/DeviceInfo.hpp
Normal file
21
src/DebugToolDrivers/Wch/DeviceInfo.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/WchFirmwareVersion.hpp"
|
||||
#include "src/DebugToolDrivers/Wch/WchGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
class DeviceInfo
|
||||
{
|
||||
public:
|
||||
WchFirmwareVersion firmwareVersion;
|
||||
std::optional<WchLinkVariant> variant;
|
||||
|
||||
explicit DeviceInfo(WchFirmwareVersion firmwareVersion, std::optional<WchLinkVariant> variant)
|
||||
: firmwareVersion(firmwareVersion)
|
||||
, variant(variant)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Responses/Response.hpp"
|
||||
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
{
|
||||
template <class PayloadContainerType = std::vector<unsigned char>>
|
||||
class Command
|
||||
{
|
||||
static_assert(
|
||||
std::is_same_v<typename PayloadContainerType::value_type, unsigned char>,
|
||||
"Invalid payload container type"
|
||||
);
|
||||
|
||||
public:
|
||||
using ExpectedResponseType = Responses::Response;
|
||||
|
||||
std::uint8_t commandId;
|
||||
PayloadContainerType payload;
|
||||
|
||||
Command(std::uint8_t commandId, PayloadContainerType&& payload)
|
||||
: commandId(commandId)
|
||||
, payload(std::move(payload))
|
||||
{};
|
||||
|
||||
explicit Command(std::uint8_t commandId)
|
||||
: commandId(commandId)
|
||||
{};
|
||||
|
||||
virtual ~Command() = default;
|
||||
|
||||
Command(const Command& other) = default;
|
||||
Command(Command&& other) noexcept = default;
|
||||
|
||||
Command& operator = (const Command& other) = default;
|
||||
Command& operator = (Command&& other) noexcept = default;
|
||||
|
||||
[[nodiscard]] auto getRawCommand() const {
|
||||
assert(this->payload.size() <= 256);
|
||||
|
||||
auto rawCommand = std::vector<unsigned char>(3 + this->payload.size());
|
||||
|
||||
rawCommand[0] = 0x81;
|
||||
rawCommand[1] = this->commandId;
|
||||
rawCommand[2] = static_cast<std::uint8_t>(this->payload.size());
|
||||
|
||||
if (!this->payload.empty()) {
|
||||
std::copy(this->payload.begin(), this->payload.end(), rawCommand.begin() + 3);
|
||||
}
|
||||
|
||||
return rawCommand;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands::Control
|
||||
{
|
||||
class AttachTarget: public Command<std::array<unsigned char, 1>>
|
||||
{
|
||||
public:
|
||||
AttachTarget()
|
||||
: Command(0x0d)
|
||||
{
|
||||
this->payload = {
|
||||
0x02
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands::Control
|
||||
{
|
||||
class DetachTarget: public Command<std::array<unsigned char, 1>>
|
||||
{
|
||||
public:
|
||||
DetachTarget()
|
||||
: Command(0x0d)
|
||||
{
|
||||
this->payload = {
|
||||
0xff
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands::Control
|
||||
{
|
||||
class GetDeviceInfo: public Command<std::array<unsigned char, 1>>
|
||||
{
|
||||
public:
|
||||
GetDeviceInfo()
|
||||
: Command(0x0d)
|
||||
{
|
||||
this->payload = {
|
||||
0x01
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands::Control
|
||||
{
|
||||
class PostAttach: public Command<std::array<unsigned char, 1>>
|
||||
{
|
||||
public:
|
||||
PostAttach()
|
||||
: Command(0x0d)
|
||||
{
|
||||
this->payload = {
|
||||
0x03
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
{
|
||||
class DebugModuleInterfaceOperation: public Command<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseType = Responses::DebugModuleInterfaceOperationResponse;
|
||||
|
||||
DebugModuleInterfaceOperation(
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperation operation,
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address,
|
||||
std::optional<DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue> value = std::nullopt
|
||||
)
|
||||
: Command(0x08)
|
||||
{
|
||||
if (!value.has_value()) {
|
||||
value = 0x00;
|
||||
}
|
||||
|
||||
this->payload = {
|
||||
address,
|
||||
static_cast<unsigned char>(*value >> 24),
|
||||
static_cast<unsigned char>(*value >> 16),
|
||||
static_cast<unsigned char>(*value >> 8),
|
||||
static_cast<unsigned char>(*value),
|
||||
static_cast<unsigned char>(operation),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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 EraseProgramMemory: public Command<std::array<unsigned char, 1>>
|
||||
{
|
||||
public:
|
||||
EraseProgramMemory()
|
||||
: Command(0x02)
|
||||
{
|
||||
this->payload = {
|
||||
0x01
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
{
|
||||
class PreparePartialFlashBlockWrite: public Command<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
PreparePartialFlashBlockWrite(Targets::TargetMemoryAddress startAddress, std::uint8_t bytes)
|
||||
: Command(
|
||||
0x0A,
|
||||
{
|
||||
static_cast<unsigned char>(startAddress >> 24),
|
||||
static_cast<unsigned char>(startAddress >> 16),
|
||||
static_cast<unsigned char>(startAddress >> 8),
|
||||
static_cast<unsigned char>(startAddress),
|
||||
static_cast<unsigned char>(bytes),
|
||||
}
|
||||
)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/Protocols/WchLink/Commands/Command.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
{
|
||||
class SetClockSpeed: public Command<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
SetClockSpeed(std::uint8_t targetGroupId, std::uint8_t speedId)
|
||||
: Command(0x0c)
|
||||
{
|
||||
this->payload = {
|
||||
targetGroupId,
|
||||
speedId,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Responses
|
||||
{
|
||||
class DebugModuleInterfaceOperationResponse
|
||||
{
|
||||
public:
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperationStatus operationStatus;
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address;
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value;
|
||||
|
||||
explicit DebugModuleInterfaceOperationResponse(const std::vector<unsigned char>& payload) {
|
||||
using DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperationStatus;
|
||||
|
||||
if (payload.size() != 6) {
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Unexpected response payload size for DMI operation command"
|
||||
};
|
||||
}
|
||||
|
||||
const auto status = payload[5];
|
||||
if (
|
||||
status != static_cast<unsigned char>(DmiOperationStatus::SUCCESS)
|
||||
&& status != static_cast<unsigned char>(DmiOperationStatus::FAILED)
|
||||
&& status != static_cast<unsigned char>(DmiOperationStatus::BUSY)
|
||||
) {
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Unknown DMI operation status returned: 0x" + Services::StringService::toHex(status)
|
||||
};
|
||||
}
|
||||
|
||||
this->operationStatus = static_cast<DmiOperationStatus>(status);
|
||||
this->address = payload[0];
|
||||
this->value = static_cast<DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue>(
|
||||
(payload[1] << 24) | (payload[2] << 16) | (payload[3] << 8) | (payload[4])
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Responses
|
||||
{
|
||||
class Response
|
||||
{
|
||||
public:
|
||||
std::vector<unsigned char> payload;
|
||||
|
||||
explicit Response(const std::vector<unsigned char>& payload)
|
||||
: payload(payload)
|
||||
{}
|
||||
};
|
||||
}
|
||||
249
src/DebugToolDrivers/Wch/Protocols/WchLink/WchLinkInterface.cpp
Normal file
249
src/DebugToolDrivers/Wch/Protocols/WchLink/WchLinkInterface.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
#include "WchLinkInterface.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
|
||||
#include "Commands/Control/GetDeviceInfo.hpp"
|
||||
#include "Commands/Control/AttachTarget.hpp"
|
||||
#include "Commands/Control/DetachTarget.hpp"
|
||||
#include "Commands/Control/PostAttach.hpp"
|
||||
#include "Commands/SetClockSpeed.hpp"
|
||||
#include "Commands/DebugModuleInterfaceOperation.hpp"
|
||||
#include "Commands/PreparePartialFlashBlockWrite.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/EraseProgramMemory.hpp"
|
||||
|
||||
#include "src/Helpers/BiMap.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
{
|
||||
using namespace ::DebugToolDrivers::Protocols::RiscVDebugSpec;
|
||||
using namespace Exceptions;
|
||||
|
||||
using DebugModule::DmiOperation;
|
||||
|
||||
WchLinkInterface::WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice)
|
||||
: usbInterface(usbInterface)
|
||||
, commandEndpointMaxPacketSize(usbDevice.getEndpointMaxPacketSize(WchLinkInterface::USB_COMMAND_ENDPOINT_OUT))
|
||||
, dataEndpointMaxPacketSize(usbDevice.getEndpointMaxPacketSize(WchLinkInterface::USB_DATA_ENDPOINT_OUT))
|
||||
{}
|
||||
|
||||
DeviceInfo WchLinkInterface::getDeviceInfo() {
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo{});
|
||||
if (response.payload.size() < 3) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Cannot construct DeviceInfo response - invalid payload"};
|
||||
}
|
||||
|
||||
static const auto variantsById = BiMap<std::uint8_t, WchLinkVariant>{
|
||||
{0x01, WchLinkVariant::LINK_CH549},
|
||||
{0x02, WchLinkVariant::LINK_E_CH32V307},
|
||||
{0x12, WchLinkVariant::LINK_E_CH32V307},
|
||||
{0x03, WchLinkVariant::LINK_S_CH32V203},
|
||||
};
|
||||
|
||||
return DeviceInfo{
|
||||
WchFirmwareVersion{response.payload[0], response.payload[1]},
|
||||
response.payload.size() >= 4
|
||||
? variantsById.valueAt(response.payload[2])
|
||||
: std::nullopt
|
||||
};
|
||||
}
|
||||
|
||||
void WchLinkInterface::setClockSpeed(WchLinkTargetClockSpeed speed, WchTargetId targetId) {
|
||||
const auto speedIdsBySpeed = BiMap<WchLinkTargetClockSpeed, std::uint8_t>{
|
||||
{WchLinkTargetClockSpeed::CLK_6000_KHZ, 0x01},
|
||||
{WchLinkTargetClockSpeed::CLK_4000_KHZ, 0x02},
|
||||
{WchLinkTargetClockSpeed::CLK_400_KHZ, 0x03},
|
||||
};
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::SetClockSpeed{targetId, speedIdsBySpeed.at(speed)}
|
||||
);
|
||||
|
||||
if (response.payload.size() != 1) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for SetClockSpeed command"};
|
||||
}
|
||||
|
||||
if (response.payload[0] != 0x01) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response for SetClockSpeed command"};
|
||||
}
|
||||
}
|
||||
|
||||
DebugModule::RegisterValue WchLinkInterface::readDebugModuleRegister(DebugModule::RegisterAddress address) {
|
||||
using DebugModule::DmiOperationStatus;
|
||||
|
||||
auto attempt = std::uint8_t{0};
|
||||
while (attempt < WchLinkInterface::DMI_OP_MAX_RETRY) {
|
||||
if (attempt > 0) {
|
||||
std::this_thread::sleep_for(this->dmiOpRetryDelay);
|
||||
}
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::DebugModuleInterfaceOperation{DmiOperation::READ, address}
|
||||
);
|
||||
|
||||
if (response.operationStatus == DmiOperationStatus::SUCCESS) {
|
||||
return response.value;
|
||||
}
|
||||
|
||||
if (response.operationStatus == DmiOperationStatus::FAILED) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation failed"};
|
||||
}
|
||||
|
||||
// Busy response...
|
||||
++attempt;
|
||||
}
|
||||
|
||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"};
|
||||
}
|
||||
|
||||
void WchLinkInterface::writeDebugModuleRegister(
|
||||
DebugModule::RegisterAddress address,
|
||||
DebugModule::RegisterValue value
|
||||
) {
|
||||
using DebugModule::DmiOperationStatus;
|
||||
|
||||
auto attempt = std::uint8_t{0};
|
||||
while (attempt < WchLinkInterface::DMI_OP_MAX_RETRY) {
|
||||
if (attempt > 0) {
|
||||
std::this_thread::sleep_for(this->dmiOpRetryDelay);
|
||||
}
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::DebugModuleInterfaceOperation{DmiOperation::WRITE, address, value}
|
||||
);
|
||||
|
||||
if (response.operationStatus == DmiOperationStatus::SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.operationStatus == DmiOperationStatus::FAILED) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation failed"};
|
||||
}
|
||||
|
||||
// Busy response...
|
||||
++attempt;
|
||||
}
|
||||
|
||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation timed out"};
|
||||
}
|
||||
|
||||
void WchLinkInterface::writeFlashPartialBlock(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
) {
|
||||
constexpr auto packetSize = WchLinkInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE;
|
||||
const auto bufferSize = static_cast<Targets::TargetMemorySize>(buffer.size());
|
||||
const auto packetsRequired = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<float>(bufferSize) / static_cast<float>(packetSize))
|
||||
);
|
||||
|
||||
for (auto i = std::size_t{0}; i < packetsRequired; ++i) {
|
||||
const auto segmentSize = std::min(
|
||||
static_cast<Targets::TargetMemorySize>(bufferSize - (i * packetSize)),
|
||||
packetSize
|
||||
);
|
||||
assert(segmentSize <= 0xFF);
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::PreparePartialFlashBlockWrite{
|
||||
static_cast<Targets::TargetMemorySize>(startAddress + (packetSize * i)),
|
||||
static_cast<std::uint8_t>(segmentSize)
|
||||
}
|
||||
);
|
||||
|
||||
if (response.payload.size() != 1) {
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Unexpected response payload size for PreparePartialFlashBlockWrite command"
|
||||
};
|
||||
}
|
||||
|
||||
this->usbInterface.writeBulk(
|
||||
WchLinkInterface::USB_DATA_ENDPOINT_OUT,
|
||||
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 block write"};
|
||||
}
|
||||
|
||||
/*
|
||||
* I have no idea what any of these bytes mean. I've not been able to find any documentation for this.
|
||||
* All I know is that these values indicate a successful write.
|
||||
*/
|
||||
if (rawResponse[0] != 0x41 || rawResponse[1] != 0x01 || rawResponse[2] != 0x01 || rawResponse[3] != 0x02) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Partial flash block write failed"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WchLinkInterface::writeFlashFullBlocks(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer,
|
||||
Targets::TargetMemorySize blockSize,
|
||||
std::span<const unsigned char> flashProgramOpcodes
|
||||
) {
|
||||
assert((buffer.size() % blockSize) == 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 block. 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, blockSize);
|
||||
|
||||
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 block write"};
|
||||
}
|
||||
|
||||
if (rawResponse[3] != 0x02 && rawResponse[3] != 0x04) {
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Flash block write failed - unexpected response (0x"
|
||||
+ Services::StringService::toHex(rawResponse[3]) + ")"
|
||||
};
|
||||
}
|
||||
|
||||
bytesWritten += length;
|
||||
}
|
||||
|
||||
this->sendCommandAndWaitForResponse(Commands::EndProgrammingSession{});
|
||||
}
|
||||
|
||||
void WchLinkInterface::eraseProgramMemory() {
|
||||
this->sendCommandAndWaitForResponse(Commands::EraseProgramMemory{});
|
||||
}
|
||||
}
|
||||
125
src/DebugToolDrivers/Wch/Protocols/WchLink/WchLinkInterface.hpp
Normal file
125
src/DebugToolDrivers/Wch/Protocols/WchLink/WchLinkInterface.hpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/Usb/UsbInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Usb/UsbDevice.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/WchGeneric.hpp"
|
||||
#include "src/DebugToolDrivers/Wch/DeviceInfo.hpp"
|
||||
|
||||
#include "Commands/Command.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
{
|
||||
/**
|
||||
* Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface.
|
||||
*/
|
||||
class WchLinkInterface
|
||||
: public ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTransportModuleInterface
|
||||
{
|
||||
public:
|
||||
static constexpr Targets::TargetMemorySize MAX_PARTIAL_BLOCK_WRITE_SIZE = 64;
|
||||
|
||||
WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice);
|
||||
|
||||
DeviceInfo getDeviceInfo();
|
||||
void setClockSpeed(WchLinkTargetClockSpeed speed, WchTargetId targetId);
|
||||
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue readDebugModuleRegister(
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address
|
||||
) override;
|
||||
void writeDebugModuleRegister(
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address,
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value
|
||||
) override;
|
||||
|
||||
void writeFlashPartialBlock(Targets::TargetMemoryAddress startAddress, Targets::TargetMemoryBufferSpan buffer);
|
||||
void writeFlashFullBlocks(
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer,
|
||||
Targets::TargetMemorySize blockSize,
|
||||
std::span<const unsigned char> flashProgramOpcodes
|
||||
);
|
||||
void eraseProgramMemory();
|
||||
|
||||
template <class CommandType>
|
||||
auto sendCommandAndWaitForResponse(const CommandType& command) {
|
||||
using Services::StringService;
|
||||
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,
|
||||
rawCommand,
|
||||
this->commandEndpointMaxPacketSize
|
||||
);
|
||||
|
||||
const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_IN);
|
||||
|
||||
if (rawResponse.size() < 4) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Invalid response size from device"};
|
||||
}
|
||||
|
||||
// The first byte of the response should be 0x82 (for success) or 0x81 (for failure)
|
||||
if (rawResponse[0] != 0x81 && rawResponse[0] != 0x82) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Invalid response code from device"};
|
||||
}
|
||||
|
||||
if (rawResponse[0] == 0x81) {
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Error response to command 0x" + StringService::toHex(command.commandId)
|
||||
};
|
||||
}
|
||||
|
||||
if (rawResponse[1] != command.commandId) {
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Missing/invalid command ID in response from device 0x" + StringService::toHex(rawResponse[1])
|
||||
+ " - expected: 0x" + StringService::toHex(command.commandId)
|
||||
};
|
||||
}
|
||||
|
||||
if ((rawResponse.size() - 3) != rawResponse[2]) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Actual response payload size mismatch"};
|
||||
}
|
||||
|
||||
return typename CommandType::ExpectedResponseType{
|
||||
std::vector<unsigned char>{rawResponse.begin() + 3, rawResponse.end()}
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr std::uint8_t USB_COMMAND_ENDPOINT_IN = 0x81;
|
||||
static constexpr std::uint8_t USB_COMMAND_ENDPOINT_OUT = 0x01;
|
||||
static constexpr std::uint8_t USB_DATA_ENDPOINT_IN = 0x82;
|
||||
static constexpr std::uint8_t USB_DATA_ENDPOINT_OUT = 0x02;
|
||||
static constexpr std::uint8_t DMI_OP_MAX_RETRY = 10;
|
||||
|
||||
Usb::UsbInterface& usbInterface;
|
||||
|
||||
std::uint16_t commandEndpointMaxPacketSize = 0;
|
||||
std::uint16_t dataEndpointMaxPacketSize = 0;
|
||||
// TODO: Move this into a config param
|
||||
std::chrono::microseconds dmiOpRetryDelay = std::chrono::microseconds{10};
|
||||
};
|
||||
}
|
||||
48
src/DebugToolDrivers/Wch/WchFirmwareVersion.hpp
Normal file
48
src/DebugToolDrivers/Wch/WchFirmwareVersion.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
class WchFirmwareVersion
|
||||
{
|
||||
public:
|
||||
std::uint8_t major = 0;
|
||||
std::uint8_t minor = 0;
|
||||
|
||||
WchFirmwareVersion() = default;
|
||||
WchFirmwareVersion(std::uint8_t major, std::uint8_t minor)
|
||||
: major{major}
|
||||
, minor{minor}
|
||||
{}
|
||||
|
||||
std::string toString() const {
|
||||
return std::to_string(this->major) + "." + std::to_string(this->minor);
|
||||
}
|
||||
|
||||
bool operator == (const WchFirmwareVersion& other) const {
|
||||
return this->major == other.major && this->minor == other.minor;
|
||||
}
|
||||
|
||||
bool operator != (const WchFirmwareVersion& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator < (const WchFirmwareVersion& other) const {
|
||||
return this->major < other.major || (this->major == other.major && this->minor < other.minor);
|
||||
}
|
||||
|
||||
bool operator > (const WchFirmwareVersion& other) const {
|
||||
return other < *this;
|
||||
}
|
||||
|
||||
bool operator <= (const WchFirmwareVersion& other) const {
|
||||
return !(other < *this);
|
||||
}
|
||||
|
||||
bool operator >= (const WchFirmwareVersion& other) const {
|
||||
return !(*this < other);
|
||||
}
|
||||
};
|
||||
}
|
||||
23
src/DebugToolDrivers/Wch/WchGeneric.hpp
Normal file
23
src/DebugToolDrivers/Wch/WchGeneric.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
using WchTargetId = std::uint8_t;
|
||||
using WchTargetVariantId = std::uint32_t;
|
||||
|
||||
enum class WchLinkVariant: std::uint8_t
|
||||
{
|
||||
LINK_CH549,
|
||||
LINK_E_CH32V307,
|
||||
LINK_S_CH32V203,
|
||||
};
|
||||
|
||||
enum class WchLinkTargetClockSpeed: std::uint8_t
|
||||
{
|
||||
CLK_400_KHZ,
|
||||
CLK_4000_KHZ,
|
||||
CLK_6000_KHZ,
|
||||
};
|
||||
}
|
||||
130
src/DebugToolDrivers/Wch/WchLinkBase.cpp
Normal file
130
src/DebugToolDrivers/Wch/WchLinkBase.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "WchLinkBase.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
|
||||
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
using Exceptions::DeviceInitializationFailure;
|
||||
|
||||
WchLinkBase::WchLinkBase(
|
||||
const DebugToolConfig& toolConfig,
|
||||
WchLinkVariant variant,
|
||||
std::uint16_t vendorId,
|
||||
std::uint16_t productId,
|
||||
std::uint16_t iapVendorId,
|
||||
std::uint16_t iapProductId,
|
||||
std::uint8_t wchLinkUsbInterfaceNumber
|
||||
)
|
||||
: UsbDevice(vendorId, productId)
|
||||
, toolConfig(WchLinkToolConfig{toolConfig})
|
||||
, iapVendorId(iapVendorId)
|
||||
, iapProductId(iapProductId)
|
||||
, variant(variant)
|
||||
, wchLinkUsbInterfaceNumber(wchLinkUsbInterfaceNumber)
|
||||
{}
|
||||
|
||||
void WchLinkBase::init() {
|
||||
if (this->toolConfig.exitIapMode && !UsbDevice::devicePresent(this->vendorId, this->productId)) {
|
||||
auto iapDevice = Usb::UsbDevice::tryDevice(this->iapVendorId, this->iapProductId);
|
||||
|
||||
if (iapDevice.has_value()) {
|
||||
Logger::warning("Found device in IAP mode - attempting exit operation");
|
||||
this->exitIapMode(*iapDevice);
|
||||
|
||||
Logger::info("Waiting for device to re-enumerate...");
|
||||
if (!Usb::UsbDevice::waitForDevice(this->vendorId, this->productId, std::chrono::seconds{8})) {
|
||||
throw DeviceInitializationFailure{"Timeout exceeded whilst waiting for device to re-enumerate"};
|
||||
}
|
||||
|
||||
Logger::info("Re-enumerated device found - IAP exit operation was successful");
|
||||
}
|
||||
}
|
||||
|
||||
UsbDevice::init();
|
||||
|
||||
this->detachKernelDriverFromInterface(this->wchLinkUsbInterfaceNumber);
|
||||
|
||||
this->wchLinkUsbInterface = std::make_unique<Usb::UsbInterface>(
|
||||
this->wchLinkUsbInterfaceNumber,
|
||||
this->libusbDeviceHandle.get()
|
||||
);
|
||||
|
||||
this->wchLinkUsbInterface->init();
|
||||
|
||||
this->wchLinkInterface = std::make_unique<Protocols::WchLink::WchLinkInterface>(
|
||||
*(this->wchLinkUsbInterface),
|
||||
*this
|
||||
);
|
||||
|
||||
if (this->getDeviceInfo().variant != this->variant) {
|
||||
throw DeviceInitializationFailure{
|
||||
"WCH-Link variant mismatch - device returned variant ID that doesn't match the " + this->getName()
|
||||
+ " variant ID"
|
||||
};
|
||||
}
|
||||
|
||||
this->initialised = true;
|
||||
}
|
||||
|
||||
void WchLinkBase::close() {
|
||||
if (this->wchLinkUsbInterface) {
|
||||
this->wchLinkUsbInterface->close();
|
||||
}
|
||||
|
||||
this->initialised = false;
|
||||
}
|
||||
|
||||
void WchLinkBase::postInit() {
|
||||
Logger::info("WCH-Link firmware version: " + this->getDeviceInfo().firmwareVersion.toString());
|
||||
}
|
||||
|
||||
bool WchLinkBase::isInitialised() const {
|
||||
return this->initialised;
|
||||
}
|
||||
|
||||
std::string WchLinkBase::getSerialNumber() {
|
||||
return UsbDevice::getSerialNumber();
|
||||
}
|
||||
|
||||
WchLinkDebugInterface* WchLinkBase::getRiscVDebugInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
|
||||
if (!this->wchLinkDebugInterface) {
|
||||
this->wchLinkDebugInterface = std::make_unique<WchLinkDebugInterface>(
|
||||
this->toolConfig,
|
||||
targetConfig,
|
||||
targetDescriptionFile,
|
||||
*(this->wchLinkInterface)
|
||||
);
|
||||
}
|
||||
return this->wchLinkDebugInterface.get();
|
||||
}
|
||||
|
||||
const DeviceInfo& WchLinkBase::getDeviceInfo() const {
|
||||
if (!this->cachedDeviceInfo.has_value()) {
|
||||
this->cachedDeviceInfo = this->wchLinkInterface->getDeviceInfo();
|
||||
}
|
||||
|
||||
return *(this->cachedDeviceInfo);
|
||||
}
|
||||
|
||||
void WchLinkBase::exitIapMode(UsbDevice& iapDevice) const {
|
||||
static constexpr auto IAP_INTERFACE_NUMBER = std::uint8_t{0};
|
||||
static constexpr auto IAP_COMMAND_ENDPOINT_ADDRESS = std::uint8_t{0x02};
|
||||
|
||||
auto interface = Usb::UsbInterface{IAP_INTERFACE_NUMBER, iapDevice.libusbDeviceHandle.get()};
|
||||
interface.init();
|
||||
|
||||
static constexpr auto COMMAND_BUFFER = std::to_array<unsigned char>({0x83});
|
||||
interface.writeBulk(IAP_COMMAND_ENDPOINT_ADDRESS, COMMAND_BUFFER, 64);
|
||||
}
|
||||
}
|
||||
72
src/DebugToolDrivers/Wch/WchLinkBase.hpp
Normal file
72
src/DebugToolDrivers/Wch/WchLinkBase.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||
#include "src/DebugToolDrivers/Usb/UsbDevice.hpp"
|
||||
#include "src/DebugToolDrivers/Usb/UsbInterface.hpp"
|
||||
|
||||
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||
|
||||
#include "WchLinkDebugInterface.hpp"
|
||||
|
||||
#include "WchLinkToolConfig.hpp"
|
||||
#include "src/ProjectConfig.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp"
|
||||
|
||||
#include "WchGeneric.hpp"
|
||||
#include "DeviceInfo.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
class WchLinkBase: public DebugTool, public Usb::UsbDevice
|
||||
{
|
||||
public:
|
||||
WchLinkBase(
|
||||
const DebugToolConfig& toolConfig,
|
||||
WchLinkVariant variant,
|
||||
std::uint16_t vendorId,
|
||||
std::uint16_t productId,
|
||||
std::uint16_t iapVendorId,
|
||||
std::uint16_t iapProductId,
|
||||
std::uint8_t wchLinkUsbInterfaceNumber
|
||||
);
|
||||
|
||||
void init() override;
|
||||
|
||||
void close() override;
|
||||
|
||||
void postInit() override;
|
||||
|
||||
[[nodiscard]] bool isInitialised() const override;
|
||||
|
||||
std::string getSerialNumber() override;
|
||||
|
||||
WchLinkDebugInterface* getRiscVDebugInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) override;
|
||||
|
||||
protected:
|
||||
WchLinkToolConfig toolConfig;
|
||||
std::uint16_t iapVendorId;
|
||||
std::uint16_t iapProductId;
|
||||
|
||||
bool initialised = false;
|
||||
|
||||
WchLinkVariant variant;
|
||||
|
||||
std::uint8_t wchLinkUsbInterfaceNumber;
|
||||
std::unique_ptr<Usb::UsbInterface> wchLinkUsbInterface = nullptr;
|
||||
std::unique_ptr<Protocols::WchLink::WchLinkInterface> wchLinkInterface = nullptr;
|
||||
std::unique_ptr<WchLinkDebugInterface> wchLinkDebugInterface = nullptr;
|
||||
|
||||
mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt;
|
||||
|
||||
const DeviceInfo& getDeviceInfo() const;
|
||||
void exitIapMode(Usb::UsbDevice& iapDevice) const;
|
||||
};
|
||||
}
|
||||
493
src/DebugToolDrivers/Wch/WchLinkDebugInterface.cpp
Normal file
493
src/DebugToolDrivers/Wch/WchLinkDebugInterface.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
#include "WchLinkDebugInterface.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Protocols/WchLink/Commands/Control/AttachTarget.hpp"
|
||||
#include "Protocols/WchLink/Commands/Control/DetachTarget.hpp"
|
||||
#include "Protocols/WchLink/Commands/Control/PostAttach.hpp"
|
||||
#include "Protocols/WchLink/Commands/Control/GetDeviceInfo.hpp"
|
||||
#include "Protocols/WchLink/Commands/DebugModuleInterfaceOperation.hpp"
|
||||
|
||||
#include "Protocols/WchLink/FlashProgramOpcodes.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/Opcodes/Opcode.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
#include "src/Targets/TargetDescription/Exceptions/InvalidTargetDescriptionDataException.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
using ::Targets::TargetExecutionState;
|
||||
using ::Targets::TargetMemoryAddress;
|
||||
using ::Targets::TargetMemoryAddressRange;
|
||||
using ::Targets::TargetMemorySize;
|
||||
using ::Targets::TargetMemoryBuffer;
|
||||
using ::Targets::TargetMemoryBufferSpan;
|
||||
using ::Targets::TargetStackPointer;
|
||||
using ::Targets::TargetAddressSpaceDescriptor;
|
||||
using ::Targets::TargetMemorySegmentDescriptor;
|
||||
using ::Targets::TargetProgramBreakpoint;
|
||||
using ::Targets::TargetMemorySegmentType;
|
||||
using ::Targets::TargetRegisterDescriptors;
|
||||
using ::Targets::TargetRegisterDescriptorAndValuePairs;
|
||||
|
||||
using namespace Protocols::WchLink;
|
||||
using namespace ::Exceptions;
|
||||
|
||||
WchLinkDebugInterface::WchLinkDebugInterface(
|
||||
const WchLinkToolConfig& toolConfig,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig,
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
Protocols::WchLink::WchLinkInterface& wchLinkInterface
|
||||
)
|
||||
: toolConfig(toolConfig)
|
||||
, targetConfig(targetConfig)
|
||||
, targetDescriptionFile(targetDescriptionFile)
|
||||
, wchLinkInterface(wchLinkInterface)
|
||||
, riscVTranslator(
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator{
|
||||
this->wchLinkInterface,
|
||||
this->toolConfig.riscVDebugTranslatorConfig,
|
||||
this->targetDescriptionFile,
|
||||
this->targetConfig
|
||||
}
|
||||
)
|
||||
, sysAddressSpaceDescriptor(this->targetDescriptionFile.getSystemAddressSpaceDescriptor())
|
||||
, mainProgramSegmentDescriptor(this->sysAddressSpaceDescriptor.getMemorySegmentDescriptor("main_program"))
|
||||
, flashProgramOpcodes(
|
||||
WchLinkDebugInterface::getFlashProgramOpcodes(
|
||||
this->targetDescriptionFile.getProperty("wch_link_interface", "programming_opcode_key").value
|
||||
)
|
||||
)
|
||||
, programmingBlockSize(
|
||||
Services::StringService::toUint32(
|
||||
this->targetDescriptionFile.getProperty("wch_link_interface", "programming_block_size").value
|
||||
)
|
||||
)
|
||||
{}
|
||||
|
||||
void WchLinkDebugInterface::activate() {
|
||||
this->wchLinkInterface.setClockSpeed(
|
||||
WchLinkTargetClockSpeed::CLK_6000_KHZ,
|
||||
this->cachedTargetId.value_or(0x01)
|
||||
);
|
||||
|
||||
auto response = this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::AttachTarget{});
|
||||
if (response.payload.size() != 5) {
|
||||
throw DeviceCommunicationFailure{"Unexpected response payload size for AttachTarget command"};
|
||||
}
|
||||
|
||||
this->cachedTargetId = response.payload[0];
|
||||
|
||||
/*
|
||||
* For some WCH targets, we must send another command to the debug tool, immediately after attaching.
|
||||
*
|
||||
* I don't know what this post-attach command does. But what I *do* know is that the target and/or the debug
|
||||
* tool will misbehave if we don't send it immediately after the attach.
|
||||
*
|
||||
* More specifically, the debug tool will read an invalid target variant ID upon the mutation of the target's
|
||||
* program buffer. So when we write to progbuf2, progbuf3, progbuf4 or progbuf5, all subsequent reads of the
|
||||
* target variant ID will yield invalid values, until the target and debug tool have been power cycled.
|
||||
* Interestingly, when we restore those progbuf registers to their original values, the reading of the target
|
||||
* variant ID works again. So I suspect the debug tool is using the target's program buffer to read the
|
||||
* variant ID, but it's assuming the program buffer hasn't changed. Maybe.
|
||||
*
|
||||
* So how does this post-attach command fix this issue? I don't know. I just know that it does.
|
||||
*
|
||||
* In addition to sending the post-attach command, we have to send another attach command, because the target
|
||||
* variant ID returned in the response of the first attach command may be invalid. Sending another attach
|
||||
* command will ensure that we have a valid target variant ID.
|
||||
*
|
||||
* TODO: Add a property to the target's TDF, to determine whether the post-attach is required, instead of
|
||||
* hardcoding target IDs here. This can be done after v2.0.0.
|
||||
*/
|
||||
if (this->cachedTargetId == 0x09) {
|
||||
this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::PostAttach{});
|
||||
response = this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::AttachTarget{});
|
||||
|
||||
if (response.payload.size() != 5) {
|
||||
throw DeviceCommunicationFailure{
|
||||
"Unexpected response payload size for subsequent AttachTarget command"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this->cachedVariantId = static_cast<WchTargetVariantId>(
|
||||
(response.payload[1] << 24) | (response.payload[2] << 16) | (response.payload[3] << 8)
|
||||
| (response.payload[4])
|
||||
);
|
||||
|
||||
this->riscVTranslator.activate();
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::deactivate() {
|
||||
this->riscVTranslator.clearAllTriggers();
|
||||
this->riscVTranslator.deactivate();
|
||||
|
||||
const auto response = this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::DetachTarget{});
|
||||
if (response.payload.size() != 1) {
|
||||
throw DeviceCommunicationFailure{"Unexpected response payload size for DetachTarget command"};
|
||||
}
|
||||
}
|
||||
|
||||
std::string WchLinkDebugInterface::getDeviceId() {
|
||||
return "0x" + Services::StringService::toHex(this->cachedVariantId.value());
|
||||
}
|
||||
|
||||
Targets::TargetExecutionState WchLinkDebugInterface::getExecutionState() {
|
||||
return this->riscVTranslator.getExecutionState();
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::stop() {
|
||||
this->riscVTranslator.stop();
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::run() {
|
||||
this->riscVTranslator.run();
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::step() {
|
||||
this->riscVTranslator.step();
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::reset() {
|
||||
this->riscVTranslator.reset();
|
||||
}
|
||||
|
||||
Targets::BreakpointResources WchLinkDebugInterface::getBreakpointResources() {
|
||||
return {
|
||||
.hardwareBreakpoints = this->riscVTranslator.getTriggerCount(),
|
||||
.softwareBreakpoints = 0xFFFFFFFF, // TODO: Use the program memory size to determine the limit.
|
||||
};
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::setProgramBreakpoint(const TargetProgramBreakpoint& breakpoint) {
|
||||
if (breakpoint.type == TargetProgramBreakpoint::Type::HARDWARE) {
|
||||
this->riscVTranslator.insertTriggerBreakpoint(breakpoint.address);
|
||||
|
||||
} else {
|
||||
this->setSoftwareBreakpoint(breakpoint);
|
||||
}
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::removeProgramBreakpoint(const TargetProgramBreakpoint& breakpoint) {
|
||||
if (breakpoint.type == TargetProgramBreakpoint::Type::HARDWARE) {
|
||||
this->riscVTranslator.clearTriggerBreakpoint(breakpoint.address);
|
||||
|
||||
} else {
|
||||
this->clearSoftwareBreakpoint(breakpoint);
|
||||
}
|
||||
}
|
||||
|
||||
Targets::TargetRegisterDescriptorAndValuePairs WchLinkDebugInterface::readCpuRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) {
|
||||
return this->riscVTranslator.readCpuRegisters(descriptors);
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) {
|
||||
return this->riscVTranslator.writeCpuRegisters(registers);
|
||||
}
|
||||
|
||||
Targets::TargetMemoryBuffer WchLinkDebugInterface::readMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges
|
||||
) {
|
||||
return this->riscVTranslator.readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
startAddress,
|
||||
bytes,
|
||||
excludedAddressRanges
|
||||
);
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::writeMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
) {
|
||||
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) {
|
||||
/*
|
||||
* WCH-Link tools cannot write to flash memory via the target's debug module. They do, however, offer a
|
||||
* set of dedicated commands for this. We invoke them here.
|
||||
*
|
||||
* There are two commands we can choose from:
|
||||
*
|
||||
* - Partial block write
|
||||
* Writes any number of bytes to flash, but limited to a maximum of 64 bytes per write. Larger writes
|
||||
* must be split into multiple writes.
|
||||
* - Full block write
|
||||
* Writes an entire block to flash, where the block size is target-specific (resides in the target's
|
||||
* TDF). Requires alignment to the block size. Requires reattaching to the target at the end of the
|
||||
* programming session.
|
||||
*
|
||||
* The full block write is much faster for writing large buffers (KiBs), such as when we're programming
|
||||
* the target. But the partial block write is faster and more suitable for writing buffers that are
|
||||
* smaller than 64 bytes, such as when we're inserting software breakpoints.
|
||||
*/
|
||||
const auto bufferSize = static_cast<TargetMemorySize>(buffer.size());
|
||||
const auto alignmentSize = this->programmingBlockSize;
|
||||
const auto alignedStartAddress = (startAddress / alignmentSize) * alignmentSize;
|
||||
const auto alignedBufferSize = static_cast<TargetMemorySize>(std::ceil(
|
||||
static_cast<double>(bufferSize) / static_cast<double>(alignmentSize)
|
||||
) * alignmentSize);
|
||||
const auto alignmentRequired = alignedStartAddress != startAddress || alignedBufferSize != bufferSize;
|
||||
|
||||
if (
|
||||
bufferSize <= WchLinkInterface::MAX_PARTIAL_BLOCK_WRITE_SIZE
|
||||
|| (
|
||||
alignmentRequired
|
||||
&& !memorySegmentDescriptor.addressRange.contains(
|
||||
TargetMemoryAddressRange{
|
||||
alignedStartAddress,
|
||||
alignedStartAddress + alignedBufferSize - 1
|
||||
}
|
||||
)
|
||||
)
|
||||
) {
|
||||
using namespace ::DebugToolDrivers::Protocols::RiscVDebugSpec;
|
||||
Logger::debug("Using partial block write command");
|
||||
|
||||
/*
|
||||
* WCH-Link tools seem to make use of the target's program buffer to service the partial block write
|
||||
* command.
|
||||
*
|
||||
* This sometimes leads to exceptions occurring on the target, when the program buffer contains certain
|
||||
* instructions before the partial block write command is invoked. This is why we clear the program
|
||||
* buffer beforehand.
|
||||
*/
|
||||
this->riscVTranslator.clearProgramBuffer();
|
||||
this->wchLinkInterface.writeFlashPartialBlock(startAddress, buffer);
|
||||
|
||||
const auto commandError = this->riscVTranslator.readAndClearAbstractCommandError();
|
||||
if (commandError != DebugModule::AbstractCommandError::NONE) {
|
||||
throw Exception{
|
||||
"Partial block write failed - abstract command error: 0x"
|
||||
+ Services::StringService::toHex(commandError)
|
||||
};
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (alignmentRequired) {
|
||||
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->writeMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
alignedBuffer
|
||||
);
|
||||
}
|
||||
|
||||
Logger::debug(
|
||||
"Using full block write command (block size: " + std::to_string(this->programmingBlockSize) + ")"
|
||||
);
|
||||
this->wchLinkInterface.writeFlashFullBlocks(
|
||||
startAddress,
|
||||
buffer,
|
||||
this->programmingBlockSize,
|
||||
this->flashProgramOpcodes
|
||||
);
|
||||
|
||||
/*
|
||||
* Would this not be better placed in endProgrammingSession()? We could persist the command type we invoked
|
||||
* to perform the write, and if required, reattach at the end of the programming session.
|
||||
*
|
||||
* I don't think that would work, because the target needs to be accessible for other operations whilst in
|
||||
* programming mode. We may perform other operations in between program memory writes, but that wouldn't
|
||||
* work if we left the target in an inaccessible state between writes. So I think we have to reattach here.
|
||||
*
|
||||
* TODO: Review after v2.0.0.
|
||||
*/
|
||||
this->deactivate();
|
||||
this->wchLinkInterface.sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo{});
|
||||
this->activate();
|
||||
return;
|
||||
}
|
||||
|
||||
this->riscVTranslator.writeMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
startAddress,
|
||||
buffer
|
||||
);
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::eraseMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||
) {
|
||||
if (memorySegmentDescriptor == this->mainProgramSegmentDescriptor) {
|
||||
return this->wchLinkInterface.eraseProgramMemory();
|
||||
}
|
||||
|
||||
// Ignore other (non-program memory) erase requests, for now.
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::enableProgrammingMode() {
|
||||
/*
|
||||
* Nothing to do here
|
||||
*
|
||||
* We cannot prepare the WCH-Link tool for a programming session here, as the preparation process differs
|
||||
* across the two types of flash write commands (full and partial block write). We don't know which command
|
||||
* we'll be utilising at this point, so we perform the preparation in writeMemory().
|
||||
*/
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::disableProgrammingMode() {
|
||||
this->softwareBreakpointRegistry.clear();
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::applyAccessRestrictions(TargetMemorySegmentDescriptor& memorySegmentDescriptor) {
|
||||
if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) {
|
||||
/*
|
||||
* Actually, we *can* write to flash memory whilst in debug mode (via a partial block write), but we don't
|
||||
* need to, so I'm just going to block it, for now.
|
||||
*/
|
||||
memorySegmentDescriptor.debugModeAccess.writeable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::applyAccessRestrictions(Targets::TargetRegisterDescriptor& registerDescriptor) {
|
||||
// I don't believe any further access restrictions are required for registers. TODO: Review after v2.0.0.
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::setSoftwareBreakpoint(const TargetProgramBreakpoint& breakpoint) {
|
||||
if (breakpoint.size != 2 && breakpoint.size != 4) {
|
||||
throw Exception{"Invalid software breakpoint size (" + std::to_string(breakpoint.size) + ")"};
|
||||
}
|
||||
|
||||
const auto originalData = this->readMemory(
|
||||
breakpoint.addressSpaceDescriptor,
|
||||
breakpoint.memorySegmentDescriptor,
|
||||
breakpoint.address,
|
||||
breakpoint.size,
|
||||
{}
|
||||
);
|
||||
|
||||
const auto softwareBreakpoint = ::Targets::RiscV::ProgramBreakpoint{
|
||||
breakpoint,
|
||||
static_cast<::Targets::RiscV::Opcodes::Opcode>(
|
||||
breakpoint.size == 2
|
||||
? (originalData[1] << 8) | originalData[0]
|
||||
: (originalData[3] << 24) | (originalData[2] << 16) | (originalData[1] << 8) | originalData[0]
|
||||
)
|
||||
};
|
||||
|
||||
static constexpr auto ebreakBytes = std::to_array<unsigned char>({
|
||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak),
|
||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak >> 8),
|
||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak >> 16),
|
||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::Ebreak >> 24)
|
||||
});
|
||||
|
||||
static constexpr auto compressedEbreakBytes = std::to_array<unsigned char>({
|
||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::EbreakCompressed),
|
||||
static_cast<unsigned char>(::Targets::RiscV::Opcodes::EbreakCompressed >> 8)
|
||||
});
|
||||
|
||||
this->writeMemory(
|
||||
softwareBreakpoint.addressSpaceDescriptor,
|
||||
softwareBreakpoint.memorySegmentDescriptor,
|
||||
softwareBreakpoint.address,
|
||||
softwareBreakpoint.size == 2
|
||||
? TargetMemoryBufferSpan{compressedEbreakBytes}
|
||||
: TargetMemoryBufferSpan{ebreakBytes}
|
||||
);
|
||||
|
||||
this->softwareBreakpointRegistry.insert(softwareBreakpoint);
|
||||
}
|
||||
|
||||
void WchLinkDebugInterface::clearSoftwareBreakpoint(const TargetProgramBreakpoint& breakpoint) {
|
||||
if (breakpoint.size != 2 && breakpoint.size != 4) {
|
||||
throw Exception{"Invalid software breakpoint size (" + std::to_string(breakpoint.size) + ")"};
|
||||
}
|
||||
|
||||
const auto softwareBreakpointOpt = this->softwareBreakpointRegistry.find(breakpoint);
|
||||
if (!softwareBreakpointOpt.has_value()) {
|
||||
throw TargetOperationFailure{
|
||||
"Unknown software breakpoint (byte address: 0x" + Services::StringService::toHex(breakpoint.address)
|
||||
+ ")"
|
||||
};
|
||||
}
|
||||
|
||||
const auto& softwareBreakpoint = softwareBreakpointOpt->get();
|
||||
if (!softwareBreakpoint.originalInstruction.has_value()) {
|
||||
throw InternalFatalErrorException{"Missing original opcode"};
|
||||
}
|
||||
|
||||
this->writeMemory(
|
||||
softwareBreakpoint.addressSpaceDescriptor,
|
||||
softwareBreakpoint.memorySegmentDescriptor,
|
||||
softwareBreakpoint.address,
|
||||
softwareBreakpoint.size == 2
|
||||
? TargetMemoryBuffer{
|
||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction)),
|
||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 8)
|
||||
}
|
||||
: TargetMemoryBuffer{
|
||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction)),
|
||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 8),
|
||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 16),
|
||||
static_cast<unsigned char>(*(softwareBreakpoint.originalInstruction) >> 24)
|
||||
}
|
||||
);
|
||||
|
||||
this->softwareBreakpointRegistry.remove(softwareBreakpoint);
|
||||
}
|
||||
|
||||
std::span<const unsigned char> WchLinkDebugInterface::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 + "\")"
|
||||
};
|
||||
}
|
||||
}
|
||||
117
src/DebugToolDrivers/Wch/WchLinkDebugInterface.hpp
Normal file
117
src/DebugToolDrivers/Wch/WchLinkDebugInterface.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
||||
|
||||
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||
|
||||
#include "WchLinkToolConfig.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
#include "src/Targets/ProgramBreakpointRegistry.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/Wch/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/RiscV/ProgramBreakpoint.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
/**
|
||||
* The RISC-V debug module cannot provide a complete implementation of the RiscVDebugInterface.
|
||||
*
|
||||
* This class combines the functionality of the RISC-V debug module (via the RiscVDebugTranslator), with the
|
||||
* WCH-Link-specific functionality, to provide a complete implementation.
|
||||
*/
|
||||
class WchLinkDebugInterface
|
||||
: public TargetInterfaces::RiscV::RiscVDebugInterface
|
||||
{
|
||||
public:
|
||||
WchLinkDebugInterface(
|
||||
const WchLinkToolConfig& toolConfig,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig,
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
Protocols::WchLink::WchLinkInterface& wchLinkInterface
|
||||
);
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
std::string getDeviceId() override;
|
||||
|
||||
Targets::TargetExecutionState getExecutionState() override;
|
||||
|
||||
void stop() override;
|
||||
void run() override;
|
||||
void step() override;
|
||||
void reset() override;
|
||||
|
||||
Targets::BreakpointResources getBreakpointResources() override;
|
||||
void setProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) override;
|
||||
void removeProgramBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint) override;
|
||||
|
||||
Targets::TargetRegisterDescriptorAndValuePairs readCpuRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) override;
|
||||
void writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) override;
|
||||
|
||||
Targets::TargetMemoryBuffer readMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges
|
||||
) override;
|
||||
void writeMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
) override;
|
||||
void eraseMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor
|
||||
) override;
|
||||
|
||||
void enableProgrammingMode() override;
|
||||
void disableProgrammingMode() override;
|
||||
|
||||
void applyAccessRestrictions(Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor) override;
|
||||
void applyAccessRestrictions(Targets::TargetRegisterDescriptor& registerDescriptor) override;
|
||||
|
||||
private:
|
||||
const WchLinkToolConfig& toolConfig;
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig;
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile;
|
||||
|
||||
Protocols::WchLink::WchLinkInterface& wchLinkInterface;
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator riscVTranslator;
|
||||
|
||||
const Targets::TargetAddressSpaceDescriptor sysAddressSpaceDescriptor;
|
||||
const Targets::TargetMemorySegmentDescriptor& mainProgramSegmentDescriptor;
|
||||
|
||||
/**
|
||||
* The 'target activation' command returns a payload of 5 bytes.
|
||||
*
|
||||
* The last 4 bytes hold the WCH target variant ID. Given that the 'target activation' command appears to be
|
||||
* the only way to obtain this ID, we cache it via WchLinkInterface::cachedVariantId and return the cached
|
||||
* value in WchLinkInterface::getTargetId().
|
||||
*/
|
||||
std::optional<WchTargetVariantId> cachedVariantId;
|
||||
std::optional<WchTargetId> cachedTargetId;
|
||||
|
||||
Targets::ProgramBreakpointRegistryGeneric<Targets::RiscV::ProgramBreakpoint> softwareBreakpointRegistry;
|
||||
|
||||
std::span<const unsigned char> flashProgramOpcodes;
|
||||
Targets::TargetMemorySize programmingBlockSize;
|
||||
|
||||
void setSoftwareBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint);
|
||||
void clearSoftwareBreakpoint(const Targets::TargetProgramBreakpoint& breakpoint);
|
||||
|
||||
static std::span<const unsigned char> getFlashProgramOpcodes(const std::string& key);
|
||||
};
|
||||
}
|
||||
20
src/DebugToolDrivers/Wch/WchLinkE/WchLinkE.cpp
Normal file
20
src/DebugToolDrivers/Wch/WchLinkE/WchLinkE.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "WchLinkE.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
WchLinkE::WchLinkE(const DebugToolConfig& toolConfig)
|
||||
: WchLinkBase(
|
||||
toolConfig,
|
||||
WchLinkVariant::LINK_E_CH32V307,
|
||||
WchLinkE::USB_VENDOR_ID,
|
||||
WchLinkE::USB_PRODUCT_ID,
|
||||
WchLinkE::IAP_USB_VENDOR_ID,
|
||||
WchLinkE::IAP_USB_PRODUCT_ID,
|
||||
WchLinkE::WCH_LINK_INTERFACE_NUMBER
|
||||
)
|
||||
{}
|
||||
|
||||
std::string WchLinkE::getName() {
|
||||
return "WCH-LinkE";
|
||||
}
|
||||
}
|
||||
32
src/DebugToolDrivers/Wch/WchLinkE/WchLinkE.hpp
Normal file
32
src/DebugToolDrivers/Wch/WchLinkE/WchLinkE.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "src/DebugToolDrivers/Wch/WchLinkBase.hpp"
|
||||
|
||||
#include "src/ProjectConfig.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
/**
|
||||
* The WCH-LinkE debug tool is a variant of the WCH-Link.
|
||||
*
|
||||
* USB:
|
||||
* Vendor ID: 0x1a86 (6790)
|
||||
* Product ID: 0x8010 (32784)
|
||||
*/
|
||||
class WchLinkE: public WchLinkBase
|
||||
{
|
||||
public:
|
||||
static const inline std::uint16_t USB_VENDOR_ID = 0x1a86;
|
||||
static const inline std::uint16_t USB_PRODUCT_ID = 0x8010;
|
||||
static const inline std::uint16_t IAP_USB_VENDOR_ID = 0x4348;
|
||||
static const inline std::uint16_t IAP_USB_PRODUCT_ID = 0x55e0;
|
||||
static const inline std::uint8_t WCH_LINK_INTERFACE_NUMBER = 0;
|
||||
|
||||
explicit WchLinkE(const DebugToolConfig& toolConfig);
|
||||
|
||||
std::string getName() override;
|
||||
};
|
||||
}
|
||||
20
src/DebugToolDrivers/Wch/WchLinkToolConfig.cpp
Normal file
20
src/DebugToolDrivers/Wch/WchLinkToolConfig.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "WchLinkToolConfig.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
WchLinkToolConfig::WchLinkToolConfig(const DebugToolConfig& toolConfig)
|
||||
: DebugToolConfig(toolConfig)
|
||||
{
|
||||
const auto& toolNode = toolConfig.toolNode;
|
||||
|
||||
if (toolNode["exit_iap_mode"]) {
|
||||
this->exitIapMode = toolNode["exit_iap_mode"].as<bool>(this->exitIapMode);
|
||||
}
|
||||
|
||||
if (toolNode["riscv_debug_translator"]) {
|
||||
this->riscVDebugTranslatorConfig = ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig{
|
||||
toolNode["riscv_debug_translator"]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/DebugToolDrivers/Wch/WchLinkToolConfig.hpp
Normal file
20
src/DebugToolDrivers/Wch/WchLinkToolConfig.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "src/ProjectConfig.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslatorConfig.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch
|
||||
{
|
||||
/**
|
||||
* Extending the generic DebugToolConfig struct to accommodate WCH-Link configuration parameters.
|
||||
*/
|
||||
struct WchLinkToolConfig: public DebugToolConfig
|
||||
{
|
||||
bool exitIapMode = true;
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslatorConfig riscVDebugTranslatorConfig = {};
|
||||
|
||||
explicit WchLinkToolConfig(const DebugToolConfig& toolConfig);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user