From 020d174a2d4b06cbf6c9cdd5b3f806bd73e6e28c Mon Sep 17 00:00:00 2001 From: Nav Date: Mon, 30 Dec 2024 15:41:52 +0000 Subject: [PATCH] Automatic switching form PIC mode to EDBG (AVR) mode, for MPLAB Snap and PICkit 4 tools. --- build/distributed/udevrules/99-bloom.rules | 12 +++ src/DebugToolDrivers/Microchip/EdbgDevice.cpp | 32 ++++++++ src/DebugToolDrivers/Microchip/EdbgDevice.hpp | 3 + .../Microchip/EdbgToolConfig.cpp | 8 ++ .../Microchip/EdbgToolConfig.hpp | 2 + .../Microchip/MplabPickit4/MplabPickit4.cpp | 70 ++++++++++++----- .../Microchip/MplabPickit4/MplabPickit4.hpp | 5 +- .../Microchip/MplabSnap/MplabSnap.cpp | 76 ++++++++++++------- .../Microchip/MplabSnap/MplabSnap.hpp | 6 +- 9 files changed, 162 insertions(+), 52 deletions(-) diff --git a/build/distributed/udevrules/99-bloom.rules b/build/distributed/udevrules/99-bloom.rules index 89ecd75d..394f7f11 100644 --- a/build/distributed/udevrules/99-bloom.rules +++ b/build/distributed/udevrules/99-bloom.rules @@ -27,9 +27,21 @@ ACTION=="add", ATTR{idVendor}=="03eb", ATTR{idProduct}=="2175", MODE="0666" # MPLAB PICkit 4 In-Circuit Debugger ACTION=="add", ATTR{idVendor}=="03eb", ATTR{idProduct}=="2177", MODE="0666" +# MPLAB PICkit 4 (PIC mode) +ACTION=="add", ATTR{idVendor}=="04d8", ATTR{idProduct}=="9012", MODE="0666" + +# MPLAB PICkit 4 (Bootloader mode) +ACTION=="add", ATTR{idVendor}=="04d8", ATTR{idProduct}=="9017", MODE="0666" + # MPLAB Snap In-Circuit Debugger ACTION=="add", ATTR{idVendor}=="03eb", ATTR{idProduct}=="2180", MODE="0666" +# MPLAB Snap (PIC mode) +ACTION=="add", ATTR{idVendor}=="04d8", ATTR{idProduct}=="9018", MODE="0666" + +# MPLAB Snap (Bootloader mode) +ACTION=="add", ATTR{idVendor}=="04d8", ATTR{idProduct}=="9019", MODE="0666" + # WCH-LinkE ACTION=="add", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="0666" diff --git a/src/DebugToolDrivers/Microchip/EdbgDevice.cpp b/src/DebugToolDrivers/Microchip/EdbgDevice.cpp index 2617dd0e..42716da0 100644 --- a/src/DebugToolDrivers/Microchip/EdbgDevice.cpp +++ b/src/DebugToolDrivers/Microchip/EdbgDevice.cpp @@ -1,8 +1,13 @@ #include "EdbgDevice.hpp" +#include +#include +#include + #include "src/DebugToolDrivers/USB/HID/HidInterface.hpp" #include "src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp" #include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrames.hpp" +#include "src/DebugToolDrivers/USB/UsbInterface.hpp" #include "src/Exceptions/InvalidConfig.hpp" #include "src/TargetController/Exceptions/DeviceFailure.hpp" @@ -194,4 +199,31 @@ namespace DebugToolDrivers::Microchip this->sessionStarted = false; } + + void EdbgDevice::exitBootloaderMode(UsbDevice& device) const { + static constexpr auto INTERFACE_NUMBER = std::uint8_t{0}; + static constexpr auto COMMAND_ENDPOINT_ADDRESS = std::uint8_t{0x02}; + + auto interface = Usb::UsbInterface{INTERFACE_NUMBER, device.libusbDeviceHandle.get()}; + interface.init(); + + static constexpr auto EXIT_BL_MODE_COMMAND = std::to_array({0xE6}); + + interface.writeBulk(COMMAND_ENDPOINT_ADDRESS, EXIT_BL_MODE_COMMAND, 64); + } + + void EdbgDevice::enableEdbgMode(UsbDevice& device) const { + static constexpr auto INTERFACE_NUMBER = std::uint8_t{0}; + static constexpr auto COMMAND_ENDPOINT_ADDRESS = std::uint8_t{0x02}; + + auto interface = Usb::UsbInterface{INTERFACE_NUMBER, device.libusbDeviceHandle.get()}; + interface.init(); + + static constexpr auto AVR_MODE_COMMAND = std::to_array({0xF0, 0x01}); + static constexpr auto RESET_COMMAND = std::to_array({0xED}); + + interface.writeBulk(COMMAND_ENDPOINT_ADDRESS, AVR_MODE_COMMAND, 64); + std::this_thread::sleep_for(std::chrono::milliseconds{250}); + interface.writeBulk(COMMAND_ENDPOINT_ADDRESS, RESET_COMMAND, 64); + } } diff --git a/src/DebugToolDrivers/Microchip/EdbgDevice.hpp b/src/DebugToolDrivers/Microchip/EdbgDevice.hpp index 0e0a9bc6..df541864 100644 --- a/src/DebugToolDrivers/Microchip/EdbgDevice.hpp +++ b/src/DebugToolDrivers/Microchip/EdbgDevice.hpp @@ -158,5 +158,8 @@ namespace DebugToolDrivers::Microchip virtual void configureAvr8Interface() { return; } + + void exitBootloaderMode(Usb::UsbDevice& device) const; + void enableEdbgMode(Usb::UsbDevice& device) const; }; } diff --git a/src/DebugToolDrivers/Microchip/EdbgToolConfig.cpp b/src/DebugToolDrivers/Microchip/EdbgToolConfig.cpp index ff461023..082230d0 100644 --- a/src/DebugToolDrivers/Microchip/EdbgToolConfig.cpp +++ b/src/DebugToolDrivers/Microchip/EdbgToolConfig.cpp @@ -10,6 +10,14 @@ namespace DebugToolDrivers::Microchip { const auto& toolNode = toolConfig.toolNode; + if (toolNode["exit_bootloader_mode"]) { + this->exitBootloaderMode = toolNode["exit_bootloader_mode"].as(this->exitBootloaderMode); + } + + if (toolNode["enable_edbg_mode"]) { + this->enableEdbgMode = toolNode["enable_edbg_mode"].as(this->enableEdbgMode); + } + const auto edbgDriverNode = toolNode["edbg_driver"]; if (edbgDriverNode) { if (edbgDriverNode["cmsis_command_delay"]) { diff --git a/src/DebugToolDrivers/Microchip/EdbgToolConfig.hpp b/src/DebugToolDrivers/Microchip/EdbgToolConfig.hpp index 4db4bd6e..6d17fae7 100644 --- a/src/DebugToolDrivers/Microchip/EdbgToolConfig.hpp +++ b/src/DebugToolDrivers/Microchip/EdbgToolConfig.hpp @@ -14,6 +14,8 @@ namespace DebugToolDrivers::Microchip struct EdbgToolConfig: public DebugToolConfig { public: + bool exitBootloaderMode = true; + bool enableEdbgMode = true; std::optional cmsisCommandDelay = std::nullopt; explicit EdbgToolConfig(const DebugToolConfig& toolConfig); diff --git a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp index 94282c25..94829340 100644 --- a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp +++ b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp @@ -1,7 +1,8 @@ #include "MplabPickit4.hpp" -#include "src/TargetController/Exceptions/DeviceNotFound.hpp" -#include "src/Services/PathService.hpp" +#include "src/Logger/Logger.hpp" + +#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" namespace DebugToolDrivers::Microchip { @@ -15,30 +16,59 @@ namespace DebugToolDrivers::Microchip {} void MplabPickit4::init() { - using Exceptions::DeviceNotFound; + using Exceptions::DeviceInitializationFailure; - try { - EdbgDevice::init(); - - } catch (const DeviceNotFound& exception) { - /* - * The MPLAB PICkit 4 could be connected but not in AVR mode - if this is the case, inform the user and - * direct them to the AVR mode article. - */ - const auto nonEdbgDevices = this->findMatchingDevices( - MplabPickit4::NON_EDBG_USB_VENDOR_ID, - MplabPickit4::NON_EDBG_USB_PRODUCT_ID + if (!UsbDevice::devicePresent(this->vendorId, this->productId)) { + auto blDevice = Usb::UsbDevice::tryDevice( + MplabPickit4::PIC_MODE_USB_VENDOR_ID, + MplabPickit4::BL_MODE_USB_PRODUCT_ID ); - if (!nonEdbgDevices.empty()) { - throw DeviceNotFound{ - "The connected MPLAB PICkit 4 device is not in \"AVR mode\". Please follow the instructions at " - + Services::PathService::homeDomainName() + "/docs/avr-mode" - }; + if (blDevice.has_value()) { + if (!this->toolConfig.exitBootloaderMode) { + throw DeviceInitializationFailure{"Device is currently in bootloader mode"}; + } + + Logger::warning("Found device in bootloader mode - attempting exit operation"); + this->exitBootloaderMode(*blDevice); + + Logger::info("Waiting for device to re-enumerate..."); + if ( + !Usb::UsbDevice::waitForDevice( + MplabPickit4::PIC_MODE_USB_VENDOR_ID, + MplabPickit4::PIC_MODE_USB_PRODUCT_ID, + std::chrono::seconds{8} + ) + ) { + throw DeviceInitializationFailure{"Timeout exceeded whilst waiting for device to re-enumerate"}; + } + + Logger::info("Re-enumerated device found - exit operation was successful"); } - throw exception; + auto picDevice = Usb::UsbDevice::tryDevice( + MplabPickit4::PIC_MODE_USB_VENDOR_ID, + MplabPickit4::PIC_MODE_USB_PRODUCT_ID + ); + + if (picDevice.has_value()) { + if (!this->toolConfig.enableEdbgMode) { + throw DeviceInitializationFailure{"Device is currently in PIC mode"}; + } + + Logger::warning("Found device in PIC mode - attempting to switch to EDBG (AVR) mode"); + this->enableEdbgMode(*picDevice); + + Logger::info("Waiting for device to re-enumerate..."); + if (!Usb::UsbDevice::waitForDevice(this->vendorId, this->productId, std::chrono::seconds{8})) { + throw DeviceInitializationFailure{"Timeout exceeded whilst waiting for device to re-enumerate"}; + } + + Logger::info("Re-enumerated device found - mode switch operation was successful"); + } } + + EdbgDevice::init(); } void MplabPickit4::configureAvr8Interface() { diff --git a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp index d2741396..1604b569 100644 --- a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp +++ b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp @@ -26,8 +26,9 @@ namespace DebugToolDrivers::Microchip static const inline std::uint16_t USB_PRODUCT_ID = 0x2177; static const inline std::uint8_t CMSIS_HID_INTERFACE_NUMBER = 0; - static const inline std::uint16_t NON_EDBG_USB_VENDOR_ID = 0x04d8; - static const inline std::uint16_t NON_EDBG_USB_PRODUCT_ID = 0x9012; + static const inline std::uint16_t PIC_MODE_USB_VENDOR_ID = 0x04d8; + static const inline std::uint16_t PIC_MODE_USB_PRODUCT_ID = 0x9012; + static const inline std::uint16_t BL_MODE_USB_PRODUCT_ID = 0x9017; MplabPickit4(const DebugToolConfig& debugToolConfig); diff --git a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp index 2cd7008d..b9476b79 100644 --- a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp +++ b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp @@ -1,7 +1,8 @@ #include "MplabSnap.hpp" -#include "src/TargetController/Exceptions/DeviceNotFound.hpp" -#include "src/Services/PathService.hpp" +#include "src/Logger/Logger.hpp" + +#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" namespace DebugToolDrivers::Microchip { @@ -15,38 +16,59 @@ namespace DebugToolDrivers::Microchip {} void MplabSnap::init() { - using Exceptions::DeviceNotFound; + using Exceptions::DeviceInitializationFailure; - try { - EdbgDevice::init(); - - } catch (const DeviceNotFound& exception) { - /* - * The MPLAB Snap could be connected but not in AVR mode - if this is the case, inform the user and direct - * them to the AVR mode article. - */ - auto nonEdbgDevices = this->findMatchingDevices( - MplabSnap::NON_EDBG_USB_VENDOR_ID, - MplabSnap::NON_EDBG_USB_PRODUCT_ID + if (!UsbDevice::devicePresent(this->vendorId, this->productId)) { + auto blDevice = Usb::UsbDevice::tryDevice( + MplabSnap::PIC_MODE_USB_VENDOR_ID, + MplabSnap::BL_MODE_USB_PRODUCT_ID ); - if (nonEdbgDevices.empty()) { - // The MPLAB Snap sometimes uses another product ID when not in AVR mode. - nonEdbgDevices = this->findMatchingDevices( - MplabSnap::NON_EDBG_USB_VENDOR_ID, - MplabSnap::NON_EDBG_USB_PRODUCT_ID_ALTERNATIVE - ); + if (blDevice.has_value()) { + if (!this->toolConfig.exitBootloaderMode) { + throw DeviceInitializationFailure{"Device is currently in bootloader mode"}; + } + + Logger::warning("Found device in bootloader mode - attempting exit operation"); + this->exitBootloaderMode(*blDevice); + + Logger::info("Waiting for device to re-enumerate..."); + if ( + !Usb::UsbDevice::waitForDevice( + MplabSnap::PIC_MODE_USB_VENDOR_ID, + MplabSnap::PIC_MODE_USB_PRODUCT_ID, + std::chrono::seconds{8} + ) + ) { + throw DeviceInitializationFailure{"Timeout exceeded whilst waiting for device to re-enumerate"}; + } + + Logger::info("Re-enumerated device found - exit operation was successful"); } - if (!nonEdbgDevices.empty()) { - throw DeviceNotFound{ - "The connected MPLAB Snap device is not in \"AVR mode\". Please follow the instructions at " - + Services::PathService::homeDomainName() + "/docs/avr-mode" - }; - } + auto picDevice = Usb::UsbDevice::tryDevice( + MplabSnap::PIC_MODE_USB_VENDOR_ID, + MplabSnap::PIC_MODE_USB_PRODUCT_ID + ); - throw exception; + if (picDevice.has_value()) { + if (!this->toolConfig.enableEdbgMode) { + throw DeviceInitializationFailure{"Device is currently in PIC mode"}; + } + + Logger::warning("Found device in PIC mode - attempting to switch to EDBG (AVR) mode"); + this->enableEdbgMode(*picDevice); + + Logger::info("Waiting for device to re-enumerate..."); + if (!Usb::UsbDevice::waitForDevice(this->vendorId, this->productId, std::chrono::seconds{8})) { + throw DeviceInitializationFailure{"Timeout exceeded whilst waiting for device to re-enumerate"}; + } + + Logger::info("Re-enumerated device found - mode switch operation was successful"); + } } + + EdbgDevice::init(); } void MplabSnap::configureAvr8Interface() { diff --git a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp index e68b8f2b..d5b14703 100644 --- a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp +++ b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp @@ -28,9 +28,9 @@ namespace DebugToolDrivers::Microchip static const inline std::uint16_t USB_PRODUCT_ID = 0x2180; static const inline std::uint8_t CMSIS_HID_INTERFACE_NUMBER = 0; - static const inline std::uint16_t NON_EDBG_USB_VENDOR_ID = 0x04d8; - static const inline std::uint16_t NON_EDBG_USB_PRODUCT_ID = 0x9018; - static const inline std::uint16_t NON_EDBG_USB_PRODUCT_ID_ALTERNATIVE = 0x9017; + static const inline std::uint16_t PIC_MODE_USB_VENDOR_ID = 0x04d8; + static const inline std::uint16_t PIC_MODE_USB_PRODUCT_ID = 0x9018; + static const inline std::uint16_t BL_MODE_USB_PRODUCT_ID = 0x9019; MplabSnap(const DebugToolConfig& debugToolConfig);