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:
Nav
2024-07-23 21:14:22 +01:00
parent 2986934485
commit 6cdbfbe950
331 changed files with 8815 additions and 8565 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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:
/*

View File

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

View File

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

View File

@@ -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)
*/

View File

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

View File

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

View File

@@ -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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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>(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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