diff --git a/CMakeLists.txt b/CMakeLists.txt index 622883a7..f273ccf8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ add_executable(Bloom src/DebugToolDrivers/Microchip/AtmelICE/AtmelIce.cpp src/DebugToolDrivers/Microchip/PowerDebugger/PowerDebugger.cpp src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp + src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp src/DebugToolDrivers/Microchip/XplainedPro/XplainedPro.cpp src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.cpp src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.cpp diff --git a/src/DebugToolDrivers/DebugTools.hpp b/src/DebugToolDrivers/DebugTools.hpp index bb097e9c..6dd96146 100644 --- a/src/DebugToolDrivers/DebugTools.hpp +++ b/src/DebugToolDrivers/DebugTools.hpp @@ -5,6 +5,7 @@ #include "src/DebugToolDrivers/Microchip/AtmelICE/AtmelIce.hpp" #include "src/DebugToolDrivers/Microchip/PowerDebugger/PowerDebugger.hpp" #include "src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.hpp" +#include "src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp" #include "src/DebugToolDrivers/Microchip/XplainedPro/XplainedPro.hpp" #include "src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.hpp" #include "src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.hpp" diff --git a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp new file mode 100644 index 00000000..72834420 --- /dev/null +++ b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp @@ -0,0 +1,94 @@ +#include "MplabPickit4.hpp" + +#include "src/TargetController/Exceptions/DeviceFailure.hpp" +#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" + +using namespace Bloom::DebugToolDrivers; +using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr; +using namespace Bloom::Exceptions; + +void MplabPickit4::init() { + UsbDevice::init(); + + // TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number + auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface(); + usbHidInterface.setNumber(0); + usbHidInterface.setLibUsbDevice(this->libUsbDevice); + usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle); + usbHidInterface.setVendorId(this->vendorId); + usbHidInterface.setProductId(this->productId); + + if (!usbHidInterface.isInitialised()) { + usbHidInterface.init(); + } + + this->getEdbgInterface().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) { + this->startSession(); + } + + this->edbgAvr8Interface = std::make_unique(this->edbgInterface); + + /* + * Like the MPLAB Snap, the PICkit 4 doesn't seem to operate correctly when actioning the masked memory read + * command. The data returned in response to the command appears to be completely incorrect. + * + * For the above reason, we avoid using the masked memory read command by implementing the masking on our end. + * See the EdbgAvr8Interface class for more. + */ + this->edbgAvr8Interface->setAvoidMaskedMemoryRead(true); + + this->setInitialised(true); +} + +void MplabPickit4::close() { + if (this->sessionStarted) { + this->endSession(); + } + + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); +} + +std::string MplabPickit4::getSerialNumber() { + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER) + ); + + if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) { + throw DeviceInitializationFailure( + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + ); + } + + auto data = response.getPayloadData(); + return std::string(data.begin(), data.end()); +} + +void MplabPickit4::startSession() { + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + CommandFrames::HouseKeeping::StartSession() + ); + + if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with MPLAB PICkit 4!"); + } + + this->sessionStarted = true; +} + +void MplabPickit4::endSession() { + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + CommandFrames::HouseKeeping::EndSession() + ); + + if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with MPLAB PICkit 4!"); + } + + this->sessionStarted = false; +} diff --git a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp new file mode 100644 index 00000000..e8225e8a --- /dev/null +++ b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#include + +#include "src/DebugToolDrivers/DebugTool.hpp" +#include "src/DebugToolDrivers/USB/UsbDevice.hpp" +#include "src/DebugToolDrivers/USB/HID/HidInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrames.hpp" + +namespace Bloom::DebugToolDrivers +{ + /** + * Like the MPLAB Snap, the PICkit 4 is a hybrid device. It can present itself as an EDBG (Embedded Debugger) + * device via a firmware update, issued by Microchip software (the MPLAB IDE and IPE). + * + * This debug tool driver currently only supports the device when in AVR mode. Because the device uses different + * vendor & product IDs depending on the mode, it is easy to determine which is which. In fact, Bloom will not even + * recognise the device if it's not in AVR mode. + * + * USB Setup (when in AVR/EDBG mode): + * Vendor ID: 0x03eb (1003) + * Product ID: 0x2177 (8567) + */ + class MplabPickit4: public DebugTool, public Usb::UsbDevice + { + public: + static const std::uint16_t USB_VENDOR_ID = 1003; + static const std::uint16_t USB_PRODUCT_ID = 8567; + + MplabPickit4(): UsbDevice(MplabPickit4::USB_VENDOR_ID, MplabPickit4::USB_PRODUCT_ID) {} + + void init() override; + + void close() override; + + Protocols::CmsisDap::Edbg::EdbgInterface& getEdbgInterface() { + return this->edbgInterface; + } + + TargetInterfaces::Microchip::Avr::Avr8::Avr8Interface* getAvr8Interface() override { + return this->edbgAvr8Interface.get(); + } + + std::string getName() override { + return "MPLAB PICkit 4"; + }; + + /** + * Retrieves the device serial number via the Discovery Protocol. + * + * @return + */ + std::string getSerialNumber() override; + + /** + * Starts a session with the EDBG-based tool using the housekeeping protocol. + */ + void startSession(); + + /** + * Ends the active session with the debug tool. + */ + void endSession(); + + private: + Protocols::CmsisDap::Edbg::EdbgInterface edbgInterface = Protocols::CmsisDap::Edbg::EdbgInterface(); + std::unique_ptr edbgAvr8Interface = nullptr; + + bool sessionStarted = false; + }; +} diff --git a/src/TargetController/TargetController.hpp b/src/TargetController/TargetController.hpp index efaf105c..90d90530 100644 --- a/src/TargetController/TargetController.hpp +++ b/src/TargetController/TargetController.hpp @@ -118,6 +118,12 @@ namespace Bloom return std::make_unique(); } }, + { + "pickit-4", + [] { + return std::make_unique(); + } + }, { "xplained-pro", [] {