Moved EDBG protocol code to more appropriate directory.

Updated necessary namespaces.
Other bits of tidying.
This commit is contained in:
Nav
2023-11-17 22:20:39 +00:00
parent 3ec365290e
commit 55d3fe76e0
89 changed files with 223 additions and 223 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,
};
}

View File

@@ -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());
}
}
}

View File

@@ -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
);
};
}

View File

@@ -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]);
}
}

View File

@@ -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);
};
}

View File

@@ -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)
{}
};
}

View File

@@ -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
);
}
}
}

View File

@@ -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);
};
}

View File

@@ -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)
{}
};
}

View File

@@ -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)
};
}
};
}

View File

@@ -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)
};
}
};
}

View File

@@ -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)
{}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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));
}
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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)
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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();
}
}
}
}
}

View File

@@ -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 = {}
);
};
}

View File

@@ -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),
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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),
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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());
}
};
}

View File

@@ -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),
};
}
};
}

View File

@@ -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));
}
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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),
};
}
};
}

View File

@@ -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());
}
};
}

View File

@@ -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)
{}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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");
}
}
}
};
}

View File

@@ -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;
}
}
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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;
}
};
}

View File

@@ -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"

View File

@@ -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)
{}
};
}

View File

@@ -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)
};
}
};
}

View File

@@ -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)
{}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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,
};
}
};
}

View File

@@ -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
};
}
};
}

View File

@@ -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)
};
}
};
}

View File

@@ -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)
{}
};
}

View File

@@ -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};
};
}

View File

@@ -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

View File

@@ -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();
};
}

View File

@@ -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];
}
}

View File

@@ -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);
};
}

View File

@@ -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;
}
}

View File

@@ -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);
};
}

View File

@@ -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"},
});
};
}

View File

@@ -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;
}
}

View File

@@ -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;
};
}

View File

@@ -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]
);
}
}
}
}

View File

@@ -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;
};
}

View File

@@ -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;
}
};
}

View File

@@ -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
);
}
};
}

View File

@@ -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]);
}
}

View File

@@ -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);
};
}

View File

@@ -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());
}
}

View File

@@ -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);
};
}

View File

@@ -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()
);
}
}

View File

@@ -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;
};
}

View File

@@ -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
);
}
}

View File

@@ -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();
};
}

View File

@@ -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]);
}
}

View File

@@ -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);
};
}

View 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};
};
}

View File

@@ -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;
}
}

View File

@@ -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();
};
}

View File

@@ -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");
}
}
}

View File

@@ -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;
};
}