diff --git a/src/DebugToolDrivers/CMakeLists.txt b/src/DebugToolDrivers/CMakeLists.txt index 9de2a5ed..7fcfcd5c 100755 --- a/src/DebugToolDrivers/CMakeLists.txt +++ b/src/DebugToolDrivers/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedMini/XplainedMini.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedNano/XplainedNano.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp ${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 diff --git a/src/DebugToolDrivers/DebugTools.hpp b/src/DebugToolDrivers/DebugTools.hpp index 6dd96146..99a09f6a 100644 --- a/src/DebugToolDrivers/DebugTools.hpp +++ b/src/DebugToolDrivers/DebugTools.hpp @@ -10,3 +10,4 @@ #include "src/DebugToolDrivers/Microchip/XplainedMini/XplainedMini.hpp" #include "src/DebugToolDrivers/Microchip/XplainedNano/XplainedNano.hpp" #include "src/DebugToolDrivers/Microchip/CuriosityNano/CuriosityNano.hpp" +#include "src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.hpp" diff --git a/src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.cpp b/src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.cpp new file mode 100644 index 00000000..de7e562b --- /dev/null +++ b/src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.cpp @@ -0,0 +1,99 @@ +#include "JtagIce3.hpp" + +#include "src/TargetController/Exceptions/DeviceFailure.hpp" +#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" + +namespace Bloom::DebugToolDrivers +{ + using namespace Protocols::CmsisDap::Edbg::Avr; + using namespace Bloom::Exceptions; + + void JtagIce3::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.detachKernelDriver(); + this->setConfiguration(0); + 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); + this->edbgAvrIspInterface = std::make_unique(this->edbgInterface); + + this->setInitialised(true); + } + + void JtagIce3::close() { + if (this->sessionStarted) { + this->endSession(); + } + + this->getEdbgInterface().getUsbHidInterface().close(); + UsbDevice::close(); + } + + std::string JtagIce3::getSerialNumber() { + using namespace CommandFrames::Discovery; + using ResponseFrames::Discovery::ResponseId; + + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + Query(QueryContext::SERIAL_NUMBER) + ); + + if (response.getResponseId() != 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 JtagIce3::startSession() { + using namespace CommandFrames::HouseKeeping; + using ResponseFrames::HouseKeeping::ResponseId; + + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + StartSession() + ); + + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceInitializationFailure("Failed to start session with the JTAGICE3!"); + } + + this->sessionStarted = true; + } + + void JtagIce3::endSession() { + using namespace CommandFrames::HouseKeeping; + using ResponseFrames::HouseKeeping::ResponseId; + + auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame( + EndSession() + ); + + if (response.getResponseId() == ResponseId::FAILED) { + // Failed response returned! + throw DeviceFailure("Failed to end session with the JTAGICE3!"); + } + + this->sessionStarted = false; + } +} diff --git a/src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.hpp b/src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.hpp new file mode 100644 index 00000000..ea659005 --- /dev/null +++ b/src/DebugToolDrivers/Microchip/JtagIce3/JtagIce3.hpp @@ -0,0 +1,70 @@ +#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/EdbgAvrIspInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrames.hpp" + +namespace Bloom::DebugToolDrivers +{ + class JtagIce3: public DebugTool, public Usb::UsbDevice + { + public: + static const std::uint16_t USB_VENDOR_ID = 1003; + static const std::uint16_t USB_PRODUCT_ID = 8512; + + JtagIce3(): UsbDevice(JtagIce3::USB_VENDOR_ID, JtagIce3::USB_PRODUCT_ID) {} + + void init() override; + + void close() override; + + Protocols::CmsisDap::Edbg::EdbgInterface& getEdbgInterface() { + return this->edbgInterface; + } + + TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface() override { + return this->edbgAvr8Interface.get(); + } + + TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface() override { + return this->edbgAvrIspInterface.get(); + } + + std::string getName() override { + return "JTAGICE3"; + }; + + /** + * 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; + std::unique_ptr edbgAvrIspInterface = nullptr; + + bool sessionStarted = false; + }; +} diff --git a/src/TargetController/TargetControllerComponent.cpp b/src/TargetController/TargetControllerComponent.cpp index ad0af754..9e9bab9b 100644 --- a/src/TargetController/TargetControllerComponent.cpp +++ b/src/TargetController/TargetControllerComponent.cpp @@ -234,6 +234,12 @@ namespace Bloom::TargetController return std::make_unique(); } }, + { + "jtagice3", + [] { + return std::make_unique(); + } + }, }; }