Moved EDBG protocol code to more appropriate directory.
Updated necessary namespaces. Other bits of tidying.
This commit is contained in:
@@ -1,18 +1,17 @@
|
||||
#include "EdbgDevice.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/USB/HID/HidInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/VendorSpecific/EDBG/AVR/CommandFrames/AvrCommandFrames.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrames.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
|
||||
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
|
||||
|
||||
namespace DebugToolDrivers
|
||||
{
|
||||
using namespace Protocols::CmsisDap::Edbg::Avr;
|
||||
using namespace Exceptions;
|
||||
using namespace Microchip::Protocols::Edbg::Avr;
|
||||
|
||||
using Protocols::CmsisDap::Edbg::EdbgInterface;
|
||||
using Protocols::CmsisDap::Edbg::EdbgTargetPowerManagementInterface;
|
||||
using Exceptions::DeviceFailure;
|
||||
using Exceptions::DeviceInitializationFailure;
|
||||
|
||||
EdbgDevice::EdbgDevice(
|
||||
std::uint16_t vendorId,
|
||||
@@ -28,6 +27,9 @@ namespace DebugToolDrivers
|
||||
{}
|
||||
|
||||
void EdbgDevice::init() {
|
||||
using Microchip::Protocols::Edbg::EdbgInterface;
|
||||
using Microchip::Protocols::Edbg::EdbgTargetPowerManagementInterface;
|
||||
|
||||
UsbDevice::init();
|
||||
|
||||
this->detachKernelDriverFromInterface(this->cmsisHidInterfaceNumber);
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include "src/DebugToolDrivers/DebugTool.hpp"
|
||||
#include "src/DebugToolDrivers/USB/UsbDevice.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/EdbgTargetPowerManagementInterface.hpp"
|
||||
#include "Protocols/EDBG/EdbgInterface.hpp"
|
||||
#include "Protocols/EDBG/AVR/EdbgAvr8Interface.hpp"
|
||||
#include "Protocols/EDBG/AVR/EdbgAvrIspInterface.hpp"
|
||||
#include "Protocols/EDBG/EdbgTargetPowerManagementInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers
|
||||
{
|
||||
@@ -112,7 +112,7 @@ namespace DebugToolDrivers
|
||||
* The EdbgInterface class provides the ability to communicate with the EDBG device, using any of the EDBG
|
||||
* sub-protocols.
|
||||
*/
|
||||
std::unique_ptr<Protocols::CmsisDap::Edbg::EdbgInterface> edbgInterface = nullptr;
|
||||
std::unique_ptr<Microchip::Protocols::Edbg::EdbgInterface> edbgInterface = nullptr;
|
||||
|
||||
/**
|
||||
* The EdbgAvr8Interface class implements the AVR8 Generic EDBG sub-protocol. This protocol is used to perform
|
||||
@@ -120,7 +120,7 @@ namespace DebugToolDrivers
|
||||
*
|
||||
* The class implements the Avr8DebugInterface.
|
||||
*/
|
||||
std::unique_ptr<Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface = nullptr;
|
||||
std::unique_ptr<Microchip::Protocols::Edbg::Avr::EdbgAvr8Interface> edbgAvr8Interface = nullptr;
|
||||
|
||||
/**
|
||||
* The EdbgAvrIspInterface class implements the AVRISP EDBG sub-protocol, for interfacing with AVR targets via
|
||||
@@ -133,7 +133,7 @@ namespace DebugToolDrivers
|
||||
* 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<Protocols::CmsisDap::Edbg::Avr::EdbgAvrIspInterface> edbgAvrIspInterface = nullptr;
|
||||
std::unique_ptr<Microchip::Protocols::Edbg::Avr::EdbgAvrIspInterface> edbgAvrIspInterface = nullptr;
|
||||
|
||||
/**
|
||||
* The EdbgTargetPowerManagementInterface class implements the "EDBG Control" (EDBG_CTRL) sub-protocol, to
|
||||
@@ -143,7 +143,7 @@ namespace DebugToolDrivers
|
||||
* EdbgDevice::getTargetPowerManagementInterface() member function.
|
||||
*/
|
||||
std::unique_ptr<
|
||||
Protocols::CmsisDap::Edbg::EdbgTargetPowerManagementInterface
|
||||
Microchip::Protocols::Edbg::EdbgTargetPowerManagementInterface
|
||||
> targetPowerManagementInterface = nullptr;
|
||||
|
||||
bool sessionStarted = false;
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "src/Targets/Microchip/AVR/AVR8/PhysicalInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
struct Avr8EdbgParameter
|
||||
{
|
||||
unsigned char context = 0x00;
|
||||
unsigned char id = 0x00;
|
||||
|
||||
constexpr Avr8EdbgParameter() = default;
|
||||
constexpr Avr8EdbgParameter(unsigned char context, unsigned char id)
|
||||
: context(context)
|
||||
, id(id)
|
||||
{};
|
||||
};
|
||||
|
||||
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};
|
||||
|
||||
// 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};
|
||||
|
||||
// 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 RUN_TIMERS_WHILST_STOPPED {0x03, 0x00};
|
||||
static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI {0x03, 0x06};
|
||||
};
|
||||
|
||||
enum class Avr8ConfigVariant: unsigned char
|
||||
{
|
||||
LOOPBACK = 0x00,
|
||||
NONE = 0xff,
|
||||
DEBUG_WIRE = 0x01,
|
||||
MEGAJTAG = 0x02,
|
||||
XMEGA = 0x03,
|
||||
UPDI = 0x05,
|
||||
};
|
||||
|
||||
enum class Avr8ConfigFunction: unsigned char
|
||||
{
|
||||
NONE = 0x00,
|
||||
PROGRAMMING = 0x01,
|
||||
DEBUGGING = 0x02,
|
||||
};
|
||||
|
||||
static inline auto getAvr8PhysicalInterfaceToIdMapping() {
|
||||
using Targets::Microchip::Avr::Avr8Bit::PhysicalInterface;
|
||||
|
||||
return std::map<PhysicalInterface, unsigned char>({
|
||||
{PhysicalInterface::DEBUG_WIRE, 0x05},
|
||||
{PhysicalInterface::PDI, 0x06},
|
||||
{PhysicalInterface::JTAG, 0x04},
|
||||
{PhysicalInterface::UPDI, 0x08},
|
||||
});
|
||||
}
|
||||
|
||||
enum class Avr8MemoryType: unsigned char
|
||||
{
|
||||
/**
|
||||
* The SRAM memory type can be used to read & write to internal memory on the target.
|
||||
*
|
||||
* It's available with all of the config variants in debugging mode.
|
||||
*/
|
||||
SRAM = 0x20,
|
||||
|
||||
/**
|
||||
* The EEPROM memory type can be used to read and write to EEPROM memory on the target.
|
||||
*
|
||||
* It's available with all of the config variants, in debugging mode.
|
||||
*/
|
||||
EEPROM = 0x22,
|
||||
|
||||
/**
|
||||
* The EEPROM_ATOMIC memory type can be used to write to EEPROM memory with automatic page erasing.
|
||||
*
|
||||
* It's only available for XMEGA and UPDI config variants.
|
||||
*
|
||||
* Only one EEPROM page can be written at a time.
|
||||
*/
|
||||
EEPROM_ATOMIC = 0xC4,
|
||||
|
||||
/**
|
||||
* The EEPROM_PAGE memory type can be used to read and write to EEPROM whilst the target is in
|
||||
* programming mode.
|
||||
*/
|
||||
EEPROM_PAGE = 0xB1,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* used whilst in debugging mode.
|
||||
*/
|
||||
FLASH_PAGE = 0xB0,
|
||||
|
||||
/**
|
||||
* The APPL_FLASH memory type can be used to read/write to the application section of the flash memory on the
|
||||
* target.
|
||||
*
|
||||
* Only available with the XMEGA and UPDI config variants.
|
||||
*
|
||||
* When in debugging mode, only read access is permitted. Programming mode will need to be enabled before
|
||||
* any attempts of writing data.
|
||||
*/
|
||||
APPL_FLASH = 0xC0,
|
||||
BOOT_FLASH = 0xC1,
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
SPM = 0xA0,
|
||||
|
||||
/**
|
||||
* The REGISTER_FILE memory type can be used to read & write to general purpose registers.
|
||||
*
|
||||
* Only available in debugging mode and with XMEGA and UPDI config variants. The SRAM memory type can be used
|
||||
* to access general purpose registers when other variants are in use.
|
||||
*/
|
||||
REGISTER_FILE = 0xB8,
|
||||
|
||||
/**
|
||||
* The FUSES memory type can be used to read and write AVR fuses in programming mode.
|
||||
*
|
||||
* Not available for the debugWire config variant.
|
||||
*/
|
||||
FUSES = 0xB2,
|
||||
};
|
||||
|
||||
enum class Avr8ResponseId: unsigned char
|
||||
{
|
||||
OK = 0x80,
|
||||
DATA = 0x84,
|
||||
FAILED = 0xA0,
|
||||
};
|
||||
|
||||
inline bool operator == (unsigned char rawId, Avr8ResponseId id) {
|
||||
return static_cast<unsigned char>(id) == rawId;
|
||||
}
|
||||
|
||||
inline bool operator == (Avr8ResponseId id, unsigned char rawId) {
|
||||
return rawId == id;
|
||||
}
|
||||
|
||||
enum class Avr8EraseMemoryMode: unsigned char
|
||||
{
|
||||
CHIP = 0x00,
|
||||
APPLICATION_SECTION = 0x01,
|
||||
BOOT_SECTION = 0x02,
|
||||
EEPROM = 0x03,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "AvrCommand.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
AvrCommand::AvrCommand(
|
||||
std::size_t fragmentCount,
|
||||
std::size_t fragmentNumber,
|
||||
const std::vector<unsigned char>& commandPacket
|
||||
)
|
||||
: Command(0x80)
|
||||
{
|
||||
const auto commandPacketSize = commandPacket.size();
|
||||
this->data.reserve(commandPacketSize + 3);
|
||||
|
||||
// FragmentInfo byte
|
||||
this->data.emplace_back(static_cast<unsigned char>((fragmentNumber << 4) | fragmentCount));
|
||||
|
||||
// Size byte
|
||||
this->data.emplace_back(static_cast<unsigned char>(commandPacketSize >> 8));
|
||||
this->data.emplace_back(static_cast<unsigned char>(commandPacketSize & 0xFF));
|
||||
|
||||
if (commandPacketSize > 0) {
|
||||
// Packet data
|
||||
this->data.insert(this->data.begin() + 3, commandPacket.begin(), commandPacket.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
||||
|
||||
#include "AvrResponse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
/**
|
||||
* AVR CMSIS-DAP vendor command.
|
||||
*/
|
||||
class AvrCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* AVR CMSIS-DAP vendor commands *do not* directly result in an AvrResponse object. The device will respond
|
||||
* immediately upon receiving this command, simply acknowledging receipt of the command.
|
||||
*
|
||||
* If a response is expected to follow upon the execution of the AVR command, it must be requested from the
|
||||
* device, using the AvrResponseCommand (see that class declaration for more info).
|
||||
*
|
||||
* For this reason, the ExpectedResponseType for this command is just the standard Response type.
|
||||
*
|
||||
* For more on the purpose of this alias, see the Command class.
|
||||
*/
|
||||
using ExpectedResponseType = ::DebugToolDrivers::Protocols::CmsisDap::Response;
|
||||
|
||||
AvrCommand(
|
||||
std::size_t fragmentCount,
|
||||
std::size_t fragmentNumber,
|
||||
const std::vector<unsigned char>& commandPacket
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "AvrEvent.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
AvrEvent::AvrEvent(const std::vector<unsigned char>& rawResponse)
|
||||
: Response(rawResponse)
|
||||
{
|
||||
if (this->id != 0x82) {
|
||||
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.");
|
||||
}
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
const auto responsePacketSize = static_cast<std::size_t>((this->data[0] << 8) | this->data[1]);
|
||||
|
||||
if (responsePacketSize == 0) {
|
||||
// No event available
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->data.size() < responsePacketSize + 7) {
|
||||
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->data.begin() + 7,
|
||||
this->data.begin() + 7 + static_cast<std::int64_t>(responsePacketSize)
|
||||
);
|
||||
|
||||
this->eventId = static_cast<AvrEventId>(this->eventData[0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
enum class AvrEventId: unsigned char
|
||||
{
|
||||
AVR8_BREAK_EVENT = 0x40,
|
||||
};
|
||||
|
||||
class AvrEvent: public ::DebugToolDrivers::Protocols::CmsisDap::Response
|
||||
{
|
||||
public:
|
||||
std::optional<AvrEventId> eventId = std::nullopt;
|
||||
std::vector<unsigned char> eventData = {};
|
||||
|
||||
explicit AvrEvent(const std::vector<unsigned char>& rawResponse);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
||||
#include "AvrEvent.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class AvrEventCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseType = AvrEvent;
|
||||
|
||||
AvrEventCommand()
|
||||
: Command(0x82)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "AvrResponse.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
AvrResponse::AvrResponse(const std::vector<unsigned char>& rawResponse)
|
||||
: Response(rawResponse)
|
||||
{
|
||||
if (this->id != 0x81) {
|
||||
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");
|
||||
}
|
||||
|
||||
if (this->data[0] == 0x00) {
|
||||
// This AVR Response contains no data (the device had no data to send), so we can stop here.
|
||||
return;
|
||||
}
|
||||
|
||||
this->fragmentCount = static_cast<std::uint8_t>(this->data[0] & 0x0FU);
|
||||
this->fragmentNumber = static_cast<std::uint8_t>(this->data[0] >> 4);
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
const auto responsePacketSize = static_cast<std::uint16_t>((this->data[1] << 8U) + this->data[2]);
|
||||
|
||||
if (responsePacketSize > 0) {
|
||||
// Packet data
|
||||
this->responsePacket.insert(
|
||||
this->responsePacket.begin(),
|
||||
this->data.begin() + 3,
|
||||
this->data.begin() + 3 + responsePacketSize
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class AvrResponse: public ::DebugToolDrivers::Protocols::CmsisDap::Response
|
||||
{
|
||||
public:
|
||||
std::uint8_t fragmentNumber = 0;
|
||||
std::uint8_t fragmentCount = 0;
|
||||
|
||||
std::vector<unsigned char> responsePacket;
|
||||
|
||||
explicit AvrResponse(const std::vector<unsigned char>& rawResponse);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
||||
|
||||
#include "AvrResponse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
/**
|
||||
* All AVR commands result in an automatic response, but that is just a response to confirm
|
||||
* receipt of the AVR Command. It is *not* a response to the AVR command.
|
||||
*
|
||||
* Responses to AVR commands are not automatically sent from the device, they must be requested from the device.
|
||||
*
|
||||
* An AvrResponseCommand is a CMSIS-DAP vendor command, used to request a response for an AVR command. In response
|
||||
* to an AvrResponseCommand, the device will send over an AvrResponse, which will contain the response to the
|
||||
* AVR command.
|
||||
*
|
||||
* For more information on this, see the 'Embedded Debugger-Based Tools Protocols User's Guide'
|
||||
* document, by Microchip. Link provided in the AtmelICE device class declaration.
|
||||
*
|
||||
* An AvrResponseCommand is very simple - it consists of a command ID and nothing more.
|
||||
*/
|
||||
class AvrResponseCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseType = AvrResponse;
|
||||
|
||||
AvrResponseCommand()
|
||||
: Command(0x81)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ActivatePhysical: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit ActivatePhysical(bool reset)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The activate physical command consists of 3 bytes:
|
||||
* 1. Command ID (0x10)
|
||||
* 2. Version (0x00)
|
||||
* 3. Reset flag (to apply external reset)
|
||||
*/
|
||||
this->payload = {
|
||||
0x10,
|
||||
0x00,
|
||||
static_cast<unsigned char>(reset)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Attach: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Attach(bool breakAfterAttach)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The attach command consists of 3 bytes:
|
||||
* 1. Command ID (0x13)
|
||||
* 2. Version (0x00)
|
||||
* 3. Break (stop) after attach flag
|
||||
*/
|
||||
this->payload = {
|
||||
0x13,
|
||||
0x00,
|
||||
static_cast<unsigned char>(breakAfterAttach)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/Avr8GenericResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class Avr8GenericCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::Avr8GenericResponseFrame;
|
||||
|
||||
Avr8GenericCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::AVR8_GENERIC)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearAllSoftwareBreakpoints: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
ClearAllSoftwareBreakpoints()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear all software breakpoints command consists of 2 bytes:
|
||||
* 1. Command ID (0x45)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x45,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit ClearHardwareBreakpoint(std::uint8_t number)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear hardware breakpoint command consists of 3 bytes:
|
||||
*
|
||||
* 1. Command ID (0x41)
|
||||
* 2. Version (0x00)
|
||||
* 3. Breakpoint Number (1, 2, or 3)
|
||||
*/
|
||||
this->payload = {
|
||||
0x41,
|
||||
0x00,
|
||||
number
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearSoftwareBreakpoints: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
explicit ClearSoftwareBreakpoints(const std::vector<std::uint32_t>& addresses)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear software breakpoints command consists of 2 bytes + 4*n bytes, where n is the number
|
||||
* of breakpoints to clear:
|
||||
*
|
||||
* 1. Command ID (0x44)
|
||||
* 2. Version (0x00)
|
||||
* ... addresses
|
||||
*/
|
||||
this->payload = {
|
||||
0x44,
|
||||
0x00,
|
||||
};
|
||||
|
||||
for (const auto& address : addresses) {
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 8));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 16));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 24));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class DeactivatePhysical: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
DeactivatePhysical()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The deactivate physical command consists of 2 bytes:
|
||||
* 1. Command ID (0x11)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x11,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Detach: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
Detach()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The detach command consists of 2 bytes:
|
||||
* 1. Command ID (0x14)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x14,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class DisableDebugWire: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
DisableDebugWire()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The disable debugWire command consists of 2 bytes:
|
||||
* 1. Command ID (0x17)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x17,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class EnterProgrammingMode: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
EnterProgrammingMode()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The enter programming mode command consists of 2 bytes:
|
||||
* 1. Command ID (0x15)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x15,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class EraseMemory: public Avr8GenericCommandFrame<std::array<unsigned char, 7>>
|
||||
{
|
||||
public:
|
||||
EraseMemory(Avr8EraseMemoryMode mode)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The "Erase memory" command consists of 7 bytes:
|
||||
* 1. Command ID (0x20)
|
||||
* 2. Version (0x00)
|
||||
* 3. Erase mode (see Avr8EraseMemoryMode enum)
|
||||
* 4. Start address (4 bytes) - this is just hardcoded to 0x00000000 for now.
|
||||
*/
|
||||
this->payload = {
|
||||
0x20,
|
||||
0x00,
|
||||
static_cast<unsigned char>(mode),
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "../../ResponseFrames/AVR8Generic/GetDeviceId.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class GetDeviceId: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::GetDeviceId;
|
||||
|
||||
GetDeviceId()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The get device ID command consists of 2 bytes:
|
||||
* 1. Command ID (0x12)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x12,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class GetParameter: public Avr8GenericCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
GetParameter(const Avr8EdbgParameter& parameter, std::uint8_t size)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The get param command consists of 5 bytes:
|
||||
* 1. Command ID (0x02)
|
||||
* 2. Version (0x00)
|
||||
* 3. Param context (Avr8Parameter::context)
|
||||
* 4. Param ID (Avr8Parameter::id)
|
||||
* 5. Param value length (size)
|
||||
*/
|
||||
this->payload = {
|
||||
0x02,
|
||||
0x00,
|
||||
static_cast<unsigned char>(parameter.context),
|
||||
static_cast<unsigned char>(parameter.id),
|
||||
static_cast<unsigned char>(size)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "../../ResponseFrames/AVR8Generic/GetProgramCounter.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class GetProgramCounter: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::GetProgramCounter;
|
||||
|
||||
GetProgramCounter()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The PC Read command consists of 2 bytes:
|
||||
* 1. Command ID (0x35)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x35,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class LeaveProgrammingMode: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
LeaveProgrammingMode()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The leave programming mode command consists of 2 bytes:
|
||||
* 1. Command ID (0x16)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x16,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "ReadMemory.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
ReadMemory::ReadMemory(
|
||||
const Avr8MemoryType& type,
|
||||
std::uint32_t startAddress,
|
||||
std::uint32_t bytes,
|
||||
const std::set<std::uint32_t>& excludedAddresses
|
||||
)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The read memory command consists of 11 + (bytes / 8) bytes:
|
||||
* 1. Command ID (0x21 for the general read memory command, 0x22 for reading with a mask)
|
||||
* 2. Version (0x00)
|
||||
* 3. Memory type (Avr8MemoryType)
|
||||
* 4. Start address (4 bytes)
|
||||
* 5. Number of bytes to read (4 bytes)
|
||||
* 6. Mask to apply (bytes / 8) - only required if we're using the masked read command (command ID 0x22).
|
||||
*/
|
||||
this->payload = std::vector<unsigned char>(11, 0x00);
|
||||
this->payload[0] = excludedAddresses.empty() ? 0x21 : 0x22;
|
||||
this->payload[1] = 0x00;
|
||||
this->payload[2] = static_cast<unsigned char>(type);
|
||||
this->payload[3] = static_cast<unsigned char>(startAddress);
|
||||
this->payload[4] = static_cast<unsigned char>(startAddress >> 8);
|
||||
this->payload[5] = static_cast<unsigned char>(startAddress >> 16);
|
||||
this->payload[6] = static_cast<unsigned char>(startAddress >> 24);
|
||||
this->payload[7] = static_cast<unsigned char>(bytes);
|
||||
this->payload[8] = static_cast<unsigned char>(bytes >> 8);
|
||||
this->payload[9] = static_cast<unsigned char>(bytes >> 16);
|
||||
this->payload[10] = static_cast<unsigned char>(bytes >> 24);
|
||||
|
||||
if (!excludedAddresses.empty()) {
|
||||
const auto endAddress = startAddress + (bytes - 1);
|
||||
|
||||
constexpr auto byteBitSize = std::numeric_limits<unsigned char>::digits;
|
||||
auto byteBitset = std::bitset<byteBitSize>();
|
||||
byteBitset.reset();
|
||||
|
||||
for (std::uint32_t address = startAddress; address <= endAddress; address++) {
|
||||
auto addressIndex = address - startAddress;
|
||||
auto bitIndex = static_cast<std::size_t>(
|
||||
addressIndex - (std::floor(addressIndex / byteBitSize) * byteBitSize)
|
||||
);
|
||||
|
||||
if (!excludedAddresses.contains(address)) {
|
||||
byteBitset[bitIndex] = true;
|
||||
}
|
||||
|
||||
if (address > 0 && (bitIndex == 7 || address == endAddress)) {
|
||||
this->payload.emplace_back(byteBitset.to_ulong());
|
||||
byteBitset.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "../../ResponseFrames/AVR8Generic/ReadMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ReadMemory: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::ReadMemory;
|
||||
|
||||
ReadMemory(
|
||||
const Avr8MemoryType& type,
|
||||
std::uint32_t address,
|
||||
std::uint32_t bytes,
|
||||
const std::set<std::uint32_t>& excludedAddresses = {}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Reset: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Reset(bool stopAtMainAddress = false)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The reset command consists of 3 bytes:
|
||||
* 1. Command ID (0x30)
|
||||
* 2. Version (0x00)
|
||||
* 3. "Level" (0x01 to stop at boot reset vector or 0x02 to stop at main address)
|
||||
*/
|
||||
this->payload = {
|
||||
0x30,
|
||||
0x00,
|
||||
static_cast<unsigned char>(stopAtMainAddress ? 0x02 : 0x01),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Run: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
Run()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The run command consists of 2 bytes:
|
||||
* 1. Command ID (0x32)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x32,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class RunTo: public Avr8GenericCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
explicit RunTo(const std::uint32_t& address)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The run-to command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x33)
|
||||
* 2. Version (0x00)
|
||||
* 3. Address to run to (4 bytes)
|
||||
*/
|
||||
|
||||
// The address to run to needs to be the 16-bit word address, not the byte address.
|
||||
const auto wordAddress = address / 2;
|
||||
|
||||
this->payload = {
|
||||
0x33,
|
||||
0x00,
|
||||
static_cast<unsigned char>(wordAddress),
|
||||
static_cast<unsigned char>(wordAddress >> 8),
|
||||
static_cast<unsigned char>(wordAddress >> 16),
|
||||
static_cast<unsigned char>(wordAddress >> 24),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 9>>
|
||||
{
|
||||
public:
|
||||
explicit SetHardwareBreakpoint(std::uint32_t address, std::uint8_t number)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set hardware breakpoint command consists of 9 bytes:
|
||||
*
|
||||
* 1. Command ID (0x40)
|
||||
* 2. Version (0x00)
|
||||
* 3. Breakpoint Type (0x01 for program break)
|
||||
* 4. Breakpoint Number (1, 2, or 3)
|
||||
* 5. Program address (4 bytes) - the EDBG Protocol document states that this should be the word address,
|
||||
* but that seems to be incorrect. The tool expects a byte address here.
|
||||
* 5. Mode (0x03 for program break)
|
||||
*/
|
||||
this->payload = {
|
||||
0x40,
|
||||
0x00,
|
||||
0x01,
|
||||
number,
|
||||
static_cast<unsigned char>(address),
|
||||
static_cast<unsigned char>(address >> 8),
|
||||
static_cast<unsigned char>(address >> 16),
|
||||
static_cast<unsigned char>(address >> 24),
|
||||
0x03
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetParameter: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
SetParameter(const Avr8EdbgParameter& parameter, const std::vector<unsigned char>& value)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set param command consists of value.size() + 5 bytes. The first five bytes consist of:
|
||||
* 1. Command ID (0x01)
|
||||
* 2. Version (0x00)
|
||||
* 3. Param context (Avr8EdbgParameter::context)
|
||||
* 4. Param ID (Avr8EdbgParameter::id)
|
||||
* 5. Param value length (value.size()) - this is only one byte in size, so its value should
|
||||
* never exceed 255.
|
||||
*/
|
||||
this->payload = std::vector<unsigned char>(5, 0x00);
|
||||
this->payload[0] = 0x01;
|
||||
this->payload[1] = 0x00;
|
||||
this->payload[2] = static_cast<unsigned char>(parameter.context);
|
||||
this->payload[3] = static_cast<unsigned char>(parameter.id);
|
||||
this->payload[4] = static_cast<unsigned char>(value.size());
|
||||
this->payload.insert(this->payload.end(), value.begin(), value.end());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetProgramCounter: public Avr8GenericCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
explicit SetProgramCounter(std::uint32_t programCounter)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The PC write command consists of 6 bytes:
|
||||
* 1. Command ID (0x36)
|
||||
* 2. Version (0x00)
|
||||
* 3. PC (4 bytes, LSB)
|
||||
*/
|
||||
this->payload = {
|
||||
0x36,
|
||||
0x00,
|
||||
static_cast<unsigned char>(programCounter),
|
||||
static_cast<unsigned char>(programCounter >> 8),
|
||||
static_cast<unsigned char>(programCounter >> 16),
|
||||
static_cast<unsigned char>(programCounter >> 24),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetSoftwareBreakpoints: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
explicit SetSoftwareBreakpoints(const std::vector<std::uint32_t>& addresses)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set software breakpoint command consists of 2 bytes + 4*n bytes, where n is the number
|
||||
* of breakpoints to set:
|
||||
*
|
||||
* 1. Command ID (0x43)
|
||||
* 2. Version (0x00)
|
||||
* ... addresses
|
||||
*/
|
||||
this->payload = {
|
||||
0x43,
|
||||
0x00,
|
||||
};
|
||||
|
||||
for (const auto& address : addresses) {
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 8));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 16));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 24));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetXmegaSoftwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 15>>
|
||||
{
|
||||
public:
|
||||
explicit SetXmegaSoftwareBreakpoint(std::uint32_t address)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
this->payload = {
|
||||
0x42,
|
||||
0x00,
|
||||
static_cast<unsigned char>(address),
|
||||
static_cast<unsigned char>(address >> 8),
|
||||
static_cast<unsigned char>(address >> 16),
|
||||
static_cast<unsigned char>(address >> 24),
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Step: public Avr8GenericCommandFrame<std::array<unsigned char, 4>>
|
||||
{
|
||||
public:
|
||||
Step()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The step command consists of 4 bytes:
|
||||
* 1. Command ID (0x34)
|
||||
* 2. Version (0x00)
|
||||
* 3. Level (0x01 for instruction level step, 0x02 for statement level step)
|
||||
* 4. Mode (0x00 for step over, 0x01 for step into, 0x02 for step out)
|
||||
*/
|
||||
this->payload = {
|
||||
0x34,
|
||||
0x00,
|
||||
0x01,
|
||||
0x01,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Stop: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Stop(bool stopImmediately = true)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The stop command consists of 3 bytes:
|
||||
* 1. Command ID (0x31)
|
||||
* 2. Version (0x00)
|
||||
* 3. Stop immediately (0x01 to stop immediately or 0x02 to stop at the next symbol)
|
||||
*/
|
||||
this->payload = {
|
||||
0x31,
|
||||
0x00,
|
||||
static_cast<unsigned char>(stopImmediately ? 0x01 : 0x02),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class WriteMemory: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
WriteMemory(const Avr8MemoryType& type, std::uint32_t address, const Targets::TargetMemoryBuffer& buffer)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The "Write memory" command consists of 12 bytes + the buffer size:
|
||||
* 1. Command ID (0x23)
|
||||
* 2. Version (0x00)
|
||||
* 3. Memory type (Avr8MemoryType)
|
||||
* 4. Start address (4 bytes)
|
||||
* 5. Number of bytes to write (4 bytes)
|
||||
* 6. Asynchronous flag (0x00 for "write first, then reply" and 0x01 for "reply first, then write")
|
||||
* 7. Buffer
|
||||
*/
|
||||
this->payload = std::vector<unsigned char>(12, 0x00);
|
||||
this->payload[0] = 0x23;
|
||||
this->payload[1] = 0x00;
|
||||
this->payload[2] = static_cast<unsigned char>(type);
|
||||
this->payload[3] = static_cast<unsigned char>(address);
|
||||
this->payload[4] = static_cast<unsigned char>(address >> 8);
|
||||
this->payload[5] = static_cast<unsigned char>(address >> 16);
|
||||
this->payload[6] = static_cast<unsigned char>(address >> 24);
|
||||
|
||||
const auto bytesToWrite = static_cast<std::uint32_t>(buffer.size());
|
||||
this->payload[7] = static_cast<unsigned char>(bytesToWrite);
|
||||
this->payload[8] = static_cast<unsigned char>(bytesToWrite >> 8);
|
||||
this->payload[9] = static_cast<unsigned char>(bytesToWrite >> 16);
|
||||
this->payload[10] = static_cast<unsigned char>(bytesToWrite >> 24);
|
||||
|
||||
// We always set the async flag to 0x00 ("write first, then reply")
|
||||
this->payload[11] = 0x00;
|
||||
|
||||
this->payload.insert(this->payload.end(), buffer.begin(), buffer.end());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVRISP/AvrIspResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class AvrIspCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::AvrIsp::AvrIspResponseFrame;
|
||||
|
||||
AvrIspCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::AVRISP)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class EnterProgrammingMode: public AvrIspCommandFrame<std::array<unsigned char, 12>>
|
||||
{
|
||||
public:
|
||||
EnterProgrammingMode(
|
||||
std::uint8_t timeout,
|
||||
std::uint8_t stabDelay,
|
||||
std::uint8_t commandExecutionDelay,
|
||||
std::uint8_t syncLoops,
|
||||
std::uint8_t byteDelay,
|
||||
std::uint8_t pollValue,
|
||||
std::uint8_t pollIndex
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The enter programming mode command consists of 12 bytes:
|
||||
*
|
||||
* 1. Command ID (0x10)
|
||||
*
|
||||
* -- The fields from this point are taken from the AVR8 TDFs --
|
||||
*
|
||||
* 2. Command timeout in milliseconds
|
||||
* 3. Pin stabilization delay in milliseconds
|
||||
* 4. Command execution delay in milliseconds
|
||||
* 5. Number of synchronisation loops
|
||||
* 6. Millisecond delay between each byte in the command
|
||||
* 7. Poll value
|
||||
* 8. Poll index
|
||||
*
|
||||
* -- The fields from this point are the four bytes of the low-level SPI command --
|
||||
*
|
||||
* 9. CMD1 - For the enter programming mode command, this appears to always be 0xAC
|
||||
* 10. CMD2 - For AVR8 targets, this also appears to be a fixed value of 0x53
|
||||
* 11. CMD3 - 0x00
|
||||
* 12. CMD4 - 0x00
|
||||
*/
|
||||
this->payload = {
|
||||
0x10,
|
||||
timeout,
|
||||
stabDelay,
|
||||
commandExecutionDelay,
|
||||
syncLoops,
|
||||
byteDelay,
|
||||
pollValue,
|
||||
pollIndex,
|
||||
0xAC,
|
||||
0x53,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class LeaveProgrammingMode: public AvrIspCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
LeaveProgrammingMode(
|
||||
std::uint8_t preDelay,
|
||||
std::uint8_t postDelay
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The leave programming mode command consists of 3 bytes:
|
||||
*
|
||||
* 1. Command ID (0x11)
|
||||
*
|
||||
* -- The fields from this point are taken from the AVR8 TDFs --
|
||||
*
|
||||
* 2. Pre-delay
|
||||
* 3. Post-delay
|
||||
*/
|
||||
this->payload = {
|
||||
0x11,
|
||||
preDelay,
|
||||
postDelay,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.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)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr::FuseType;
|
||||
|
||||
/*
|
||||
* The program fuse command consists of 5 bytes:
|
||||
*
|
||||
* 1. Command ID (0x17)
|
||||
* 2. Return Address
|
||||
* 3. CMD1
|
||||
* 4. CMD2
|
||||
* 5. CMD3
|
||||
* 6. CMD4
|
||||
*/
|
||||
this->payload = {
|
||||
0x17,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
value,
|
||||
};
|
||||
|
||||
switch (fuseType) {
|
||||
case FuseType::LOW: {
|
||||
this->payload[1] = 0xAC;
|
||||
this->payload[2] = 0xA0;
|
||||
this->payload[3] = 0x00;
|
||||
break;
|
||||
}
|
||||
case FuseType::HIGH: {
|
||||
this->payload[1] = 0xAC;
|
||||
this->payload[2] = 0xA8;
|
||||
this->payload[3] = 0x00;
|
||||
break;
|
||||
}
|
||||
case FuseType::EXTENDED: {
|
||||
this->payload[1] = 0xAC;
|
||||
this->payload[2] = 0xA4;
|
||||
this->payload[3] = 0x00;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw Exceptions::Exception("Unsupported fuse type");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.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
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr::FuseType;
|
||||
|
||||
/*
|
||||
* The read fuse command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x18)
|
||||
* 2. Return Address
|
||||
* 3. CMD1
|
||||
* 4. CMD2
|
||||
* 5. CMD3
|
||||
* 6. CMD4
|
||||
*/
|
||||
this->payload = {
|
||||
0x18,
|
||||
returnAddress,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
|
||||
switch (fuseType) {
|
||||
case FuseType::LOW: {
|
||||
this->payload[2] = 0x50;
|
||||
this->payload[3] = 0x00;
|
||||
this->payload[4] = 0x00;
|
||||
this->payload[5] = 0x00;
|
||||
break;
|
||||
}
|
||||
case FuseType::HIGH: {
|
||||
this->payload[2] = 0x58;
|
||||
this->payload[3] = 0x08;
|
||||
this->payload[4] = 0x00;
|
||||
this->payload[5] = 0x00;
|
||||
break;
|
||||
}
|
||||
case FuseType::EXTENDED: {
|
||||
this->payload[2] = 0x50;
|
||||
this->payload[3] = 0x08;
|
||||
this->payload[4] = 0x00;
|
||||
this->payload[5] = 0x00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ReadLock: public AvrIspCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
explicit ReadLock(std::uint8_t returnAddress)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The read lock command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x1A)
|
||||
* 2. Return Address
|
||||
* 3. CMD1
|
||||
* 4. CMD2
|
||||
* 5. CMD3
|
||||
* 6. CMD4
|
||||
*/
|
||||
this->payload = {
|
||||
0x1A,
|
||||
returnAddress,
|
||||
0x58,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <src/Targets/Microchip/AVR/Fuse.hpp>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/Fuse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ReadSignature: public AvrIspCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
ReadSignature(
|
||||
std::uint8_t signatureByteAddress,
|
||||
std::uint8_t returnAddress
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr::FuseType;
|
||||
|
||||
/*
|
||||
* The read signature command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x1B)
|
||||
* 2. Return Address
|
||||
* 3. CMD1
|
||||
* 4. CMD2
|
||||
* 5. CMD3
|
||||
* 6. CMD4
|
||||
*/
|
||||
this->payload = {
|
||||
0x1B,
|
||||
returnAddress,
|
||||
0x30,
|
||||
0x00,
|
||||
static_cast<unsigned char>(signatureByteAddress & 0x03),
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Command.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/Edbg.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrCommand.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
static inline std::atomic<std::uint16_t> lastSequenceId = 0;
|
||||
|
||||
template <class PayloadContainerType = std::vector<unsigned char>>
|
||||
class AvrCommandFrame
|
||||
{
|
||||
/*
|
||||
* Every AVR command frame contains a payload. For many commands, the payload size is fixed, meaning we can
|
||||
* use automatic storage. In other cases, the size of the payload is determined at runtime, requiring the use
|
||||
* of dynamic storage.
|
||||
*
|
||||
* For example, consider the Get Device ID command from the AVR8 Generic Protocol. The size of the payload
|
||||
* for this command is fixed at 2 bytes. So there is no need to use dynamic storage duration for the payload.
|
||||
*
|
||||
* Now consider the Write Memory command from the same protocol. The payload size for that command depends
|
||||
* on the size of memory we wish to write, which is not known at compile time. For this command, the payload
|
||||
* would have dynamic storage.
|
||||
*
|
||||
* For the above reason, the AvrCommandFrame class is a template class in which the payload container type can
|
||||
* be specified for individual commands.
|
||||
*
|
||||
* For now, we only permit two payload container types:
|
||||
* - std::array<unsigned char> for payloads with automatic storage duration.
|
||||
* - std::vector<unsigned char> for payloads with dynamic storage duration.
|
||||
*/
|
||||
static_assert(
|
||||
(
|
||||
std::is_same_v<PayloadContainerType, std::vector<unsigned char>>
|
||||
|| std::is_same_v<typename PayloadContainerType::value_type, unsigned char>
|
||||
),
|
||||
"Invalid payload container type - must be an std::array<unsigned char, X> or an std::vector<unsigned char>"
|
||||
);
|
||||
|
||||
public:
|
||||
/*
|
||||
* All AVR command frames result in one or more response frames from the EDBG device. The structure and
|
||||
* contents of the response frame depends on the command frame that was sent.
|
||||
*
|
||||
* The ExpectedResponseFrameType alias is used to map command frame types to response frame types.
|
||||
* This is used in some template functions, such as EdbgInterface::sendAvrCommandFrameAndWaitForResponseFrame().
|
||||
* That function will use the alias when constructing and returning a response frame object.
|
||||
*
|
||||
* For example, consider the GetDeviceId command - this command instructs the EDBG device to extract the
|
||||
* signature from the connected AVR target, and return it in a response frame. The GetDeviceId command frame
|
||||
* maps to the GetDeviceId response frame type (via the ExpectedResponseFrameType alias). When we send the
|
||||
* command, the correct response frame object is returned:
|
||||
*
|
||||
* EdbgInterface edbgInterface;
|
||||
* CommandFrames::Avr8Generic::GetDeviceId getDeviceIdCommandFrame;
|
||||
*
|
||||
* auto responseFrame = edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(getDeviceIdCommandFrame);
|
||||
* Targets::Microchip::Avr::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).
|
||||
* This works because the EdbgInterface::sendAvrCommandFrameAndWaitForResponseFrame() function uses the
|
||||
* CommandFrames::Avr8Generic::GetDeviceId::ExpectedResponseFrameType alias to construct the response frame.
|
||||
*
|
||||
* For more, see the implementation of EdbgInterface::sendAvrCommandFrameAndWaitForResponseFrame().
|
||||
*/
|
||||
using ExpectedResponseFrameType = AvrResponseFrame;
|
||||
|
||||
/**
|
||||
* Incrementing from 0
|
||||
*/
|
||||
std::uint16_t sequenceId = 0;
|
||||
|
||||
/**
|
||||
* Destination sub-protocol handler ID
|
||||
*/
|
||||
ProtocolHandlerId protocolHandlerId = ProtocolHandlerId::DISCOVERY;
|
||||
|
||||
PayloadContainerType payload;
|
||||
|
||||
explicit AvrCommandFrame(ProtocolHandlerId protocolHandlerId)
|
||||
: sequenceId(++lastSequenceId)
|
||||
, protocolHandlerId(protocolHandlerId)
|
||||
{};
|
||||
|
||||
virtual ~AvrCommandFrame() = default;
|
||||
|
||||
AvrCommandFrame(const AvrCommandFrame& other) = default;
|
||||
AvrCommandFrame(AvrCommandFrame&& other) noexcept = default;
|
||||
|
||||
AvrCommandFrame& operator = (const AvrCommandFrame& other) = default;
|
||||
AvrCommandFrame& operator = (AvrCommandFrame&& other) noexcept = default;
|
||||
|
||||
/**
|
||||
* Converts the command frame into a container of unsigned char - a raw buffer to include in an AVR
|
||||
* command packet, for sending to the EDBG device.
|
||||
*
|
||||
* See AvrCommandFrame::generateAvrCommands() and the AvrCommand class for more.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] auto getRawCommandFrame() const {
|
||||
auto rawCommand = std::vector<unsigned char>(5 + this->payload.size());
|
||||
|
||||
rawCommand[0] = 0x0E; // Start of frame (SOF) byte
|
||||
rawCommand[1] = 0x00; // Protocol version
|
||||
|
||||
rawCommand[2] = static_cast<unsigned char>(this->sequenceId);
|
||||
rawCommand[3] = static_cast<unsigned char>(this->sequenceId >> 8);
|
||||
|
||||
rawCommand[4] = static_cast<unsigned char>(this->protocolHandlerId);
|
||||
|
||||
if (!this->payload.empty()) {
|
||||
std::copy(this->payload.begin(), this->payload.end(), rawCommand.begin() + 5);
|
||||
}
|
||||
|
||||
return rawCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* AvrCommandFrames are sent to the device via AvrCommands (CMSIS-DAP vendor commands).
|
||||
*
|
||||
* If the size of an AvrCommandFrame exceeds the maximum packet size of an AVRCommand, it will need to
|
||||
* be split into multiple AvrCommands before being sent to the device.
|
||||
*
|
||||
* This methods generates AVR commands from an AvrCommandFrame. The number of AvrCommands generated depends
|
||||
* on the size of the AvrCommandFrame and the passed maximumCommandPacketSize.
|
||||
*
|
||||
* @param maximumCommandPacketSize
|
||||
* The maximum size of an AVRCommand command packet. This is usually the REPORT_SIZE of the HID
|
||||
* endpoint, minus a few bytes to account for other AVRCommand fields. The maximumCommandPacketSize is used to
|
||||
* determine the number of AvrCommands to be generated.
|
||||
*
|
||||
* @return
|
||||
* 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();
|
||||
|
||||
std::size_t commandFrameSize = rawCommandFrame.size();
|
||||
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++) {
|
||||
// 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));
|
||||
|
||||
avrCommands.emplace_back(AvrCommand(
|
||||
commandsRequired,
|
||||
i + 1,
|
||||
std::vector<unsigned char>(
|
||||
rawCommandFrame.begin() + static_cast<std::int64_t>(copiedPacketSize),
|
||||
rawCommandFrame.begin() + static_cast<std::int64_t>(copiedPacketSize + commandPacketSize)
|
||||
)
|
||||
));
|
||||
copiedPacketSize += commandPacketSize;
|
||||
}
|
||||
|
||||
return avrCommands;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "AvrCommandFrame.hpp"
|
||||
|
||||
#include "Discovery/DiscoveryCommandFrame.hpp"
|
||||
#include "Discovery/Query.hpp"
|
||||
|
||||
#include "HouseKeeping/HouseKeepingCommandFrame.hpp"
|
||||
#include "HouseKeeping/StartSession.hpp"
|
||||
#include "HouseKeeping/EndSession.hpp"
|
||||
#include "HouseKeeping/GetParameter.hpp"
|
||||
|
||||
#include "AVR8Generic/Avr8GenericCommandFrame.hpp"
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/Discovery/DiscoveryResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Discovery
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class DiscoveryCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Discovery::DiscoveryResponseFrame;
|
||||
|
||||
DiscoveryCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::DISCOVERY)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "DiscoveryCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Discovery
|
||||
{
|
||||
/**
|
||||
* The query context is the type of query to execute.
|
||||
*/
|
||||
enum class QueryContext: unsigned char
|
||||
{
|
||||
COMMAND_HANDLERS = 0x00,
|
||||
TOOL_NAME = 0x80,
|
||||
SERIAL_NUMBER = 0x81,
|
||||
MANUFACTURE_DATE = 0x82,
|
||||
};
|
||||
|
||||
/**
|
||||
* The Discovery protocol handler only supports one command; the Query command. This command is used to
|
||||
* query information from the device, such as device capabilities, manufacture date, serial number, etc.
|
||||
*/
|
||||
class Query: public DiscoveryCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Query(QueryContext context)
|
||||
: DiscoveryCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The payload for the Query command consists of three bytes. A command ID (0x00), version (0x00) and a
|
||||
* query context.
|
||||
*/
|
||||
this->payload = {
|
||||
0x00,
|
||||
0x00,
|
||||
static_cast<unsigned char>(context)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/EDBGControl/EdbgControlResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class EdbgControlCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::EdbgControl::EdbgControlResponseFrame;
|
||||
|
||||
EdbgControlCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::EDBG_CONTROL)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "EdbgControlCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl
|
||||
{
|
||||
class GetParameter: public EdbgControlCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
GetParameter(const EdbgParameter& parameter)
|
||||
: EdbgControlCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The EDBG Get Parameter command consists of 5 bytes:
|
||||
*
|
||||
* 1. Command ID (0x02)
|
||||
* 2. Version (0x00)
|
||||
* 3. Parameter context
|
||||
* 4. Parameter ID
|
||||
* 5. Parameter size
|
||||
*/
|
||||
this->payload = {
|
||||
0x02,
|
||||
0x00,
|
||||
parameter.context,
|
||||
parameter.id,
|
||||
parameter.size,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "EdbgControlCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl
|
||||
{
|
||||
class SetParameter: public EdbgControlCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
SetParameter(const EdbgParameter& parameter, unsigned char value)
|
||||
: EdbgControlCommandFrame()
|
||||
{
|
||||
assert(parameter.size == 1);
|
||||
|
||||
/*
|
||||
* The EDBG Set Parameter command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x01)
|
||||
* 2. Version (0x00)
|
||||
* 3. Parameter context
|
||||
* 4. Parameter
|
||||
* 5. Parameter size
|
||||
* 6. Parameter value
|
||||
*/
|
||||
this->payload = {
|
||||
0x01,
|
||||
0x00,
|
||||
parameter.context,
|
||||
parameter.id,
|
||||
parameter.size,
|
||||
value,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
/**
|
||||
* The End Session command ends the active session with the tool.
|
||||
*/
|
||||
class EndSession: public HouseKeepingCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
EndSession()
|
||||
: HouseKeepingCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The payload for the End Session command consists of three bytes. A command ID byte (0x11), a
|
||||
* version byte (0x00) and a reset flag (0x00 for no reset, 0x01 for tool reset).
|
||||
*/
|
||||
this->payload = {
|
||||
0x11,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
#include "Parameters.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
class GetParameter: public HouseKeepingCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
GetParameter(const Parameter& parameter, std::uint8_t size)
|
||||
: HouseKeepingCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The get param command consists of 5 bytes:
|
||||
* 1. Command ID (0x02)
|
||||
* 2. Version (0x00)
|
||||
* 3. Param context (Parameter::context)
|
||||
* 4. Param ID (Parameter::id)
|
||||
* 5. Param value length (size)
|
||||
*/
|
||||
this->payload = {
|
||||
0x02,
|
||||
0x00,
|
||||
static_cast<unsigned char>(parameter.context),
|
||||
static_cast<unsigned char>(parameter.id),
|
||||
static_cast<unsigned char>(size)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/HouseKeeping/HouseKeepingResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class HouseKeepingCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::HouseKeeping::HouseKeepingResponseFrame;
|
||||
|
||||
HouseKeepingCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::HOUSE_KEEPING)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
enum class ParameterContext: unsigned char
|
||||
{
|
||||
CONFIG = 0x00,
|
||||
ANALOG = 0x01,
|
||||
USB = 0x03,
|
||||
};
|
||||
|
||||
struct Parameter
|
||||
{
|
||||
ParameterContext context;
|
||||
unsigned char id;
|
||||
std::uint8_t size;
|
||||
|
||||
constexpr Parameter(ParameterContext context, unsigned char id, std::uint8_t size)
|
||||
: context(context)
|
||||
, id(id)
|
||||
, size(size)
|
||||
{};
|
||||
};
|
||||
|
||||
struct Parameters
|
||||
{
|
||||
static constexpr Parameter USB_MAX_READ{ParameterContext::USB, 0x00, 2};
|
||||
static constexpr Parameter USB_MAX_WRITE{ParameterContext::USB, 0x01, 2};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
/**
|
||||
* The Start Session command begins a session with the tool.
|
||||
*/
|
||||
class StartSession: public HouseKeepingCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
StartSession()
|
||||
: HouseKeepingCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The payload for the Start Session command consists of two bytes. A command ID byte (0x10) and a
|
||||
* version byte (0x00).
|
||||
*/
|
||||
this->payload = {
|
||||
0x10,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,653 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp"
|
||||
|
||||
#include "Avr8Generic.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetRegister.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/Family.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/PhysicalInterface.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
/**
|
||||
* The EdbgAvr8Interface implements the AVR8 Generic EDBG/CMSIS-DAP protocol, as an Avr8DebugInterface.
|
||||
*
|
||||
* See the "AVR8 Generic 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 (such as the Atmel-ICE,
|
||||
* Power Debugger, the MPLAB SNAP debugger (in "AVR mode"), etc).
|
||||
*/
|
||||
class EdbgAvr8Interface: public ::DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgAvr8Interface(
|
||||
EdbgInterface* edbgInterface,
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig,
|
||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters,
|
||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById
|
||||
);
|
||||
|
||||
/**
|
||||
* Some EDBG devices don't seem to operate correctly when actioning the masked memory read EDBG command. The
|
||||
* data returned in response to the command appears to be completely incorrect.
|
||||
*
|
||||
* Setting this flag to true will disable the EdbgAvr8Interface driver's use of the masked memory read command.
|
||||
* The driver will perform the masking itself, and then issue standard read memory commands. See the
|
||||
* implementation of EdbgAvr8Interface::readMemory() for more.
|
||||
*
|
||||
* NOTE: Masked memory read commands are only implemented for SRAM reads. EDBG debug tools report EEPROM and
|
||||
* FLASH as invalid memory types, when using the masked memory read command. So any masked reads to non-SRAM
|
||||
* will result in driver-side masking, regardless of the value of this flag.
|
||||
*
|
||||
* NOTE: We now avoid masked memory read commands by default, unless this flag is explicitly set to false.
|
||||
* This means the default value of this->avoidMaskedMemoryRead is true.
|
||||
*
|
||||
* @param avoidMaskedMemoryRead
|
||||
*/
|
||||
void setAvoidMaskedMemoryRead(bool avoidMaskedMemoryRead) {
|
||||
this->avoidMaskedMemoryRead = avoidMaskedMemoryRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some EDBG AVR8 debug tools behave in a really weird way when servicing read memory requests that exceed a
|
||||
* certain size. For example, the ATMEGA4809-XPRO Xplained Pro debug tool returns incorrect data for any read
|
||||
* memory command that exceeds 256 bytes in the size of the read request, despite the fact that the HID report
|
||||
* size is 512 bytes. The debug tool doesn't report any error, it just returns incorrect data.
|
||||
*
|
||||
* To address this, debug tool drivers can set a soft limit on the number of bytes this EdbgAvr8Interface instance
|
||||
* will attempt to access in a single request, using the EdbgAvr8Interface::setMaximumMemoryAccessSizePerRequest()
|
||||
* member function.
|
||||
*
|
||||
* This limit will be enforced in all forms of memory access on the AVR8 target, including register access.
|
||||
* However, it will *NOT* be enforced for memory types that require page alignment, where the page size is
|
||||
* greater than the set limit. In these cases, the limit will effectively be ignored. I've tested this on
|
||||
* the ATXMEGAA1U-XPRO Xplained Pro debug tool, where the flash page size is 512 bytes (but the limit is set to
|
||||
* 256 bytes), and flash memory access works fine. This makes me suspect that this issue may be specific to the
|
||||
* ATMEGA4809-XPRO Xplained Pro debug tool.
|
||||
*
|
||||
* @param maximumSize
|
||||
*/
|
||||
void setMaximumMemoryAccessSizePerRequest(Targets::TargetMemorySize maximumSize) {
|
||||
this->maximumMemoryAccessSizePerRequest = maximumSize;
|
||||
}
|
||||
|
||||
void setReactivateJtagTargetPostProgrammingMode(bool reactivateJtagTargetPostProgrammingMode) {
|
||||
this->reactivateJtagTargetPostProgrammingMode = reactivateJtagTargetPostProgrammingMode;
|
||||
}
|
||||
|
||||
/*
|
||||
* The public methods below implement the interface defined by the Avr8Interface class.
|
||||
* See the comments in that class for more info on the expected behaviour of each method.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialises the AVR8 Generic protocol interface by setting the appropriate parameters on the debug tool.
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
/**
|
||||
* Issues the "stop" command to the debug tool, halting target execution.
|
||||
*/
|
||||
void stop() override;
|
||||
|
||||
/**
|
||||
* Issues the "run" command to the debug tool, resuming execution on the target.
|
||||
*/
|
||||
void run() override;
|
||||
|
||||
/**
|
||||
* Issues the "run to" command to the debug tool, resuming execution on the target, up to a specific byte
|
||||
* address. The target will dispatch an AVR BREAK event once it reaches the specified address.
|
||||
*
|
||||
* @param address
|
||||
* The (byte) address to run to.
|
||||
*/
|
||||
void runTo(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "step" command to the debug tool, stepping the execution on the target. The stepping can be
|
||||
* configured to step in, out or over. But currently we only support stepping in. The target will dispatch
|
||||
* an AVR BREAK event once it reaches the next instruction.
|
||||
*/
|
||||
void step() override;
|
||||
|
||||
/**
|
||||
* Issues the "reset" command to the debug tool, resetting target execution.
|
||||
*/
|
||||
void reset() override;
|
||||
|
||||
/**
|
||||
* Activates the physical interface and starts a debug session on the target (via attach()).
|
||||
*/
|
||||
void activate() override;
|
||||
|
||||
/**
|
||||
* Terminates any active debug session on the target and severs the connection between the debug tool and
|
||||
* the target (by deactivating the physical interface).
|
||||
*/
|
||||
void deactivate() override;
|
||||
|
||||
/**
|
||||
* Issues the "PC Read" command to the debug tool, to extract the current program counter.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemoryAddress getProgramCounter() override;
|
||||
|
||||
/**
|
||||
* Issues the "PC Write" command to the debug tool, setting the program counter on the target.
|
||||
*
|
||||
* @param programCounter
|
||||
* The byte address to set as the program counter.
|
||||
*/
|
||||
void setProgramCounter(Targets::TargetMemoryAddress programCounter) override;
|
||||
|
||||
/**
|
||||
* Issues the "Get ID" command to the debug tool, to extract the signature from the target.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::Microchip::Avr::TargetSignature getDeviceId() override;
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Set" command to the debug tool, setting a software breakpoint at the given
|
||||
* byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address to place the breakpoint.
|
||||
*/
|
||||
void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Clear" command to the debug tool, clearing any software breakpoint at the
|
||||
* given byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address of the breakpoint to clear.
|
||||
*/
|
||||
void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Hardware Breakpoint Set" command to the debug tool, setting a hardware breakpoint at the given
|
||||
* byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address to place the breakpoint.
|
||||
*/
|
||||
void setHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Hardware Breakpoint Clear" command to the debug tool, clearing any hardware breakpoint at the
|
||||
* given byte address.
|
||||
*
|
||||
* @param address
|
||||
* The byte address of the breakpoint to clear.
|
||||
*/
|
||||
void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) override;
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Clear All" command to the debug tool, clearing all software breakpoints
|
||||
* that were set *in the current debug session*.
|
||||
*
|
||||
* If the debug session ended before any of the set breakpoints were cleared, this will *not* clear them.
|
||||
*/
|
||||
void clearAllBreakpoints() override;
|
||||
|
||||
/**
|
||||
* Reads registers from the target.
|
||||
*
|
||||
* @param descriptorIds
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) override;
|
||||
|
||||
/**
|
||||
* Writes registers to target.
|
||||
*
|
||||
* @param registers
|
||||
*/
|
||||
void writeRegisters(const Targets::TargetRegisters& registers) override;
|
||||
|
||||
/**
|
||||
* This is an overloaded method.
|
||||
*
|
||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls readMemory().
|
||||
*
|
||||
* @param memoryType
|
||||
* @param startAddress
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemoryBuffer readMemory(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddressRange>& excludedAddressRanges = {}
|
||||
) override;
|
||||
|
||||
/**
|
||||
* This is an overloaded method.
|
||||
*
|
||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls writeMemory().
|
||||
*
|
||||
* @param memoryType
|
||||
* @param startAddress
|
||||
* @param buffer
|
||||
*/
|
||||
void writeMemory(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
const Targets::TargetMemoryBuffer& buffer
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Issues the "Erase" command to erase a particular section of program memory.
|
||||
*
|
||||
* @param section
|
||||
*/
|
||||
void eraseProgramMemory(
|
||||
std::optional<Targets::Microchip::Avr::Avr8Bit::ProgramMemorySection> section = std::nullopt
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Issues the "Erase" command to erase the entire chip.
|
||||
*/
|
||||
void eraseChip() override;
|
||||
|
||||
/**
|
||||
* Returns the current state of the target.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetState getTargetState() override;
|
||||
|
||||
/**
|
||||
* Enters programming mode on the EDBG debug tool.
|
||||
*/
|
||||
void enableProgrammingMode() override;
|
||||
|
||||
/**
|
||||
* Leaves programming mode on the EDBG debug tool.
|
||||
*/
|
||||
void disableProgrammingMode() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The AVR8 Generic 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 = nullptr;
|
||||
|
||||
/**
|
||||
* Project's AVR8 target configuration.
|
||||
*/
|
||||
const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig;
|
||||
|
||||
/**
|
||||
* The target family is taken into account when configuring the AVR8 Generic protocol on the EDBG device.
|
||||
*
|
||||
* We use this to determine which config variant to select.
|
||||
* See EdbgAvr8Interface::resolveConfigVariant() for more.
|
||||
*/
|
||||
Targets::Microchip::Avr::Avr8Bit::Family family;
|
||||
|
||||
/**
|
||||
* The AVR8 Generic protocol provides two functions: Debugging and programming. The desired function must be
|
||||
* configured via the setting of the "AVR8_CONFIG_FUNCTION" parameter.
|
||||
*/
|
||||
Avr8ConfigFunction configFunction = Avr8ConfigFunction::DEBUGGING;
|
||||
|
||||
/**
|
||||
* Configuring of the AVR8 Generic protocol depends on some characteristics of the target.
|
||||
* The "AVR8_CONFIG_VARIANT" parameter allows us to determine which target parameters are required by the
|
||||
* debug tool.
|
||||
*/
|
||||
Avr8ConfigVariant configVariant = Avr8ConfigVariant::NONE;
|
||||
|
||||
/**
|
||||
* EDBG-based debug tools require target specific parameters such as memory locations, page sizes and
|
||||
* register addresses. It is the AVR8 target's responsibility to obtain the required information and pass it
|
||||
* to the Avr8Interface. See Avr8::getTargetParameters() and Avr8::postPromotionConfigure().
|
||||
*
|
||||
* For the EdbgAvr8Interface, we send the required parameters to the debug tool immediately upon receiving
|
||||
* them. See EdbgAvr8Interface::setTargetParameters().
|
||||
*/
|
||||
const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters;
|
||||
|
||||
const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById;
|
||||
|
||||
/**
|
||||
* See the comment for EdbgAvr8Interface::setAvoidMaskedMemoryRead().
|
||||
*/
|
||||
bool avoidMaskedMemoryRead = true;
|
||||
|
||||
/**
|
||||
* See the comment for EdbgAvr8Interface::setMaximumMemoryAccessSizePerRequest().
|
||||
*/
|
||||
std::optional<Targets::TargetMemorySize> maximumMemoryAccessSizePerRequest;
|
||||
|
||||
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.
|
||||
*
|
||||
* @TODO: Review this. Is the above assumption correct? Always? Explore the option of polling the target state.
|
||||
*/
|
||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||
|
||||
/**
|
||||
* Upon configuration, the physical interface must be activated on the debug tool. We keep record of this to
|
||||
* assist in our decision to deactivate the physical interface, when deactivate() is called.
|
||||
*/
|
||||
bool physicalInterfaceActivated = false;
|
||||
|
||||
/**
|
||||
* As with activating the physical interface, we are required to issue the "attach" command to the debug
|
||||
* tool, in order to start a debug session in the target.
|
||||
*/
|
||||
bool targetAttached = false;
|
||||
|
||||
bool programmingModeEnabled = false;
|
||||
|
||||
/**
|
||||
* Every hardware breakpoint is assigned a "breakpoint number", which we need to keep track of in order to
|
||||
* clear a hardware breakpoint.
|
||||
*/
|
||||
std::map<Targets::TargetMemoryAddress, std::uint8_t> hardwareBreakpointNumbersByAddress;
|
||||
|
||||
/**
|
||||
* Sends the necessary target parameters to the debug tool.
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
void setTargetParameters();
|
||||
|
||||
/**
|
||||
* This mapping allows us to determine which config variant to select, based on the target family and the
|
||||
* selected physical interface.
|
||||
*/
|
||||
static std::map<
|
||||
Targets::Microchip::Avr::Avr8Bit::Family,
|
||||
std::map<Targets::Microchip::Avr::Avr8Bit::PhysicalInterface, Avr8ConfigVariant>
|
||||
> getConfigVariantsByFamilyAndPhysicalInterface();
|
||||
|
||||
/**
|
||||
* Determines the config variant given a target family and physical interface.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static Avr8ConfigVariant resolveConfigVariant(
|
||||
Targets::Microchip::Avr::Avr8Bit::Family targetFamily,
|
||||
Targets::Microchip::Avr::Avr8Bit::PhysicalInterface physicalInterface
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets an AVR8 parameter on the debug tool. See the Avr8EdbgParameters class and protocol documentation
|
||||
* for more on available parameters.
|
||||
*
|
||||
* @param parameter
|
||||
* @param value
|
||||
*/
|
||||
void setParameter(const Avr8EdbgParameter& parameter, const std::vector<unsigned char>& value);
|
||||
|
||||
/**
|
||||
* Overload for setting parameters with single byte values.
|
||||
*
|
||||
* @param parameter
|
||||
* @param value
|
||||
*/
|
||||
void setParameter(const Avr8EdbgParameter& parameter, std::uint8_t value) {
|
||||
this->setParameter(parameter, std::vector<unsigned char>(1, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload for setting parameters with four byte integer values.
|
||||
*
|
||||
* @param parameter
|
||||
* @param value
|
||||
*/
|
||||
void setParameter(const Avr8EdbgParameter& parameter, std::uint32_t value) {
|
||||
auto paramValue = std::vector<unsigned char>(4);
|
||||
paramValue[0] = static_cast<unsigned char>(value);
|
||||
paramValue[1] = static_cast<unsigned char>(value >> 8);
|
||||
paramValue[2] = static_cast<unsigned char>(value >> 16);
|
||||
paramValue[3] = static_cast<unsigned char>(value >> 24);
|
||||
|
||||
this->setParameter(parameter, paramValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload for setting parameters with two byte integer values.
|
||||
*
|
||||
* @param parameter
|
||||
* @param value
|
||||
*/
|
||||
void setParameter(const Avr8EdbgParameter& parameter, std::uint16_t value) {
|
||||
auto paramValue = std::vector<unsigned char>(2);
|
||||
paramValue[0] = static_cast<unsigned char>(value);
|
||||
paramValue[1] = static_cast<unsigned char>(value >> 8);
|
||||
|
||||
this->setParameter(parameter, paramValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an AV8 parameter from the debug tool.
|
||||
*
|
||||
* @param parameter
|
||||
* @param size
|
||||
* @return
|
||||
*/
|
||||
std::vector<unsigned char> getParameter(const Avr8EdbgParameter& parameter, std::uint8_t size);
|
||||
|
||||
/**
|
||||
* EDBG-based debug tools require target specific parameters such as memory locations, page sizes and
|
||||
* register addresses. These parameters can be sent to the tool before and during a session.
|
||||
*
|
||||
* 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. This is why
|
||||
* we sometimes have separate parameters for sending the same data, where they differ only in parameter IDs
|
||||
* (and sometimes size constraints). For example, the Avr8EdbgParameters::FLASH_PAGE_BYTES parameter is used
|
||||
* to specify the size of a single page in flash memory. The parameter is assigned an address (ID) of 0x00. But
|
||||
* the Avr8EdbgParameters::DEVICE_XMEGA_FLASH_PAGE_BYTES parameter is used to send the same data (flash page
|
||||
* size), but only for sessions with the PDI physical interface. The address is 0x26.
|
||||
*
|
||||
* - 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.
|
||||
*/
|
||||
void setDebugWireAndJtagParameters();
|
||||
void setPdiParameters();
|
||||
void setUpdiParameters();
|
||||
|
||||
/**
|
||||
* Sends the "Activate Physical" command to the debug tool, activating the physical interface and thus enabling
|
||||
* communication between the debug tool and the target.
|
||||
*
|
||||
* @param applyExternalReset
|
||||
*/
|
||||
void activatePhysical(bool applyExternalReset = false);
|
||||
|
||||
/**
|
||||
* Sends the "Deactivate Physical" command to the debug tool, which will result in the connection between the
|
||||
* debug tool and the target being severed.
|
||||
*/
|
||||
void deactivatePhysical();
|
||||
|
||||
/**
|
||||
* Sends the "Attach" command to the debug tool, which starts a debug session on the target.
|
||||
*/
|
||||
void attach();
|
||||
|
||||
/**
|
||||
* Sends the "Detach" command to the debug tool, which terminates any active debug session on the target.
|
||||
*/
|
||||
void detach();
|
||||
|
||||
/**
|
||||
* Fetches any queued events belonging to the AVR8 Generic protocol (such as target break events).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::unique_ptr<AvrEvent> getAvrEvent();
|
||||
|
||||
/**
|
||||
* Clears (discards) any queued AVR8 Generic protocol events on the debug tool.
|
||||
*/
|
||||
void clearEvents();
|
||||
|
||||
/**
|
||||
* Checks if alignment is required for memory access via a given Avr8MemoryType.
|
||||
*
|
||||
* @param memoryType
|
||||
* @return
|
||||
*/
|
||||
bool alignmentRequired(Avr8MemoryType memoryType);
|
||||
|
||||
/**
|
||||
* Aligns a memory address for a given memory type's page size.
|
||||
*
|
||||
* @param memoryType
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemoryAddress alignMemoryAddress(Avr8MemoryType memoryType, Targets::TargetMemoryAddress address);
|
||||
|
||||
/**
|
||||
* Aligns a number of bytes used to address memory, for a given memory type's page size.
|
||||
*
|
||||
* @param memoryType
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemorySize alignMemoryBytes(Avr8MemoryType memoryType, Targets::TargetMemorySize bytes);
|
||||
|
||||
/**
|
||||
* Checks if a maximum memory access size is imposed for a 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);
|
||||
|
||||
/**
|
||||
* Reads memory on the target.
|
||||
*
|
||||
* This method will handle any alignment requirements for the selected memory type.
|
||||
*
|
||||
* See the Avr8MemoryType enum for list of supported AVR8 memory types.
|
||||
*
|
||||
* @param type
|
||||
* The type of memory to access (Flash, EEPROM, SRAM, etc). See protocol documentation for more on this.
|
||||
*
|
||||
* @param startAddress
|
||||
* The start address (byte address)
|
||||
*
|
||||
* @param bytes
|
||||
* Number of bytes to access.
|
||||
*
|
||||
* @param excludedAddresses
|
||||
* A set of addresses to exclude from the read operation. This is used to read memory ranges that could
|
||||
* involve accessing an illegal address, like the OCDDR address.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemoryBuffer readMemory(
|
||||
Avr8MemoryType type,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemorySize bytes,
|
||||
const std::set<Targets::TargetMemoryAddress>& excludedAddresses = {}
|
||||
);
|
||||
|
||||
/**
|
||||
* Writes memory to the target.
|
||||
*
|
||||
* This method will handle any alignment requirements for the selected memory type.
|
||||
*
|
||||
* See the Avr8MemoryType enum for list of supported AVR8 memory types.
|
||||
*
|
||||
* @param type
|
||||
* @param address
|
||||
* @param buffer
|
||||
*/
|
||||
void writeMemory(Avr8MemoryType type, Targets::TargetMemoryAddress address, const Targets::TargetMemoryBuffer& buffer);
|
||||
|
||||
/**
|
||||
* Fetches the current target state.
|
||||
*
|
||||
* This currently uses AVR BREAK events to determine if a target has stopped. The lack of any
|
||||
* queued BREAK events leads to the assumption that the target is still running.
|
||||
*/
|
||||
void refreshTargetState();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Waits for an AVR event of a specific type.
|
||||
*
|
||||
* @tparam AvrEventType
|
||||
* Type of AVR event to wait for. See AvrEvent class for more.
|
||||
*
|
||||
* @param maximumAttempts
|
||||
* Maximum number of attempts to poll the debug tool for the expected event.
|
||||
*
|
||||
* @return
|
||||
* If an event is found before maximumAttempts is reached, the event will be returned. Otherwise a nullptr
|
||||
* will be returned.
|
||||
*/
|
||||
template <class AvrEventType>
|
||||
std::unique_ptr<AvrEventType> waitForAvrEvent(int maximumAttempts = 20) {
|
||||
int attemptCount = 0;
|
||||
|
||||
while (attemptCount <= maximumAttempts) {
|
||||
auto genericEvent = this->getAvrEvent();
|
||||
|
||||
if (genericEvent != nullptr) {
|
||||
// Attempt to downcast event
|
||||
auto event = std::unique_ptr<AvrEventType>(dynamic_cast<AvrEventType*>(genericEvent.release()));
|
||||
|
||||
if (event != nullptr) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
attemptCount++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an AVR BREAK event.
|
||||
*
|
||||
* This simply wraps a call to waitForAvrEvent<BreakEvent>(). An exception will be thrown if the call doesn't
|
||||
* return a BreakEvent.
|
||||
*
|
||||
* This should only be used when a BreakEvent is always expected.
|
||||
*/
|
||||
void waitForStoppedEvent();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "EdbgAvrIspInterface.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
// Command frames
|
||||
#include "CommandFrames/AVRISP/EnterProgrammingMode.hpp"
|
||||
#include "CommandFrames/AVRISP/LeaveProgrammingMode.hpp"
|
||||
#include "CommandFrames/AVRISP/ReadSignature.hpp"
|
||||
#include "CommandFrames/AVRISP/ReadFuse.hpp"
|
||||
#include "CommandFrames/AVRISP/ReadLock.hpp"
|
||||
#include "CommandFrames/AVRISP/ProgramFuse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::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;
|
||||
|
||||
EdbgAvrIspInterface::EdbgAvrIspInterface(EdbgInterface* edbgInterface)
|
||||
: edbgInterface(edbgInterface)
|
||||
{}
|
||||
|
||||
void EdbgAvrIspInterface::setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) {
|
||||
this->ispParameters = ispParameters;
|
||||
}
|
||||
|
||||
void EdbgAvrIspInterface::activate() {
|
||||
const auto responseFrame = 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 (responseFrame.statusCode != 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() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
LeaveProgrammingMode(
|
||||
this->ispParameters.programModePreDelay,
|
||||
this->ispParameters.programModePostDelay
|
||||
)
|
||||
);
|
||||
|
||||
if (responseFrame.statusCode != 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) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadFuse(fuseType, this->ispParameters.readFusePollIndex)
|
||||
);
|
||||
|
||||
if (
|
||||
responseFrame.statusCode != StatusCode::OK
|
||||
|| responseFrame.payload.size() < 4
|
||||
|| static_cast<StatusCode>(responseFrame.payload[3]) != StatusCode::OK
|
||||
) {
|
||||
throw TargetOperationFailure(
|
||||
"Failed to read fuse via ISP - response frame status code/size indicates a failure."
|
||||
);
|
||||
}
|
||||
|
||||
return Fuse(fuseType, responseFrame.payload[2]);
|
||||
}
|
||||
|
||||
unsigned char EdbgAvrIspInterface::readLockBitByte() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadLock(this->ispParameters.readLockPollIndex)
|
||||
);
|
||||
|
||||
if (
|
||||
responseFrame.statusCode != StatusCode::OK
|
||||
|| responseFrame.payload.size() < 4
|
||||
|| static_cast<StatusCode>(responseFrame.payload[3]) != StatusCode::OK
|
||||
) {
|
||||
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) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ProgramFuse(fuse.type, fuse.value)
|
||||
);
|
||||
|
||||
if (responseFrame.statusCode != StatusCode::OK || responseFrame.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) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadSignature(signatureByteAddress, this->ispParameters.readSignaturePollIndex)
|
||||
);
|
||||
|
||||
if (
|
||||
responseFrame.statusCode != StatusCode::OK
|
||||
|| responseFrame.payload.size() < 4
|
||||
|| static_cast<StatusCode>(responseFrame.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 responseFrame.payload[2];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "BreakEvent.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
using Targets::TargetBreakCause;
|
||||
|
||||
BreakEvent::BreakEvent(const AvrEvent& event)
|
||||
: AvrEvent(event)
|
||||
{
|
||||
if (this->eventData.size() < 8) {
|
||||
/*
|
||||
* All BreakEvent packets must consist of at least 8 bytes:
|
||||
* 1 byte for event ID
|
||||
* 4 bytes for program counter
|
||||
* 1 byte for break cause
|
||||
* 2 bytes for extended info
|
||||
*/
|
||||
throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size.");
|
||||
}
|
||||
|
||||
// Program counter consists of 4 bytes
|
||||
this->programCounter = static_cast<std::uint32_t>(
|
||||
(this->eventData[4] << 24) | (this->eventData[3] << 16) | (this->eventData[2] << 8) | this->eventData[1]
|
||||
) * 2;
|
||||
|
||||
// Break cause is 1 byte, where 0x01 is 'program breakpoint' and 0x00 'unspecified'
|
||||
this->breakCause = this->eventData[7] == 0x01 ? TargetBreakCause::BREAKPOINT : TargetBreakCause::UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrEvent.hpp"
|
||||
#include "src/Targets/TargetBreakpoint.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class BreakEvent: public AvrEvent
|
||||
{
|
||||
public:
|
||||
std::uint32_t programCounter;
|
||||
Targets::TargetBreakCause breakCause;
|
||||
|
||||
explicit BreakEvent(const AvrEvent& event);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/Avr8GenericResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
enum class Avr8CommandFailureCode: std::uint8_t
|
||||
{
|
||||
DEBUGWIRE_PHYSICAL_ERROR = 0x10,
|
||||
JTAGM_FAILED_TO_INITIALISE = 0x11,
|
||||
UNKNOWN_JTAG_ERROR = 0x12,
|
||||
JTAG_LOW_LEVEL_ERROR = 0x13,
|
||||
UNSUPPORTED_JTAGM_VERSION = 0x14,
|
||||
JTAG_MASTER_TIMED_OUT = 0x15,
|
||||
JTAG_BIT_BANGER_TIMED_OUT = 0x16,
|
||||
PARITY_ERROR_IN_RECEIVED_DATA = 0x17,
|
||||
MISSING_EMPTY_BYTE = 0x18,
|
||||
PDI_PHYSICAL_TIMED_OUT = 0x19,
|
||||
PHYSICAL_LEVEL_COLLISION = 0x1A,
|
||||
PDI_ENABLE_FAILED = 0x1B,
|
||||
TARGET_NOT_FOUND = 0x20,
|
||||
BAUD_INCREASE_FAILURE = 0x21,
|
||||
TARGET_POWER_NOT_DETECTED = 0x22,
|
||||
MUST_RUN_ATTACH_COMMAND_FIRST = 0x23,
|
||||
DEVICES_EXCEED_31 = 0x24,
|
||||
CONFIGURED_DEVICE_BITS_DO_NOT_ADD_UP_TO_DETECTED_BITS = 0x25,
|
||||
PHYSICAL_NOT_ACTIVATED = 0x31,
|
||||
ILLEGAL_RUN_OR_STOPPED_STATE = 0x32,
|
||||
INVALID_CONFIG_FOR_ACTIVATE_PHYSICAL = 0x33,
|
||||
NOT_A_VALID_MEMTYPE = 0x34,
|
||||
TOO_MANY_OR_TOO_FEW_BYTES = 0x35,
|
||||
ASKED_FOR_A_BAD_ADDRESS = 0x36,
|
||||
ASKED_FOR_BADLY_ALIGNED_DATA = 0x37,
|
||||
ADDRESS_NOT_WITHIN_LEGAL_RANGE = 0x38,
|
||||
ILLEGAL_VALUE_GIVEN = 0x39,
|
||||
ILLEGAL_TARGET_ID = 0x3A,
|
||||
CLOCK_VALUE_OUT_OF_RANGE = 0x3B,
|
||||
TIMEOUT_OCCURRED = 0x3C,
|
||||
READ_AN_ILLEGAL_OCD_STATUS = 0x3D,
|
||||
NVM_FAILED_TO_BE_ENABLED = 0x40,
|
||||
NVM_FAILED_TO_BE_DISABLED = 0x41,
|
||||
ILLEGAL_CONTROL_OR_STATUS_BITS = 0x42,
|
||||
CRC_MISMATCH = 0x43,
|
||||
FAILED_TO_ENABLE_OCD = 0x44,
|
||||
DEVICE_NOT_UNDER_CONTROL = 0x50,
|
||||
ERROR_WHEN_READING_PC = 0x60,
|
||||
ERROR_WHEN_READING_REGISTER = 0x61,
|
||||
ERROR_WHILE_READING = 0x70,
|
||||
ERROR_WHILE_WRITING = 0x71,
|
||||
TIMEOUT_WHILE_READING = 0x72,
|
||||
INVALID_BREAKPOINT_CONFIGURATION = 0x80,
|
||||
NOT_ENOUGH_AVAILABLE_RESOURCES = 0x81,
|
||||
FEATURE_NOT_AVAILABLE = 0x90,
|
||||
UNKNOWN_COMMAND = 0x91,
|
||||
UNKNOWN_ERROR = 0xFF,
|
||||
};
|
||||
|
||||
class Avr8CommandFailure: public Exceptions::TargetOperationFailure
|
||||
{
|
||||
public:
|
||||
std::optional<Avr8CommandFailureCode> code;
|
||||
|
||||
explicit Avr8CommandFailure(const std::string& message): TargetOperationFailure(message) {
|
||||
this->message = message;
|
||||
}
|
||||
|
||||
explicit Avr8CommandFailure(const char* message): TargetOperationFailure(message) {
|
||||
this->message = std::string(message);
|
||||
}
|
||||
|
||||
explicit Avr8CommandFailure(
|
||||
const std::string& message,
|
||||
const ResponseFrames::Avr8Generic::Avr8GenericResponseFrame& responseFrame
|
||||
)
|
||||
: TargetOperationFailure(message)
|
||||
{
|
||||
this->message = message;
|
||||
|
||||
if (responseFrame.payload.size() == 3) {
|
||||
/*
|
||||
* The response includes a failure code - lookup the corresponding description and append it to the
|
||||
* exception message.
|
||||
*/
|
||||
const auto failureCode = static_cast<Avr8CommandFailureCode>(responseFrame.payload[2]);
|
||||
const auto failureCodeDescriptionIt = this->failureCodeToDescription.find(failureCode);
|
||||
|
||||
if (failureCodeDescriptionIt != this->failureCodeToDescription.end()) {
|
||||
this->code = failureCode;
|
||||
this->message += " - Failure reason: " + failureCodeDescriptionIt->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
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"},
|
||||
{Avr8CommandFailureCode::JTAG_LOW_LEVEL_ERROR, "JTAG low level error"},
|
||||
{Avr8CommandFailureCode::UNSUPPORTED_JTAGM_VERSION, "Unsupported version of JTAGM"},
|
||||
{Avr8CommandFailureCode::JTAG_MASTER_TIMED_OUT, "JTAG master timed out"},
|
||||
{Avr8CommandFailureCode::JTAG_BIT_BANGER_TIMED_OUT, "JTAG bit banger timed out"},
|
||||
{Avr8CommandFailureCode::PARITY_ERROR_IN_RECEIVED_DATA, "Parity error in received data"},
|
||||
{Avr8CommandFailureCode::MISSING_EMPTY_BYTE, "Did not receive EMPTY byte"},
|
||||
{Avr8CommandFailureCode::PDI_PHYSICAL_TIMED_OUT, "PDI physical timed out"},
|
||||
{Avr8CommandFailureCode::PHYSICAL_LEVEL_COLLISION, "Collision on physical level"},
|
||||
{Avr8CommandFailureCode::PDI_ENABLE_FAILED, "PDI enable failed"},
|
||||
{Avr8CommandFailureCode::TARGET_NOT_FOUND, "Target not found"},
|
||||
{Avr8CommandFailureCode::BAUD_INCREASE_FAILURE, "Failure when increasing baud"},
|
||||
{Avr8CommandFailureCode::TARGET_POWER_NOT_DETECTED, "Target power not detected"},
|
||||
{Avr8CommandFailureCode::MUST_RUN_ATTACH_COMMAND_FIRST, "Must run attach command first"},
|
||||
{Avr8CommandFailureCode::DEVICES_EXCEED_31, "Devices > 31"},
|
||||
{Avr8CommandFailureCode::CONFIGURED_DEVICE_BITS_DO_NOT_ADD_UP_TO_DETECTED_BITS, "Configured device bits do not add up to detected bits"},
|
||||
{Avr8CommandFailureCode::PHYSICAL_NOT_ACTIVATED, "Physical not activated"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_RUN_OR_STOPPED_STATE, "Illegal run / stopped state"},
|
||||
{Avr8CommandFailureCode::INVALID_CONFIG_FOR_ACTIVATE_PHYSICAL, "Invalid config for activate physical"},
|
||||
{Avr8CommandFailureCode::NOT_A_VALID_MEMTYPE, "Not a valid memtype"},
|
||||
{Avr8CommandFailureCode::TOO_MANY_OR_TOO_FEW_BYTES, "Too many or too few bytes"},
|
||||
{Avr8CommandFailureCode::ASKED_FOR_A_BAD_ADDRESS, "Asked for a bad address"},
|
||||
{Avr8CommandFailureCode::ASKED_FOR_BADLY_ALIGNED_DATA, "Asked for badly aligned data"},
|
||||
{Avr8CommandFailureCode::ADDRESS_NOT_WITHIN_LEGAL_RANGE, "Address not within legal range"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_VALUE_GIVEN, "Illegal value given"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_TARGET_ID, "Illegal target ID"},
|
||||
{Avr8CommandFailureCode::CLOCK_VALUE_OUT_OF_RANGE, "Clock value out of range"},
|
||||
{Avr8CommandFailureCode::TIMEOUT_OCCURRED, "A timeout occurred"},
|
||||
{Avr8CommandFailureCode::READ_AN_ILLEGAL_OCD_STATUS, "Read an illegal OCD status - check OCDEN fuse bit (if applicable)"},
|
||||
{Avr8CommandFailureCode::NVM_FAILED_TO_BE_ENABLED, "NVM failed to be enabled"},
|
||||
{Avr8CommandFailureCode::NVM_FAILED_TO_BE_DISABLED, "NVM failed to be disabled"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_CONTROL_OR_STATUS_BITS, "Illegal control/status bits"},
|
||||
{Avr8CommandFailureCode::CRC_MISMATCH, "CRC mismatch"},
|
||||
{Avr8CommandFailureCode::FAILED_TO_ENABLE_OCD, "Failed to enable OCD"},
|
||||
{Avr8CommandFailureCode::DEVICE_NOT_UNDER_CONTROL, "Device not under control - check OCDEN fuse bit (if applicable)"},
|
||||
{Avr8CommandFailureCode::ERROR_WHEN_READING_PC, "Error when reading PC"},
|
||||
{Avr8CommandFailureCode::ERROR_WHEN_READING_REGISTER, "Error when reading register"},
|
||||
{Avr8CommandFailureCode::ERROR_WHILE_READING, "Error while reading"},
|
||||
{Avr8CommandFailureCode::ERROR_WHILE_WRITING, "Error while writing"},
|
||||
{Avr8CommandFailureCode::TIMEOUT_WHILE_READING, "Timeout while reading"},
|
||||
{Avr8CommandFailureCode::INVALID_BREAKPOINT_CONFIGURATION, "Invalid breakpoint configuration"},
|
||||
{Avr8CommandFailureCode::NOT_ENOUGH_AVAILABLE_RESOURCES, "Not enough available resources"},
|
||||
{Avr8CommandFailureCode::FEATURE_NOT_AVAILABLE, "Feature not available"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_COMMAND, "Command has not been implemented"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_ERROR, "Unknown error reported by EDBG device"},
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
Avr8GenericResponseFrame::Avr8GenericResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from AVR8 Generic response frame payload.");
|
||||
}
|
||||
|
||||
this->id = static_cast<Avr8ResponseId>(this->payload[0]);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Avr8GenericResponseFrame::getPayloadData() const {
|
||||
/*
|
||||
* 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
|
||||
);
|
||||
|
||||
std::reverse(data.begin(), data.end());
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class Avr8GenericResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
Avr8ResponseId id;
|
||||
|
||||
explicit Avr8GenericResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
[[nodiscard]] std::vector<unsigned char> getPayloadData() const;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "GetDeviceId.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
GetDeviceId::GetDeviceId(const std::vector<AvrResponse>& AvrResponses)
|
||||
: Avr8GenericResponseFrame(AvrResponses)
|
||||
{}
|
||||
|
||||
Targets::Microchip::Avr::TargetSignature GetDeviceId::extractSignature(
|
||||
Targets::Microchip::Avr::Avr8Bit::PhysicalInterface physicalInterface
|
||||
) const {
|
||||
using Targets::Microchip::Avr::Avr8Bit::PhysicalInterface;
|
||||
const auto payloadData = this->getPayloadData();
|
||||
|
||||
switch (physicalInterface) {
|
||||
case PhysicalInterface::DEBUG_WIRE: {
|
||||
/*
|
||||
* 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]);
|
||||
}
|
||||
case PhysicalInterface::PDI:
|
||||
case PhysicalInterface::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]);
|
||||
}
|
||||
case PhysicalInterface::JTAG: {
|
||||
/*
|
||||
* When using the JTAG interface, the get device ID command returns a 32 bit JTAG ID. This takes
|
||||
* the following form:
|
||||
*
|
||||
* VVVV PPPPPPPPPPPPPPPP MMMMMMMMMMM L
|
||||
*
|
||||
* - (V) - Version nibble (4 bits)
|
||||
* - (P) - Part number (this is typically the AVR signature excluding the manufacture
|
||||
* byte (0x1E)) (16 bits)
|
||||
* - (M) - Manufacture identity (11 bits)
|
||||
* - (L) - LSB indicator (1 bit)
|
||||
*
|
||||
* We convert this into a Avr::TargetSignature by extracting the relevant part number and assuming
|
||||
* a manufacture byte of 0x1E.
|
||||
*
|
||||
* TODO: We're not considering the value of the LSB indicator bit. We're just assuming it will
|
||||
* always be MSB. Is this assumption correct?
|
||||
*/
|
||||
const auto jtagId = static_cast<std::uint32_t>(
|
||||
(payloadData[0] << 24) | (payloadData[1] << 16) | (payloadData[2] << 8) | (payloadData[3])
|
||||
);
|
||||
|
||||
return Targets::Microchip::Avr::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]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/AVR/TargetSignature.hpp"
|
||||
#include "src/Targets/Microchip/AVR/AVR8/PhysicalInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class GetDeviceId: public Avr8GenericResponseFrame
|
||||
{
|
||||
public:
|
||||
explicit GetDeviceId(const std::vector<AvrResponse>& AvrResponses);
|
||||
|
||||
Targets::Microchip::Avr::TargetSignature extractSignature(
|
||||
Targets::Microchip::Avr::Avr8Bit::PhysicalInterface physicalInterface
|
||||
) const;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class GetProgramCounter: public Avr8GenericResponseFrame
|
||||
{
|
||||
public:
|
||||
explicit GetProgramCounter(const std::vector<AvrResponse>& avrResponses)
|
||||
: Avr8GenericResponseFrame(avrResponses)
|
||||
{}
|
||||
|
||||
Targets::TargetMemoryAddress extractProgramCounter() const {
|
||||
/*
|
||||
* The payload for the PC Read command should always consist of six bytes. Thr first two being the
|
||||
* 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.");
|
||||
}
|
||||
|
||||
return static_cast<Targets::TargetMemoryAddress>(
|
||||
this->payload[5] << 24 | this->payload[4] << 16 | this->payload[3] << 8 | this->payload[2]
|
||||
) * 2;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class ReadMemory: public Avr8GenericResponseFrame
|
||||
{
|
||||
public:
|
||||
explicit ReadMemory(const std::vector<AvrResponse>& avrResponses)
|
||||
: Avr8GenericResponseFrame(avrResponses)
|
||||
{}
|
||||
|
||||
Targets::TargetMemoryBuffer getMemoryData() const {
|
||||
/*
|
||||
* 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
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "AvrIspResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::AvrIsp
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
AvrIspResponseFrame::AvrIspResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.size() < 2) {
|
||||
throw Exception("Status code missing from AVRISP response frame payload.");
|
||||
}
|
||||
|
||||
this->statusCode = static_cast<StatusCode>(this->payload[1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::AvrIsp
|
||||
{
|
||||
enum class StatusCode: unsigned char
|
||||
{
|
||||
OK = 0x00,
|
||||
TIMEOUT = 0x80,
|
||||
FAILED = 0xC0,
|
||||
};
|
||||
|
||||
class AvrIspResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
StatusCode statusCode;
|
||||
|
||||
explicit AvrIspResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "AvrResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
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>();
|
||||
|
||||
for (const auto& avrResponse : avrResponses) {
|
||||
rawFrame.insert(rawFrame.end(), avrResponse.responsePacket.begin(), avrResponse.responsePacket.end());
|
||||
}
|
||||
|
||||
return this->initFromRawFrame(rawFrame);
|
||||
}
|
||||
|
||||
void AvrResponseFrame::initFromRawFrame(const std::vector<unsigned char>& rawFrame) {
|
||||
if (rawFrame.size() < 4) {
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
if (rawFrame[0] != 0x0E) {
|
||||
// Invalid SOF byte value
|
||||
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]);
|
||||
this->protocolHandlerId = static_cast<ProtocolHandlerId>(rawFrame[3]);
|
||||
|
||||
this->payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/Edbg.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrResponse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Incrementing from 0x00
|
||||
*/
|
||||
std::uint16_t sequenceId = 0;
|
||||
|
||||
/**
|
||||
* Destination sub-protocol handler ID
|
||||
*/
|
||||
ProtocolHandlerId protocolHandlerId = ProtocolHandlerId::AVR8_GENERIC;
|
||||
|
||||
std::vector<unsigned char> payload;
|
||||
|
||||
explicit AvrResponseFrame(const std::vector<AvrResponse>& avrResponses) {
|
||||
this->initFromAvrResponses(avrResponses);
|
||||
}
|
||||
|
||||
virtual ~AvrResponseFrame() = default;
|
||||
|
||||
AvrResponseFrame(const AvrResponseFrame& other) = default;
|
||||
AvrResponseFrame(AvrResponseFrame&& other) = default;
|
||||
|
||||
AvrResponseFrame& operator = (const AvrResponseFrame& other) = default;
|
||||
AvrResponseFrame& operator = (AvrResponseFrame&& other) = default;
|
||||
|
||||
/**
|
||||
* An AvrResponse contains a single fragment of an AvrResponseFrame.
|
||||
*
|
||||
* This method will construct an AvrResponseFrame from a vector of AvrResponse.
|
||||
*
|
||||
* @param avrResponses
|
||||
*/
|
||||
void initFromAvrResponses(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
private:
|
||||
virtual void initFromRawFrame(const std::vector<unsigned char>& rawFrame);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "DiscoveryResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Discovery
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
DiscoveryResponseFrame::DiscoveryResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from DISCOVERY response frame payload.");
|
||||
}
|
||||
|
||||
this->id = static_cast<ResponseId>(payload[0]);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> DiscoveryResponseFrame::getPayloadData() const {
|
||||
if (this->payload.size() <= 2) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Discovery
|
||||
{
|
||||
/**
|
||||
* Discovery commands can only return two responses; A LIST response and a failure.
|
||||
*/
|
||||
enum class ResponseId: unsigned char
|
||||
{
|
||||
/*
|
||||
* According to the protocol docs, response ID 0x81 is for a LIST response, but this doesn't seem to be
|
||||
* well-defined. So just going to use a generic name.
|
||||
*/
|
||||
OK = 0x81,
|
||||
FAILED = 0xA0,
|
||||
};
|
||||
|
||||
class DiscoveryResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
ResponseId id;
|
||||
|
||||
explicit DiscoveryResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
std::vector<unsigned char> getPayloadData() const;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "EdbgControlResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::EdbgControl
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
EdbgControlResponseFrame::EdbgControlResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from EDBG Control response frame payload.");
|
||||
}
|
||||
|
||||
this->id = static_cast<EdbgControlResponseId>(this->payload[0]);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> EdbgControlResponseFrame::getPayloadData() {
|
||||
if (this->payload.size() <= 3) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::EdbgControl
|
||||
{
|
||||
enum class EdbgControlResponseId: unsigned char
|
||||
{
|
||||
OK = 0x80,
|
||||
DATA = 0x84,
|
||||
FAILED = 0xA0,
|
||||
};
|
||||
|
||||
class EdbgControlResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
EdbgControlResponseId id;
|
||||
|
||||
explicit EdbgControlResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
[[nodiscard]] std::vector<unsigned char> getPayloadData();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "HouseKeepingResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::HouseKeeping
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
HouseKeepingResponseFrame::HouseKeepingResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception("Response ID missing from HOUSEKEEPING response frame payload.");
|
||||
}
|
||||
|
||||
this->id = static_cast<ResponseId>(this->payload[0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::HouseKeeping
|
||||
{
|
||||
enum class ResponseId: unsigned char
|
||||
{
|
||||
OK = 0x80,
|
||||
LIST = 0x81,
|
||||
DATA = 0x84,
|
||||
FAILED = 0xA0,
|
||||
FAILED_WITH_DATA = 0xA1,
|
||||
};
|
||||
|
||||
class HouseKeepingResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
ResponseId id;
|
||||
|
||||
explicit HouseKeepingResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
};
|
||||
}
|
||||
35
src/DebugToolDrivers/Microchip/Protocols/EDBG/Edbg.hpp
Normal file
35
src/DebugToolDrivers/Microchip/Protocols/EDBG/Edbg.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
{
|
||||
enum class ProtocolHandlerId: unsigned char
|
||||
{
|
||||
DISCOVERY = 0x00,
|
||||
HOUSE_KEEPING = 0x01,
|
||||
AVRISP = 0x11,
|
||||
AVR8_GENERIC = 0x12,
|
||||
AVR32_GENERIC = 0x13,
|
||||
EDBG_CONTROL = 0x20,
|
||||
};
|
||||
|
||||
struct EdbgParameter
|
||||
{
|
||||
unsigned char context;
|
||||
unsigned char id;
|
||||
std::uint8_t size;
|
||||
|
||||
constexpr EdbgParameter(unsigned char context, unsigned char id, std::uint8_t size)
|
||||
: context(context)
|
||||
, id(id)
|
||||
, size(size)
|
||||
{};
|
||||
};
|
||||
|
||||
struct EdbgParameters
|
||||
{
|
||||
static constexpr EdbgParameter CONTROL_LED_USAGE {0x00, 0x00, 1};
|
||||
static constexpr EdbgParameter CONTROL_TARGET_POWER {0x00, 0x10, 1};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "EdbgInterface.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
EdbgInterface::EdbgInterface(Usb::HidInterface&& cmsisHidInterface)
|
||||
: CmsisDapInterface(std::move(cmsisHidInterface))
|
||||
{}
|
||||
|
||||
::DebugToolDrivers::Protocols::CmsisDap::Response EdbgInterface::sendAvrCommandsAndWaitForResponse(
|
||||
const std::vector<Avr::AvrCommand>& avrCommands
|
||||
) {
|
||||
for (const auto& avrCommand : avrCommands) {
|
||||
// Send command to device
|
||||
auto response = this->sendCommandAndWaitForResponse(avrCommand);
|
||||
|
||||
if (&avrCommand == &avrCommands.back()) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
// This should never happen
|
||||
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());
|
||||
|
||||
if (avrEventResponse.id != 0x82) {
|
||||
throw DeviceCommunicationFailure("Unexpected response to AvrEventCommand from device");
|
||||
}
|
||||
|
||||
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 avrResponse = this->sendCommandAndWaitForResponse(responseCommand);
|
||||
responses.push_back(avrResponse);
|
||||
const auto fragmentCount = avrResponse.fragmentCount;
|
||||
|
||||
while (responses.size() < fragmentCount) {
|
||||
// There are more response packets
|
||||
auto avrResponse = this->sendCommandAndWaitForResponse(responseCommand);
|
||||
|
||||
if (avrResponse.fragmentCount != fragmentCount) {
|
||||
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"
|
||||
);
|
||||
}
|
||||
|
||||
if (avrResponse.fragmentNumber == 0) {
|
||||
// End of response data ( &this packet can be ignored)
|
||||
break;
|
||||
}
|
||||
|
||||
responses.push_back(avrResponse);
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp"
|
||||
|
||||
#include "AVR/AvrCommand.hpp"
|
||||
#include "AVR/AvrResponse.hpp"
|
||||
#include "AVR/AvrEventCommand.hpp"
|
||||
#include "AVR/AvrEvent.hpp"
|
||||
#include "AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "AVR/AvrResponseCommand.hpp"
|
||||
#include "AVR/ResponseFrames/AvrResponseFrame.hpp"
|
||||
#include "AVR/CommandFrames/AvrCommandFrame.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
explicit EdbgInterface(Usb::HidInterface&& cmsisHidInterface);
|
||||
|
||||
/**
|
||||
* Send an AvrCommandFrame to the debug tool and wait for a response.
|
||||
*
|
||||
* NOTE: The response this method waits for is *not* an AvrResponseFrame, but rather, just a response from
|
||||
* the debug tool indicating successful receipt of the AvrCommandFrame.
|
||||
* See EdbgInterface::sendAvrCommandFrameAndWaitForResponseFrame().
|
||||
*
|
||||
* @param avrCommandFrame
|
||||
* @return
|
||||
*/
|
||||
template <class PayloadContainerType>
|
||||
::DebugToolDrivers::Protocols::CmsisDap::Response sendAvrCommandFrameAndWaitForResponse(
|
||||
const Avr::AvrCommandFrame<PayloadContainerType>& avrCommandFrame
|
||||
) {
|
||||
/*
|
||||
* An AVR command frame can be split into multiple CMSIS-DAP commands. Each command containing a fragment
|
||||
* of the AvrCommandFrame.
|
||||
*/
|
||||
return this->sendAvrCommandsAndWaitForResponse(avrCommandFrame.generateAvrCommands(
|
||||
this->getUsbHidInputReportSize() - 4 // Minus 4 to accommodate AVR command bytes
|
||||
));
|
||||
}
|
||||
|
||||
virtual ::DebugToolDrivers::Protocols::CmsisDap::Response sendAvrCommandsAndWaitForResponse(
|
||||
const std::vector<Avr::AvrCommand>& avrCommands
|
||||
);
|
||||
|
||||
template<class CommandFrameType>
|
||||
typename CommandFrameType::ExpectedResponseFrameType sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
const CommandFrameType& avrCommandFrame
|
||||
) {
|
||||
static_assert(
|
||||
std::is_base_of<
|
||||
Avr::AvrResponseFrame,
|
||||
typename CommandFrameType::ExpectedResponseFrameType
|
||||
>::value,
|
||||
"AVR Command must specify a valid response frame type, derived from AvrResponseFrame."
|
||||
);
|
||||
|
||||
const auto response = this->sendAvrCommandFrameAndWaitForResponse(avrCommandFrame);
|
||||
|
||||
if (response.data[0] != 0x01) {
|
||||
// The last response packet should always acknowledge receipt of the AvrCommandFrame
|
||||
throw Exceptions::DeviceCommunicationFailure(
|
||||
"Failed to send AvrCommandFrame to device - device did not acknowledge receipt."
|
||||
);
|
||||
}
|
||||
|
||||
return typename CommandFrameType::ExpectedResponseFrameType(this->requestAvrResponses());
|
||||
}
|
||||
|
||||
virtual std::optional<Avr::AvrEvent> requestAvrEvent();
|
||||
|
||||
private:
|
||||
virtual std::vector<Avr::AvrResponse> requestAvrResponses();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "EdbgTargetPowerManagementInterface.hpp"
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/EDBGControl/GetParameter.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/EDBGControl/SetParameter.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
using Microchip::Protocols::Edbg::Avr::ResponseFrames::EdbgControl::EdbgControlResponseId;
|
||||
|
||||
using Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl::GetParameter;
|
||||
using Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl::SetParameter;
|
||||
|
||||
EdbgTargetPowerManagementInterface::EdbgTargetPowerManagementInterface(EdbgInterface* edbgInterface)
|
||||
: edbgInterface(edbgInterface)
|
||||
{}
|
||||
|
||||
void EdbgTargetPowerManagementInterface::enableTargetPower() {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
SetParameter(EdbgParameters::CONTROL_TARGET_POWER, 0x01)
|
||||
);
|
||||
|
||||
if (responseFrame.id == EdbgControlResponseId::FAILED) {
|
||||
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)
|
||||
);
|
||||
|
||||
if (responseFrame.id == EdbgControlResponseId::FAILED) {
|
||||
throw Exception("Failed to disable target power via EDBG Control protocol");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/TargetPowerManagementInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg
|
||||
{
|
||||
class EdbgTargetPowerManagementInterface: public TargetInterfaces::TargetPowerManagementInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgTargetPowerManagementInterface(EdbgInterface* edbgInterface);
|
||||
|
||||
/**
|
||||
* Issues a Set Parameter command to the EDBG tool, to enable the target power.
|
||||
*/
|
||||
void enableTargetPower() override;
|
||||
|
||||
/**
|
||||
* Issues a Set Parameter command to the EDBG tool, to disable the target power.
|
||||
*/
|
||||
void disableTargetPower() override;
|
||||
|
||||
private:
|
||||
EdbgInterface* edbgInterface;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user