Massive refactor to accommodate RISC-V targets
- Refactored entire codebase (excluding the Insight component) to accommodate multiple target architectures (no longer specific to AVR) - Deleted 'generate SVD' GDB monitor command - I will eventually move this functionality to the Bloom website - Added unit size property to address spaces - Many other changes which I couldn't be bothered to describe here
This commit is contained in:
@@ -7,6 +7,8 @@ target_sources(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/CmsisDapInterface.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Command.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Response.cpp
|
||||
|
||||
# Microchip EDBG implementation
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrCommand.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp
|
||||
@@ -27,6 +29,8 @@ target_sources(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp
|
||||
|
||||
# Microchip EDBG debug tools
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/EdbgDevice.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AtmelICE/AtmelIce.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/PowerDebugger/PowerDebugger.cpp
|
||||
@@ -37,7 +41,12 @@ target_sources(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedNano/XplainedNano.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp
|
||||
|
||||
# RISC-V debug tools
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkInterface.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp
|
||||
|
||||
# RISC-V Debug Translator implementation
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Protocols/RiscVDebugSpec/DebugTranslator.cpp
|
||||
)
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
|
||||
#include "TargetInterfaces/TargetPowerManagementInterface.hpp"
|
||||
|
||||
#include "TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp"
|
||||
#include "TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp"
|
||||
#include "TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp"
|
||||
#include "TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp"
|
||||
|
||||
#include "TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
||||
#include "TargetInterfaces/RiscV/RiscVProgramInterface.hpp"
|
||||
#include "TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp"
|
||||
#include "src/Targets/RiscV/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/RiscV/RiscVTargetConfig.hpp"
|
||||
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
|
||||
@@ -44,6 +46,8 @@ public:
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual bool isInitialised() const = 0;
|
||||
|
||||
virtual std::string getName() = 0;
|
||||
|
||||
virtual std::string getSerialNumber() = 0;
|
||||
@@ -75,11 +79,9 @@ public:
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
|
||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
|
||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
|
||||
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -94,8 +96,9 @@ public:
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface(
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
|
||||
virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -110,7 +113,10 @@ public:
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface() {
|
||||
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -127,19 +133,31 @@ public:
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface() {
|
||||
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isInitialised() const {
|
||||
return this->initialised;
|
||||
/**
|
||||
* The RISC-V debug spec does not define a target ID. But vendors typically assign each model with an ID and
|
||||
* provide a means to extract it from the connected target, via the debug tool.
|
||||
*
|
||||
* For example, WCH debug tools return the target ID in response to the target activation command. For more, see
|
||||
* the implementation of the WCH-Link protocol.
|
||||
*
|
||||
* Bloom uses the target ID for verification purposes. We simply compare it to the one we have in the TDF and shout
|
||||
* if they don't match.
|
||||
*
|
||||
* Note: the caller of this function will not manage the lifetime of the returned instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVIdentificationInterface* getRiscVIdentificationInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
void setInitialised(bool initialised) {
|
||||
this->initialised = initialised;
|
||||
}
|
||||
|
||||
private:
|
||||
bool initialised = false;
|
||||
};
|
||||
|
||||
@@ -38,14 +38,14 @@ namespace DebugToolDrivers::Microchip
|
||||
this->setConfiguration(this->configurationIndex.value());
|
||||
}
|
||||
|
||||
auto cmsisHidInterface = Usb::HidInterface(
|
||||
auto cmsisHidInterface = Usb::HidInterface{
|
||||
this->cmsisHidInterfaceNumber,
|
||||
this->getEndpointMaxPacketSize(
|
||||
this->getFirstEndpointAddress(this->cmsisHidInterfaceNumber, LIBUSB_ENDPOINT_IN)
|
||||
),
|
||||
this->vendorId,
|
||||
this->productId
|
||||
);
|
||||
};
|
||||
|
||||
cmsisHidInterface.init();
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace DebugToolDrivers::Microchip
|
||||
* Because of this, we have to enforce a minimum time gap between commands. See comment
|
||||
* in CmsisDapInterface class declaration for more info.
|
||||
*/
|
||||
this->edbgInterface->setMinimumCommandTimeGap(std::chrono::milliseconds(35));
|
||||
this->edbgInterface->setMinimumCommandTimeGap(std::chrono::milliseconds{35});
|
||||
|
||||
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
|
||||
if (!this->sessionStarted) {
|
||||
@@ -70,9 +70,7 @@ namespace DebugToolDrivers::Microchip
|
||||
);
|
||||
}
|
||||
|
||||
this->edbgAvrIspInterface = std::make_unique<EdbgAvrIspInterface>(this->edbgInterface.get());
|
||||
|
||||
this->setInitialised(true);
|
||||
this->initialised = true;
|
||||
}
|
||||
|
||||
void EdbgDevice::close() {
|
||||
@@ -82,21 +80,50 @@ namespace DebugToolDrivers::Microchip
|
||||
|
||||
this->edbgInterface->getUsbHidInterface().close();
|
||||
UsbDevice::close();
|
||||
this->initialised = false;
|
||||
}
|
||||
|
||||
TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface(
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
|
||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
|
||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
|
||||
bool EdbgDevice::isInitialised() const {
|
||||
return this->initialised;
|
||||
}
|
||||
|
||||
std::string EdbgDevice::getSerialNumber() {
|
||||
using namespace CommandFrames::Discovery;
|
||||
using ResponseFrames::Discovery::ResponseId;
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
Query{QueryContext::SERIAL_NUMBER}
|
||||
);
|
||||
|
||||
if (responseFrame.id != ResponseId::OK) {
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
|
||||
};
|
||||
}
|
||||
|
||||
const auto data = responseFrame.getPayloadData();
|
||||
return std::string{data.begin(), data.end()};
|
||||
}
|
||||
|
||||
std::string EdbgDevice::getFirmwareVersionString() {
|
||||
// TODO: Implement this
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* EdbgDevice::getTargetPowerManagementInterface()
|
||||
{
|
||||
return this->targetPowerManagementInterface.get();
|
||||
}
|
||||
|
||||
TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
) {
|
||||
if (this->edbgAvr8Interface == nullptr) {
|
||||
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(
|
||||
this->edbgInterface.get(),
|
||||
targetConfig,
|
||||
targetFamily,
|
||||
targetParameters,
|
||||
targetRegisterDescriptorsById
|
||||
targetDescriptionFile,
|
||||
targetConfig
|
||||
);
|
||||
|
||||
this->configureAvr8Interface();
|
||||
@@ -105,40 +132,30 @@ namespace DebugToolDrivers::Microchip
|
||||
return this->edbgAvr8Interface.get();
|
||||
}
|
||||
|
||||
std::string EdbgDevice::getSerialNumber() {
|
||||
using namespace CommandFrames::Discovery;
|
||||
using ResponseFrames::Discovery::ResponseId;
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
Query(QueryContext::SERIAL_NUMBER)
|
||||
);
|
||||
|
||||
if (responseFrame.id != ResponseId::OK) {
|
||||
throw DeviceInitializationFailure(
|
||||
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
|
||||
TargetInterfaces::Microchip::Avr8::AvrIspInterface* EdbgDevice::getAvrIspInterface(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
) {
|
||||
if (this->edbgAvrIspInterface == nullptr) {
|
||||
this->edbgAvrIspInterface = std::make_unique<EdbgAvrIspInterface>(
|
||||
this->edbgInterface.get(),
|
||||
targetDescriptionFile
|
||||
);
|
||||
|
||||
this->configureAvr8Interface();
|
||||
}
|
||||
|
||||
const auto data = responseFrame.getPayloadData();
|
||||
return std::string(data.begin(), data.end());
|
||||
}
|
||||
|
||||
std::string EdbgDevice::getFirmwareVersionString() {
|
||||
// TODO: Implement this
|
||||
return "UNKNOWN";
|
||||
return this->edbgAvrIspInterface.get();
|
||||
}
|
||||
|
||||
void EdbgDevice::startSession() {
|
||||
using namespace CommandFrames::HouseKeeping;
|
||||
using ResponseFrames::HouseKeeping::ResponseId;
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
StartSession()
|
||||
);
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(StartSession{});
|
||||
if (responseFrame.id == ResponseId::FAILED) {
|
||||
// Failed response returned!
|
||||
throw DeviceInitializationFailure("Failed to start session with EDBG device!");
|
||||
throw DeviceInitializationFailure{"Failed to start session with EDBG device!"};
|
||||
}
|
||||
|
||||
this->sessionStarted = true;
|
||||
@@ -148,13 +165,10 @@ namespace DebugToolDrivers::Microchip
|
||||
using namespace CommandFrames::HouseKeeping;
|
||||
using ResponseFrames::HouseKeeping::ResponseId;
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
EndSession()
|
||||
);
|
||||
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(EndSession{});
|
||||
if (responseFrame.id == ResponseId::FAILED) {
|
||||
// Failed response returned!
|
||||
throw DeviceFailure("Failed to end session with EDBG device!");
|
||||
throw DeviceFailure{"Failed to end session with EDBG device!"};
|
||||
}
|
||||
|
||||
this->sessionStarted = false;
|
||||
|
||||
@@ -50,22 +50,7 @@ namespace DebugToolDrivers::Microchip
|
||||
*/
|
||||
void close() override;
|
||||
|
||||
TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
|
||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
|
||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
|
||||
) override;
|
||||
|
||||
TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface(
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
|
||||
) override {
|
||||
return this->edbgAvrIspInterface.get();
|
||||
}
|
||||
|
||||
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* getTargetPowerManagementInterface() override {
|
||||
return this->targetPowerManagementInterface.get();
|
||||
}
|
||||
[[nodiscard]] bool isInitialised() const override;
|
||||
|
||||
/**
|
||||
* Retrieves the device serial number via the "Discovery" EDBG sub-protocol.
|
||||
@@ -81,6 +66,18 @@ namespace DebugToolDrivers::Microchip
|
||||
*/
|
||||
std::string getFirmwareVersionString() override;
|
||||
|
||||
DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* getTargetPowerManagementInterface() override;
|
||||
|
||||
TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
) override;
|
||||
|
||||
TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Starts a session with the EDBG device using the "Housekeeping" EDBG sub-protocol.
|
||||
*/
|
||||
@@ -92,6 +89,8 @@ namespace DebugToolDrivers::Microchip
|
||||
void endSession();
|
||||
|
||||
protected:
|
||||
bool initialised = false;
|
||||
|
||||
/**
|
||||
* The USB interface number of the CMSIS-DAP HID interface.
|
||||
*/
|
||||
@@ -136,9 +135,9 @@ namespace DebugToolDrivers::Microchip
|
||||
* ISP cannot be used for debugging operations. The EdbgAvrIspInterface class does *not* implement
|
||||
* the Avr8DebugInterface.
|
||||
*
|
||||
* Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWire
|
||||
* targets. We use the interface to inspect and update the "debugWire enable" (DWEN) fuse-bit, before making a
|
||||
* second connection attempt via the debugWire interface.
|
||||
* Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWIRE
|
||||
* targets. We use the interface to inspect and update the "debugWIRE enable" (DWEN) fuse-bit, before making a
|
||||
* second connection attempt via the debugWIRE interface.
|
||||
*/
|
||||
std::unique_ptr<Microchip::Protocols::Edbg::Avr::EdbgAvrIspInterface> edbgAvrIspInterface = nullptr;
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace DebugToolDrivers::Microchip
|
||||
);
|
||||
|
||||
if (!nonEdbgDevices.empty()) {
|
||||
throw DeviceNotFound(
|
||||
throw DeviceNotFound{
|
||||
"The connected MPLAB PICkit 4 device is not in \"AVR mode\". Please follow the instructions at "
|
||||
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
throw exception;
|
||||
|
||||
@@ -38,10 +38,10 @@ namespace DebugToolDrivers::Microchip
|
||||
}
|
||||
|
||||
if (!nonEdbgDevices.empty()) {
|
||||
throw DeviceNotFound(
|
||||
throw DeviceNotFound{
|
||||
"The connected MPLAB Snap device is not in \"AVR mode\". Please follow the instructions at "
|
||||
+ Services::PathService::homeDomainName() + "/docs/avr-mode"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
throw exception;
|
||||
|
||||
@@ -8,10 +8,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
struct Avr8EdbgParameter
|
||||
{
|
||||
unsigned char context = 0x00;
|
||||
unsigned char id = 0x00;
|
||||
unsigned char context;
|
||||
unsigned char id;
|
||||
|
||||
constexpr Avr8EdbgParameter() = default;
|
||||
constexpr Avr8EdbgParameter(unsigned char context, unsigned char id)
|
||||
: context(context)
|
||||
, id(id)
|
||||
@@ -20,70 +19,70 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
|
||||
struct Avr8EdbgParameters
|
||||
{
|
||||
static constexpr Avr8EdbgParameter CONFIG_VARIANT {0x00, 0x00};
|
||||
static constexpr Avr8EdbgParameter CONFIG_FUNCTION {0x00, 0x01};
|
||||
static constexpr Avr8EdbgParameter PHYSICAL_INTERFACE {0x01, 0x00};
|
||||
static constexpr Avr8EdbgParameter DW_CLOCK_DIVISION_FACTOR {0x01, 0x10};
|
||||
static constexpr Avr8EdbgParameter PDI_CLOCK_SPEED {0x01, 0x31};
|
||||
static constexpr Avr8EdbgParameter MEGA_DEBUG_CLOCK {0x01, 0x21};
|
||||
static constexpr Avr8EdbgParameter JTAG_DAISY_CHAIN_SETTINGS {0x01, 0x01};
|
||||
static constexpr Avr8EdbgParameter CONFIG_VARIANT = {0x00, 0x00};
|
||||
static constexpr Avr8EdbgParameter CONFIG_FUNCTION = {0x00, 0x01};
|
||||
static constexpr Avr8EdbgParameter PHYSICAL_INTERFACE = {0x01, 0x00};
|
||||
static constexpr Avr8EdbgParameter DW_CLOCK_DIVISION_FACTOR = {0x01, 0x10};
|
||||
static constexpr Avr8EdbgParameter PDI_CLOCK_SPEED = {0x01, 0x31};
|
||||
static constexpr Avr8EdbgParameter MEGA_DEBUG_CLOCK = {0x01, 0x21};
|
||||
static constexpr Avr8EdbgParameter JTAG_DAISY_CHAIN_SETTINGS = {0x01, 0x01};
|
||||
|
||||
// debugWire and JTAG parameters
|
||||
static constexpr Avr8EdbgParameter DEVICE_BOOT_START_ADDR {0x02, 0x0A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_BASE {0x02, 0x06};
|
||||
static constexpr Avr8EdbgParameter DEVICE_SRAM_START {0x02, 0x0E};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEPROM_SIZE {0x02, 0x10};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEPROM_PAGE_SIZE {0x02, 0x12};
|
||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_PAGE_SIZE {0x02, 0x00};
|
||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_SIZE {0x02, 0x02};
|
||||
static constexpr Avr8EdbgParameter DEVICE_OCD_REVISION {0x02, 0x13};
|
||||
static constexpr Avr8EdbgParameter DEVICE_OCD_DATA_REGISTER {0x02, 0x18};
|
||||
static constexpr Avr8EdbgParameter DEVICE_SPMCR_REGISTER {0x02, 0x1D};
|
||||
static constexpr Avr8EdbgParameter DEVICE_OSCCAL_ADDR {0x02, 0x1E};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEARH_ADDR {0x02, 0x19};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEARL_ADDR {0x02, 0x1A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EECR_ADDR {0x02, 0x1B};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEDR_ADDR {0x02, 0x1C};
|
||||
// debugWIRE and JTAG parameters
|
||||
static constexpr Avr8EdbgParameter DEVICE_BOOT_START_ADDR = {0x02, 0x0A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_BASE = {0x02, 0x06};
|
||||
static constexpr Avr8EdbgParameter DEVICE_SRAM_START = {0x02, 0x0E};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEPROM_SIZE = {0x02, 0x10};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEPROM_PAGE_SIZE = {0x02, 0x12};
|
||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_PAGE_SIZE = {0x02, 0x00};
|
||||
static constexpr Avr8EdbgParameter DEVICE_FLASH_SIZE = {0x02, 0x02};
|
||||
static constexpr Avr8EdbgParameter DEVICE_OCD_REVISION = {0x02, 0x13};
|
||||
static constexpr Avr8EdbgParameter DEVICE_OCD_DATA_REGISTER = {0x02, 0x18};
|
||||
static constexpr Avr8EdbgParameter DEVICE_SPMCR_REGISTER = {0x02, 0x1D};
|
||||
static constexpr Avr8EdbgParameter DEVICE_OSCCAL_ADDR = {0x02, 0x1E};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEARH_ADDR = {0x02, 0x19};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEARL_ADDR = {0x02, 0x1A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EECR_ADDR = {0x02, 0x1B};
|
||||
static constexpr Avr8EdbgParameter DEVICE_EEDR_ADDR = {0x02, 0x1C};
|
||||
|
||||
// PDI/XMega device parameters
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPL_BASE_ADDR {0x02, 0x00};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BASE_ADDR {0x02, 0x04};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_BASE_ADDR {0x02, 0x08};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FUSE_BASE_ADDR {0x02, 0x0C};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_LOCKBIT_BASE_ADDR {0x02, 0x10};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_USER_SIGN_BASE_ADDR {0x02, 0x14};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_PROD_SIGN_BASE_ADDR {0x02, 0x18};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_DATA_BASE_ADDR {0x02, 0x1C};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPLICATION_BYTES {0x02, 0x20};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BYTES {0x02, 0x24};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_NVM_BASE {0x02, 0x2B};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_SIGNATURE_OFFSET {0x02, 0x2D};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FLASH_PAGE_BYTES {0x02, 0x26};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_SIZE {0x02, 0x28};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_PAGE_SIZE {0x02, 0x2A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPL_BASE_ADDR = {0x02, 0x00};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BASE_ADDR = {0x02, 0x04};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_BASE_ADDR = {0x02, 0x08};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FUSE_BASE_ADDR = {0x02, 0x0C};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_LOCKBIT_BASE_ADDR = {0x02, 0x10};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_USER_SIGN_BASE_ADDR = {0x02, 0x14};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_PROD_SIGN_BASE_ADDR = {0x02, 0x18};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_DATA_BASE_ADDR = {0x02, 0x1C};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPLICATION_BYTES = {0x02, 0x20};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BYTES = {0x02, 0x24};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_NVM_BASE = {0x02, 0x2B};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_SIGNATURE_OFFSET = {0x02, 0x2D};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_FLASH_PAGE_BYTES = {0x02, 0x26};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_SIZE = {0x02, 0x28};
|
||||
static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_PAGE_SIZE = {0x02, 0x2A};
|
||||
|
||||
// UPDI device parameters
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR {0x02, 0x00};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE {0x02, 0x02};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_PAGE_SIZE {0x02, 0x03};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_NVMCTRL_ADDR {0x02, 0x04};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_OCD_ADDR {0x02, 0x06};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_SIZE {0x02, 0x12};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_SIZE {0x02, 0x16};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_SIZE {0x02, 0x18};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_SIZE {0x02, 0x1A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_BASE_ADDR {0x02, 0x20};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_BASE_ADDR {0x02, 0x22};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_SIG_BASE_ADDR {0x02, 0x24};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_BASE_ADDR {0x02, 0x26};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_LOCK_BASE_ADDR {0x02, 0x28};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_DEVICE_ID {0x02, 0x2A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB {0x02, 0x2C};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE_MSB {0x02, 0x2D};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE {0x02, 0x2E};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR = {0x02, 0x00};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE = {0x02, 0x02};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_PAGE_SIZE = {0x02, 0x03};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_NVMCTRL_ADDR = {0x02, 0x04};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_OCD_ADDR = {0x02, 0x06};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_SIZE = {0x02, 0x12};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_SIZE = {0x02, 0x16};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_SIZE = {0x02, 0x18};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_SIZE = {0x02, 0x1A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_BASE_ADDR = {0x02, 0x20};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_BASE_ADDR = {0x02, 0x22};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_SIG_BASE_ADDR = {0x02, 0x24};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_BASE_ADDR = {0x02, 0x26};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_LOCK_BASE_ADDR = {0x02, 0x28};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_DEVICE_ID = {0x02, 0x2A};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB = {0x02, 0x2C};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE_MSB = {0x02, 0x2D};
|
||||
static constexpr Avr8EdbgParameter DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE = {0x02, 0x2E};
|
||||
|
||||
static constexpr Avr8EdbgParameter RUN_TIMERS_WHILST_STOPPED {0x03, 0x00};
|
||||
static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI {0x03, 0x06};
|
||||
static constexpr Avr8EdbgParameter RUN_TIMERS_WHILST_STOPPED = {0x03, 0x00};
|
||||
static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI = {0x03, 0x06};
|
||||
};
|
||||
|
||||
enum class Avr8ConfigVariant: unsigned char
|
||||
@@ -103,16 +102,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
DEBUGGING = 0x02,
|
||||
};
|
||||
|
||||
static inline auto getPhysicalInterfaceToAvr8IdMapping() {
|
||||
using Targets::TargetPhysicalInterface;
|
||||
|
||||
return std::map<TargetPhysicalInterface, unsigned char>({
|
||||
{TargetPhysicalInterface::DEBUG_WIRE, 0x05},
|
||||
{TargetPhysicalInterface::PDI, 0x06},
|
||||
{TargetPhysicalInterface::JTAG, 0x04},
|
||||
{TargetPhysicalInterface::UPDI, 0x08},
|
||||
});
|
||||
}
|
||||
enum class Avr8PhysicalInterface: unsigned char
|
||||
{
|
||||
NONE = 0x00,
|
||||
JTAG = 0x04,
|
||||
DEBUG_WIRE = 0x05,
|
||||
PDI = 0x06,
|
||||
PDI_1W = 0x08,
|
||||
};
|
||||
|
||||
enum class Avr8MemoryType: unsigned char
|
||||
{
|
||||
@@ -148,10 +145,10 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
/**
|
||||
* The FLASH_PAGE memory type can be used to read and write full flash pages on the target.
|
||||
*
|
||||
* Only available with the JTAG and debugWire config variants.
|
||||
* Only available with the JTAG and debugWIRE config variants.
|
||||
*
|
||||
* This memory type is not available with the JTAG config variant in debugging mode. Programming mode will need
|
||||
* to be enabled before it can be used with JTAG targets. With the debugWire variant, this memory type *can* be
|
||||
* to be enabled before it can be used with JTAG targets. With the debugWIRE variant, this memory type *can* be
|
||||
* used whilst in debugging mode.
|
||||
*/
|
||||
FLASH_PAGE = 0xB0,
|
||||
@@ -171,7 +168,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
/**
|
||||
* The SPM memory type can be used to read memory from the target whilst in debugging mode.
|
||||
*
|
||||
* Only available with JTAG and debugWire config variants.
|
||||
* Only available with JTAG and debugWIRE config variants.
|
||||
*/
|
||||
SPM = 0xA0,
|
||||
|
||||
@@ -186,7 +183,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
/**
|
||||
* The FUSES memory type can be used to read and write AVR fuses in programming mode.
|
||||
*
|
||||
* Not available for the debugWire config variant.
|
||||
* Not available for the debugWIRE config variant.
|
||||
*/
|
||||
FUSES = 0xB2,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
/**
|
||||
* AVR CMSIS-DAP vendor command.
|
||||
*/
|
||||
class AvrCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
class AvrCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
/*
|
||||
|
||||
@@ -12,11 +12,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
: Response(rawResponse)
|
||||
{
|
||||
if (this->id != 0x82) {
|
||||
throw Exception("Failed to construct AvrEvent object - invalid response ID.");
|
||||
throw Exception{"Failed to construct AvrEvent object - invalid response ID."};
|
||||
}
|
||||
|
||||
if (this->data.size() < 7) {
|
||||
throw Exception("Failed to construct AvrEvent object - unexpected size of AVR_EVT response.");
|
||||
throw Exception{"Failed to construct AvrEvent object - unexpected size of AVR_EVT response."};
|
||||
}
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
@@ -28,17 +28,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
}
|
||||
|
||||
if (this->data.size() < responsePacketSize + 7) {
|
||||
throw Exception("Failed to construct AvrEvent object - invalid size of AVR_EVT response packet.");
|
||||
throw Exception{"Failed to construct AvrEvent object - invalid size of AVR_EVT response packet."};
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the SOF, protocol version, handler ID, sequence ID and size bytes (which all make up 7 bytes
|
||||
* in total).
|
||||
*/
|
||||
this->eventData = std::vector<unsigned char>(
|
||||
this->eventData = std::vector<unsigned char>{
|
||||
this->data.begin() + 7,
|
||||
this->data.begin() + 7 + static_cast<std::int64_t>(responsePacketSize)
|
||||
);
|
||||
};
|
||||
|
||||
this->eventId = static_cast<AvrEventId>(this->eventData[0]);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
: Response(rawResponse)
|
||||
{
|
||||
if (this->id != 0x81) {
|
||||
throw Exception("Failed to construct AvrResponse object - invalid response ID.");
|
||||
throw Exception{"Failed to construct AvrResponse object - invalid response ID."};
|
||||
}
|
||||
|
||||
if (this->data.empty()) {
|
||||
// All AVR responses should contain at least one byte (the fragment info byte)
|
||||
throw Exception("Failed to construct AvrResponse object - malformed AVR_RSP data");
|
||||
throw Exception{"Failed to construct AvrResponse object - malformed AVR_RSP data"};
|
||||
}
|
||||
|
||||
if (this->data[0] == 0x00) {
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The disable debugWire command consists of 2 bytes:
|
||||
* The disable debugWIRE command consists of 2 bytes:
|
||||
* 1. Command ID (0x17)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
|
||||
@@ -4,17 +4,18 @@
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Fuse.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ProgramFuse: public AvrIspCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
ProgramFuse(Targets::Microchip::Avr::FuseType fuseType, unsigned char value)
|
||||
ProgramFuse(Targets::Microchip::Avr8::FuseType fuseType, Targets::Microchip::Avr8::FuseValue value)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr::FuseType;
|
||||
using Targets::Microchip::Avr8::FuseType;
|
||||
|
||||
/*
|
||||
* The program fuse command consists of 5 bytes:
|
||||
@@ -54,7 +55,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrI
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw Exceptions::Exception("Unsupported fuse type");
|
||||
throw Exceptions::InternalFatalErrorException{"Unsupported fuse type"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,20 +4,18 @@
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Fuse.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ReadFuse: public AvrIspCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
ReadFuse(
|
||||
Targets::Microchip::Avr::FuseType fuseType,
|
||||
std::uint8_t returnAddress
|
||||
)
|
||||
ReadFuse(Targets::Microchip::Avr8::FuseType fuseType, std::uint8_t returnAddress)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr::FuseType;
|
||||
using Targets::Microchip::Avr8::FuseType;
|
||||
|
||||
/*
|
||||
* The read fuse command consists of 6 bytes:
|
||||
@@ -60,6 +58,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrI
|
||||
this->payload[5] = 0x00;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw Exceptions::InternalFatalErrorException{"Unsupported fuse type"};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <src/Targets/Microchip/AVR/Fuse.hpp>
|
||||
#include <src/Targets/Microchip/AVR8/Fuse.hpp>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Fuse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
@@ -18,7 +18,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrI
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr::FuseType;
|
||||
using Targets::Microchip::Avr8::FuseType;
|
||||
|
||||
/*
|
||||
* The read signature command consists of 6 bytes:
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* CommandFrames::Avr8Generic::GetDeviceId getDeviceIdCommandFrame;
|
||||
*
|
||||
* auto responseFrame = edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(getDeviceIdCommandFrame);
|
||||
* Targets::Microchip::Avr::TargetSignature avrSignature = responseFrame->extractSignature();
|
||||
* Targets::Microchip::Avr8::TargetSignature avrSignature = responseFrame->extractSignature();
|
||||
*
|
||||
* In the code above, the responseFrame object will be an instance of the ResponseFrames::Avr8Generic::GetDeviceId
|
||||
* class, which provides the extractSignature() function (to extract the AVR signature from the response frame).
|
||||
@@ -145,27 +145,30 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* A vector of sequenced AvrCommands, each containing a segment of the AvrCommandFrame.
|
||||
*/
|
||||
[[nodiscard]] std::vector<AvrCommand> generateAvrCommands(std::size_t maximumCommandPacketSize) const {
|
||||
auto rawCommandFrame = this->getRawCommandFrame();
|
||||
const auto rawCommandFrame = this->getRawCommandFrame();
|
||||
|
||||
std::size_t commandFrameSize = rawCommandFrame.size();
|
||||
auto commandsRequired = static_cast<std::size_t>(
|
||||
const auto commandFrameSize = rawCommandFrame.size();
|
||||
const auto commandsRequired = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<float>(commandFrameSize) / static_cast<float>(maximumCommandPacketSize))
|
||||
);
|
||||
|
||||
std::vector<AvrCommand> avrCommands;
|
||||
std::size_t copiedPacketSize = 0;
|
||||
for (std::size_t i = 0; i < commandsRequired; i++) {
|
||||
auto avrCommands = std::vector<AvrCommand>{};
|
||||
auto copiedPacketSize = std::size_t{0};
|
||||
for (auto i = std::size_t{0}; i < commandsRequired; ++i) {
|
||||
// If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame
|
||||
std::size_t commandPacketSize = ((i + 1) != commandsRequired) ? maximumCommandPacketSize
|
||||
: (commandFrameSize - (maximumCommandPacketSize * i));
|
||||
const auto commandPacketSize = static_cast<std::size_t>(
|
||||
((i + 1) != commandsRequired)
|
||||
? maximumCommandPacketSize
|
||||
: (commandFrameSize - (maximumCommandPacketSize * i))
|
||||
);
|
||||
|
||||
avrCommands.emplace_back(AvrCommand(
|
||||
commandsRequired,
|
||||
i + 1,
|
||||
std::vector<unsigned char>(
|
||||
std::vector<unsigned char>{
|
||||
rawCommandFrame.begin() + static_cast<std::int64_t>(copiedPacketSize),
|
||||
rawCommandFrame.begin() + static_cast<std::int64_t>(copiedPacketSize + commandPacketSize)
|
||||
)
|
||||
}
|
||||
));
|
||||
copiedPacketSize += commandPacketSize;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp"
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp"
|
||||
|
||||
#include "Avr8Generic.hpp"
|
||||
@@ -15,10 +15,8 @@
|
||||
#include "src/Targets/TargetPhysicalInterface.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Family.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
@@ -31,13 +29,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* This implementation should work with any Microchip EDBG-based CMSIS-DAP debug tool (such as the Atmel-ICE,
|
||||
* Power Debugger, the MPLAB SNAP debugger (in "AVR mode"), etc).
|
||||
*/
|
||||
class EdbgAvr8Interface: public ::DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface
|
||||
class EdbgAvr8Interface: public ::DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgAvr8Interface(
|
||||
EdbgInterface* edbgInterface,
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -140,6 +138,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*/
|
||||
void deactivate() override;
|
||||
|
||||
Targets::TargetRegisterAccess getRegisterAccess(
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Issues the "PC Read" command to the debug tool, to extract the current program counter.
|
||||
*
|
||||
@@ -160,7 +163,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::Microchip::Avr::TargetSignature getDeviceId() override;
|
||||
Targets::Microchip::Avr8::TargetSignature getDeviceId() override;
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Set" command to the debug tool, setting a software breakpoint at the given
|
||||
@@ -212,27 +215,31 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* @param descriptorIds
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) override;
|
||||
Targets::TargetRegisterDescriptorAndValuePairs readRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Writes registers to target.
|
||||
*
|
||||
* @param registers
|
||||
*/
|
||||
void writeRegisters(const Targets::TargetRegisters& registers) override;
|
||||
void writeRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) override;
|
||||
|
||||
/**
|
||||
* This is an overloaded method.
|
||||
*
|
||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls readMemory().
|
||||
*
|
||||
* @param memoryType
|
||||
* @param addressSpaceDescriptor
|
||||
* @param memorySegmentDescriptor
|
||||
* @param startAddress
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemoryBuffer readMemory(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges = {}
|
||||
@@ -243,12 +250,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*
|
||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls writeMemory().
|
||||
*
|
||||
* @param memoryType
|
||||
* @param addressSpaceDescriptor
|
||||
* @param memorySegmentDescriptor
|
||||
* @param startAddress
|
||||
* @param buffer
|
||||
*/
|
||||
void writeMemory(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
const Targets::TargetMemoryBuffer& buffer
|
||||
) override;
|
||||
@@ -259,7 +268,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* @param section
|
||||
*/
|
||||
void eraseProgramMemory(
|
||||
std::optional<Targets::Microchip::Avr::Avr8Bit::ProgramMemorySection> section = std::nullopt
|
||||
std::optional<Targets::Microchip::Avr8::ProgramMemorySection> section = std::nullopt
|
||||
) override;
|
||||
|
||||
/**
|
||||
@@ -272,7 +281,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetState getTargetState() override;
|
||||
Targets::TargetExecutionState getExecutionState() override;
|
||||
|
||||
/**
|
||||
* Enters programming mode on the EDBG debug tool.
|
||||
@@ -311,13 +320,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
bool reactivateJtagTargetPostProgrammingMode = false;
|
||||
|
||||
/**
|
||||
* We keep record of the current target state for caching purposes. We'll only refresh the target state if the
|
||||
* target is running. If it has already stopped, then we assume it cannot transition to a running state without
|
||||
* an instruction from us.
|
||||
* We keep record of the current target execution state for caching purposes.
|
||||
*
|
||||
* We'll only refresh the target state if the target is running. If it has already stopped, then we assume it
|
||||
* cannot transition to a running state without an instruction from us.
|
||||
*
|
||||
* @TODO: Review this. Is the above assumption correct? Always? Explore the option of polling the target state.
|
||||
*/
|
||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||
Targets::TargetExecutionState cachedExecutionState = Targets::TargetExecutionState::UNKNOWN;
|
||||
|
||||
/**
|
||||
* Upon configuration, the physical interface must be activated on the debug tool. We keep record of this to
|
||||
@@ -411,7 +421,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* What parameters we need to send depend on the physical interface (and config variant) selected by the user.
|
||||
* For target parameters, the address (ID) of the parameter also varies across config variants.
|
||||
*
|
||||
* - The setDebugWireAndJtagParameters() function sends the required target parameters for debugWire and JTAG
|
||||
* - The setDebugWireAndJtagParameters() function sends the required target parameters for debugWIRE and JTAG
|
||||
* sessions. Both sessions are covered in a single function because they require the same parameters.
|
||||
* - The setPdiParameters() function sends the required target parameters for PDI sessions.
|
||||
* - The setUpdiParameters() function sends the required target parameters for UPDI sessions.
|
||||
@@ -459,6 +469,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*/
|
||||
void clearEvents();
|
||||
|
||||
Avr8MemoryType getRegisterMemoryType(const Targets::TargetRegisterDescriptor& descriptor);
|
||||
|
||||
/**
|
||||
* Checks if alignment is required for memory access via a given Avr8MemoryType.
|
||||
*
|
||||
@@ -486,14 +498,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
Targets::TargetMemorySize alignMemoryBytes(Avr8MemoryType memoryType, Targets::TargetMemorySize bytes);
|
||||
|
||||
/**
|
||||
* Checks if a maximum memory access size is imposed for a given Avr8MemoryType.
|
||||
* Determines the maximum memory access size imposed on the given Avr8MemoryType.
|
||||
*
|
||||
* @param memoryType
|
||||
* The imposed maximum size, or std::nullopt if a maximum isn't required.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::optional<Targets::TargetMemorySize> maximumMemoryAccessSize(Avr8MemoryType memoryType);
|
||||
Targets::TargetMemorySize maximumMemoryAccessSize(Avr8MemoryType memoryType);
|
||||
|
||||
/**
|
||||
* Reads memory on the target.
|
||||
@@ -546,7 +557,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
void refreshTargetState();
|
||||
|
||||
/**
|
||||
* Temporarily disables the debugWire module on the target. This does not affect the DWEN fuse. The module
|
||||
* Temporarily disables the debugWIRE module on the target. This does not affect the DWEN fuse. The module
|
||||
* will be reactivated upon the cycling of the target power.
|
||||
*/
|
||||
void disableDebugWire();
|
||||
@@ -580,7 +591,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{50});
|
||||
attemptCount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
EdbgAvr8Session::EdbgAvr8Session(
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
)
|
||||
: targetDescriptionFile(targetDescriptionFile)
|
||||
, targetConfig(targetConfig)
|
||||
, programAddressSpace(this->targetDescriptionFile.getProgramAddressSpace())
|
||||
, ramAddressSpace(this->targetDescriptionFile.getRamAddressSpace())
|
||||
, registerFileAddressSpace(this->targetDescriptionFile.getRegisterFileAddressSpace())
|
||||
, dataAddressSpace(this->targetDescriptionFile.getDataAddressSpace())
|
||||
, eepromAddressSpace(this->targetDescriptionFile.getEepromAddressSpace())
|
||||
, ioAddressSpace(this->targetDescriptionFile.getIoAddressSpace())
|
||||
, signatureAddressSpace(this->targetDescriptionFile.getSignatureAddressSpace())
|
||||
@@ -38,20 +39,20 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
);
|
||||
|
||||
if (!resolvedConfigVariant.has_value()) {
|
||||
throw Exceptions::Exception(
|
||||
throw Exceptions::Exception{
|
||||
"Failed to resolve EDBG config variant from the selected physical interface and the AVR target family"
|
||||
" - please review the selected physical interface"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->configVariant = *resolvedConfigVariant;
|
||||
}
|
||||
|
||||
std::optional<Avr8ConfigVariant> EdbgAvr8Session::tryResolveConfigVariant(
|
||||
Targets::Microchip::Avr::Avr8Bit::Family avrFamily,
|
||||
Targets::Microchip::Avr8::Family avrFamily,
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
) {
|
||||
using Targets::Microchip::Avr::Avr8Bit::Family;
|
||||
using Targets::Microchip::Avr8::Family;
|
||||
using Targets::TargetPhysicalInterface;
|
||||
|
||||
if (avrFamily == Family::MEGA || avrFamily == Family::TINY) {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
|
||||
@@ -21,12 +21,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
/**
|
||||
* AVR8 TDF, from which we extract all target info to configure the EDBG debug tool.
|
||||
*/
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile;
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile;
|
||||
|
||||
/**
|
||||
* Project's AVR8 target configuration.
|
||||
*/
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig;
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig;
|
||||
|
||||
/**
|
||||
* The EDBG config variant parameter.
|
||||
@@ -37,7 +37,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
Avr8ConfigVariant configVariant = Avr8ConfigVariant::NONE;
|
||||
|
||||
const Targets::TargetDescription::AddressSpace& programAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& ramAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& registerFileAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& dataAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& eepromAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& ioAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& signatureAddressSpace;
|
||||
@@ -59,8 +60,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
std::optional<std::uint8_t> ocdDataRegister;
|
||||
|
||||
EdbgAvr8Session(
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
);
|
||||
|
||||
private:
|
||||
@@ -75,7 +76,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* any particular EDBG config variant.
|
||||
*/
|
||||
static std::optional<Avr8ConfigVariant> tryResolveConfigVariant(
|
||||
Targets::Microchip::Avr::Avr8Bit::Family avrFamily,
|
||||
Targets::Microchip::Avr8::Family avrFamily,
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "EdbgAvrIspInterface.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
// Command frames
|
||||
@@ -13,7 +14,7 @@
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Targets::Microchip::Avr;
|
||||
using namespace Targets::Microchip::Avr8;
|
||||
|
||||
using CommandFrames::AvrIsp::EnterProgrammingMode;
|
||||
using CommandFrames::AvrIsp::LeaveProgrammingMode;
|
||||
@@ -26,17 +27,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
|
||||
using Exceptions::TargetOperationFailure;
|
||||
|
||||
EdbgAvrIspInterface::EdbgAvrIspInterface(EdbgInterface* edbgInterface)
|
||||
EdbgAvrIspInterface::EdbgAvrIspInterface(
|
||||
EdbgInterface* edbgInterface,
|
||||
const TargetDescriptionFile& targetDescriptionFile
|
||||
)
|
||||
: edbgInterface(edbgInterface)
|
||||
, ispParameters(IspParameters{targetDescriptionFile})
|
||||
{}
|
||||
|
||||
void EdbgAvrIspInterface::setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) {
|
||||
this->ispParameters = ispParameters;
|
||||
}
|
||||
|
||||
void EdbgAvrIspInterface::activate() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
EnterProgrammingMode(
|
||||
EnterProgrammingMode{
|
||||
this->ispParameters.programModeTimeout,
|
||||
this->ispParameters.programModeStabilizationDelay,
|
||||
this->ispParameters.programModeCommandExecutionDelay,
|
||||
@@ -44,42 +45,35 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
this->ispParameters.programModeByteDelay,
|
||||
this->ispParameters.programModePollValue,
|
||||
this->ispParameters.programModePollIndex
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
if (responseFrame.statusCode != StatusCode::OK) {
|
||||
throw TargetOperationFailure(
|
||||
throw TargetOperationFailure{
|
||||
"Failed to enable programming mode via the ISP interface - check target's SPI connection "
|
||||
"and/or its SPIEN fuse bit."
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void EdbgAvrIspInterface::deactivate() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
LeaveProgrammingMode(
|
||||
this->ispParameters.programModePreDelay,
|
||||
this->ispParameters.programModePostDelay
|
||||
)
|
||||
LeaveProgrammingMode{this->ispParameters.programModePreDelay, this->ispParameters.programModePostDelay}
|
||||
);
|
||||
|
||||
if (responseFrame.statusCode != StatusCode::OK) {
|
||||
throw TargetOperationFailure("Failed to disable programming mode via the ISP interface.");
|
||||
throw TargetOperationFailure{"Failed to disable programming mode via the ISP interface."};
|
||||
}
|
||||
}
|
||||
|
||||
TargetSignature EdbgAvrIspInterface::getDeviceId() {
|
||||
// The read signature command only allows us to read one signature byte at a time.
|
||||
return TargetSignature(
|
||||
this->readSignatureByte(0),
|
||||
this->readSignatureByte(1),
|
||||
this->readSignatureByte(2)
|
||||
);
|
||||
return {this->readSignatureByte(0), this->readSignatureByte(1), this->readSignatureByte(2)};
|
||||
}
|
||||
|
||||
Fuse EdbgAvrIspInterface::readFuse(FuseType fuseType) {
|
||||
FuseValue EdbgAvrIspInterface::readFuse(const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadFuse(fuseType, this->ispParameters.readFusePollIndex)
|
||||
ReadFuse{this->resolveFuseType(fuseRegisterDescriptor), this->ispParameters.readFusePollIndex}
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -87,17 +81,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
|| responseFrame.payload.size() < 4
|
||||
|| static_cast<StatusCode>(responseFrame.payload[3]) != StatusCode::OK
|
||||
) {
|
||||
throw TargetOperationFailure(
|
||||
throw TargetOperationFailure{
|
||||
"Failed to read fuse via ISP - response frame status code/size indicates a failure."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return Fuse(fuseType, responseFrame.payload[2]);
|
||||
return static_cast<FuseValue>(responseFrame.payload[2]);
|
||||
}
|
||||
|
||||
unsigned char EdbgAvrIspInterface::readLockBitByte() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadLock(this->ispParameters.readLockPollIndex)
|
||||
ReadLock{this->ispParameters.readLockPollIndex}
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -105,29 +99,32 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
|| responseFrame.payload.size() < 4
|
||||
|| static_cast<StatusCode>(responseFrame.payload[3]) != StatusCode::OK
|
||||
) {
|
||||
throw TargetOperationFailure(
|
||||
throw TargetOperationFailure{
|
||||
"Failed to read lock bit byte via ISP - response frame status code/size indicates a failure."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return responseFrame.payload[2];
|
||||
}
|
||||
|
||||
void EdbgAvrIspInterface::programFuse(Targets::Microchip::Avr::Fuse fuse) {
|
||||
void EdbgAvrIspInterface::programFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor,
|
||||
FuseValue value
|
||||
) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ProgramFuse(fuse.type, fuse.value)
|
||||
ProgramFuse{this->resolveFuseType(fuseRegisterDescriptor), value}
|
||||
);
|
||||
|
||||
if (responseFrame.statusCode != StatusCode::OK || responseFrame.payload.size() < 2) {
|
||||
throw TargetOperationFailure(
|
||||
throw TargetOperationFailure{
|
||||
"Failed to program fuse via ISP - response frame status code/size indicates a failure."
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char EdbgAvrIspInterface::readSignatureByte(std::uint8_t signatureByteAddress) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadSignature(signatureByteAddress, this->ispParameters.readSignaturePollIndex)
|
||||
ReadSignature{signatureByteAddress, this->ispParameters.readSignaturePollIndex}
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -135,12 +132,30 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
|| responseFrame.payload.size() < 4
|
||||
|| static_cast<StatusCode>(responseFrame.payload[3]) != StatusCode::OK
|
||||
) {
|
||||
throw TargetOperationFailure(
|
||||
throw TargetOperationFailure{
|
||||
"Failed to read signature byte (address: " + std::to_string(signatureByteAddress)
|
||||
+ ") via ISP - response frame status code/size indicates a failure."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return responseFrame.payload[2];
|
||||
}
|
||||
|
||||
FuseType EdbgAvrIspInterface::resolveFuseType(const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor) {
|
||||
if (fuseRegisterDescriptor.key == "low") {
|
||||
return FuseType::LOW;
|
||||
}
|
||||
|
||||
if (fuseRegisterDescriptor.key == "high") {
|
||||
return FuseType::HIGH;
|
||||
}
|
||||
|
||||
if (fuseRegisterDescriptor.key == "extended") {
|
||||
return FuseType::EXTENDED;
|
||||
}
|
||||
|
||||
throw Exceptions::InternalFatalErrorException{
|
||||
"Could not resolve fuse type from register descriptor (key: \"" + fuseRegisterDescriptor.key + "\")"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp"
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
@@ -19,25 +20,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* This implementation should work with any Microchip EDBG-based CMSIS-DAP debug tool with ISP support (such as
|
||||
* the Atmel-ICE, Power Debugger, the MPLAB SNAP debugger (in "AVR mode"), etc).
|
||||
*/
|
||||
class EdbgAvrIspInterface: public TargetInterfaces::Microchip::Avr::AvrIspInterface
|
||||
class EdbgAvrIspInterface: public TargetInterfaces::Microchip::Avr8::AvrIspInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgAvrIspInterface(EdbgInterface* edbgInterface);
|
||||
|
||||
/**
|
||||
* The EdbgAvrIspInterface doesn't actually require any config from the user, at this point in time. So this
|
||||
* function does nothing, for now.
|
||||
*
|
||||
* @param targetConfig
|
||||
*/
|
||||
void configure(const TargetConfig& targetConfig) override {};
|
||||
|
||||
/**
|
||||
* Accepts the target's ISP parameters. These should be extracted from the target's TDF.
|
||||
*
|
||||
* @param ispParameters
|
||||
*/
|
||||
void setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) override;
|
||||
explicit EdbgAvrIspInterface(
|
||||
EdbgInterface* edbgInterface,
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile
|
||||
);
|
||||
|
||||
/**
|
||||
* Initialises the ISP interface by enabling "programming mode" on the debug tool. This will activate the
|
||||
@@ -56,15 +45,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::Microchip::Avr::TargetSignature getDeviceId() override;
|
||||
Targets::Microchip::Avr8::TargetSignature getDeviceId() override;
|
||||
|
||||
/**
|
||||
* Reads a particular fuse byte from the AVR target.
|
||||
*
|
||||
* @param fuseType
|
||||
* @param fuseRegisterDescriptor
|
||||
* @return
|
||||
*/
|
||||
Targets::Microchip::Avr::Fuse readFuse(Targets::Microchip::Avr::FuseType fuseType) override;
|
||||
Targets::Microchip::Avr8::FuseValue readFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Reads the lock bit byte from the AVR target.
|
||||
@@ -76,9 +67,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
/**
|
||||
* Programs a particular fuse on the AVR target.
|
||||
*
|
||||
* @param fuse
|
||||
* @param fuseRegisterDescriptor
|
||||
* @param value
|
||||
*/
|
||||
void programFuse(Targets::Microchip::Avr::Fuse fuse) override;
|
||||
void programFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor,
|
||||
Targets::Microchip::Avr8::FuseValue value
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -89,7 +84,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
*/
|
||||
EdbgInterface* edbgInterface;
|
||||
|
||||
Targets::Microchip::Avr::IspParameters ispParameters;
|
||||
Targets::Microchip::Avr8::IspParameters ispParameters;
|
||||
|
||||
/**
|
||||
* The EDBG AVRISP protocol only allows us to read a single signature byte at a time.
|
||||
@@ -100,5 +95,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] unsigned char readSignatureByte(std::uint8_t signatureByteAddress);
|
||||
|
||||
Targets::Microchip::Avr8::FuseType resolveFuseType(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* 1 byte for break cause
|
||||
* 2 bytes for extended info
|
||||
*/
|
||||
throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size.");
|
||||
throw Exception{"Failed to process BreakEvent from AvrEvent - unexpected packet size."};
|
||||
}
|
||||
|
||||
// Program counter consists of 4 bytes
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
}
|
||||
|
||||
explicit Avr8CommandFailure(const char* message): TargetOperationFailure(message) {
|
||||
this->message = std::string(message);
|
||||
this->message = std::string{message};
|
||||
}
|
||||
|
||||
explicit Avr8CommandFailure(
|
||||
@@ -95,7 +95,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
}
|
||||
|
||||
private:
|
||||
static const inline auto failureCodeToDescription = std::map<Avr8CommandFailureCode, std::string>({
|
||||
static const inline auto failureCodeToDescription = std::map<Avr8CommandFailureCode, std::string>{
|
||||
{Avr8CommandFailureCode::DEBUGWIRE_PHYSICAL_ERROR, "debugWIRE physical error"},
|
||||
{Avr8CommandFailureCode::JTAGM_FAILED_TO_INITIALISE, "JTAGM failed to initialise"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_JTAG_ERROR, "JTAGM did something strange"},
|
||||
@@ -143,6 +143,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{Avr8CommandFailureCode::FEATURE_NOT_AVAILABLE, "Feature not available"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_COMMAND, "Command has not been implemented"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_ERROR, "Unknown error reported by EDBG device"},
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#include "DebugWireJtagParameters.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
DebugWireJtagParameters::DebugWireJtagParameters(
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile
|
||||
) {
|
||||
using Services::StringService;
|
||||
|
||||
@@ -55,7 +54,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen
|
||||
this->eearAddressLow = static_cast<std::uint8_t>(eearlDescriptor.startAddress);
|
||||
|
||||
/*
|
||||
* Some debugWire targets only have a single-byte `EEARL` register.
|
||||
* Some debugWIRE targets only have a single-byte `EEARL` register.
|
||||
*
|
||||
* In the absence of an `EEARH` register, and if there is no high byte in the `EEARL` register, the
|
||||
* `eearAddressHigh` parameter should be equal to the `eearAddressLow` parameter, as stated in the
|
||||
@@ -80,8 +79,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen
|
||||
const auto cpuPeripheralDescriptor = targetDescriptionFile.getTargetPeripheralDescriptor("cpu");
|
||||
const auto& cpuRegisterGroupDescriptor = cpuPeripheralDescriptor.getRegisterGroupDescriptor("cpu");
|
||||
|
||||
const auto spmcsrDescriptor = cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcsr")
|
||||
?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcr");
|
||||
const auto spmcsrDescriptor = cpuRegisterGroupDescriptor.tryGetFirstRegisterDescriptor({"spmcsr", "spmcr"});
|
||||
|
||||
if (spmcsrDescriptor.has_value()) {
|
||||
this->spmcrAddress = static_cast<std::uint8_t>(spmcsrDescriptor->get().startAddress);
|
||||
@@ -90,24 +88,27 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen
|
||||
const auto bootLoadPeripheral = targetDescriptionFile.getTargetPeripheralDescriptor("boot_load");
|
||||
const auto& bootLoaderRegisterGroupDescriptor = bootLoadPeripheral.getRegisterGroupDescriptor("boot_load");
|
||||
|
||||
const auto spmcsrDescriptor = bootLoaderRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcsr")
|
||||
?: bootLoaderRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcr");
|
||||
const auto spmcsrDescriptor = bootLoaderRegisterGroupDescriptor.tryGetFirstRegisterDescriptor(
|
||||
{"spmcsr", "spmcr"}
|
||||
);
|
||||
|
||||
if (!spmcsrDescriptor.has_value()) {
|
||||
throw Exceptions::InternalFatalErrorException("Could not extract SPMCS register from TDF");
|
||||
throw Exceptions::InternalFatalErrorException{"Could not extract SPMCS register from TDF"};
|
||||
}
|
||||
|
||||
this->spmcrAddress = static_cast<std::uint8_t>(spmcsrDescriptor->get().startAddress);
|
||||
}
|
||||
|
||||
const auto osccalDescriptor = cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("osccal")
|
||||
?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("osccal0")
|
||||
?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("osccal1")
|
||||
?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("fosccal")
|
||||
?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("sosccala");
|
||||
const auto osccalDescriptor = cpuRegisterGroupDescriptor.tryGetFirstRegisterDescriptor({
|
||||
"osccal",
|
||||
"osccal0",
|
||||
"osccal1",
|
||||
"fosccal",
|
||||
"sosccala",
|
||||
});
|
||||
|
||||
if (!osccalDescriptor.has_value()) {
|
||||
throw Exceptions::InternalFatalErrorException("Could not extract OSCCAL register from TDF");
|
||||
throw Exceptions::InternalFatalErrorException{"Could not extract OSCCAL register from TDF"};
|
||||
}
|
||||
|
||||
this->osccalAddress = static_cast<std::uint8_t>(osccalDescriptor->get().startAddress);
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
/**
|
||||
* EDBG parameters for debugWire and JTAG AVR targets.
|
||||
* EDBG parameters for debugWIRE and JTAG AVR targets.
|
||||
*
|
||||
* See Microchip's "EDBG-based Tools Protocols" document for more on these parameters.
|
||||
*/
|
||||
@@ -30,6 +30,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen
|
||||
std::uint8_t spmcrAddress;
|
||||
std::uint8_t osccalAddress;
|
||||
|
||||
DebugWireJtagParameters(const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile);
|
||||
DebugWireJtagParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
PdiParameters::PdiParameters(
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile
|
||||
) {
|
||||
PdiParameters::PdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile) {
|
||||
using Services::StringService;
|
||||
|
||||
const auto& pdiGroup = targetDescriptionFile.getPropertyGroup("pdi_interface");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
@@ -29,6 +29,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen
|
||||
std::uint16_t nvmModuleBaseAddress;
|
||||
std::uint16_t signaturesPdiOffset;
|
||||
|
||||
PdiParameters(const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile);
|
||||
PdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
UpdiParameters::UpdiParameters(
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile
|
||||
) {
|
||||
UpdiParameters::UpdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile) {
|
||||
using Services::StringService;
|
||||
|
||||
const auto& updiGroup = targetDescriptionFile.getPropertyGroup("updi_interface");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
@@ -28,6 +28,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen
|
||||
std::uint16_t fuseSegmentStartAddress;
|
||||
std::uint16_t lockbitSegmentStartAddress;
|
||||
|
||||
UpdiParameters(const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile);
|
||||
UpdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from AVR8 Generic response frame payload.");
|
||||
throw Exception{"Response ID missing from AVR8 Generic response frame payload."};
|
||||
}
|
||||
|
||||
this->id = static_cast<Avr8ResponseId>(this->payload[0]);
|
||||
@@ -21,11 +21,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
* AVR8 data payloads are in little endian form and include two bytes before the data (response ID and
|
||||
* version byte) as well as an additional byte after the data, known as the 'status code'.
|
||||
*/
|
||||
auto data = std::vector<unsigned char>(
|
||||
this->payload.begin() + 2,
|
||||
this->payload.end() - 1
|
||||
);
|
||||
|
||||
auto data = std::vector<unsigned char>{this->payload.begin() + 2, this->payload.end() - 1};
|
||||
std::reverse(data.begin(), data.end());
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
: Avr8GenericResponseFrame(AvrResponses)
|
||||
{}
|
||||
|
||||
Targets::Microchip::Avr::TargetSignature GetDeviceId::extractSignature(
|
||||
Targets::Microchip::Avr8::TargetSignature GetDeviceId::extractSignature(
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
) const {
|
||||
using Targets::TargetPhysicalInterface;
|
||||
using Targets::Microchip::Avr8::TargetSignature;
|
||||
|
||||
const auto payloadData = this->getPayloadData();
|
||||
|
||||
switch (physicalInterface) {
|
||||
@@ -18,14 +20,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
* When using the DebugWire physical interface, the get device ID command will return
|
||||
* four bytes, where the first can be ignored.
|
||||
*/
|
||||
return Targets::Microchip::Avr::TargetSignature(payloadData[1], payloadData[2], payloadData[3]);
|
||||
return TargetSignature{payloadData[1], payloadData[2], payloadData[3]};
|
||||
}
|
||||
case TargetPhysicalInterface::PDI:
|
||||
case TargetPhysicalInterface::UPDI: {
|
||||
/*
|
||||
* When using the PDI physical interface, the signature is returned in LSB format.
|
||||
*/
|
||||
return Targets::Microchip::Avr::TargetSignature(payloadData[3], payloadData[2], payloadData[1]);
|
||||
return TargetSignature{payloadData[3], payloadData[2], payloadData[1]};
|
||||
}
|
||||
case TargetPhysicalInterface::JTAG: {
|
||||
/*
|
||||
@@ -50,18 +52,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
(payloadData[0] << 24) | (payloadData[1] << 16) | (payloadData[2] << 8) | (payloadData[3])
|
||||
);
|
||||
|
||||
return Targets::Microchip::Avr::TargetSignature(
|
||||
return TargetSignature{
|
||||
0x1E,
|
||||
static_cast<unsigned char>((jtagId << 4) >> 24),
|
||||
static_cast<unsigned char>((jtagId << 12) >> 24)
|
||||
);
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return Targets::Microchip::Avr::TargetSignature(
|
||||
payloadData[0],
|
||||
payloadData[1],
|
||||
payloadData[2]
|
||||
);
|
||||
return TargetSignature{payloadData[0], payloadData[1], payloadData[2]};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetSignature.hpp"
|
||||
#include "src/Targets/TargetPhysicalInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
@@ -12,7 +12,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
public:
|
||||
explicit GetDeviceId(const std::vector<AvrResponse>& AvrResponses);
|
||||
|
||||
Targets::Microchip::Avr::TargetSignature extractSignature(
|
||||
Targets::Microchip::Avr8::TargetSignature extractSignature(
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
) const;
|
||||
};
|
||||
|
||||
@@ -23,8 +23,10 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
* command ID and version, the other four being the PC. The four PC bytes are little-endian.
|
||||
*/
|
||||
if (this->payload.size() != 6) {
|
||||
throw Exceptions::Exception("Failed to extract PC from payload of PC read command response "
|
||||
"frame - unexpected payload size.");
|
||||
throw Exceptions::Exception{
|
||||
"Failed to extract PC from payload of PC read command response "
|
||||
"frame - unexpected payload size."
|
||||
};
|
||||
}
|
||||
|
||||
return static_cast<Targets::TargetMemoryAddress>(
|
||||
|
||||
@@ -17,10 +17,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
* AVR8 data payloads are typically in little endian form, but this does not apply to the data returned
|
||||
* from the READ MEMORY commands.
|
||||
*/
|
||||
return std::vector<unsigned char>(
|
||||
this->payload.begin() + 2,
|
||||
this->payload.end() - 1
|
||||
);
|
||||
return {this->payload.begin() + 2, this->payload.end() - 1};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.size() < 2) {
|
||||
throw Exception("Status code missing from AVRISP response frame payload.");
|
||||
throw Exception{"Status code missing from AVRISP response frame payload."};
|
||||
}
|
||||
|
||||
this->statusCode = static_cast<StatusCode>(this->payload[1]);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
|
||||
void AvrResponseFrame::initFromAvrResponses(const std::vector<AvrResponse>& avrResponses) {
|
||||
// Build a raw frame buffer from the AvrResponse objects and just call initFromRawFrame()
|
||||
auto rawFrame = std::vector<unsigned char>();
|
||||
auto rawFrame = std::vector<unsigned char>{};
|
||||
|
||||
for (const auto& avrResponse : avrResponses) {
|
||||
rawFrame.insert(rawFrame.end(), avrResponse.responsePacket.begin(), avrResponse.responsePacket.end());
|
||||
@@ -23,12 +23,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
* All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and
|
||||
* a protocol handler ID).
|
||||
*/
|
||||
throw Exception("Failed to construct AvrResponseFrame - unexpected end to raw frame");
|
||||
throw Exception{"Failed to construct AvrResponseFrame - unexpected end to raw frame"};
|
||||
}
|
||||
|
||||
if (rawFrame[0] != 0x0E) {
|
||||
// Invalid SOF byte value
|
||||
throw Exception("Failed to construct AvrResponseFrame - unexpected SOF byte value in raw frame");
|
||||
throw Exception{"Failed to construct AvrResponseFrame - unexpected SOF byte value in raw frame"};
|
||||
}
|
||||
|
||||
this->sequenceId = static_cast<std::uint16_t>((rawFrame[2] << 8) + rawFrame[1]);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Dis
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from DISCOVERY response frame payload.");
|
||||
throw Exception{"Response ID missing from DISCOVERY response frame payload."};
|
||||
}
|
||||
|
||||
this->id = static_cast<ResponseId>(payload[0]);
|
||||
@@ -22,9 +22,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Dis
|
||||
}
|
||||
|
||||
// DISCOVERY payloads include two bytes before the data (response ID and version byte).
|
||||
return std::vector<unsigned char>(
|
||||
this->payload.begin() + 2,
|
||||
this->payload.end()
|
||||
);
|
||||
return {this->payload.begin() + 2, this->payload.end()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Edb
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from EDBG Control response frame payload.");
|
||||
throw Exception{"Response ID missing from EDBG Control response frame payload."};
|
||||
}
|
||||
|
||||
this->id = static_cast<EdbgControlResponseId>(this->payload[0]);
|
||||
@@ -25,9 +25,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Edb
|
||||
* EDBG Control data payloads include two bytes before the data (response ID and version byte) as well as an
|
||||
* additional byte after the data, known as the 'status code'.
|
||||
*/
|
||||
return std::vector<unsigned char>(
|
||||
this->payload.begin() + 2,
|
||||
this->payload.end() - 1
|
||||
);
|
||||
return {this->payload.begin() + 2, this->payload.end() - 1};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Hou
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from HOUSEKEEPING response frame payload.");
|
||||
throw Exception{"Response ID missing from HOUSEKEEPING response frame payload."};
|
||||
}
|
||||
|
||||
this->id = static_cast<ResponseId>(this->payload[0]);
|
||||
|
||||
@@ -25,26 +25,25 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
}
|
||||
|
||||
// This should never happen
|
||||
throw DeviceCommunicationFailure(
|
||||
throw DeviceCommunicationFailure{
|
||||
"Cannot send AVR command frame - failed to generate CMSIS-DAP Vendor (AVR) commands"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<Microchip::Protocols::Edbg::Avr::AvrEvent> EdbgInterface::requestAvrEvent() {
|
||||
auto avrEventResponse = this->sendCommandAndWaitForResponse(Avr::AvrEventCommand());
|
||||
|
||||
auto avrEventResponse = this->sendCommandAndWaitForResponse(Avr::AvrEventCommand{});
|
||||
if (avrEventResponse.id != 0x82) {
|
||||
throw DeviceCommunicationFailure("Unexpected response to AvrEventCommand from device");
|
||||
}
|
||||
|
||||
return !avrEventResponse.eventData.empty() ? std::optional(avrEventResponse) : std::nullopt;
|
||||
return !avrEventResponse.eventData.empty() ? std::optional{avrEventResponse} : std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<Microchip::Protocols::Edbg::Avr::AvrResponse> EdbgInterface::requestAvrResponses() {
|
||||
using Microchip::Protocols::Edbg::Avr::AvrResponseCommand;
|
||||
|
||||
std::vector<Microchip::Protocols::Edbg::Avr::AvrResponse> responses;
|
||||
AvrResponseCommand responseCommand;
|
||||
auto responses = std::vector<Microchip::Protocols::Edbg::Avr::AvrResponse>{};
|
||||
const auto responseCommand = AvrResponseCommand{};
|
||||
|
||||
auto avrResponse = this->sendCommandAndWaitForResponse(responseCommand);
|
||||
responses.push_back(avrResponse);
|
||||
@@ -55,15 +54,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
auto avrResponse = this->sendCommandAndWaitForResponse(responseCommand);
|
||||
|
||||
if (avrResponse.fragmentCount != fragmentCount) {
|
||||
throw DeviceCommunicationFailure(
|
||||
throw DeviceCommunicationFailure{
|
||||
"Failed to fetch AvrResponse objects - invalid fragment count returned."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
if (avrResponse.fragmentCount == 0 && avrResponse.fragmentNumber == 0) {
|
||||
throw DeviceCommunicationFailure(
|
||||
"Failed to fetch AvrResponse objects - unexpected empty response"
|
||||
);
|
||||
throw DeviceCommunicationFailure{"Failed to fetch AvrResponse objects - unexpected empty response"};
|
||||
}
|
||||
|
||||
if (avrResponse.fragmentNumber == 0) {
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
* The EdbgInterface class implements the EDBG sub-protocol, which takes the form of numerous CMSIS-DAP vendor
|
||||
* commands.
|
||||
*/
|
||||
class EdbgInterface: public ::DebugToolDrivers::Protocols::CmsisDap::CmsisDapInterface
|
||||
class EdbgInterface: public ::DebugToolDrivers::Protocols::CmsisDap::CmsisDapInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgInterface(Usb::HidInterface&& cmsisHidInterface);
|
||||
@@ -71,9 +71,9 @@ class EdbgInterface: public ::DebugToolDrivers::Protocols::CmsisDap::CmsisDapInt
|
||||
|
||||
if (response.data[0] != 0x01) {
|
||||
// The last response packet should always acknowledge receipt of the AvrCommandFrame
|
||||
throw Exceptions::DeviceCommunicationFailure(
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Failed to send AvrCommandFrame to device - device did not acknowledge receipt."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return typename CommandFrameType::ExpectedResponseFrameType(this->requestAvrResponses());
|
||||
|
||||
@@ -18,21 +18,21 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
|
||||
void EdbgTargetPowerManagementInterface::enableTargetPower() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
SetParameter(EdbgParameters::CONTROL_TARGET_POWER, 0x01)
|
||||
SetParameter{EdbgParameters::CONTROL_TARGET_POWER, 0x01}
|
||||
);
|
||||
|
||||
if (responseFrame.id == EdbgControlResponseId::FAILED) {
|
||||
throw Exception("Failed to enable target power via EDBG Control protocol");
|
||||
throw Exception{"Failed to enable target power via EDBG Control protocol"};
|
||||
}
|
||||
}
|
||||
|
||||
void EdbgTargetPowerManagementInterface::disableTargetPower() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
SetParameter(EdbgParameters::CONTROL_TARGET_POWER, 0x00)
|
||||
SetParameter{EdbgParameters::CONTROL_TARGET_POWER, 0x00}
|
||||
);
|
||||
|
||||
if (responseFrame.id == EdbgControlResponseId::FAILED) {
|
||||
throw Exception("Failed to disable target power via EDBG Control protocol");
|
||||
throw Exception{"Failed to disable target power via EDBG Control protocol"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@ namespace DebugToolDrivers::Protocols::CmsisDap
|
||||
|
||||
void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) {
|
||||
if (this->commandDelay.count() > 0) {
|
||||
using namespace std::chrono;
|
||||
std::int64_t now = duration_cast<milliseconds>(high_resolution_clock::now().time_since_epoch()).count();
|
||||
std::int64_t difference = (now - this->lastCommandSentTimeStamp);
|
||||
const auto now = duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()
|
||||
).count();
|
||||
const auto difference = (now - this->lastCommandSentTimeStamp);
|
||||
|
||||
if (difference < this->commandDelay.count()) {
|
||||
std::this_thread::sleep_for(milliseconds(this->commandDelay.count() - difference));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{this->commandDelay.count() - difference});
|
||||
}
|
||||
|
||||
this->lastCommandSentTimeStamp = now;
|
||||
|
||||
@@ -65,10 +65,9 @@ namespace DebugToolDrivers::Protocols::CmsisDap
|
||||
"CMSIS Response type must be derived from the Response class."
|
||||
);
|
||||
|
||||
const auto rawResponse = this->getUsbHidInterface().read(std::chrono::milliseconds(60000));
|
||||
|
||||
const auto rawResponse = this->getUsbHidInterface().read(std::chrono::milliseconds{60000});
|
||||
if (rawResponse.empty()) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Empty CMSIS-DAP response received");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Empty CMSIS-DAP response received"};
|
||||
}
|
||||
|
||||
return ResponseType(rawResponse);
|
||||
@@ -99,7 +98,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap
|
||||
auto response = this->getResponse<typename CommandType::ExpectedResponseType>();
|
||||
|
||||
if (response.id != cmsisDapCommand.id) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command.");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response to CMSIS-DAP command."};
|
||||
}
|
||||
|
||||
return response;
|
||||
@@ -121,7 +120,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap
|
||||
*
|
||||
* Because of this, we may need to enforce a minimum time gap between sending CMSIS commands.
|
||||
*/
|
||||
std::chrono::milliseconds commandDelay = std::chrono::milliseconds(0);
|
||||
std::chrono::milliseconds commandDelay = std::chrono::milliseconds{0};
|
||||
std::int64_t lastCommandSentTimeStamp = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace DebugToolDrivers::Protocols::CmsisDap
|
||||
{
|
||||
Response::Response(const std::vector<unsigned char>& rawResponse) {
|
||||
if (rawResponse.empty()) {
|
||||
throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response");
|
||||
throw Exceptions::Exception{"Failed to process CMSIS-DAP response - invalid response"};
|
||||
}
|
||||
|
||||
this->id = rawResponse[0];
|
||||
this->data = std::vector<unsigned char>(rawResponse.begin() + 1, rawResponse.end());
|
||||
this->data = std::vector<unsigned char>{rawResponse.begin() + 1, rawResponse.end()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule
|
||||
{
|
||||
using RegisterAddress = std::uint8_t;
|
||||
using RegisterValue = std::uint32_t;
|
||||
using HartIndex = std::uint32_t;
|
||||
|
||||
enum class DmiOperation: std::uint8_t
|
||||
{
|
||||
IGNORE = 0x00,
|
||||
READ = 0x01,
|
||||
WRITE = 0x02,
|
||||
};
|
||||
|
||||
enum class DmiOperationStatus: std::uint8_t
|
||||
{
|
||||
SUCCESS = 0x00,
|
||||
FAILED = 0x02,
|
||||
BUSY = 0x03,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct AbstractCommandRegister
|
||||
{
|
||||
enum CommandType: std::uint8_t
|
||||
{
|
||||
REGISTER_ACCESS = 0x00,
|
||||
QUICK_ACCESS = 0x01,
|
||||
MEMORY_ACCESS = 0x02,
|
||||
};
|
||||
|
||||
std::uint32_t control = 0;
|
||||
CommandType commandType = CommandType::REGISTER_ACCESS;
|
||||
|
||||
AbstractCommandRegister() = default;
|
||||
|
||||
constexpr AbstractCommandRegister(std::uint32_t control, CommandType commandType)
|
||||
: control(control)
|
||||
, commandType(commandType)
|
||||
{}
|
||||
|
||||
constexpr explicit AbstractCommandRegister(RegisterValue registerValue)
|
||||
: control(static_cast<std::uint32_t>(registerValue & 0x00FFFFFF))
|
||||
, commandType(static_cast<CommandType>((registerValue >> 24) & 0xFF))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
assert(this->control <= 0x00FFFFFF);
|
||||
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->control & 0x00FFFFFF)
|
||||
| static_cast<RegisterValue>(this->commandType) << 24
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct AbstractControlStatusRegister
|
||||
{
|
||||
enum CommandError: std::uint8_t
|
||||
{
|
||||
NONE = 0x00,
|
||||
BUSY = 0x01,
|
||||
NOT_SUPPORTED = 0x02,
|
||||
EXCEPTION = 0x03,
|
||||
HALT_RESUME = 0x04,
|
||||
BUS = 0x05,
|
||||
OTHER = 0x07,
|
||||
};
|
||||
|
||||
std::uint8_t dataCount:4 = 0;
|
||||
CommandError commandError:3 = CommandError::NONE;
|
||||
bool relaxedPrivilege:1 = false;
|
||||
bool busy:1 = false;
|
||||
std::uint8_t programBufferSize:5 = 0;
|
||||
|
||||
AbstractControlStatusRegister() = default;
|
||||
|
||||
constexpr explicit AbstractControlStatusRegister(RegisterValue registerValue)
|
||||
: dataCount(static_cast<std::uint8_t>(registerValue & 0x0F))
|
||||
, commandError(static_cast<CommandError>((registerValue >> 8) & 0x07))
|
||||
, relaxedPrivilege(static_cast<bool>(registerValue & (0x01 << 11)))
|
||||
, busy(static_cast<bool>(registerValue & (0x01 << 12)))
|
||||
, programBufferSize(static_cast<std::uint8_t>((registerValue >> 24) & 0x1F))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->dataCount)
|
||||
| static_cast<RegisterValue>(this->commandError) << 8
|
||||
| static_cast<RegisterValue>(this->relaxedPrivilege) << 11
|
||||
| static_cast<RegisterValue>(this->busy) << 12
|
||||
| static_cast<RegisterValue>(this->programBufferSize) << 24
|
||||
;
|
||||
}
|
||||
|
||||
constexpr void clearCommandError() {
|
||||
// Setting all of the bits will clear the field
|
||||
this->commandError = CommandError::OTHER;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct ControlRegister
|
||||
{
|
||||
enum HartSelectionMode: std::uint8_t
|
||||
{
|
||||
SINGLE = 0x00,
|
||||
MULTI = 0x01,
|
||||
};
|
||||
|
||||
bool debugModuleActive:1 = false;
|
||||
bool ndmReset:1 = false;
|
||||
bool clearResetHaltRequest:1 = false;
|
||||
bool setResetHaltRequest:1 = false;
|
||||
bool clearKeepAlive:1 = false;
|
||||
bool setKeepAlive:1 = false;
|
||||
HartIndex selectedHartIndex = 0;
|
||||
HartSelectionMode hartSelectionMode:1 = HartSelectionMode::SINGLE;
|
||||
bool acknowledgeUnavailableHarts:1 = false;
|
||||
bool acknowledgeHaveReset:1 = false;
|
||||
bool hartReset:1 = false;
|
||||
bool resumeRequest:1 = false;
|
||||
bool haltRequest:1 = false;
|
||||
|
||||
ControlRegister() = default;
|
||||
|
||||
constexpr explicit ControlRegister(RegisterValue registerValue)
|
||||
: debugModuleActive(static_cast<bool>(registerValue & 0x01))
|
||||
, ndmReset(static_cast<bool>(registerValue & (0x01 << 1)))
|
||||
, clearResetHaltRequest(static_cast<bool>(registerValue & (0x01 << 2)))
|
||||
, setResetHaltRequest(static_cast<bool>(registerValue & (0x01 << 3)))
|
||||
, clearKeepAlive(static_cast<bool>(registerValue & (0x01 << 4)))
|
||||
, setKeepAlive(static_cast<bool>(registerValue & (0x01 << 5)))
|
||||
, selectedHartIndex((((registerValue >> 6) & 0x3FF) << 10) | ((registerValue >> 16) & 0x3FF))
|
||||
, hartSelectionMode(static_cast<HartSelectionMode>(registerValue & (0x01 << 26)))
|
||||
, acknowledgeUnavailableHarts(static_cast<bool>(registerValue & (0x01 << 27)))
|
||||
, acknowledgeHaveReset(static_cast<bool>(registerValue & (0x01 << 28)))
|
||||
, hartReset(static_cast<bool>(registerValue & (0x01 << 29)))
|
||||
, resumeRequest(static_cast<bool>(registerValue & (0x01 << 30)))
|
||||
, haltRequest(static_cast<bool>(registerValue & static_cast<std::uint32_t>(0x01 << 31)))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
assert(this->selectedHartIndex <= 0xFFFFF);
|
||||
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->debugModuleActive)
|
||||
| static_cast<RegisterValue>(this->ndmReset) << 1
|
||||
| static_cast<RegisterValue>(this->clearResetHaltRequest) << 2
|
||||
| static_cast<RegisterValue>(this->setResetHaltRequest) << 3
|
||||
| static_cast<RegisterValue>(this->clearKeepAlive) << 4
|
||||
| static_cast<RegisterValue>(this->setKeepAlive) << 5
|
||||
| static_cast<RegisterValue>((this->selectedHartIndex & 0xFFFFF) >> 10) << 6
|
||||
| static_cast<RegisterValue>(this->selectedHartIndex & 0x3FF) << 16
|
||||
| static_cast<RegisterValue>(this->hartSelectionMode) << 26
|
||||
| static_cast<RegisterValue>(this->acknowledgeUnavailableHarts) << 27
|
||||
| static_cast<RegisterValue>(this->acknowledgeHaveReset) << 28
|
||||
| static_cast<RegisterValue>(this->hartReset) << 29
|
||||
| static_cast<RegisterValue>(this->resumeRequest) << 30
|
||||
| static_cast<RegisterValue>(this->haltRequest) << 31
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct MemoryAccessControlField
|
||||
{
|
||||
enum class MemorySize: std::uint8_t
|
||||
{
|
||||
SIZE_8 = 0x00,
|
||||
SIZE_16 = 0x01,
|
||||
SIZE_32 = 0x02,
|
||||
SIZE_64 = 0x03,
|
||||
SIZE_128 = 0x04,
|
||||
};
|
||||
|
||||
bool write:1 = false;
|
||||
bool postIncrement:1 = false;
|
||||
MemorySize size:3 = MemorySize::SIZE_32;
|
||||
bool virtualAddress:1 = false;
|
||||
|
||||
constexpr MemoryAccessControlField() = default;
|
||||
|
||||
constexpr MemoryAccessControlField(
|
||||
bool write,
|
||||
bool postIncrement,
|
||||
MemorySize size,
|
||||
bool virtualAddress
|
||||
)
|
||||
: write(write)
|
||||
, postIncrement(postIncrement)
|
||||
, size(size)
|
||||
, virtualAddress(virtualAddress)
|
||||
{}
|
||||
|
||||
constexpr explicit MemoryAccessControlField(std::uint32_t controlValue)
|
||||
: write(static_cast<bool>(controlValue & (0x01 << 16)))
|
||||
, postIncrement(static_cast<bool>(controlValue & (0x01 << 19)))
|
||||
, size(static_cast<MemorySize>((controlValue >> 20) & 0x07))
|
||||
, virtualAddress(static_cast<bool>(controlValue & (0x01 << 23)))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr std::uint32_t value() const {
|
||||
return std::uint32_t{0}
|
||||
| static_cast<std::uint32_t>(this->write) << 16
|
||||
| static_cast<std::uint32_t>(this->postIncrement) << 19
|
||||
| static_cast<std::uint32_t>(this->size) << 20
|
||||
| static_cast<std::uint32_t>(this->virtualAddress) << 23
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct RegisterAccessControlField
|
||||
{
|
||||
enum class RegisterSize: std::uint8_t
|
||||
{
|
||||
SIZE_32 = 0x02,
|
||||
SIZE_64 = 0x03,
|
||||
SIZE_128 = 0x04,
|
||||
};
|
||||
|
||||
RegisterNumber registerNumber;
|
||||
bool write:1 = false;
|
||||
bool transfer:1 = false;
|
||||
bool postExecute:1 = false;
|
||||
bool postIncrement:1 = false;
|
||||
RegisterSize size:3 = RegisterSize::SIZE_32;
|
||||
|
||||
RegisterAccessControlField(
|
||||
RegisterNumber registerNumber,
|
||||
bool write,
|
||||
bool transfer,
|
||||
bool postExecute,
|
||||
bool postIncrement,
|
||||
RegisterSize size
|
||||
)
|
||||
: registerNumber(registerNumber)
|
||||
, write(write)
|
||||
, transfer(transfer)
|
||||
, postExecute(postExecute)
|
||||
, postIncrement(postIncrement)
|
||||
, size(size)
|
||||
{}
|
||||
|
||||
constexpr explicit RegisterAccessControlField(std::uint32_t controlValue)
|
||||
: registerNumber(static_cast<RegisterNumber>(controlValue & 0xFFFF))
|
||||
, write(static_cast<bool>(controlValue & (0x01 << 16)))
|
||||
, transfer(static_cast<bool>(controlValue & (0x01 << 17)))
|
||||
, postExecute(static_cast<bool>(controlValue & (0x01 << 18)))
|
||||
, postIncrement(static_cast<bool>(controlValue & (0x01 << 19)))
|
||||
, size(static_cast<RegisterSize>((controlValue >> 20) & 0x07))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr std::uint32_t value() const {
|
||||
return std::uint32_t{0}
|
||||
| static_cast<std::uint32_t>(this->registerNumber)
|
||||
| static_cast<std::uint32_t>(this->write) << 16
|
||||
| static_cast<std::uint32_t>(this->transfer) << 17
|
||||
| static_cast<std::uint32_t>(this->postExecute) << 18
|
||||
| static_cast<std::uint32_t>(this->postIncrement) << 19
|
||||
| static_cast<std::uint32_t>(this->size) << 20
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
enum class RegisterAddress: RegisterAddress
|
||||
{
|
||||
ABSTRACT_DATA_0 = 0x04,
|
||||
ABSTRACT_DATA_1 = 0x05,
|
||||
ABSTRACT_DATA_2 = 0x06,
|
||||
ABSTRACT_DATA_3 = 0x07,
|
||||
ABSTRACT_DATA_4 = 0x08,
|
||||
ABSTRACT_DATA_5 = 0x09,
|
||||
ABSTRACT_DATA_6 = 0x0a,
|
||||
ABSTRACT_DATA_7 = 0x0b,
|
||||
ABSTRACT_DATA_8 = 0x0c,
|
||||
ABSTRACT_DATA_9 = 0x0d,
|
||||
ABSTRACT_DATA_10 = 0x0e,
|
||||
ABSTRACT_DATA_11 = 0x0f,
|
||||
CONTROL_REGISTER = 0x10,
|
||||
STATUS_REGISTER = 0x11,
|
||||
ABSTRACT_CONTROL_STATUS_REGISTER = 0x16,
|
||||
ABSTRACT_COMMAND_REGISTER = 0x17,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers
|
||||
{
|
||||
struct StatusRegister
|
||||
{
|
||||
std::uint8_t version:4 = 0;
|
||||
bool validConfigStructurePointer:1 = false;
|
||||
bool supportsResetHalt:1 = false;
|
||||
bool authBusy:1 = false;
|
||||
bool authenticated:1 = false;
|
||||
bool anyHalted:1 = false;
|
||||
bool allHalted:1 = false;
|
||||
bool anyRunning:1 = false;
|
||||
bool allRunning:1 = false;
|
||||
bool anyUnavailable:1 = false;
|
||||
bool allUnavailable:1 = false;
|
||||
bool anyNonExistent:1 = false;
|
||||
bool allNonExistent:1 = false;
|
||||
bool anyResumeAcknowledge:1 = false;
|
||||
bool allResumeAcknowledge:1 = false;
|
||||
bool anyHaveReset:1 = false;
|
||||
bool allHaveReset:1 = false;
|
||||
bool implicitBreak:1 = false;
|
||||
bool stickyUnavailableBits:1 = false;
|
||||
bool ndmResetPending:1 = false;
|
||||
|
||||
constexpr explicit StatusRegister(RegisterValue registerValue)
|
||||
: version(static_cast<std::uint8_t>(registerValue & 0x0F))
|
||||
, validConfigStructurePointer(static_cast<bool>(registerValue & (0x01 << 4)))
|
||||
, supportsResetHalt(static_cast<bool>(registerValue & (0x01 << 5)))
|
||||
, authBusy(static_cast<bool>(registerValue & (0x01 << 6)))
|
||||
, authenticated(static_cast<bool>(registerValue & (0x01 << 7)))
|
||||
, anyHalted(static_cast<bool>(registerValue & (0x01 << 8)))
|
||||
, allHalted(static_cast<bool>(registerValue & (0x01 << 9)))
|
||||
, anyRunning(static_cast<bool>(registerValue & (0x01 << 10)))
|
||||
, allRunning(static_cast<bool>(registerValue & (0x01 << 11)))
|
||||
, anyUnavailable(static_cast<bool>(registerValue & (0x01 << 12)))
|
||||
, allUnavailable(static_cast<bool>(registerValue & (0x01 << 13)))
|
||||
, anyNonExistent(static_cast<bool>(registerValue & (0x01 << 14)))
|
||||
, allNonExistent(static_cast<bool>(registerValue & (0x01 << 15)))
|
||||
, anyResumeAcknowledge(static_cast<bool>(registerValue & (0x01 << 16)))
|
||||
, allResumeAcknowledge(static_cast<bool>(registerValue & (0x01 << 17)))
|
||||
, anyHaveReset(static_cast<bool>(registerValue & (0x01 << 18)))
|
||||
, allHaveReset(static_cast<bool>(registerValue & (0x01 << 19)))
|
||||
, implicitBreak(static_cast<bool>(registerValue & (0x01 << 22)))
|
||||
, stickyUnavailableBits(static_cast<bool>(registerValue & (0x01 << 23)))
|
||||
, ndmResetPending(static_cast<bool>(registerValue & (0x01 << 24)))
|
||||
{}
|
||||
|
||||
[[nodiscard]] constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->version)
|
||||
| static_cast<RegisterValue>(this->validConfigStructurePointer) << 4
|
||||
| static_cast<RegisterValue>(this->supportsResetHalt) << 5
|
||||
| static_cast<RegisterValue>(this->authBusy) << 6
|
||||
| static_cast<RegisterValue>(this->authenticated) << 7
|
||||
| static_cast<RegisterValue>(this->anyHalted) << 8
|
||||
| static_cast<RegisterValue>(this->allHalted) << 9
|
||||
| static_cast<RegisterValue>(this->anyRunning) << 10
|
||||
| static_cast<RegisterValue>(this->allRunning) << 11
|
||||
| static_cast<RegisterValue>(this->anyUnavailable) << 12
|
||||
| static_cast<RegisterValue>(this->allUnavailable) << 13
|
||||
| static_cast<RegisterValue>(this->anyNonExistent) << 14
|
||||
| static_cast<RegisterValue>(this->allNonExistent) << 15
|
||||
| static_cast<RegisterValue>(this->anyResumeAcknowledge) << 16
|
||||
| static_cast<RegisterValue>(this->allResumeAcknowledge) << 17
|
||||
| static_cast<RegisterValue>(this->anyHaveReset) << 18
|
||||
| static_cast<RegisterValue>(this->allHaveReset) << 19
|
||||
| static_cast<RegisterValue>(this->implicitBreak) << 22
|
||||
| static_cast<RegisterValue>(this->stickyUnavailableBits) << 23
|
||||
| static_cast<RegisterValue>(this->ndmResetPending) << 24
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,592 @@
|
||||
#include "DebugTranslator.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
|
||||
#include "Registers/CpuRegisterNumbers.hpp"
|
||||
#include "DebugModule/Registers/RegisterAddresses.hpp"
|
||||
#include "DebugModule/Registers/RegisterAccessControlField.hpp"
|
||||
#include "DebugModule/Registers/MemoryAccessControlField.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Exceptions/InvalidConfig.hpp"
|
||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
using Registers::DebugControlStatusRegister;
|
||||
|
||||
using DebugModule::Registers::RegisterAddress;
|
||||
using DebugModule::Registers::ControlRegister;
|
||||
using DebugModule::Registers::StatusRegister;
|
||||
using DebugModule::Registers::AbstractControlStatusRegister;
|
||||
using DebugModule::Registers::AbstractCommandRegister;
|
||||
|
||||
using Registers::CpuRegisterNumber;
|
||||
|
||||
using namespace ::Targets::RiscV;
|
||||
|
||||
using ::Targets::TargetExecutionState;
|
||||
using ::Targets::TargetMemoryAddress;
|
||||
using ::Targets::TargetMemoryAddressRange;
|
||||
using ::Targets::TargetMemorySize;
|
||||
using ::Targets::TargetMemoryBuffer;
|
||||
using ::Targets::TargetStackPointer;
|
||||
using ::Targets::TargetAddressSpaceDescriptor;
|
||||
using ::Targets::TargetMemorySegmentDescriptor;
|
||||
using ::Targets::TargetRegisterDescriptors;
|
||||
using ::Targets::TargetRegisterDescriptorAndValuePairs;
|
||||
|
||||
DebugTranslator::DebugTranslator(
|
||||
DebugTransportModuleInterface& dtmInterface,
|
||||
const TargetDescriptionFile& targetDescriptionFile,
|
||||
const RiscVTargetConfig& targetConfig
|
||||
)
|
||||
: dtmInterface(dtmInterface)
|
||||
, targetDescriptionFile(targetDescriptionFile)
|
||||
, targetConfig(targetConfig)
|
||||
{}
|
||||
|
||||
void DebugTranslator::init() {
|
||||
// No pre-activation initialisation required.
|
||||
return;
|
||||
}
|
||||
|
||||
void DebugTranslator::activate() {
|
||||
this->dtmInterface.activate();
|
||||
|
||||
this->hartIndices = this->discoverHartIndices();
|
||||
if (this->hartIndices.empty()) {
|
||||
throw Exceptions::TargetOperationFailure{"Failed to discover any RISC-V harts"};
|
||||
}
|
||||
|
||||
Logger::debug("Discovered RISC-V harts: " + std::to_string(this->hartIndices.size()));
|
||||
|
||||
/*
|
||||
* We only support debugging a single RISC-V hart, for now.
|
||||
*
|
||||
* If we discover more than one, we select the first one and ensure that this is explicitly communicated to the
|
||||
* user.
|
||||
*/
|
||||
if (this->hartIndices.size() > 1) {
|
||||
Logger::warning("Bloom only supports debugging a single RISC-V hart - selecting first available");
|
||||
}
|
||||
|
||||
this->selectedHartIndex = this->hartIndices.front();
|
||||
Logger::info("Selected RISC-V hart index: " + std::to_string(this->selectedHartIndex));
|
||||
|
||||
/*
|
||||
* Disabling the debug module before enabling it will clear any state from a previous debug session that
|
||||
* wasn't terminated properly.
|
||||
*/
|
||||
this->disableDebugModule();
|
||||
this->enableDebugModule();
|
||||
|
||||
this->stop();
|
||||
this->reset();
|
||||
|
||||
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
||||
debugControlStatusRegister.breakUMode = true;
|
||||
debugControlStatusRegister.breakSMode = true;
|
||||
debugControlStatusRegister.breakMMode = true;
|
||||
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
}
|
||||
|
||||
void DebugTranslator::deactivate() {
|
||||
this->disableDebugModule();
|
||||
this->dtmInterface.deactivate();
|
||||
}
|
||||
|
||||
TargetExecutionState DebugTranslator::getExecutionState() {
|
||||
return this->readDebugModuleStatusRegister().anyRunning
|
||||
? TargetExecutionState::RUNNING
|
||||
: TargetExecutionState::STOPPED;
|
||||
}
|
||||
|
||||
void DebugTranslator::stop() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.haltRequest = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
auto statusRegister = this->readDebugModuleStatusRegister();
|
||||
|
||||
for (auto attempts = 1; !statusRegister.allHalted && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
statusRegister = this->readDebugModuleStatusRegister();
|
||||
}
|
||||
|
||||
controlRegister.haltRequest = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
if (!statusRegister.allHalted) {
|
||||
throw Exceptions::Exception{"Target took too long to halt selected harts"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::run() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.resumeRequest = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
auto statusRegister = this->readDebugModuleStatusRegister();
|
||||
|
||||
for (auto attempts = 1; !statusRegister.allResumeAcknowledge && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
statusRegister = this->readDebugModuleStatusRegister();
|
||||
}
|
||||
|
||||
controlRegister.resumeRequest = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
if (!statusRegister.allResumeAcknowledge) {
|
||||
throw Exceptions::Exception{"Target took too long to acknowledge resume request"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::step() {
|
||||
auto debugControlStatusRegister = this->readDebugControlStatusRegister();
|
||||
debugControlStatusRegister.step = true;
|
||||
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.resumeRequest = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
controlRegister.resumeRequest = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
debugControlStatusRegister.step = false;
|
||||
this->writeDebugControlStatusRegister(debugControlStatusRegister);
|
||||
}
|
||||
|
||||
void DebugTranslator::reset() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.setResetHaltRequest = true;
|
||||
controlRegister.haltRequest = true;
|
||||
controlRegister.ndmReset = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
controlRegister.ndmReset = false;
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
auto statusRegister = this->readDebugModuleStatusRegister();
|
||||
|
||||
for (auto attempts = 1; !statusRegister.allHaveReset && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
statusRegister = this->readDebugModuleStatusRegister();
|
||||
}
|
||||
|
||||
controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
controlRegister.clearResetHaltRequest = true;
|
||||
controlRegister.acknowledgeHaveReset = true;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
if (!statusRegister.allHaveReset) {
|
||||
throw Exceptions::Exception{"Target took too long to reset"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::setSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::clearSoftwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::setHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::clearHardwareBreakpoint(TargetMemoryAddress address) {
|
||||
|
||||
}
|
||||
|
||||
void DebugTranslator::clearAllBreakpoints() {
|
||||
|
||||
}
|
||||
|
||||
TargetRegisterDescriptorAndValuePairs DebugTranslator::readCpuRegisters(
|
||||
const TargetRegisterDescriptors& descriptors
|
||||
) {
|
||||
auto output = TargetRegisterDescriptorAndValuePairs{};
|
||||
|
||||
for (const auto& descriptor : descriptors) {
|
||||
const auto registerValue = this->readCpuRegister(static_cast<RegisterNumber>(descriptor->startAddress));
|
||||
output.emplace_back(
|
||||
*descriptor,
|
||||
TargetMemoryBuffer{
|
||||
static_cast<unsigned char>(registerValue >> 24),
|
||||
static_cast<unsigned char>(registerValue >> 16),
|
||||
static_cast<unsigned char>(registerValue >> 8),
|
||||
static_cast<unsigned char>(registerValue),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void DebugTranslator::writeCpuRegisters(const TargetRegisterDescriptorAndValuePairs& registers) {
|
||||
for (const auto& [descriptor, value] : registers) {
|
||||
assert((value.size() * 8) > std::numeric_limits<RegisterValue>::digits);
|
||||
|
||||
auto registerValue = RegisterValue{0};
|
||||
for (const auto& registerByte : value) {
|
||||
registerValue = (registerValue << 8) | registerByte;
|
||||
}
|
||||
|
||||
this->writeCpuRegister(static_cast<RegisterNumber>(descriptor.startAddress), registerValue);
|
||||
}
|
||||
}
|
||||
|
||||
TargetMemoryBuffer DebugTranslator::readMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
TargetMemorySize bytes,
|
||||
const std::set<TargetMemoryAddressRange>& excludedAddressRanges
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
|
||||
// TODO: excluded addresses
|
||||
|
||||
const auto pageSize = 4;
|
||||
if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) {
|
||||
// Alignment required
|
||||
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, pageSize);
|
||||
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), pageSize);
|
||||
|
||||
const auto memoryBuffer = this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
alignedBytes,
|
||||
excludedAddressRanges
|
||||
);
|
||||
|
||||
const auto offset = memoryBuffer.begin() + (startAddress - alignedStartAddress);
|
||||
return TargetMemoryBuffer{offset, offset + bytes};
|
||||
}
|
||||
|
||||
auto output = TargetMemoryBuffer{};
|
||||
output.reserve(bytes);
|
||||
|
||||
/*
|
||||
* We only need to set the address once. No need to update it as we use the post-increment function to
|
||||
* increment the address. See MemoryAccessControlField::postIncrement
|
||||
*/
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
||||
|
||||
constexpr auto command = AbstractCommandRegister{
|
||||
MemoryAccessControlField{
|
||||
false,
|
||||
true,
|
||||
MemoryAccessControlField::MemorySize::SIZE_32,
|
||||
false
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
for (auto address = startAddress; address <= (startAddress + bytes - 1); address += 4) {
|
||||
this->executeAbstractCommand(command);
|
||||
|
||||
const auto data = this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
||||
output.emplace_back(static_cast<unsigned char>(data));
|
||||
output.emplace_back(static_cast<unsigned char>(data >> 8));
|
||||
output.emplace_back(static_cast<unsigned char>(data >> 16));
|
||||
output.emplace_back(static_cast<unsigned char>(data >> 24));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void DebugTranslator::writeMemory(
|
||||
const TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
TargetMemoryAddress startAddress,
|
||||
const TargetMemoryBuffer& buffer
|
||||
) {
|
||||
using DebugModule::Registers::MemoryAccessControlField;
|
||||
|
||||
constexpr auto alignTo = TargetMemorySize{4};
|
||||
const auto bytes = static_cast<TargetMemorySize>(buffer.size());
|
||||
if ((startAddress % alignTo) != 0 || (bytes % alignTo) != 0) {
|
||||
/*
|
||||
* Alignment required
|
||||
*
|
||||
* To align the write operation, we read the front and back offset bytes and use them to construct an
|
||||
* aligned buffer.
|
||||
*/
|
||||
const auto alignedStartAddress = this->alignMemoryAddress(startAddress, alignTo);
|
||||
const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), alignTo);
|
||||
|
||||
assert(alignedBytes > bytes);
|
||||
|
||||
auto alignedBuffer = (alignedStartAddress < startAddress)
|
||||
? this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
(startAddress - alignedStartAddress)
|
||||
)
|
||||
: TargetMemoryBuffer{};
|
||||
alignedBuffer.reserve(alignedBytes);
|
||||
|
||||
// Read the offset bytes required to align the buffer size
|
||||
const auto dataBack = this->readMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
startAddress + bytes,
|
||||
alignedBytes - bytes - (startAddress - alignedStartAddress)
|
||||
);
|
||||
alignedBuffer.insert(alignedBuffer.end(), dataBack.begin(), dataBack.end());
|
||||
|
||||
return this->writeMemory(
|
||||
addressSpaceDescriptor,
|
||||
memorySegmentDescriptor,
|
||||
alignedStartAddress,
|
||||
alignedBuffer
|
||||
);
|
||||
}
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress);
|
||||
|
||||
constexpr auto command = AbstractCommandRegister{
|
||||
MemoryAccessControlField{
|
||||
true,
|
||||
true,
|
||||
MemoryAccessControlField::MemorySize::SIZE_32,
|
||||
false
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::MEMORY_ACCESS
|
||||
};
|
||||
|
||||
for (TargetMemoryAddress offset = 0; offset < buffer.size(); offset += 4) {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_DATA_0,
|
||||
static_cast<RegisterValue>(
|
||||
(buffer[offset + 3] << 24)
|
||||
| (buffer[offset + 2] << 16)
|
||||
| (buffer[offset + 1] << 8)
|
||||
| (buffer[offset])
|
||||
)
|
||||
);
|
||||
|
||||
this->executeAbstractCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DebugModule::HartIndex> DebugTranslator::discoverHartIndices() {
|
||||
auto hartIndices = std::vector<DebugModule::HartIndex>{};
|
||||
|
||||
/*
|
||||
* We can obtain the maximum hart index by setting all of the hartsel bits in the control register and then
|
||||
* read the value back.
|
||||
*/
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = 0xFFFFF;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
const auto maxHartIndex = this->readDebugModuleControlRegister().selectedHartIndex;
|
||||
|
||||
for (auto hartIndex = DebugModule::HartIndex{0}; hartIndex <= maxHartIndex; ++hartIndex) {
|
||||
/*
|
||||
* We can't just assume that everything between 0 and the maximum hart index are valid hart indices. We
|
||||
* have to test each index until we find one that is non-existent.
|
||||
*/
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = hartIndex;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
/*
|
||||
* It's worth noting that some RISC-V targets **do not** set the non-existent flags. I'm not sure why.
|
||||
* Maybe hartsel has been hardwired to 0 on targets that only support a single hart, preventing the
|
||||
* selection of non-existent harts.
|
||||
*
|
||||
* Relying on the maximum hart index seems to be all we can do in this case.
|
||||
*/
|
||||
if (this->readDebugModuleStatusRegister().anyNonExistent) {
|
||||
break;
|
||||
}
|
||||
|
||||
hartIndices.emplace_back(hartIndex);
|
||||
}
|
||||
|
||||
return hartIndices;
|
||||
}
|
||||
|
||||
ControlRegister DebugTranslator::readDebugModuleControlRegister() {
|
||||
return ControlRegister{this->dtmInterface.readDebugModuleRegister(RegisterAddress::CONTROL_REGISTER)};
|
||||
}
|
||||
|
||||
StatusRegister DebugTranslator::readDebugModuleStatusRegister() {
|
||||
return StatusRegister{this->dtmInterface.readDebugModuleRegister(RegisterAddress::STATUS_REGISTER)};
|
||||
}
|
||||
|
||||
AbstractControlStatusRegister DebugTranslator::readDebugModuleAbstractControlStatusRegister() {
|
||||
return AbstractControlStatusRegister{
|
||||
this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_CONTROL_STATUS_REGISTER)
|
||||
};
|
||||
}
|
||||
|
||||
DebugControlStatusRegister DebugTranslator::readDebugControlStatusRegister() {
|
||||
return DebugControlStatusRegister{
|
||||
this->readCpuRegister(static_cast<RegisterNumber>(CpuRegisterNumber::DEBUG_CONTROL_STATUS_REGISTER))
|
||||
};
|
||||
}
|
||||
|
||||
void DebugTranslator::enableDebugModule() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = true;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
|
||||
for (auto attempts = 1; !controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
}
|
||||
|
||||
if (!controlRegister.debugModuleActive) {
|
||||
throw Exceptions::Exception{"Took too long to enable debug module"};
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTranslator::disableDebugModule() {
|
||||
auto controlRegister = ControlRegister{};
|
||||
controlRegister.debugModuleActive = false;
|
||||
controlRegister.selectedHartIndex = this->selectedHartIndex;
|
||||
|
||||
this->writeDebugModuleControlRegister(controlRegister);
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
|
||||
for (auto attempts = 1; controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
controlRegister = this->readDebugModuleControlRegister();
|
||||
}
|
||||
|
||||
if (controlRegister.debugModuleActive) {
|
||||
throw Exceptions::Exception{"Took too long to disable debug module"};
|
||||
}
|
||||
}
|
||||
|
||||
RegisterValue DebugTranslator::readCpuRegister(RegisterNumber number) {
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
|
||||
this->executeAbstractCommand(AbstractCommandRegister{
|
||||
RegisterAccessControlField{
|
||||
number,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
RegisterAccessControlField::RegisterSize::SIZE_32
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::REGISTER_ACCESS
|
||||
});
|
||||
|
||||
return this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0);
|
||||
}
|
||||
|
||||
void DebugTranslator::writeCpuRegister(RegisterNumber number, RegisterValue value) {
|
||||
using DebugModule::Registers::RegisterAccessControlField;
|
||||
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0, value);
|
||||
this->executeAbstractCommand(AbstractCommandRegister{
|
||||
RegisterAccessControlField{
|
||||
number,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
RegisterAccessControlField::RegisterSize::SIZE_32
|
||||
}.value(),
|
||||
AbstractCommandRegister::CommandType::REGISTER_ACCESS
|
||||
});
|
||||
}
|
||||
|
||||
void DebugTranslator::writeDebugModuleControlRegister(const ControlRegister& controlRegister) {
|
||||
this->dtmInterface.writeDebugModuleRegister(RegisterAddress::CONTROL_REGISTER, controlRegister.value());
|
||||
}
|
||||
|
||||
void DebugTranslator::writeDebugControlStatusRegister(const DebugControlStatusRegister& controlRegister) {
|
||||
this->writeCpuRegister(
|
||||
static_cast<RegisterNumber>(CpuRegisterNumber::DEBUG_CONTROL_STATUS_REGISTER),
|
||||
controlRegister.value()
|
||||
);
|
||||
}
|
||||
|
||||
void DebugTranslator::executeAbstractCommand(
|
||||
const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister
|
||||
) {
|
||||
this->dtmInterface.writeDebugModuleRegister(
|
||||
RegisterAddress::ABSTRACT_COMMAND_REGISTER,
|
||||
abstractCommandRegister.value()
|
||||
);
|
||||
|
||||
auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
if (abstractStatusRegister.commandError != AbstractControlStatusRegister::CommandError::NONE) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to execute abstract command - error: "
|
||||
+ Services::StringService::toHex(abstractStatusRegister.commandError)
|
||||
};
|
||||
}
|
||||
|
||||
constexpr auto maxAttempts = 10;
|
||||
for (auto attempts = 1; abstractStatusRegister.busy && attempts <= maxAttempts; ++attempts) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{10});
|
||||
abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister();
|
||||
}
|
||||
|
||||
if (abstractStatusRegister.busy) {
|
||||
throw Exceptions::Exception{"Abstract command took too long to execute"};
|
||||
}
|
||||
}
|
||||
|
||||
TargetMemoryAddress DebugTranslator::alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo) {
|
||||
return static_cast<TargetMemoryAddress>(
|
||||
std::floor(static_cast<float>(address) / static_cast<float>(alignTo))
|
||||
) * alignTo;
|
||||
}
|
||||
|
||||
TargetMemorySize DebugTranslator::alignMemorySize(TargetMemorySize size, TargetMemorySize alignTo) {
|
||||
return static_cast<TargetMemorySize>(
|
||||
std::ceil(static_cast<float>(size) / static_cast<float>(alignTo))
|
||||
) * alignTo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
||||
|
||||
#include "DebugTransportModuleInterface.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/RiscV/RiscVTargetConfig.hpp"
|
||||
|
||||
#include "RiscVGeneric.hpp"
|
||||
#include "Registers/CpuRegisterNumbers.hpp"
|
||||
#include "Registers/DebugControlStatusRegister.hpp"
|
||||
|
||||
#include "DebugModule/DebugModule.hpp"
|
||||
#include "DebugModule/Registers/ControlRegister.hpp"
|
||||
#include "DebugModule/Registers/StatusRegister.hpp"
|
||||
#include "DebugModule/Registers/AbstractControlStatusRegister.hpp"
|
||||
#include "DebugModule/Registers/AbstractCommandRegister.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
/**
|
||||
* Implementation of a RISC-V debug translator
|
||||
*/
|
||||
class DebugTranslator: public ::DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface
|
||||
{
|
||||
public:
|
||||
DebugTranslator(
|
||||
DebugTransportModuleInterface& dtmInterface,
|
||||
const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const ::Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
);
|
||||
|
||||
virtual ~DebugTranslator() = default;
|
||||
|
||||
void init() override;
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
Targets::TargetExecutionState getExecutionState() override;
|
||||
|
||||
void stop() override;
|
||||
void run() override;
|
||||
void step() override;
|
||||
void reset() override;
|
||||
|
||||
void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
void setHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
void clearAllBreakpoints() 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,
|
||||
const Targets::TargetMemoryBuffer& buffer
|
||||
) override;
|
||||
|
||||
private:
|
||||
DebugTransportModuleInterface& dtmInterface;
|
||||
|
||||
const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile;
|
||||
const ::Targets::RiscV::RiscVTargetConfig& targetConfig;
|
||||
|
||||
std::vector<DebugModule::HartIndex> hartIndices;
|
||||
DebugModule::HartIndex selectedHartIndex = 0;
|
||||
|
||||
std::vector<DebugModule::HartIndex> discoverHartIndices();
|
||||
|
||||
DebugModule::Registers::ControlRegister readDebugModuleControlRegister();
|
||||
DebugModule::Registers::StatusRegister readDebugModuleStatusRegister();
|
||||
DebugModule::Registers::AbstractControlStatusRegister readDebugModuleAbstractControlStatusRegister();
|
||||
Registers::DebugControlStatusRegister readDebugControlStatusRegister();
|
||||
|
||||
void enableDebugModule();
|
||||
void disableDebugModule();
|
||||
|
||||
RegisterValue readCpuRegister(RegisterNumber number);
|
||||
void writeCpuRegister(RegisterNumber number, RegisterValue value);
|
||||
|
||||
void writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister);
|
||||
void writeDebugControlStatusRegister(const Registers::DebugControlStatusRegister& controlRegister);
|
||||
|
||||
void executeAbstractCommand(const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister);
|
||||
|
||||
Targets::TargetMemoryAddress alignMemoryAddress(
|
||||
Targets::TargetMemoryAddress address,
|
||||
Targets::TargetMemoryAddress alignTo
|
||||
);
|
||||
Targets::TargetMemorySize alignMemorySize(Targets::TargetMemorySize size, Targets::TargetMemorySize alignTo);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "RiscVGeneric.hpp"
|
||||
#include "DebugModule/DebugModule.hpp"
|
||||
#include "DebugModule/Registers/RegisterAddresses.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
/**
|
||||
* Provides access to the RISC-V target's debug transport module (DTM).
|
||||
*/
|
||||
class DebugTransportModuleInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Should prepare for and then activate the physical interface between the debug tool and the RISC-V target.
|
||||
*
|
||||
* Should throw an exception if activation fails. The error will be considered fatal, and result in a shutdown.
|
||||
*
|
||||
* Unless otherwise stated, it can be assumed that this function will be called (and must succeed)
|
||||
* before any of the other functions below this point are called. In other words, we can assume that the
|
||||
* interface has been activated in the implementations of any of the functions below this point.
|
||||
*/
|
||||
virtual void activate() = 0;
|
||||
|
||||
/**
|
||||
* Should deactivate the physical interface between the debug tool and the RISC-V target.
|
||||
*
|
||||
* CAUTION: This function **CAN** be called before activate(), or in instances where activate() failed (threw
|
||||
* an exception). Implementations must accommodate this.
|
||||
*/
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
/**
|
||||
* Should read the value of a debug module register.
|
||||
*
|
||||
* @param address
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual DebugModule::RegisterValue readDebugModuleRegister(DebugModule::RegisterAddress address) = 0;
|
||||
|
||||
DebugModule::RegisterValue readDebugModuleRegister(DebugModule::Registers::RegisterAddress address) {
|
||||
return this->readDebugModuleRegister(static_cast<DebugModule::RegisterAddress>(address));
|
||||
};
|
||||
|
||||
/**
|
||||
* Should write a value to a debug module register.
|
||||
*
|
||||
* @param address
|
||||
* @param value
|
||||
*/
|
||||
virtual void writeDebugModuleRegister(
|
||||
DebugModule::RegisterAddress address,
|
||||
DebugModule::RegisterValue value
|
||||
) = 0;
|
||||
|
||||
void writeDebugModuleRegister(
|
||||
DebugModule::Registers::RegisterAddress address,
|
||||
DebugModule::RegisterValue value
|
||||
) {
|
||||
return this->writeDebugModuleRegister(
|
||||
static_cast<DebugModule::RegisterAddress>(address),
|
||||
value
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers
|
||||
{
|
||||
enum class CpuRegisterNumber: RegisterNumber
|
||||
{
|
||||
DEBUG_CONTROL_STATUS_REGISTER = 0x07b0,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers
|
||||
{
|
||||
struct DebugControlStatusRegister
|
||||
{
|
||||
enum class DebugModeCause: std::uint8_t
|
||||
{
|
||||
BREAK = 0x01,
|
||||
TRIGGER = 0x02,
|
||||
HALT_REQUEST = 0x03,
|
||||
STEP = 0x04,
|
||||
RESET_HALT_REQUEST = 0x05,
|
||||
GROUP = 0x06,
|
||||
};
|
||||
|
||||
PrivilegeMode privilegeMode:2;
|
||||
bool step:1 = false;
|
||||
bool nmiPending:1 = false;
|
||||
bool mprvEnabled:1 = false;
|
||||
DebugModeCause debugModeCause:3;
|
||||
bool stopTime:1 = false;
|
||||
bool stopCount:1 = false;
|
||||
bool stepInterruptsEnabled:1 = false;
|
||||
bool breakUMode:1 = false;
|
||||
bool breakSMode:1 = false;
|
||||
bool breakMMode:1 = false;
|
||||
bool breakVUMode:1 = false;
|
||||
bool breakVSMode:1 = false;
|
||||
std::uint8_t debugVersion:4 = 0;
|
||||
|
||||
DebugControlStatusRegister() = default;
|
||||
|
||||
constexpr explicit DebugControlStatusRegister(RegisterValue registerValue)
|
||||
: privilegeMode(static_cast<PrivilegeMode>(registerValue & 0x03))
|
||||
, step(static_cast<bool>(registerValue & (0x01 << 2)))
|
||||
, nmiPending(static_cast<bool>(registerValue & (0x01 << 3)))
|
||||
, mprvEnabled(static_cast<bool>(registerValue & (0x01 << 4)))
|
||||
, debugModeCause(static_cast<DebugModeCause>((registerValue >> 6) & 0x07))
|
||||
, stopTime(static_cast<bool>(registerValue & (0x01 << 9)))
|
||||
, stopCount(static_cast<bool>(registerValue & (0x01 << 10)))
|
||||
, stepInterruptsEnabled(static_cast<bool>(registerValue & (0x01 << 11)))
|
||||
, breakUMode(static_cast<bool>(registerValue & (0x01 << 12)))
|
||||
, breakSMode(static_cast<bool>(registerValue & (0x01 << 13)))
|
||||
, breakMMode(static_cast<bool>(registerValue & (0x01 << 15)))
|
||||
, breakVUMode(static_cast<bool>(registerValue & (0x01 << 16)))
|
||||
, breakVSMode(static_cast<bool>(registerValue & (0x01 << 17)))
|
||||
, debugVersion(static_cast<std::uint8_t>(registerValue >> 28) & 0x0F)
|
||||
{}
|
||||
|
||||
constexpr RegisterValue value() const {
|
||||
return RegisterValue{0}
|
||||
| static_cast<RegisterValue>(this->privilegeMode)
|
||||
| static_cast<RegisterValue>(this->step) << 2
|
||||
| static_cast<RegisterValue>(this->nmiPending) << 3
|
||||
| static_cast<RegisterValue>(this->mprvEnabled) << 4
|
||||
| static_cast<RegisterValue>(this->debugModeCause) << 6
|
||||
| static_cast<RegisterValue>(this->stopTime) << 9
|
||||
| static_cast<RegisterValue>(this->stopCount) << 10
|
||||
| static_cast<RegisterValue>(this->stepInterruptsEnabled) << 11
|
||||
| static_cast<RegisterValue>(this->breakUMode) << 12
|
||||
| static_cast<RegisterValue>(this->breakSMode) << 13
|
||||
| static_cast<RegisterValue>(this->breakMMode) << 15
|
||||
| static_cast<RegisterValue>(this->breakVUMode) << 16
|
||||
| static_cast<RegisterValue>(this->breakVSMode) << 17
|
||||
| static_cast<RegisterValue>(this->debugVersion) << 28
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::Protocols::RiscVDebugSpec
|
||||
{
|
||||
using RegisterValue = std::uint32_t;
|
||||
using RegisterNumber = std::uint16_t;
|
||||
|
||||
enum class PrivilegeMode: std::uint8_t
|
||||
{
|
||||
U_MODE = 0x00,
|
||||
S_MODE = 0x01,
|
||||
M_MODE = 0x03,
|
||||
};
|
||||
}
|
||||
@@ -2,21 +2,21 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Family.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/ProgramMemorySection.hpp"
|
||||
|
||||
#include "src/Targets/TargetDescriptor.hpp"
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
#include "src/Targets/TargetState.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
|
||||
namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* Interfacing with an AVR8 target for debugging operations can vary significantly, depending on the debug tool
|
||||
@@ -41,40 +41,17 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
|
||||
Avr8DebugInterface& operator = (const Avr8DebugInterface& other) = default;
|
||||
Avr8DebugInterface& operator = (Avr8DebugInterface&& other) = default;
|
||||
|
||||
/**
|
||||
* Should initialise the interface between the debug tool and the AVR8 target.
|
||||
*/
|
||||
virtual void init() = 0;
|
||||
|
||||
/**
|
||||
* Should stop execution on that target.
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/**
|
||||
* Should resume execution on the AVR8 target.
|
||||
*/
|
||||
virtual void run() = 0;
|
||||
|
||||
/**
|
||||
* Continue execution up to a specific byte address.
|
||||
*/
|
||||
virtual void runTo(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Step execution on teh AVR8 target.
|
||||
*/
|
||||
virtual void step() = 0;
|
||||
|
||||
/**
|
||||
* Should reset the AVR8 target.
|
||||
*/
|
||||
virtual void reset() = 0;
|
||||
|
||||
/**
|
||||
* Should activate the physical interface between the debug tool and the AVR8 target.
|
||||
*
|
||||
* If the debugWire interface has been selected - this function should throw a DebugWirePhysicalInterfaceError
|
||||
* If the debugWIRE interface has been selected - this function should throw a DebugWirePhysicalInterfaceError
|
||||
* exception, in the event of a failure when activating the interface. The reason for this is to allow us the
|
||||
* chance to check the DWEN fuse bit, via an ISP interface. See Avr8::activate() for more.
|
||||
*/
|
||||
@@ -85,6 +62,30 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
|
||||
*/
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
/**
|
||||
* We can specify access restrictions for individual registers in our TDFs, but this is only at a target
|
||||
* level - it does not account for any restrictions that the debug interface may be subject to.
|
||||
*
|
||||
* For example, EDBG debug tools cannot access fuse registers on JTAG targets, during a debug session. Those
|
||||
* registers can only be accessed during a programming session. This restriction is specific to the EDBG debug
|
||||
* interface.
|
||||
*
|
||||
* This function should communicate any access restrictions for the given register, which apply during a debug
|
||||
* session. It does not need to account for any access restrictions that only apply outside of a debug session.
|
||||
*
|
||||
* @param registerDescriptor
|
||||
* The descriptor of the register.
|
||||
*
|
||||
* @param addressSpaceDescriptor
|
||||
* The descriptor of the address space in which the register resides.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::TargetRegisterAccess getRegisterAccess(
|
||||
const Targets::TargetRegisterDescriptor& registerDescriptor,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Should retrieve the AVR8 target signature of the AVR8 target.
|
||||
*
|
||||
@@ -93,97 +94,29 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::Microchip::Avr::TargetSignature getDeviceId() = 0;
|
||||
virtual Targets::Microchip::Avr8::TargetSignature getDeviceId() = 0;
|
||||
|
||||
/**
|
||||
* Should set a software breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove a software breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should set a hardware breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void setHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove a hardware breakpoint at a given address.
|
||||
*
|
||||
* @param address
|
||||
*/
|
||||
virtual void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
/**
|
||||
* Should remove all software and hardware breakpoints on the target.
|
||||
*/
|
||||
virtual void clearAllBreakpoints() = 0;
|
||||
|
||||
/**
|
||||
* Should retrieve the current program counter value from the target.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::TargetMemoryAddress getProgramCounter() = 0;
|
||||
|
||||
/**
|
||||
* Should update the program counter value on the target.
|
||||
*
|
||||
* @param programCounter
|
||||
*/
|
||||
virtual void setProgramCounter(Targets::TargetMemoryAddress programCounter) = 0;
|
||||
|
||||
/**
|
||||
* Should read the requested registers from the target.
|
||||
*
|
||||
* @param descriptorIds
|
||||
* A collection of register descriptor IDs, for the registers to be read.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) = 0;
|
||||
|
||||
/**
|
||||
* Should update the value of the given registers.
|
||||
*
|
||||
* @param registers
|
||||
*/
|
||||
virtual void writeRegisters(const Targets::TargetRegisters& registers) = 0;
|
||||
|
||||
/**
|
||||
* Should read memory from the target, for the given memory type.
|
||||
*
|
||||
* @param memoryType
|
||||
* @param startAddress
|
||||
* @param bytes
|
||||
* @param excludedAddressRanges
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::TargetRegisterDescriptorAndValuePairs readRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) = 0;
|
||||
virtual void writeRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) = 0;
|
||||
virtual Targets::TargetMemoryBuffer readMemory(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges = {}
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Should write memory to the target, for a given memory type.
|
||||
*
|
||||
* @param memoryType
|
||||
* @param startAddress
|
||||
* @param buffer
|
||||
*/
|
||||
virtual void writeMemory(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
const Targets::TargetMemoryBuffer& buffer
|
||||
) = 0;
|
||||
@@ -195,29 +128,12 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8
|
||||
* The section to erase, or std::nullopt to erase the entire program memory.
|
||||
*/
|
||||
virtual void eraseProgramMemory(
|
||||
std::optional<Targets::Microchip::Avr::Avr8Bit::ProgramMemorySection> section = std::nullopt
|
||||
std::optional<Targets::Microchip::Avr8::ProgramMemorySection> section = std::nullopt
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Should erase the chip.
|
||||
*/
|
||||
virtual void eraseChip() = 0;
|
||||
|
||||
/**
|
||||
* Should obtain the current target state.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::TargetState getTargetState() = 0;
|
||||
|
||||
/**
|
||||
* Should prepare the debug interface for programming the target.
|
||||
*/
|
||||
virtual Targets::TargetExecutionState getExecutionState() = 0;
|
||||
virtual void enableProgrammingMode() = 0;
|
||||
|
||||
/**
|
||||
* Should prepare the debug interface for resuming debugging operations after a programming session.
|
||||
*/
|
||||
virtual void disableProgrammingMode() = 0;
|
||||
};
|
||||
}
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR/IspParameters.hpp"
|
||||
#include "src/Targets/Microchip/AVR/Fuse.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/IspParameters.hpp"
|
||||
#include "src/Targets/Microchip/AVR8/Fuse.hpp"
|
||||
|
||||
#include "src/ProjectConfig.hpp"
|
||||
|
||||
namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr
|
||||
namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr8
|
||||
{
|
||||
/**
|
||||
* Many AVRs can be programmed via an SPI interface. Some debug tools provide access to this interface via the AVR
|
||||
@@ -17,8 +17,8 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr
|
||||
* This interface class is incomplete - it only provides the ability to read the device ID and access AVR fuses and
|
||||
* lockbit bytes (as that's all we need, for now).
|
||||
*
|
||||
* Currently, Bloom only uses the ISP interface for accessing fuses and lockbits on debugWire targets. We can't
|
||||
* access fuses via the debugWire interface, so we have to use the ISP interface.
|
||||
* Currently, Bloom only uses the ISP interface for accessing fuses and lockbits on debugWIRE targets. We can't
|
||||
* access fuses via the debugWIRE interface, so we have to use the ISP interface.
|
||||
*
|
||||
* @see Avr8::updateDwenFuseBit() for more.
|
||||
*/
|
||||
@@ -34,20 +34,6 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr
|
||||
AvrIspInterface& operator = (const AvrIspInterface& other) = default;
|
||||
AvrIspInterface& operator = (AvrIspInterface&& other) = default;
|
||||
|
||||
/**
|
||||
* Configures the ISP interface with user-provided config parameters.
|
||||
*
|
||||
* @param targetConfig
|
||||
*/
|
||||
virtual void configure(const TargetConfig& targetConfig) = 0;
|
||||
|
||||
/**
|
||||
* Configures the ISP interface with the target's ISP parameters.
|
||||
*
|
||||
* @param ispParameters
|
||||
*/
|
||||
virtual void setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) = 0;
|
||||
|
||||
/**
|
||||
* Should initialise and activate the ISP interface between the debug tool and the AVR target.
|
||||
*/
|
||||
@@ -63,15 +49,17 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::Microchip::Avr::TargetSignature getDeviceId() = 0;
|
||||
virtual Targets::Microchip::Avr8::TargetSignature getDeviceId() = 0;
|
||||
|
||||
/**
|
||||
* Should read a fuse from the AVR target.
|
||||
*
|
||||
* @param fuseType
|
||||
* @param fuseRegisterDescriptor
|
||||
* @return
|
||||
*/
|
||||
virtual Targets::Microchip::Avr::Fuse readFuse(Targets::Microchip::Avr::FuseType fuseType) = 0;
|
||||
virtual Targets::Microchip::Avr8::FuseValue readFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Should read the lock bit byte from the AVR target.
|
||||
@@ -83,8 +71,12 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr
|
||||
/**
|
||||
* Should program a particular fuse byte.
|
||||
*
|
||||
* @param fuse
|
||||
* @param fuseRegisterDescriptor
|
||||
* @param value
|
||||
*/
|
||||
virtual void programFuse(Targets::Microchip::Avr::Fuse fuse) = 0;
|
||||
virtual void programFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor,
|
||||
Targets::Microchip::Avr8::FuseValue value
|
||||
) = 0;
|
||||
};
|
||||
}
|
||||
@@ -1,99 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/RiscV/RiscVGeneric.hpp"
|
||||
#include "src/Targets/RiscV/DebugModule/DebugModule.hpp"
|
||||
#include "src/Targets/RiscV/DebugModule/Registers/RegisterAddresses.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/TargetParameters.hpp"
|
||||
#include "src/Targets/TargetDescriptor.hpp"
|
||||
#include "src/Targets/TargetAddressSpaceDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
#include "src/Targets/TargetState.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::TargetInterfaces::RiscV
|
||||
{
|
||||
class RiscVDebugInterface
|
||||
{
|
||||
public:
|
||||
RiscVDebugInterface() = default;
|
||||
virtual ~RiscVDebugInterface() = default;
|
||||
|
||||
RiscVDebugInterface(const RiscVDebugInterface& other) = default;
|
||||
RiscVDebugInterface(RiscVDebugInterface&& other) = default;
|
||||
|
||||
RiscVDebugInterface& operator = (const RiscVDebugInterface& other) = default;
|
||||
RiscVDebugInterface& operator = (RiscVDebugInterface&& other) = default;
|
||||
|
||||
/**
|
||||
* Should prepare for and then activate the physical interface between the debug tool and the RISC-V target.
|
||||
*
|
||||
* Should throw an exception if activation fails. The error will be considered fatal, and result in a shutdown.
|
||||
*
|
||||
* Unless otherwise stated, it can be assumed that this function will be called (and must succeed)
|
||||
* before any of the other functions below this point are called. In other words, we can assume that the
|
||||
* interface has been activated in the implementations of any of the functions below this point.
|
||||
*
|
||||
* @param targetParameters
|
||||
* Parameters for the RISC-V target. These can be ignored if a particular implementation does not require
|
||||
* any target parameters for activation.
|
||||
*/
|
||||
virtual void activate(const Targets::RiscV::TargetParameters& targetParameters) = 0;
|
||||
|
||||
/**
|
||||
* Should deactivate the physical interface between the debug tool and the RISC-V target.
|
||||
*
|
||||
* CAUTION: This function **CAN** be called before activate(), or in instances where activate() failed (threw
|
||||
* an exception). Implementations must accommodate this.
|
||||
*/
|
||||
virtual void init() = 0;
|
||||
virtual void activate() = 0;
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
/**
|
||||
* Should retrieve the RISC-V target ID in string form.
|
||||
*
|
||||
* @return
|
||||
* The target ID, in the form of a string.
|
||||
*/
|
||||
virtual std::string getDeviceId() = 0;
|
||||
virtual Targets::TargetExecutionState getExecutionState() = 0;
|
||||
|
||||
/**
|
||||
* Should read the value of a debug module register.
|
||||
*
|
||||
* @param address
|
||||
* The address of the debug module register to read.
|
||||
*
|
||||
* @return
|
||||
* The register value.
|
||||
*/
|
||||
virtual Targets::RiscV::DebugModule::RegisterValue readDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::RegisterAddress address
|
||||
virtual void stop() = 0;
|
||||
virtual void run() = 0;
|
||||
virtual void step() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
virtual void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
|
||||
virtual void setHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
virtual void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0;
|
||||
virtual void clearAllBreakpoints() = 0;
|
||||
|
||||
virtual Targets::TargetRegisterDescriptorAndValuePairs readCpuRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) = 0;
|
||||
virtual void writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) = 0;
|
||||
|
||||
Targets::RiscV::DebugModule::RegisterValue readDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::Registers::RegisterAddress address
|
||||
) {
|
||||
return this->readDebugModuleRegister(static_cast<Targets::RiscV::DebugModule::RegisterAddress>(address));
|
||||
};
|
||||
|
||||
/**
|
||||
* Should write a value to a debug module register.
|
||||
*
|
||||
* @param address
|
||||
* The address of the debug module to update.
|
||||
*
|
||||
* @param value
|
||||
* The value to write.
|
||||
*/
|
||||
virtual void writeDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::RegisterAddress address,
|
||||
Targets::RiscV::DebugModule::RegisterValue value
|
||||
virtual Targets::TargetMemoryBuffer readMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges = {}
|
||||
) = 0;
|
||||
virtual void writeMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
const Targets::TargetMemoryBuffer& buffer
|
||||
) = 0;
|
||||
|
||||
void writeDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::Registers::RegisterAddress address,
|
||||
Targets::RiscV::DebugModule::RegisterValue value
|
||||
) {
|
||||
return this->writeDebugModuleRegister(
|
||||
static_cast<Targets::RiscV::DebugModule::RegisterAddress>(address),
|
||||
value
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace DebugToolDrivers::TargetInterfaces::RiscV
|
||||
{
|
||||
class RiscVIdentificationInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Should retrieve the RISC-V target ID in string form.
|
||||
*
|
||||
* @return
|
||||
* The target ID, in the form of a string.
|
||||
*/
|
||||
virtual std::string getDeviceId() = 0;
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::TargetInterfaces
|
||||
{
|
||||
/**
|
||||
@@ -14,20 +12,13 @@ namespace DebugToolDrivers::TargetInterfaces
|
||||
TargetPowerManagementInterface() = default;
|
||||
virtual ~TargetPowerManagementInterface() = default;
|
||||
|
||||
TargetPowerManagementInterface(const TargetPowerManagementInterface& other) = default;
|
||||
TargetPowerManagementInterface(TargetPowerManagementInterface&& other) = default;
|
||||
TargetPowerManagementInterface(const TargetPowerManagementInterface& other) = delete;
|
||||
TargetPowerManagementInterface& operator = (const TargetPowerManagementInterface& other) = delete;
|
||||
|
||||
TargetPowerManagementInterface& operator = (const TargetPowerManagementInterface& other) = default;
|
||||
TargetPowerManagementInterface(TargetPowerManagementInterface&& other) = default;
|
||||
TargetPowerManagementInterface& operator = (TargetPowerManagementInterface&& other) = default;
|
||||
|
||||
/**
|
||||
* Should enable the target power if currently disabled.
|
||||
*/
|
||||
virtual void enableTargetPower() = 0;
|
||||
|
||||
/**
|
||||
* Should disable the target power if currently enabled.
|
||||
*/
|
||||
virtual void disableTargetPower() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Usb
|
||||
Logger::debug("HID device path: " + hidInterfacePath);
|
||||
|
||||
if ((hidDevice = ::hid_open_path(hidInterfacePath.c_str())) == nullptr) {
|
||||
throw DeviceInitializationFailure("Failed to open HID device via hidapi.");
|
||||
throw DeviceInitializationFailure{"Failed to open HID device via hidapi."};
|
||||
}
|
||||
|
||||
this->hidDevice.reset(hidDevice);
|
||||
@@ -41,11 +41,11 @@ namespace Usb
|
||||
}
|
||||
|
||||
std::vector<unsigned char> HidInterface::read(std::optional<std::chrono::milliseconds> timeout) {
|
||||
auto output = std::vector<unsigned char>();
|
||||
auto output = std::vector<unsigned char>{};
|
||||
|
||||
const auto readSize = this->inputReportSize;
|
||||
auto transferredByteCount = int(0);
|
||||
auto totalByteCount = std::size_t(0);
|
||||
auto totalByteCount = std::size_t{0};
|
||||
|
||||
do {
|
||||
output.resize(totalByteCount + readSize, 0x00);
|
||||
@@ -58,12 +58,12 @@ namespace Usb
|
||||
);
|
||||
|
||||
if (transferredByteCount == -1) {
|
||||
throw DeviceCommunicationFailure("Failed to read from HID device.");
|
||||
throw DeviceCommunicationFailure{"Failed to read from HID device."};
|
||||
}
|
||||
|
||||
if (totalByteCount == 0) {
|
||||
// After the first read, set the timeout to 1 millisecond, as we don't want to wait for subsequent data
|
||||
timeout = std::chrono::milliseconds(1);
|
||||
timeout = std::chrono::milliseconds{1};
|
||||
}
|
||||
|
||||
totalByteCount += static_cast<std::size_t>(transferredByteCount);
|
||||
@@ -76,9 +76,7 @@ namespace Usb
|
||||
|
||||
void HidInterface::write(std::vector<unsigned char>&& buffer) {
|
||||
if (buffer.size() > this->inputReportSize) {
|
||||
throw DeviceCommunicationFailure(
|
||||
"Cannot send data via HID interface - data exceeds maximum packet size."
|
||||
);
|
||||
throw DeviceCommunicationFailure{"Cannot send data via HID interface - data exceeds maximum packet size."};
|
||||
}
|
||||
|
||||
if (buffer.size() < this->inputReportSize) {
|
||||
@@ -93,9 +91,11 @@ namespace Usb
|
||||
const auto length = buffer.size();
|
||||
|
||||
if ((transferred = ::hid_write(this->hidDevice.get(), buffer.data(), length)) != length) {
|
||||
Logger::debug("Attempted to write " + std::to_string(length)
|
||||
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred));
|
||||
throw DeviceCommunicationFailure("Failed to write data to HID interface.");
|
||||
Logger::debug(
|
||||
"Attempted to write " + std::to_string(length)
|
||||
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred)
|
||||
);
|
||||
throw DeviceCommunicationFailure{"Failed to write data to HID interface."};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Usb
|
||||
::hid_free_enumeration
|
||||
);
|
||||
|
||||
auto matchedDevice = std::optional<::hid_device_info*>();
|
||||
auto matchedDevice = std::optional<::hid_device_info*>{};
|
||||
|
||||
auto* hidDeviceInfo = hidDeviceInfoList.get();
|
||||
while (hidDeviceInfo != nullptr) {
|
||||
@@ -118,9 +118,9 @@ namespace Usb
|
||||
}
|
||||
|
||||
if (!matchedDevice.has_value()) {
|
||||
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
|
||||
throw DeviceInitializationFailure{"Failed to match interface number with HID interface."};
|
||||
}
|
||||
|
||||
return std::string(matchedDevice.value()->path);
|
||||
return {matchedDevice.value()->path};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Usb
|
||||
private:
|
||||
using HidDevice = std::unique_ptr<::hid_device, decltype(&::hid_close)>;
|
||||
|
||||
HidDevice hidDevice = HidDevice(nullptr, ::hid_close);
|
||||
HidDevice hidDevice = {nullptr, ::hid_close};
|
||||
|
||||
std::uint16_t vendorId = 0;
|
||||
std::uint16_t productId = 0;
|
||||
|
||||
@@ -30,18 +30,18 @@ namespace Usb
|
||||
auto devices = this->findMatchingDevices(this->vendorId, this->productId);
|
||||
|
||||
if (devices.empty()) {
|
||||
throw DeviceNotFound(
|
||||
throw DeviceNotFound{
|
||||
"Failed to find USB device with matching vendor and product ID. Please examine the debug tool's USB "
|
||||
"connection, as well as the selected environment's debug tool configuration, in bloom.yaml"
|
||||
);
|
||||
"connection, as well as the selected environment's debug tool configuration, in bloom.yaml"
|
||||
};
|
||||
}
|
||||
|
||||
if (devices.size() > 1) {
|
||||
// TODO: implement support for multiple devices via serial number matching?
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Numerous devices of matching vendor and product ID found.\n"
|
||||
"Please ensure that only one debug tool is connected and then try again."
|
||||
);
|
||||
"Please ensure that only one debug tool is connected and then try again."
|
||||
};
|
||||
}
|
||||
|
||||
// For now, just use the first device found.
|
||||
@@ -51,9 +51,9 @@ namespace Usb
|
||||
const int libusbStatusCode = ::libusb_open(this->libusbDevice.get(), &deviceHandle);
|
||||
|
||||
if (libusbStatusCode < 0) {
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to open USB device - error code " + std::to_string(libusbStatusCode) + " returned."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->libusbDeviceHandle.reset(deviceHandle);
|
||||
@@ -65,12 +65,12 @@ namespace Usb
|
||||
|
||||
auto statusCode = ::libusb_get_device_descriptor(this->libusbDevice.get(), &desc);
|
||||
if (statusCode != 0) {
|
||||
throw DeviceCommunicationFailure(
|
||||
throw DeviceCommunicationFailure{
|
||||
"Failed to retrieve USB device descriptor - status code: " + std::to_string(statusCode)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
auto data = std::array<unsigned char, 256>();
|
||||
auto data = std::array<unsigned char, 256>{};
|
||||
const auto transferredBytes = ::libusb_get_string_descriptor_ascii(
|
||||
this->libusbDeviceHandle.get(),
|
||||
desc.iSerialNumber,
|
||||
@@ -79,12 +79,12 @@ namespace Usb
|
||||
);
|
||||
|
||||
if (transferredBytes <= 0) {
|
||||
throw DeviceCommunicationFailure(
|
||||
throw DeviceCommunicationFailure{
|
||||
"Failed to retrieve serial number from USB device - status code: " + std::to_string(transferredBytes)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return std::string(data.begin(), data.begin() + transferredBytes);
|
||||
return {data.begin(), data.begin() + transferredBytes};
|
||||
}
|
||||
|
||||
void UsbDevice::setConfiguration(std::uint8_t configurationIndex) {
|
||||
@@ -96,9 +96,9 @@ namespace Usb
|
||||
);
|
||||
|
||||
if (libusbStatusCode < 0) {
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to set USB configuration - error code " + std::to_string(libusbStatusCode) + " returned."
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Usb
|
||||
}
|
||||
}
|
||||
|
||||
throw DeviceInitializationFailure("Failed to obtain address of USB endpoint");
|
||||
throw DeviceInitializationFailure{"Failed to obtain address of USB endpoint"};
|
||||
}
|
||||
|
||||
std::uint16_t UsbDevice::getEndpointMaxPacketSize(std::uint8_t endpointAddress) {
|
||||
@@ -142,11 +142,11 @@ namespace Usb
|
||||
}
|
||||
}
|
||||
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to obtain maximum packet size of USB endpoint (address: 0x"
|
||||
+ Services::StringService::toHex(endpointAddress) + "). Endpoint not found. Selected configuration "
|
||||
"value (" + std::to_string(activeConfigDescriptor->bConfigurationValue) + ")"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<LibusbDevice> UsbDevice::findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId) {
|
||||
@@ -156,9 +156,9 @@ namespace Usb
|
||||
|
||||
auto libusbStatusCode = ::libusb_get_device_list(UsbDevice::libusbContext.get(), &devices);
|
||||
if (libusbStatusCode < 0) {
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to retrieve USB devices - return code: '" + std::to_string(libusbStatusCode) + "'"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
ssize_t i = 0;
|
||||
@@ -167,8 +167,10 @@ namespace Usb
|
||||
struct ::libusb_device_descriptor desc = {};
|
||||
|
||||
if ((libusbStatusCode = ::libusb_get_device_descriptor(device, &desc)) < 0) {
|
||||
Logger::warning("Failed to retrieve USB device descriptor - return code: '"
|
||||
+ std::to_string(libusbStatusCode) + "'");
|
||||
Logger::warning(
|
||||
"Failed to retrieve USB device descriptor - return code: '"
|
||||
+ std::to_string(libusbStatusCode) + "'"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -191,16 +193,13 @@ namespace Usb
|
||||
: ::libusb_get_active_config_descriptor(this->libusbDevice.get(), &configDescriptor);
|
||||
|
||||
if (libusbStatusCode < 0) {
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libusbStatusCode)
|
||||
+ " returned."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return LibusbConfigDescriptor(
|
||||
configDescriptor,
|
||||
::libusb_free_config_descriptor
|
||||
);
|
||||
return {configDescriptor, ::libusb_free_config_descriptor};
|
||||
}
|
||||
|
||||
void UsbDevice::detachKernelDriverFromInterface(std::uint8_t interfaceNumber) {
|
||||
@@ -214,7 +213,7 @@ namespace Usb
|
||||
}
|
||||
|
||||
} else if (libusbStatusCode != 0) {
|
||||
throw DeviceInitializationFailure("Failed to check for active kernel driver on USB interface.");
|
||||
throw DeviceInitializationFailure{"Failed to check for active kernel driver on USB interface."};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,10 +64,10 @@ namespace Usb
|
||||
virtual ~UsbDevice();
|
||||
|
||||
protected:
|
||||
static inline LibusbContext libusbContext = LibusbContext(nullptr, ::libusb_exit);
|
||||
static inline LibusbContext libusbContext = {nullptr, ::libusb_exit};
|
||||
|
||||
LibusbDevice libusbDevice = LibusbDevice(nullptr, ::libusb_unref_device);
|
||||
LibusbDeviceHandle libusbDeviceHandle = LibusbDeviceHandle(nullptr, ::libusb_close);
|
||||
LibusbDevice libusbDevice = {nullptr, ::libusb_unref_device};
|
||||
LibusbDeviceHandle libusbDeviceHandle = {nullptr, ::libusb_close};
|
||||
|
||||
std::vector<LibusbDevice> findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId);
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ namespace Usb
|
||||
|
||||
const auto statusCode = ::libusb_claim_interface(this->deviceHandle, this->interfaceNumber);
|
||||
if (statusCode != 0) {
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"Failed to claim USB interface (number: " + std::to_string(this->interfaceNumber) + ") - error code: "
|
||||
+ std::to_string(statusCode)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->claimed = true;
|
||||
@@ -49,11 +49,11 @@ namespace Usb
|
||||
std::uint8_t endpointAddress,
|
||||
std::optional<std::chrono::milliseconds> timeout
|
||||
) {
|
||||
auto output = std::vector<unsigned char>();
|
||||
auto output = std::vector<unsigned char>{};
|
||||
|
||||
constexpr auto transferSize = 512;
|
||||
auto bytesTransferred = int(0);
|
||||
auto totalByteCount = std::size_t(0);
|
||||
auto bytesTransferred = int{0};
|
||||
auto totalByteCount = std::size_t{0};
|
||||
|
||||
do {
|
||||
output.resize(totalByteCount + transferSize, 0x00);
|
||||
@@ -68,12 +68,12 @@ namespace Usb
|
||||
);
|
||||
|
||||
if (statusCode != 0) {
|
||||
throw DeviceCommunicationFailure("Failed to read from bulk endpoint");
|
||||
throw DeviceCommunicationFailure{"Failed to read from bulk endpoint"};
|
||||
}
|
||||
|
||||
if (totalByteCount == 0) {
|
||||
// After the first read, set the timeout to 1 millisecond, as we don't want to wait for subsequent data
|
||||
timeout = std::chrono::milliseconds(1);
|
||||
timeout = std::chrono::milliseconds{1};
|
||||
}
|
||||
|
||||
totalByteCount += static_cast<std::size_t>(bytesTransferred);
|
||||
@@ -86,11 +86,11 @@ namespace Usb
|
||||
|
||||
void UsbInterface::writeBulk(std::uint8_t endpointAddress, std::vector<unsigned char>&& buffer) {
|
||||
if (buffer.size() > std::numeric_limits<int>::max()) {
|
||||
throw DeviceCommunicationFailure("Attempted to send too much data to bulk endpoint");
|
||||
throw DeviceCommunicationFailure{"Attempted to send too much data to bulk endpoint"};
|
||||
}
|
||||
|
||||
const auto length = static_cast<int>(buffer.size());
|
||||
auto bytesTransferred = int(0);
|
||||
auto bytesTransferred = int{0};
|
||||
|
||||
const auto statusCode = ::libusb_bulk_transfer(
|
||||
this->deviceHandle,
|
||||
@@ -106,7 +106,7 @@ namespace Usb
|
||||
"Attempted to write " + std::to_string(length) + " bytes to USB bulk endpoint. Bytes written: "
|
||||
+ std::to_string(bytesTransferred) + ". Status code: " + std::to_string(statusCode)
|
||||
);
|
||||
throw DeviceCommunicationFailure("Failed to write data to bulk endpoint");
|
||||
throw DeviceCommunicationFailure{"Failed to write data to bulk endpoint"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
|
||||
rawCommand[0] = 0x81;
|
||||
rawCommand[1] = this->commandId;
|
||||
rawCommand[2] = static_cast<std::uint8_t >(this->payload.size());
|
||||
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);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp"
|
||||
#include "src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/DebugModule/DebugModule.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
{
|
||||
@@ -16,9 +16,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands
|
||||
using ExpectedResponseType = Responses::DebugModuleInterfaceOperationResponse;
|
||||
|
||||
DebugModuleInterfaceOperation(
|
||||
Targets::RiscV::DebugModule::DmiOperation operation,
|
||||
Targets::RiscV::DebugModule::RegisterAddress address,
|
||||
std::optional<Targets::RiscV::DebugModule::RegisterValue> value = std::nullopt
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperation operation,
|
||||
DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address,
|
||||
std::optional<DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue> value = std::nullopt
|
||||
)
|
||||
: Command(0x08)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/Targets/RiscV/DebugModule/DebugModule.hpp"
|
||||
#include "src/Helpers/BiMap.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp"
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
@@ -13,32 +12,33 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Responses
|
||||
class DebugModuleInterfaceOperationResponse
|
||||
{
|
||||
public:
|
||||
Targets::RiscV::DebugModule::DmiOperationStatus operationStatus;
|
||||
Targets::RiscV::DebugModule::RegisterAddress address;
|
||||
Targets::RiscV::DebugModule::RegisterValue value;
|
||||
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;
|
||||
|
||||
explicit DebugModuleInterfaceOperationResponse(const std::vector<unsigned char>& payload)
|
||||
{
|
||||
if (payload.size() != 6) {
|
||||
throw Exceptions::DeviceCommunicationFailure(
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Unexpected response payload size for DMI operation command"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const auto status = payload[5];
|
||||
if (
|
||||
status != static_cast<unsigned char>(Targets::RiscV::DebugModule::DmiOperationStatus::SUCCESS)
|
||||
&& status != static_cast<unsigned char>(Targets::RiscV::DebugModule::DmiOperationStatus::FAILED)
|
||||
&& status != static_cast<unsigned char>(Targets::RiscV::DebugModule::DmiOperationStatus::BUSY)
|
||||
status != static_cast<unsigned char>(DmiOperationStatus::SUCCESS)
|
||||
&& status != static_cast<unsigned char>(DmiOperationStatus::FAILED)
|
||||
&& status != static_cast<unsigned char>(DmiOperationStatus::BUSY)
|
||||
) {
|
||||
throw Exceptions::DeviceCommunicationFailure(
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Unknown DMI operation status returned: 0x" + Services::StringService::toHex(status)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->operationStatus = static_cast<Targets::RiscV::DebugModule::DmiOperationStatus>(status);
|
||||
this->operationStatus = static_cast<DmiOperationStatus>(status);
|
||||
this->address = payload[0];
|
||||
this->value = static_cast<Targets::RiscV::DebugModule::RegisterValue>(
|
||||
this->value = static_cast<DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue>(
|
||||
(payload[1] << 24) | (payload[2] << 16) | (payload[3] << 8) | (payload[4])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
{
|
||||
using namespace ::DebugToolDrivers::Protocols::RiscVDebugSpec;
|
||||
using namespace Exceptions;
|
||||
|
||||
using Targets::RiscV::DebugModule::DmiOperation;
|
||||
using DebugModule::DmiOperation;
|
||||
|
||||
WchLinkInterface::WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice)
|
||||
: usbInterface(usbInterface)
|
||||
@@ -29,34 +30,32 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
{}
|
||||
|
||||
DeviceInfo WchLinkInterface::getDeviceInfo() {
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo());
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo{});
|
||||
if (response.payload.size() < 3) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Cannot construct DeviceInfo response - invalid payload");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Cannot construct DeviceInfo response - invalid payload"};
|
||||
}
|
||||
|
||||
static const auto variantsById = BiMap<std::uint8_t, WchLinkVariant>({
|
||||
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]),
|
||||
WchFirmwareVersion{response.payload[0], response.payload[1]},
|
||||
response.payload.size() >= 4
|
||||
? std::optional(variantsById.valueAt(response.payload[2]).value_or(WchLinkVariant::UNKNOWN))
|
||||
? std::optional{variantsById.valueAt(response.payload[2]).value_or(WchLinkVariant::UNKNOWN)}
|
||||
: std::nullopt
|
||||
);
|
||||
}
|
||||
|
||||
void WchLinkInterface::activate(const Targets::RiscV::TargetParameters& targetParameters) {
|
||||
void WchLinkInterface::activate() {
|
||||
this->setClockSpeed(WchLinkTargetClockSpeed::CLK_6000_KHZ);
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::AttachTarget());
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::AttachTarget{});
|
||||
if (response.payload.size() != 5) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Unexpected response payload size for AttachTarget command");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for AttachTarget command"};
|
||||
}
|
||||
|
||||
this->cachedTargetId = static_cast<WchTargetId>(
|
||||
@@ -67,10 +66,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
}
|
||||
|
||||
void WchLinkInterface::deactivate() {
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::DetachTarget());
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(Commands::Control::DetachTarget{});
|
||||
if (response.payload.size() != 1) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Unexpected response payload size for DetachTarget command");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for DetachTarget command"};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,34 +76,32 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
return "0x" + Services::StringService::toHex(this->cachedTargetId.value());
|
||||
}
|
||||
|
||||
Targets::RiscV::DebugModule::RegisterValue WchLinkInterface::readDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::RegisterAddress address
|
||||
) {
|
||||
using Targets::RiscV::DebugModule::DmiOperationStatus ;
|
||||
DebugModule::RegisterValue WchLinkInterface::readDebugModuleRegister(DebugModule::RegisterAddress address) {
|
||||
using DebugModule::DmiOperationStatus;
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::DebugModuleInterfaceOperation(DmiOperation::READ, address)
|
||||
Commands::DebugModuleInterfaceOperation{DmiOperation::READ, address}
|
||||
);
|
||||
|
||||
if (response.operationStatus != DmiOperationStatus::SUCCESS) {
|
||||
throw Exceptions::DeviceCommunicationFailure("DMI operation failed");
|
||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation failed"};
|
||||
}
|
||||
|
||||
return response.value;
|
||||
}
|
||||
|
||||
void WchLinkInterface::writeDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::RegisterAddress address,
|
||||
Targets::RiscV::DebugModule::RegisterValue value
|
||||
DebugModule::RegisterAddress address,
|
||||
DebugModule::RegisterValue value
|
||||
) {
|
||||
using Targets::RiscV::DebugModule::DmiOperationStatus ;
|
||||
using DebugModule::DmiOperationStatus;
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::DebugModuleInterfaceOperation(DmiOperation::WRITE, address, value)
|
||||
Commands::DebugModuleInterfaceOperation{DmiOperation::WRITE, address, value}
|
||||
);
|
||||
|
||||
if (response.operationStatus != DmiOperationStatus::SUCCESS) {
|
||||
throw Exceptions::DeviceCommunicationFailure("DMI operation failed");
|
||||
throw Exceptions::DeviceCommunicationFailure{"DMI operation failed"};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,36 +115,35 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
std::ceil(static_cast<float>(bufferSize) / static_cast<float>(packetSize))
|
||||
);
|
||||
|
||||
for (std::uint32_t i = 0; i < packetsRequired; ++i) {
|
||||
for (auto i = std::uint32_t{0}; i < packetsRequired; ++i) {
|
||||
const auto segmentSize = static_cast<std::uint8_t>(std::min(bufferSize - (i * packetSize), packetSize));
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::PreparePartialFlashPageWrite(startAddress + (packetSize * i), segmentSize)
|
||||
Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize}
|
||||
);
|
||||
|
||||
if (response.payload.size() != 1) {
|
||||
throw Exceptions::DeviceCommunicationFailure(
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Unexpected response payload size for PreparePartialFlashPageWrite command"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->usbInterface.writeBulk(
|
||||
WchLinkInterface::USB_DATA_ENDPOINT_OUT,
|
||||
std::vector<unsigned char>(
|
||||
std::vector<unsigned char>{
|
||||
buffer.begin() + (packetSize * i),
|
||||
buffer.begin() + (packetSize * i) + segmentSize
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_DATA_ENDPOINT_IN);
|
||||
|
||||
if (rawResponse.size() != 4) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Unexpected response size for partial flash page write");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response size for partial flash page write"};
|
||||
}
|
||||
|
||||
/*
|
||||
* I have no idea what any of these bytes mean. There's no documentation available for this.
|
||||
*
|
||||
* All I know is that these values indicate a successful write.
|
||||
* I have no idea what any of these bytes mean. I've not been a le to find any documentation for
|
||||
* this. All I know is that these values indicate a successful write.
|
||||
*/
|
||||
if (
|
||||
rawResponse[0] != 0x41
|
||||
@@ -156,28 +151,28 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
|| rawResponse[2] != 0x01
|
||||
|| rawResponse[3] != 0x02
|
||||
) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Partial flash page write failed");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Partial flash page write failed"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WchLinkInterface::setClockSpeed(WchLinkTargetClockSpeed speed) {
|
||||
const auto speedIdsBySpeed = BiMap<WchLinkTargetClockSpeed, std::uint8_t>({
|
||||
const auto speedIdsBySpeed = BiMap<WchLinkTargetClockSpeed, std::uint8_t>{
|
||||
{WchLinkTargetClockSpeed::CLK_6000_KHZ, 0x01},
|
||||
{WchLinkTargetClockSpeed::CLK_4000_KHZ, 0x02},
|
||||
{WchLinkTargetClockSpeed::CLK_400_KHZ, 0x03},
|
||||
});
|
||||
};
|
||||
|
||||
const auto response = this->sendCommandAndWaitForResponse(
|
||||
Commands::SetClockSpeed(this->cachedTargetGroupId.value_or(0x01), speedIdsBySpeed.at(speed))
|
||||
Commands::SetClockSpeed{this->cachedTargetGroupId.value_or(0x01), speedIdsBySpeed.at(speed)}
|
||||
);
|
||||
|
||||
if (response.payload.size() != 1) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Unexpected response payload size for SetClockSpeed command");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for SetClockSpeed command"};
|
||||
}
|
||||
|
||||
if (response.payload[0] != 0x01) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Unexpected response for SetClockSpeed command");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Unexpected response for SetClockSpeed command"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp"
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp"
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/USB/UsbInterface.hpp"
|
||||
#include "src/DebugToolDrivers/USB/UsbDevice.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/WCH/WchGeneric.hpp"
|
||||
#include "Commands/Command.hpp"
|
||||
|
||||
#include "src/Targets/RiscV/DebugModule/DebugModule.hpp"
|
||||
#include "src/DebugToolDrivers/WCH/DeviceInfo.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
@@ -21,30 +22,33 @@
|
||||
namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
{
|
||||
/**
|
||||
* The WchLinkInterface implements the WCH-Link protocol.
|
||||
* Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface, a
|
||||
* programming interface and a target identification interface.
|
||||
*
|
||||
* WCH debug tools cannot write to program memory via the target's RISC-V debug module, so we cannot program the
|
||||
* target via the tool's RISC-V DTM interface. Instead, the WCH-Link protocol provides a dedicated command for
|
||||
* writing to program memory, which is why this class implements the RISC-V programming interface.
|
||||
* See WchLinkInterface::writeFlashMemory() for more.
|
||||
*/
|
||||
class WchLinkInterface
|
||||
: public TargetInterfaces::RiscV::RiscVDebugInterface
|
||||
: public ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTransportModuleInterface
|
||||
, public TargetInterfaces::RiscV::RiscVProgramInterface
|
||||
, public TargetInterfaces::RiscV::RiscVIdentificationInterface
|
||||
{
|
||||
public:
|
||||
WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice);
|
||||
|
||||
DeviceInfo getDeviceInfo();
|
||||
|
||||
void activate(const Targets::RiscV::TargetParameters& targetParameters) override;
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
|
||||
std::string getDeviceId() override;
|
||||
|
||||
Targets::RiscV::DebugModule::RegisterValue readDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::RegisterAddress address
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue readDebugModuleRegister(
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address
|
||||
) override;
|
||||
|
||||
void writeDebugModuleRegister(
|
||||
Targets::RiscV::DebugModule::RegisterAddress address,
|
||||
Targets::RiscV::DebugModule::RegisterValue value
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address,
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value
|
||||
) override;
|
||||
|
||||
void writeFlashMemory(
|
||||
@@ -85,9 +89,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
auto rawCommand = command.getRawCommand();
|
||||
|
||||
if (rawCommand.size() > this->commandEndpointMaxPacketSize) {
|
||||
throw Exceptions::DeviceCommunicationFailure(
|
||||
throw Exceptions::DeviceCommunicationFailure{
|
||||
"Raw command size exceeds maximum packet size for command endpoint"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->usbInterface.writeBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_OUT, std::move(rawCommand));
|
||||
@@ -95,30 +99,30 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink
|
||||
const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_IN);
|
||||
|
||||
if (rawResponse.size() < 3) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Invalid response size from device");
|
||||
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 && rawResponse[0] != 0x82) {
|
||||
throw Exceptions::DeviceCommunicationFailure{"Invalid response code from device"};
|
||||
}
|
||||
|
||||
if (rawResponse[0] == 0x81) {
|
||||
// TODO: Create ErrorResponse exception class and throw an instance of it here.
|
||||
throw Exceptions::DeviceCommunicationFailure("Error response");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Error response"};
|
||||
}
|
||||
|
||||
if (rawResponse[1] != command.commandId) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Missing/invalid command ID in response from device");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Missing/invalid command ID in response from device"};
|
||||
}
|
||||
|
||||
if ((rawResponse.size() - 3) != rawResponse[2]) {
|
||||
throw Exceptions::DeviceCommunicationFailure("Actual response payload size mismatch");
|
||||
throw Exceptions::DeviceCommunicationFailure{"Actual response payload size mismatch"};
|
||||
}
|
||||
|
||||
return typename CommandType::ExpectedResponseType(
|
||||
std::vector<unsigned char>(rawResponse.begin() + 3, rawResponse.end())
|
||||
);
|
||||
return typename CommandType::ExpectedResponseType{
|
||||
std::vector<unsigned char>{rawResponse.begin() + 3, rawResponse.end()}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "WchLinkBase.hpp"
|
||||
|
||||
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||
|
||||
#include "src/Logger/Logger.hpp"
|
||||
@@ -37,19 +39,25 @@ namespace DebugToolDrivers::Wch
|
||||
);
|
||||
|
||||
if (this->getDeviceInfo().variant != this->variant) {
|
||||
throw DeviceInitializationFailure(
|
||||
throw DeviceInitializationFailure{
|
||||
"WCH-Link variant mismatch - device returned variant ID that doesn't match the " + this->getName()
|
||||
+ " variant ID"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
this->setInitialised(true);
|
||||
this->initialised = true;
|
||||
}
|
||||
|
||||
void WchLinkBase::close() {
|
||||
if (this->wchLinkUsbInterface) {
|
||||
this->wchLinkUsbInterface->close();
|
||||
}
|
||||
|
||||
this->initialised = false;
|
||||
}
|
||||
|
||||
bool WchLinkBase::isInitialised() const {
|
||||
return this->initialised;
|
||||
}
|
||||
|
||||
std::string WchLinkBase::getSerialNumber() {
|
||||
@@ -60,6 +68,36 @@ namespace DebugToolDrivers::Wch
|
||||
return "v" + this->getDeviceInfo().firmwareVersion.toString();
|
||||
}
|
||||
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator* WchLinkBase::getRiscVDebugInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
using ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator;
|
||||
|
||||
if (!this->wchRiscVTranslator) {
|
||||
this->wchRiscVTranslator = std::make_unique<DebugTranslator>(
|
||||
*(this->wchLinkInterface.get()),
|
||||
targetDescriptionFile,
|
||||
targetConfig
|
||||
);
|
||||
}
|
||||
return this->wchRiscVTranslator.get();
|
||||
}
|
||||
|
||||
Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVProgramInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
return this->wchLinkInterface.get();
|
||||
}
|
||||
|
||||
Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVIdentificationInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) {
|
||||
return this->wchLinkInterface.get();
|
||||
}
|
||||
|
||||
const DeviceInfo& WchLinkBase::getDeviceInfo() const {
|
||||
if (!this->cachedDeviceInfo.has_value()) {
|
||||
this->cachedDeviceInfo = this->wchLinkInterface->getDeviceInfo();
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
|
||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||
#include "src/DebugToolDrivers/USB/UsbDevice.hpp"
|
||||
#include "src/DebugToolDrivers/USB/UsbInterface.hpp"
|
||||
|
||||
#include "Protocols/WchLink/WchLinkInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp"
|
||||
|
||||
#include "WchGeneric.hpp"
|
||||
#include "DeviceInfo.hpp"
|
||||
@@ -28,13 +30,16 @@ namespace DebugToolDrivers::Wch
|
||||
|
||||
void close() override;
|
||||
|
||||
[[nodiscard]] bool isInitialised() const override;
|
||||
|
||||
std::string getSerialNumber() override;
|
||||
|
||||
std::string getFirmwareVersionString() override;
|
||||
|
||||
DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface() override {
|
||||
return this->wchLinkInterface.get();
|
||||
}
|
||||
::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator* getRiscVDebugInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) override;
|
||||
|
||||
/**
|
||||
* WCH-Link debug tools cannot write to flash memory via the RISC-V debug interface (RiscVDebugInterface).
|
||||
@@ -46,20 +51,27 @@ namespace DebugToolDrivers::Wch
|
||||
* target driver forwards any flash memory writes to this implementation (instead of relying on the debug
|
||||
* interface).
|
||||
*
|
||||
* The WchLinkInterface implements both the RiscVDebugInterface and the RiscVProgramInterface.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface() override {
|
||||
return this->wchLinkInterface.get();
|
||||
}
|
||||
Protocols::WchLink::WchLinkInterface* getRiscVProgramInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) override;
|
||||
|
||||
Protocols::WchLink::WchLinkInterface* getRiscVIdentificationInterface(
|
||||
const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::RiscV::RiscVTargetConfig& targetConfig
|
||||
) override;
|
||||
|
||||
protected:
|
||||
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<::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator> wchRiscVTranslator = nullptr;
|
||||
|
||||
mutable std::optional<DeviceInfo> cachedDeviceInfo = std::nullopt;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user