diff --git a/CMakeLists.txt b/CMakeLists.txt index c6c7938f..4c8718bf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ add_executable(Bloom src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.cpp src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvr8Interface.cpp + src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.cpp # Targets src/Targets/TargetDescription/TargetDescriptionFile.cpp diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.cpp new file mode 100644 index 00000000..83e8f09b --- /dev/null +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.cpp @@ -0,0 +1,144 @@ +#include "EdbgAvrIspInterface.hpp" + +#include "src/TargetController/Exceptions/TargetOperationFailure.hpp" +#include "src/Logger/Logger.hpp" + +// Command frames +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVRISP/EnterProgrammingMode.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVRISP/LeaveProgrammingMode.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVRISP/ReadSignature.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVRISP/ReadFuse.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVRISP/ReadLock.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AVRISP/ProgramFuse.hpp" + +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + using namespace Targets::Microchip::Avr; + + using CommandFrames::AvrIsp::EnterProgrammingMode; + using CommandFrames::AvrIsp::LeaveProgrammingMode; + using CommandFrames::AvrIsp::ReadSignature; + using CommandFrames::AvrIsp::ReadFuse; + using CommandFrames::AvrIsp::ReadLock; + using CommandFrames::AvrIsp::ProgramFuse; + + using ResponseFrames::AvrIsp::StatusCode; + + using Exceptions::TargetOperationFailure; + + void EdbgAvrIspInterface::setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) { + this->ispParameters = ispParameters; + } + + void EdbgAvrIspInterface::activate() { + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + EnterProgrammingMode( + this->ispParameters.programModeTimeout, + this->ispParameters.programModeStabilizationDelay, + this->ispParameters.programModeCommandExecutionDelay, + this->ispParameters.programModeSyncLoops, + this->ispParameters.programModeByteDelay, + this->ispParameters.programModePollValue, + this->ispParameters.programModePollIndex + ) + ); + + if (response.getStatusCode() != StatusCode::OK) { + 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() { + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + LeaveProgrammingMode( + this->ispParameters.programModePreDelay, + this->ispParameters.programModePostDelay + ) + ); + + if (response.getStatusCode() != StatusCode::OK) { + 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) + ); + } + + Fuse EdbgAvrIspInterface::readFuse(FuseType fuseType) { + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + ReadFuse(fuseType, this->ispParameters.readFusePollIndex) + ); + + const auto& payload = response.getPayload(); + if (response.getStatusCode() != StatusCode::OK + || payload.size() < 4 + || static_cast(payload[3]) != StatusCode::OK + ) { + throw TargetOperationFailure( + "Failed to read fuse via ISP - response frame status code/size indicates a failure." + ); + } + + return Fuse(fuseType, payload[2]); + } + + unsigned char EdbgAvrIspInterface::readLockBitByte() { + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + ReadLock(this->ispParameters.readLockPollIndex) + ); + + const auto& payload = response.getPayload(); + if (response.getStatusCode() != StatusCode::OK + || payload.size() < 4 + || static_cast(payload[3]) != StatusCode::OK + ) { + throw TargetOperationFailure( + "Failed to read lock bit byte via ISP - response frame status code/size indicates a failure." + ); + } + + return payload[2]; + } + + void EdbgAvrIspInterface::programFuse(Targets::Microchip::Avr::Fuse fuse) { + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + ProgramFuse(fuse.type, fuse.value) + ); + + const auto& payload = response.getPayload(); + if (response.getStatusCode() != StatusCode::OK || payload.size() < 2) { + throw TargetOperationFailure( + "Failed to program fuse via ISP - response frame status code/size indicates a failure." + ); + } + } + + unsigned char EdbgAvrIspInterface::readSignatureByte(std::uint8_t signatureByteAddress) { + auto response = this->edbgInterface.sendAvrCommandFrameAndWaitForResponseFrame( + ReadSignature(signatureByteAddress, this->ispParameters.readSignaturePollIndex) + ); + + const auto& payload = response.getPayload(); + + if (response.getStatusCode() != StatusCode::OK + || payload.size() < 4 + || static_cast(payload[3]) != StatusCode::OK + ) { + throw TargetOperationFailure( + "Failed to read signature byte (address: " + std::to_string(signatureByteAddress) + + ") via ISP - response frame status code/size indicates a failure." + ); + } + + return payload[2]; + } +} diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.hpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.hpp new file mode 100644 index 00000000..43c36539 --- /dev/null +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/EdbgAvrIspInterface.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include +#include +#include + +#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp" +#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/EdbgInterface.hpp" + +namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr +{ + /** + * The EdbgAvrIspInterface implements the AVRISP EDBG/CMSIS-DAP protocol, as an AvrIspInterface. + * + * See the "AVR ISP Protocol" section in the DS50002630A document by Microchip, for more information on the + * protocol. + * + * 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 + { + public: + explicit EdbgAvrIspInterface(EdbgInterface& edbgInterface) + : 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; + + /** + * Initialises the ISP interface by enabling "programming mode" on the debug tool. This will activate the + * physical (SPI) interface between the debug tool and AVR target. + */ + void activate() override; + + /** + * Disables "programming mode" on the debug tool, which subsequently deactivates the SPI interface between the + * debug tool and AVR target. + */ + void deactivate() override; + + /** + * Obtains the AVR signature from the connected AVR. + * + * @return + */ + Targets::Microchip::Avr::TargetSignature getDeviceId() override; + + /** + * Reads a particular fuse byte from the AVR target. + * + * @param fuseType + * @return + */ + Targets::Microchip::Avr::Fuse readFuse(Targets::Microchip::Avr::FuseType fuseType) override; + + /** + * Reads the lock bit byte from the AVR target. + * + * @return + */ + unsigned char readLockBitByte() override; + + /** + * Programs a particular fuse on the AVR target. + * + * @param fuse + */ + void programFuse(Targets::Microchip::Avr::Fuse fuse) override; + + private: + /** + * The AVRISP protocol is a sub-protocol of the EDBG AVR protocol, which is served via CMSIS-DAP vendor + * commands. + * + * Every EDBG based debug tool that utilises this implementation must provide access to its EDBG interface. + */ + EdbgInterface& edbgInterface; + + Targets::Microchip::Avr::IspParameters ispParameters; + + /** + * The EDBG AVRISP protocol only allows us to read a single signature byte at a time. + * This function will read a single signature byte. See implementation of EdbgAvrIspInterface::getDeviceId() + * for more. + * + * @param signatureByteAddress + * @return + */ + [[nodiscard]] unsigned char readSignatureByte(std::uint8_t signatureByteAddress); + }; +}