Consistent casing in directory names

This commit is contained in:
Nav
2025-01-07 23:31:48 +00:00
parent e98a73e687
commit 2a51f8af75
462 changed files with 486 additions and 486 deletions

View File

@@ -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;
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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),
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

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

View File

@@ -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,
};
}
};
}

View File

@@ -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),
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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);
}

View File

@@ -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])
);
}
};
}

View File

@@ -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)
{}
};
}

View 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{});
}
}

View 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};
};
}