Consistent casing in directory names
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "src/Targets/TargetPhysicalInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
struct Avr8EdbgParameter
|
||||
{
|
||||
unsigned char context;
|
||||
unsigned char id;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
enum class Avr8PhysicalInterface: unsigned char
|
||||
{
|
||||
NONE = 0x00,
|
||||
JTAG = 0x04,
|
||||
DEBUG_WIRE = 0x05,
|
||||
PDI = 0x06,
|
||||
PDI_1W = 0x08,
|
||||
};
|
||||
|
||||
enum class Avr8MemoryType: unsigned char
|
||||
{
|
||||
/**
|
||||
* The SRAM memory type can be used to read & write to internal memory on the target.
|
||||
*
|
||||
* It's available with all of the config variants in debugging mode.
|
||||
*/
|
||||
SRAM = 0x20,
|
||||
|
||||
/**
|
||||
* The EEPROM memory type can be used to read and write to EEPROM memory on the target.
|
||||
*
|
||||
* It's available with all of the config variants, in debugging mode.
|
||||
*/
|
||||
EEPROM = 0x22,
|
||||
|
||||
/**
|
||||
* The EEPROM_ATOMIC memory type can be used to write to EEPROM memory with automatic page erasing.
|
||||
*
|
||||
* It's only available for XMEGA and UPDI config variants.
|
||||
*
|
||||
* Only one EEPROM page can be written at a time.
|
||||
*/
|
||||
EEPROM_ATOMIC = 0xC4,
|
||||
|
||||
/**
|
||||
* The EEPROM_PAGE memory type can be used to read and write to EEPROM whilst the target is in
|
||||
* programming mode.
|
||||
*/
|
||||
EEPROM_PAGE = 0xB1,
|
||||
|
||||
/**
|
||||
* The FLASH_PAGE memory type can be used to read and write full flash pages on the target.
|
||||
*
|
||||
* Only available with the JTAG and debugWIRE config variants.
|
||||
*
|
||||
* This memory type is not available with the JTAG config variant in debugging mode. Programming mode will need
|
||||
* to be enabled before it can be used with JTAG targets. With the debugWIRE variant, this memory type *can* be
|
||||
* used whilst in debugging mode.
|
||||
*/
|
||||
FLASH_PAGE = 0xB0,
|
||||
|
||||
/**
|
||||
* The APPL_FLASH memory type can be used to read/write to the application section of the flash memory on the
|
||||
* target.
|
||||
*
|
||||
* Only available with the XMEGA and UPDI config variants.
|
||||
*
|
||||
* When in debugging mode, only read access is permitted. Programming mode will need to be enabled before
|
||||
* any attempts of writing data.
|
||||
*/
|
||||
APPL_FLASH = 0xC0,
|
||||
BOOT_FLASH = 0xC1,
|
||||
|
||||
/**
|
||||
* The SPM memory type can be used to read memory from the target whilst in debugging mode.
|
||||
*
|
||||
* Only available with JTAG and debugWIRE config variants.
|
||||
*/
|
||||
SPM = 0xA0,
|
||||
|
||||
/**
|
||||
* The REGISTER_FILE memory type can be used to read & write to general purpose registers.
|
||||
*
|
||||
* Only available in debugging mode and with XMEGA and UPDI config variants. The SRAM memory type can be used
|
||||
* to access general purpose registers when other variants are in use.
|
||||
*/
|
||||
REGISTER_FILE = 0xB8,
|
||||
|
||||
/**
|
||||
* The FUSES memory type can be used to read and write AVR fuses in programming mode.
|
||||
*
|
||||
* Not available for the debugWIRE config variant.
|
||||
*/
|
||||
FUSES = 0xB2,
|
||||
};
|
||||
|
||||
enum class Avr8ResponseId: unsigned char
|
||||
{
|
||||
OK = 0x80,
|
||||
DATA = 0x84,
|
||||
FAILED = 0xA0,
|
||||
};
|
||||
|
||||
inline bool operator == (unsigned char rawId, Avr8ResponseId id) {
|
||||
return static_cast<unsigned char>(id) == rawId;
|
||||
}
|
||||
|
||||
inline bool operator == (Avr8ResponseId id, unsigned char rawId) {
|
||||
return rawId == id;
|
||||
}
|
||||
|
||||
enum class Avr8EraseMemoryMode: unsigned char
|
||||
{
|
||||
CHIP = 0x00,
|
||||
APPLICATION_SECTION = 0x01,
|
||||
BOOT_SECTION = 0x02,
|
||||
EEPROM = 0x03,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "AvrCommand.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
AvrCommand::AvrCommand(
|
||||
std::size_t fragmentCount,
|
||||
std::size_t fragmentNumber,
|
||||
const std::vector<unsigned char>& commandPacket
|
||||
)
|
||||
: Command(0x80)
|
||||
{
|
||||
const auto commandPacketSize = commandPacket.size();
|
||||
this->data.reserve(commandPacketSize + 3);
|
||||
|
||||
// FragmentInfo byte
|
||||
this->data.emplace_back(static_cast<unsigned char>((fragmentNumber << 4) | fragmentCount));
|
||||
|
||||
// Size byte
|
||||
this->data.emplace_back(static_cast<unsigned char>(commandPacketSize >> 8));
|
||||
this->data.emplace_back(static_cast<unsigned char>(commandPacketSize & 0xFF));
|
||||
|
||||
if (commandPacketSize > 0) {
|
||||
// Packet data
|
||||
this->data.insert(this->data.begin() + 3, commandPacket.begin(), commandPacket.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CmsisDap/Command.hpp"
|
||||
|
||||
#include "AvrResponse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
/**
|
||||
* AVR CMSIS-DAP vendor command.
|
||||
*/
|
||||
class AvrCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* AVR CMSIS-DAP vendor commands *do not* directly result in an AvrResponse object. The device will respond
|
||||
* immediately upon receiving this command, simply acknowledging receipt of the command.
|
||||
*
|
||||
* If a response is expected to follow upon the execution of the AVR command, it must be requested from the
|
||||
* device, using the AvrResponseCommand (see that class declaration for more info).
|
||||
*
|
||||
* For this reason, the ExpectedResponseType for this command is just the standard Response type.
|
||||
*
|
||||
* For more on the purpose of this alias, see the Command class.
|
||||
*/
|
||||
using ExpectedResponseType = ::DebugToolDrivers::Protocols::CmsisDap::Response;
|
||||
|
||||
AvrCommand(
|
||||
std::size_t fragmentCount,
|
||||
std::size_t fragmentNumber,
|
||||
const std::vector<unsigned char>& commandPacket
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "AvrEvent.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
AvrEvent::AvrEvent(const std::vector<unsigned char>& rawResponse)
|
||||
: Response(rawResponse)
|
||||
{
|
||||
if (this->id != 0x82) {
|
||||
throw Exception{"Failed to construct AvrEvent object - invalid response ID."};
|
||||
}
|
||||
|
||||
if (this->data.size() < 7) {
|
||||
throw Exception{"Failed to construct AvrEvent object - unexpected size of AVR_EVT response."};
|
||||
}
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
const auto responsePacketSize = static_cast<std::size_t>((this->data[0] << 8) | this->data[1]);
|
||||
|
||||
if (responsePacketSize == 0) {
|
||||
// No event available
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->data.size() < responsePacketSize + 7) {
|
||||
throw Exception{"Failed to construct AvrEvent object - invalid size of AVR_EVT response packet."};
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the SOF, protocol version, handler ID, sequence ID and size bytes (which all make up 7 bytes
|
||||
* in total).
|
||||
*/
|
||||
this->eventData = std::vector<unsigned char>{
|
||||
this->data.begin() + 7,
|
||||
this->data.begin() + 7 + static_cast<std::int64_t>(responsePacketSize)
|
||||
};
|
||||
|
||||
this->eventId = static_cast<AvrEventId>(this->eventData[0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CmsisDap/Response.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
enum class AvrEventId: unsigned char
|
||||
{
|
||||
AVR8_BREAK_EVENT = 0x40,
|
||||
};
|
||||
|
||||
class AvrEvent: public ::DebugToolDrivers::Protocols::CmsisDap::Response
|
||||
{
|
||||
public:
|
||||
std::optional<AvrEventId> eventId = std::nullopt;
|
||||
std::vector<unsigned char> eventData = {};
|
||||
|
||||
explicit AvrEvent(const std::vector<unsigned char>& rawResponse);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CmsisDap/Command.hpp"
|
||||
#include "AvrEvent.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class AvrEventCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseType = AvrEvent;
|
||||
|
||||
AvrEventCommand()
|
||||
: Command(0x82)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "AvrResponse.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
AvrResponse::AvrResponse(const std::vector<unsigned char>& rawResponse)
|
||||
: Response(rawResponse)
|
||||
{
|
||||
if (this->id != 0x81) {
|
||||
throw Exception{"Failed to construct AvrResponse object - invalid response ID."};
|
||||
}
|
||||
|
||||
if (this->data.empty()) {
|
||||
// All AVR responses should contain at least one byte (the fragment info byte)
|
||||
throw Exception{"Failed to construct AvrResponse object - malformed AVR_RSP data"};
|
||||
}
|
||||
|
||||
if (this->data[0] == 0x00) {
|
||||
// This AVR Response contains no data (the device had no data to send), so we can stop here.
|
||||
return;
|
||||
}
|
||||
|
||||
this->fragmentCount = static_cast<std::uint8_t>(this->data[0] & 0x0FU);
|
||||
this->fragmentNumber = static_cast<std::uint8_t>(this->data[0] >> 4);
|
||||
|
||||
// Response size is two bytes, MSB
|
||||
const auto responsePacketSize = static_cast<std::uint16_t>((this->data[1] << 8U) + this->data[2]);
|
||||
|
||||
if (responsePacketSize > 0) {
|
||||
// Packet data
|
||||
this->responsePacket.insert(
|
||||
this->responsePacket.begin(),
|
||||
this->data.begin() + 3,
|
||||
this->data.begin() + 3 + responsePacketSize
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CmsisDap/Response.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class AvrResponse: public ::DebugToolDrivers::Protocols::CmsisDap::Response
|
||||
{
|
||||
public:
|
||||
std::uint8_t fragmentNumber = 0;
|
||||
std::uint8_t fragmentCount = 0;
|
||||
|
||||
std::vector<unsigned char> responsePacket;
|
||||
|
||||
explicit AvrResponse(const std::vector<unsigned char>& rawResponse);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CmsisDap/Command.hpp"
|
||||
|
||||
#include "AvrResponse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
/**
|
||||
* All AVR commands result in an automatic response, but that is just a response to confirm
|
||||
* receipt of the AVR Command. It is *not* a response to the AVR command.
|
||||
*
|
||||
* Responses to AVR commands are not automatically sent from the device, they must be requested from the device.
|
||||
*
|
||||
* An AvrResponseCommand is a CMSIS-DAP vendor command, used to request a response for an AVR command. In response
|
||||
* to an AvrResponseCommand, the device will send over an AvrResponse, which will contain the response to the
|
||||
* AVR command.
|
||||
*
|
||||
* For more information on this, see the 'Embedded Debugger-Based Tools Protocols User's Guide'
|
||||
* document, by Microchip. Link provided in the AtmelICE device class declaration.
|
||||
*
|
||||
* An AvrResponseCommand is very simple - it consists of a command ID and nothing more.
|
||||
*/
|
||||
class AvrResponseCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseType = AvrResponse;
|
||||
|
||||
AvrResponseCommand()
|
||||
: Command(0x81)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ActivatePhysical: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit ActivatePhysical(bool reset)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The activate physical command consists of 3 bytes:
|
||||
* 1. Command ID (0x10)
|
||||
* 2. Version (0x00)
|
||||
* 3. Reset flag (to apply external reset)
|
||||
*/
|
||||
this->payload = {
|
||||
0x10,
|
||||
0x00,
|
||||
static_cast<unsigned char>(reset)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Attach: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Attach(bool breakAfterAttach)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The attach command consists of 3 bytes:
|
||||
* 1. Command ID (0x13)
|
||||
* 2. Version (0x00)
|
||||
* 3. Break (stop) after attach flag
|
||||
*/
|
||||
this->payload = {
|
||||
0x13,
|
||||
0x00,
|
||||
static_cast<unsigned char>(breakAfterAttach)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/Avr8Generic.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/Avr8Generic/Avr8GenericResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class Avr8GenericCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::Avr8GenericResponseFrame;
|
||||
|
||||
Avr8GenericCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::AVR8_GENERIC)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearAllSoftwareBreakpoints: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
ClearAllSoftwareBreakpoints()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear all software breakpoints command consists of 2 bytes:
|
||||
* 1. Command ID (0x45)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x45,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit ClearHardwareBreakpoint(std::uint8_t number)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear hardware breakpoint command consists of 3 bytes:
|
||||
*
|
||||
* 1. Command ID (0x41)
|
||||
* 2. Version (0x00)
|
||||
* 3. Breakpoint Number (1, 2, or 3)
|
||||
*/
|
||||
this->payload = {
|
||||
0x41,
|
||||
0x00,
|
||||
number
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ClearSoftwareBreakpoints: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
explicit ClearSoftwareBreakpoints(const std::vector<std::uint32_t>& addresses)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The clear software breakpoints command consists of 2 bytes + 4*n bytes, where n is the number
|
||||
* of breakpoints to clear:
|
||||
*
|
||||
* 1. Command ID (0x44)
|
||||
* 2. Version (0x00)
|
||||
* ... addresses
|
||||
*/
|
||||
this->payload = {
|
||||
0x44,
|
||||
0x00,
|
||||
};
|
||||
|
||||
for (const auto& address : addresses) {
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 8));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 16));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 24));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class DeactivatePhysical: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
DeactivatePhysical()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The deactivate physical command consists of 2 bytes:
|
||||
* 1. Command ID (0x11)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x11,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Detach: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
Detach()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The detach command consists of 2 bytes:
|
||||
* 1. Command ID (0x14)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x14,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class DisableDebugWire: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
DisableDebugWire()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The disable debugWIRE command consists of 2 bytes:
|
||||
* 1. Command ID (0x17)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x17,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class EnterProgrammingMode: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
EnterProgrammingMode()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The enter programming mode command consists of 2 bytes:
|
||||
* 1. Command ID (0x15)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x15,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class EraseMemory: public Avr8GenericCommandFrame<std::array<unsigned char, 7>>
|
||||
{
|
||||
public:
|
||||
EraseMemory(Avr8EraseMemoryMode mode)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The "Erase memory" command consists of 7 bytes:
|
||||
* 1. Command ID (0x20)
|
||||
* 2. Version (0x00)
|
||||
* 3. Erase mode (see Avr8EraseMemoryMode enum)
|
||||
* 4. Start address (4 bytes) - this is just hardcoded to 0x00000000 for now.
|
||||
*/
|
||||
this->payload = {
|
||||
0x20,
|
||||
0x00,
|
||||
static_cast<unsigned char>(mode),
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "../../ResponseFrames/Avr8Generic/GetDeviceId.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class GetDeviceId: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::GetDeviceId;
|
||||
|
||||
GetDeviceId()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The get device ID command consists of 2 bytes:
|
||||
* 1. Command ID (0x12)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x12,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class GetParameter: public Avr8GenericCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
GetParameter(const Avr8EdbgParameter& parameter, std::uint8_t size)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The get param command consists of 5 bytes:
|
||||
* 1. Command ID (0x02)
|
||||
* 2. Version (0x00)
|
||||
* 3. Param context (Avr8Parameter::context)
|
||||
* 4. Param ID (Avr8Parameter::id)
|
||||
* 5. Param value length (size)
|
||||
*/
|
||||
this->payload = {
|
||||
0x02,
|
||||
0x00,
|
||||
static_cast<unsigned char>(parameter.context),
|
||||
static_cast<unsigned char>(parameter.id),
|
||||
static_cast<unsigned char>(size)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "../../ResponseFrames/Avr8Generic/GetProgramCounter.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class GetProgramCounter: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::GetProgramCounter;
|
||||
|
||||
GetProgramCounter()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The PC Read command consists of 2 bytes:
|
||||
* 1. Command ID (0x35)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x35,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class LeaveProgrammingMode: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
LeaveProgrammingMode()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The leave programming mode command consists of 2 bytes:
|
||||
* 1. Command ID (0x16)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x16,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "ReadMemory.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include <cmath>
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
ReadMemory::ReadMemory(
|
||||
const Avr8MemoryType& type,
|
||||
std::uint32_t startAddress,
|
||||
std::uint32_t bytes,
|
||||
const std::set<std::uint32_t>& excludedAddresses
|
||||
)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The read memory command consists of 11 + (bytes / 8) bytes:
|
||||
* 1. Command ID (0x21 for the general read memory command, 0x22 for reading with a mask)
|
||||
* 2. Version (0x00)
|
||||
* 3. Memory type (Avr8MemoryType)
|
||||
* 4. Start address (4 bytes)
|
||||
* 5. Number of bytes to read (4 bytes)
|
||||
* 6. Mask to apply (bytes / 8) - only required if we're using the masked read command (command ID 0x22).
|
||||
*/
|
||||
this->payload = std::vector<unsigned char>(11, 0x00);
|
||||
this->payload[0] = excludedAddresses.empty() ? 0x21 : 0x22;
|
||||
this->payload[1] = 0x00;
|
||||
this->payload[2] = static_cast<unsigned char>(type);
|
||||
this->payload[3] = static_cast<unsigned char>(startAddress);
|
||||
this->payload[4] = static_cast<unsigned char>(startAddress >> 8);
|
||||
this->payload[5] = static_cast<unsigned char>(startAddress >> 16);
|
||||
this->payload[6] = static_cast<unsigned char>(startAddress >> 24);
|
||||
this->payload[7] = static_cast<unsigned char>(bytes);
|
||||
this->payload[8] = static_cast<unsigned char>(bytes >> 8);
|
||||
this->payload[9] = static_cast<unsigned char>(bytes >> 16);
|
||||
this->payload[10] = static_cast<unsigned char>(bytes >> 24);
|
||||
|
||||
if (!excludedAddresses.empty()) {
|
||||
const auto endAddress = startAddress + (bytes - 1);
|
||||
|
||||
constexpr auto byteBitSize = std::numeric_limits<unsigned char>::digits;
|
||||
auto byteBitset = std::bitset<byteBitSize>();
|
||||
byteBitset.reset();
|
||||
|
||||
for (std::uint32_t address = startAddress; address <= endAddress; address++) {
|
||||
auto addressIndex = address - startAddress;
|
||||
auto bitIndex = static_cast<std::size_t>(
|
||||
addressIndex - (std::floor(addressIndex / byteBitSize) * byteBitSize)
|
||||
);
|
||||
|
||||
if (!excludedAddresses.contains(address)) {
|
||||
byteBitset[bitIndex] = true;
|
||||
}
|
||||
|
||||
if (address > 0 && (bitIndex == 7 || address == endAddress)) {
|
||||
this->payload.emplace_back(byteBitset.to_ulong());
|
||||
byteBitset.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "../../ResponseFrames/Avr8Generic/ReadMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class ReadMemory: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Avr8Generic::ReadMemory;
|
||||
|
||||
ReadMemory(
|
||||
const Avr8MemoryType& type,
|
||||
std::uint32_t address,
|
||||
std::uint32_t bytes,
|
||||
const std::set<std::uint32_t>& excludedAddresses = {}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Reset: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Reset(bool stopAtMainAddress = false)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The reset command consists of 3 bytes:
|
||||
* 1. Command ID (0x30)
|
||||
* 2. Version (0x00)
|
||||
* 3. "Level" (0x01 to stop at boot reset vector or 0x02 to stop at main address)
|
||||
*/
|
||||
this->payload = {
|
||||
0x30,
|
||||
0x00,
|
||||
static_cast<unsigned char>(stopAtMainAddress ? 0x02 : 0x01),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Run: public Avr8GenericCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
Run()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The run command consists of 2 bytes:
|
||||
* 1. Command ID (0x32)
|
||||
* 2. Version (0x00)
|
||||
*/
|
||||
this->payload = {
|
||||
0x32,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class RunTo: public Avr8GenericCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
explicit RunTo(const std::uint32_t& address)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The run-to command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x33)
|
||||
* 2. Version (0x00)
|
||||
* 3. Address to run to (4 bytes)
|
||||
*/
|
||||
|
||||
// The address to run to needs to be the 16-bit word address, not the byte address.
|
||||
const auto wordAddress = address / 2;
|
||||
|
||||
this->payload = {
|
||||
0x33,
|
||||
0x00,
|
||||
static_cast<unsigned char>(wordAddress),
|
||||
static_cast<unsigned char>(wordAddress >> 8),
|
||||
static_cast<unsigned char>(wordAddress >> 16),
|
||||
static_cast<unsigned char>(wordAddress >> 24),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetHardwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 9>>
|
||||
{
|
||||
public:
|
||||
explicit SetHardwareBreakpoint(std::uint32_t address, std::uint8_t number)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set hardware breakpoint command consists of 9 bytes:
|
||||
*
|
||||
* 1. Command ID (0x40)
|
||||
* 2. Version (0x00)
|
||||
* 3. Breakpoint Type (0x01 for program break)
|
||||
* 4. Breakpoint Number (1, 2, or 3)
|
||||
* 5. Program address (4 bytes) - the EDBG Protocol document states that this should be the word address,
|
||||
* but that seems to be incorrect. The tool expects a byte address here.
|
||||
* 5. Mode (0x03 for program break)
|
||||
*/
|
||||
this->payload = {
|
||||
0x40,
|
||||
0x00,
|
||||
0x01,
|
||||
number,
|
||||
static_cast<unsigned char>(address),
|
||||
static_cast<unsigned char>(address >> 8),
|
||||
static_cast<unsigned char>(address >> 16),
|
||||
static_cast<unsigned char>(address >> 24),
|
||||
0x03
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetParameter: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
SetParameter(const Avr8EdbgParameter& parameter, const std::vector<unsigned char>& value)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set param command consists of value.size() + 5 bytes. The first five bytes consist of:
|
||||
* 1. Command ID (0x01)
|
||||
* 2. Version (0x00)
|
||||
* 3. Param context (Avr8EdbgParameter::context)
|
||||
* 4. Param ID (Avr8EdbgParameter::id)
|
||||
* 5. Param value length (value.size()) - this is only one byte in size, so its value should
|
||||
* never exceed 255.
|
||||
*/
|
||||
this->payload = std::vector<unsigned char>(5, 0x00);
|
||||
this->payload[0] = 0x01;
|
||||
this->payload[1] = 0x00;
|
||||
this->payload[2] = static_cast<unsigned char>(parameter.context);
|
||||
this->payload[3] = static_cast<unsigned char>(parameter.id);
|
||||
this->payload[4] = static_cast<unsigned char>(value.size());
|
||||
this->payload.insert(this->payload.end(), value.begin(), value.end());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetProgramCounter: public Avr8GenericCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
explicit SetProgramCounter(std::uint32_t programCounter)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The PC write command consists of 6 bytes:
|
||||
* 1. Command ID (0x36)
|
||||
* 2. Version (0x00)
|
||||
* 3. PC (4 bytes, LSB)
|
||||
*/
|
||||
this->payload = {
|
||||
0x36,
|
||||
0x00,
|
||||
static_cast<unsigned char>(programCounter),
|
||||
static_cast<unsigned char>(programCounter >> 8),
|
||||
static_cast<unsigned char>(programCounter >> 16),
|
||||
static_cast<unsigned char>(programCounter >> 24),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetSoftwareBreakpoints: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
explicit SetSoftwareBreakpoints(const std::vector<std::uint32_t>& addresses)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The set software breakpoint command consists of 2 bytes + 4*n bytes, where n is the number
|
||||
* of breakpoints to set:
|
||||
*
|
||||
* 1. Command ID (0x43)
|
||||
* 2. Version (0x00)
|
||||
* ... addresses
|
||||
*/
|
||||
this->payload = {
|
||||
0x43,
|
||||
0x00,
|
||||
};
|
||||
|
||||
for (const auto& address : addresses) {
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 8));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 16));
|
||||
this->payload.emplace_back(static_cast<unsigned char>(address >> 24));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class SetXmegaSoftwareBreakpoint: public Avr8GenericCommandFrame<std::array<unsigned char, 15>>
|
||||
{
|
||||
public:
|
||||
explicit SetXmegaSoftwareBreakpoint(std::uint32_t address)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
this->payload = {
|
||||
0x42,
|
||||
0x00,
|
||||
static_cast<unsigned char>(address),
|
||||
static_cast<unsigned char>(address >> 8),
|
||||
static_cast<unsigned char>(address >> 16),
|
||||
static_cast<unsigned char>(address >> 24),
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Step: public Avr8GenericCommandFrame<std::array<unsigned char, 4>>
|
||||
{
|
||||
public:
|
||||
Step()
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The step command consists of 4 bytes:
|
||||
* 1. Command ID (0x34)
|
||||
* 2. Version (0x00)
|
||||
* 3. Level (0x01 for instruction level step, 0x02 for statement level step)
|
||||
* 4. Mode (0x00 for step over, 0x01 for step into, 0x02 for step out)
|
||||
*/
|
||||
this->payload = {
|
||||
0x34,
|
||||
0x00,
|
||||
0x01,
|
||||
0x01,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class Stop: public Avr8GenericCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Stop(bool stopImmediately = true)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The stop command consists of 3 bytes:
|
||||
* 1. Command ID (0x31)
|
||||
* 2. Version (0x00)
|
||||
* 3. Stop immediately (0x01 to stop immediately or 0x02 to stop at the next symbol)
|
||||
*/
|
||||
this->payload = {
|
||||
0x31,
|
||||
0x00,
|
||||
static_cast<unsigned char>(stopImmediately ? 0x01 : 0x02),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericCommandFrame.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8Generic
|
||||
{
|
||||
class WriteMemory: public Avr8GenericCommandFrame<std::vector<unsigned char>>
|
||||
{
|
||||
public:
|
||||
WriteMemory(const Avr8MemoryType& type, std::uint32_t address, Targets::TargetMemoryBufferSpan buffer)
|
||||
: Avr8GenericCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The "Write memory" command consists of 12 bytes + the buffer size:
|
||||
* 1. Command ID (0x23)
|
||||
* 2. Version (0x00)
|
||||
* 3. Memory type (Avr8MemoryType)
|
||||
* 4. Start address (4 bytes)
|
||||
* 5. Number of bytes to write (4 bytes)
|
||||
* 6. Asynchronous flag (0x00 for "write first, then reply" and 0x01 for "reply first, then write")
|
||||
* 7. Buffer
|
||||
*/
|
||||
this->payload = std::vector<unsigned char>(12, 0x00);
|
||||
this->payload[0] = 0x23;
|
||||
this->payload[1] = 0x00;
|
||||
this->payload[2] = static_cast<unsigned char>(type);
|
||||
this->payload[3] = static_cast<unsigned char>(address);
|
||||
this->payload[4] = static_cast<unsigned char>(address >> 8);
|
||||
this->payload[5] = static_cast<unsigned char>(address >> 16);
|
||||
this->payload[6] = static_cast<unsigned char>(address >> 24);
|
||||
|
||||
const auto bytesToWrite = static_cast<std::uint32_t>(buffer.size());
|
||||
this->payload[7] = static_cast<unsigned char>(bytesToWrite);
|
||||
this->payload[8] = static_cast<unsigned char>(bytesToWrite >> 8);
|
||||
this->payload[9] = static_cast<unsigned char>(bytesToWrite >> 16);
|
||||
this->payload[10] = static_cast<unsigned char>(bytesToWrite >> 24);
|
||||
|
||||
// We always set the async flag to 0x00 ("write first, then reply")
|
||||
this->payload[11] = 0x00;
|
||||
|
||||
this->payload.insert(this->payload.end(), buffer.begin(), buffer.end());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
#include "src/DebugToolDrivers/Protocols/CmsisDap/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::Avr8::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 {
|
||||
const auto rawCommandFrame = this->getRawCommandFrame();
|
||||
|
||||
const auto commandFrameSize = rawCommandFrame.size();
|
||||
const auto commandsRequired = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<float>(commandFrameSize) / static_cast<float>(maximumCommandPacketSize))
|
||||
);
|
||||
|
||||
auto avrCommands = std::vector<AvrCommand>{};
|
||||
auto copiedPacketSize = std::size_t{0};
|
||||
for (auto i = std::size_t{0}; i < commandsRequired; ++i) {
|
||||
// If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame
|
||||
const auto commandPacketSize = static_cast<std::size_t>(
|
||||
((i + 1) != commandsRequired)
|
||||
? maximumCommandPacketSize
|
||||
: (commandFrameSize - (maximumCommandPacketSize * i))
|
||||
);
|
||||
|
||||
avrCommands.emplace_back(AvrCommand(
|
||||
commandsRequired,
|
||||
i + 1,
|
||||
std::vector<unsigned char>{
|
||||
rawCommandFrame.begin() + static_cast<std::int64_t>(copiedPacketSize),
|
||||
rawCommandFrame.begin() + static_cast<std::int64_t>(copiedPacketSize + commandPacketSize)
|
||||
}
|
||||
));
|
||||
copiedPacketSize += commandPacketSize;
|
||||
}
|
||||
|
||||
return avrCommands;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "AvrCommandFrame.hpp"
|
||||
|
||||
#include "Discovery/DiscoveryCommandFrame.hpp"
|
||||
#include "Discovery/Query.hpp"
|
||||
|
||||
#include "HouseKeeping/HouseKeepingCommandFrame.hpp"
|
||||
#include "HouseKeeping/StartSession.hpp"
|
||||
#include "HouseKeeping/EndSession.hpp"
|
||||
#include "HouseKeeping/GetParameter.hpp"
|
||||
|
||||
#include "Avr8Generic/Avr8GenericCommandFrame.hpp"
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/Avr8Generic.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/AvrIsp/AvrIspResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class AvrIspCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::AvrIsp::AvrIspResponseFrame;
|
||||
|
||||
AvrIspCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::AVRISP)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class EnterProgrammingMode: public AvrIspCommandFrame<std::array<unsigned char, 12>>
|
||||
{
|
||||
public:
|
||||
EnterProgrammingMode(
|
||||
std::uint8_t timeout,
|
||||
std::uint8_t stabDelay,
|
||||
std::uint8_t commandExecutionDelay,
|
||||
std::uint8_t syncLoops,
|
||||
std::uint8_t byteDelay,
|
||||
std::uint8_t pollValue,
|
||||
std::uint8_t pollIndex
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The enter programming mode command consists of 12 bytes:
|
||||
*
|
||||
* 1. Command ID (0x10)
|
||||
*
|
||||
* -- The fields from this point are taken from the AVR8 TDFs --
|
||||
*
|
||||
* 2. Command timeout in milliseconds
|
||||
* 3. Pin stabilization delay in milliseconds
|
||||
* 4. Command execution delay in milliseconds
|
||||
* 5. Number of synchronisation loops
|
||||
* 6. Millisecond delay between each byte in the command
|
||||
* 7. Poll value
|
||||
* 8. Poll index
|
||||
*
|
||||
* -- The fields from this point are the four bytes of the low-level SPI command --
|
||||
*
|
||||
* 9. CMD1 - For the enter programming mode command, this appears to always be 0xAC
|
||||
* 10. CMD2 - For AVR8 targets, this also appears to be a fixed value of 0x53
|
||||
* 11. CMD3 - 0x00
|
||||
* 12. CMD4 - 0x00
|
||||
*/
|
||||
this->payload = {
|
||||
0x10,
|
||||
timeout,
|
||||
stabDelay,
|
||||
commandExecutionDelay,
|
||||
syncLoops,
|
||||
byteDelay,
|
||||
pollValue,
|
||||
pollIndex,
|
||||
0xAC,
|
||||
0x53,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class LeaveProgrammingMode: public AvrIspCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
LeaveProgrammingMode(
|
||||
std::uint8_t preDelay,
|
||||
std::uint8_t postDelay
|
||||
)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The leave programming mode command consists of 3 bytes:
|
||||
*
|
||||
* 1. Command ID (0x11)
|
||||
*
|
||||
* -- The fields from this point are taken from the AVR8 TDFs --
|
||||
*
|
||||
* 2. Pre-delay
|
||||
* 3. Post-delay
|
||||
*/
|
||||
this->payload = {
|
||||
0x11,
|
||||
preDelay,
|
||||
postDelay,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/Fuse.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ProgramFuse: public AvrIspCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
ProgramFuse(Targets::Microchip::Avr8::FuseType fuseType, Targets::Microchip::Avr8::FuseValue value)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr8::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::InternalFatalErrorException{"Unsupported fuse type"};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/Fuse.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ReadFuse: public AvrIspCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
ReadFuse(Targets::Microchip::Avr8::FuseType fuseType, std::uint8_t returnAddress)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
using Targets::Microchip::Avr8::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;
|
||||
}
|
||||
default: {
|
||||
throw Exceptions::InternalFatalErrorException{"Unsupported fuse type"};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp
|
||||
{
|
||||
class ReadLock: public AvrIspCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
explicit ReadLock(std::uint8_t returnAddress)
|
||||
: AvrIspCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The read lock command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x1A)
|
||||
* 2. Return Address
|
||||
* 3. CMD1
|
||||
* 4. CMD2
|
||||
* 5. CMD3
|
||||
* 6. CMD4
|
||||
*/
|
||||
this->payload = {
|
||||
0x1A,
|
||||
returnAddress,
|
||||
0x58,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <src/Targets/Microchip/Avr8/Fuse.hpp>
|
||||
|
||||
#include "AvrIspCommandFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/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::Avr8::FuseType;
|
||||
|
||||
/*
|
||||
* The read signature command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x1B)
|
||||
* 2. Return Address
|
||||
* 3. CMD1
|
||||
* 4. CMD2
|
||||
* 5. CMD3
|
||||
* 6. CMD4
|
||||
*/
|
||||
this->payload = {
|
||||
0x1B,
|
||||
returnAddress,
|
||||
0x30,
|
||||
0x00,
|
||||
static_cast<unsigned char>(signatureByteAddress & 0x03),
|
||||
0x00,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/Discovery/DiscoveryResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Discovery
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class DiscoveryCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::Discovery::DiscoveryResponseFrame;
|
||||
|
||||
DiscoveryCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::DISCOVERY)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "DiscoveryCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Discovery
|
||||
{
|
||||
/**
|
||||
* The query context is the type of query to execute.
|
||||
*/
|
||||
enum class QueryContext: unsigned char
|
||||
{
|
||||
COMMAND_HANDLERS = 0x00,
|
||||
TOOL_NAME = 0x80,
|
||||
SERIAL_NUMBER = 0x81,
|
||||
MANUFACTURE_DATE = 0x82,
|
||||
};
|
||||
|
||||
/**
|
||||
* The Discovery protocol handler only supports one command; the Query command. This command is used to
|
||||
* query information from the device, such as device capabilities, manufacture date, serial number, etc.
|
||||
*/
|
||||
class Query: public DiscoveryCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
explicit Query(QueryContext context)
|
||||
: DiscoveryCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The payload for the Query command consists of three bytes. A command ID (0x00), version (0x00) and a
|
||||
* query context.
|
||||
*/
|
||||
this->payload = {
|
||||
0x00,
|
||||
0x00,
|
||||
static_cast<unsigned char>(context)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/EdbgControl/EdbgControlResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class EdbgControlCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::EdbgControl::EdbgControlResponseFrame;
|
||||
|
||||
EdbgControlCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::EDBG_CONTROL)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "EdbgControlCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl
|
||||
{
|
||||
class GetParameter: public EdbgControlCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
GetParameter(const EdbgParameter& parameter)
|
||||
: EdbgControlCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The EDBG Get Parameter command consists of 5 bytes:
|
||||
*
|
||||
* 1. Command ID (0x02)
|
||||
* 2. Version (0x00)
|
||||
* 3. Parameter context
|
||||
* 4. Parameter ID
|
||||
* 5. Parameter size
|
||||
*/
|
||||
this->payload = {
|
||||
0x02,
|
||||
0x00,
|
||||
parameter.context,
|
||||
parameter.id,
|
||||
parameter.size,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include "EdbgControlCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::EdbgControl
|
||||
{
|
||||
class SetParameter: public EdbgControlCommandFrame<std::array<unsigned char, 6>>
|
||||
{
|
||||
public:
|
||||
SetParameter(const EdbgParameter& parameter, unsigned char value)
|
||||
: EdbgControlCommandFrame()
|
||||
{
|
||||
assert(parameter.size == 1);
|
||||
|
||||
/*
|
||||
* The EDBG Set Parameter command consists of 6 bytes:
|
||||
*
|
||||
* 1. Command ID (0x01)
|
||||
* 2. Version (0x00)
|
||||
* 3. Parameter context
|
||||
* 4. Parameter
|
||||
* 5. Parameter size
|
||||
* 6. Parameter value
|
||||
*/
|
||||
this->payload = {
|
||||
0x01,
|
||||
0x00,
|
||||
parameter.context,
|
||||
parameter.id,
|
||||
parameter.size,
|
||||
value,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
/**
|
||||
* The End Session command ends the active session with the tool.
|
||||
*/
|
||||
class EndSession: public HouseKeepingCommandFrame<std::array<unsigned char, 3>>
|
||||
{
|
||||
public:
|
||||
EndSession()
|
||||
: HouseKeepingCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The payload for the End Session command consists of three bytes. A command ID byte (0x11), a
|
||||
* version byte (0x00) and a reset flag (0x00 for no reset, 0x01 for tool reset).
|
||||
*/
|
||||
this->payload = {
|
||||
0x11,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
#include "Parameters.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
class GetParameter: public HouseKeepingCommandFrame<std::array<unsigned char, 5>>
|
||||
{
|
||||
public:
|
||||
GetParameter(const Parameter& parameter, std::uint8_t size)
|
||||
: HouseKeepingCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The get param command consists of 5 bytes:
|
||||
* 1. Command ID (0x02)
|
||||
* 2. Version (0x00)
|
||||
* 3. Param context (Parameter::context)
|
||||
* 4. Param ID (Parameter::id)
|
||||
* 5. Param value length (size)
|
||||
*/
|
||||
this->payload = {
|
||||
0x02,
|
||||
0x00,
|
||||
static_cast<unsigned char>(parameter.context),
|
||||
static_cast<unsigned char>(parameter.id),
|
||||
static_cast<unsigned char>(size)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/CommandFrames/AvrCommandFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/HouseKeeping/HouseKeepingResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
template<class PayloadContainerType>
|
||||
class HouseKeepingCommandFrame: public AvrCommandFrame<PayloadContainerType>
|
||||
{
|
||||
public:
|
||||
using ExpectedResponseFrameType = ResponseFrames::HouseKeeping::HouseKeepingResponseFrame;
|
||||
|
||||
HouseKeepingCommandFrame()
|
||||
: AvrCommandFrame<PayloadContainerType>(ProtocolHandlerId::HOUSE_KEEPING)
|
||||
{}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
enum class ParameterContext: unsigned char
|
||||
{
|
||||
CONFIG = 0x00,
|
||||
ANALOG = 0x01,
|
||||
USB = 0x03,
|
||||
};
|
||||
|
||||
struct Parameter
|
||||
{
|
||||
ParameterContext context;
|
||||
unsigned char id;
|
||||
std::uint8_t size;
|
||||
|
||||
constexpr Parameter(ParameterContext context, unsigned char id, std::uint8_t size)
|
||||
: context(context)
|
||||
, id(id)
|
||||
, size(size)
|
||||
{};
|
||||
};
|
||||
|
||||
struct Parameters
|
||||
{
|
||||
static constexpr Parameter USB_MAX_READ{ParameterContext::USB, 0x00, 2};
|
||||
static constexpr Parameter USB_MAX_WRITE{ParameterContext::USB, 0x01, 2};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "HouseKeepingCommandFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::HouseKeeping
|
||||
{
|
||||
/**
|
||||
* The Start Session command begins a session with the tool.
|
||||
*/
|
||||
class StartSession: public HouseKeepingCommandFrame<std::array<unsigned char, 2>>
|
||||
{
|
||||
public:
|
||||
StartSession()
|
||||
: HouseKeepingCommandFrame()
|
||||
{
|
||||
/*
|
||||
* The payload for the Start Session command consists of two bytes. A command ID byte (0x10) and a
|
||||
* version byte (0x00).
|
||||
*/
|
||||
this->payload = {
|
||||
0x10,
|
||||
0x00
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,621 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/Avr8/Avr8DebugInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/EdbgInterface.hpp"
|
||||
|
||||
#include "Avr8Generic.hpp"
|
||||
#include "EdbgAvr8Session.hpp"
|
||||
|
||||
#include "src/Targets/TargetPhysicalInterface.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
#include "src/Targets/Microchip/Avr8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/Avr8/Family.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::Avr8::Avr8DebugInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgAvr8Interface(
|
||||
EdbgInterface* edbgInterface,
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
void applyAccessRestrictions(
|
||||
Targets::TargetRegisterDescriptor& registerDescriptor,
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor
|
||||
) 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::Avr8::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;
|
||||
|
||||
/**
|
||||
* Reads registers from the target.
|
||||
*
|
||||
* @param descriptorIds
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetRegisterDescriptorAndValuePairs readRegisters(
|
||||
const Targets::TargetRegisterDescriptors& descriptors
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Writes registers to target.
|
||||
*
|
||||
* @param registers
|
||||
*/
|
||||
void writeRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) override;
|
||||
|
||||
/**
|
||||
* This is an overloaded method.
|
||||
*
|
||||
* Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls readMemory().
|
||||
*
|
||||
* @param addressSpaceDescriptor
|
||||
* @param memorySegmentDescriptor
|
||||
* @param startAddress
|
||||
* @param bytes
|
||||
* @return
|
||||
*/
|
||||
Targets::TargetMemoryBuffer readMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
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 addressSpaceDescriptor
|
||||
* @param memorySegmentDescriptor
|
||||
* @param startAddress
|
||||
* @param buffer
|
||||
*/
|
||||
void writeMemory(
|
||||
const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor,
|
||||
const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor,
|
||||
Targets::TargetMemoryAddress startAddress,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Issues the "Erase" command to erase a particular section of program memory.
|
||||
*
|
||||
* @param section
|
||||
*/
|
||||
void eraseProgramMemory(
|
||||
std::optional<Targets::Microchip::Avr8::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::TargetExecutionState getExecutionState() 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;
|
||||
|
||||
/**
|
||||
* The active EDBG AVR8 session.
|
||||
*/
|
||||
EdbgAvr8Session session;
|
||||
|
||||
/**
|
||||
* 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 execution 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::TargetExecutionState cachedExecutionState = Targets::TargetExecutionState::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();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* - 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.
|
||||
*
|
||||
* We extract the required parameters from the TDF. See the constructors for the `DebugWireJtagParameters`,
|
||||
* `PdiParameters` and `UpdiParameters` structs for more.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* Issues the "Software Breakpoint Clear All" command to the debug tool, clearing all software breakpoints
|
||||
* immediately.
|
||||
*/
|
||||
void clearAllSoftwareBreakpoints();
|
||||
|
||||
/**
|
||||
* Clears all software and hardware breakpoints on the target.
|
||||
*
|
||||
* This function will not clear any untracked hardware breakpoints (breakpoints that were installed in a
|
||||
* previous session).
|
||||
*/
|
||||
void clearAllBreakpoints();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
Avr8MemoryType getRegisterMemoryType(const Targets::TargetRegisterDescriptor& descriptor);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Determines the maximum memory access size imposed on the given Avr8MemoryType.
|
||||
*
|
||||
* @param memoryType
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
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,
|
||||
Targets::TargetMemoryBufferSpan buffer
|
||||
);
|
||||
|
||||
/**
|
||||
* Fetches the current target state.
|
||||
*
|
||||
* This currently uses AVR BREAK events to determine if a target has stopped. The lack of any
|
||||
* queued BREAK events leads to the assumption that the target is still running.
|
||||
*/
|
||||
void refreshTargetState();
|
||||
|
||||
/**
|
||||
* Temporarily disables the debugWIRE module on the target. This does not affect the DWEN fuse. The module
|
||||
* will be reactivated upon the cycling of the target power.
|
||||
*/
|
||||
void disableDebugWire();
|
||||
|
||||
/**
|
||||
* Waits for an AVR event of a specific type.
|
||||
*
|
||||
* @tparam AvrEventType
|
||||
* Type of AVR event to wait for. See AvrEvent class for more.
|
||||
*
|
||||
* @param maximumAttempts
|
||||
* Maximum number of attempts to poll the debug tool for the expected event.
|
||||
*
|
||||
* @return
|
||||
* If an event is found before maximumAttempts is reached, the event will be returned. Otherwise a nullptr
|
||||
* will be returned.
|
||||
*/
|
||||
template <class AvrEventType>
|
||||
std::unique_ptr<AvrEventType> waitForAvrEvent(int maximumAttempts = 20) {
|
||||
int attemptCount = 0;
|
||||
|
||||
while (attemptCount <= maximumAttempts) {
|
||||
auto genericEvent = this->getAvrEvent();
|
||||
|
||||
if (genericEvent != nullptr) {
|
||||
// Attempt to downcast event
|
||||
auto event = std::unique_ptr<AvrEventType>(dynamic_cast<AvrEventType*>(genericEvent.release()));
|
||||
|
||||
if (event != nullptr) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{50});
|
||||
attemptCount++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for an AVR BREAK event.
|
||||
*
|
||||
* This simply wraps a call to waitForAvrEvent<BreakEvent>(). An exception will be thrown if the call doesn't
|
||||
* return a BreakEvent.
|
||||
*
|
||||
* This should only be used when a BreakEvent is always expected.
|
||||
*/
|
||||
void waitForStoppedEvent();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "EdbgAvr8Session.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
EdbgAvr8Session::EdbgAvr8Session(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
)
|
||||
: targetDescriptionFile(targetDescriptionFile)
|
||||
, targetConfig(targetConfig)
|
||||
, programAddressSpace(this->targetDescriptionFile.getProgramAddressSpace())
|
||||
, registerFileAddressSpace(this->targetDescriptionFile.getRegisterFileAddressSpace())
|
||||
, dataAddressSpace(this->targetDescriptionFile.getDataAddressSpace())
|
||||
, eepromAddressSpace(this->targetDescriptionFile.getEepromAddressSpace())
|
||||
, ioAddressSpace(this->targetDescriptionFile.getIoAddressSpace())
|
||||
, signatureAddressSpace(this->targetDescriptionFile.getSignatureAddressSpace())
|
||||
, programMemorySegment(this->targetDescriptionFile.getProgramMemorySegment())
|
||||
, ramMemorySegment(this->targetDescriptionFile.getRamMemorySegment())
|
||||
, eepromMemorySegment(this->targetDescriptionFile.getEepromMemorySegment())
|
||||
, ioMemorySegment(this->targetDescriptionFile.getIoMemorySegment())
|
||||
, fuseMemorySegment(this->targetDescriptionFile.getFuseMemorySegment())
|
||||
, signatureMemorySegment(this->targetDescriptionFile.getSignatureMemorySegment())
|
||||
, programAppSection(this->programMemorySegment.tryGetSection("app_section"))
|
||||
, programBootSection(this->programMemorySegment.tryGetSection("boot_section"))
|
||||
{
|
||||
using Services::StringService;
|
||||
|
||||
const auto ocdDataRegisterProperty = this->targetDescriptionFile.tryGetProperty("ocd", "ocd_datareg");
|
||||
if (ocdDataRegisterProperty.has_value()) {
|
||||
this->ocdDataRegister = StringService::toUint8(ocdDataRegisterProperty->get().value);
|
||||
}
|
||||
|
||||
const auto resolvedConfigVariant = EdbgAvr8Session::tryResolveConfigVariant(
|
||||
this->targetDescriptionFile.getAvrFamily(),
|
||||
this->targetConfig.physicalInterface
|
||||
);
|
||||
|
||||
if (!resolvedConfigVariant.has_value()) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to resolve EDBG config variant from the selected physical interface and the AVR target family"
|
||||
" - please review the selected physical interface"
|
||||
};
|
||||
}
|
||||
|
||||
this->configVariant = *resolvedConfigVariant;
|
||||
}
|
||||
|
||||
std::optional<Avr8ConfigVariant> EdbgAvr8Session::tryResolveConfigVariant(
|
||||
Targets::Microchip::Avr8::Family avrFamily,
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
) {
|
||||
using Targets::Microchip::Avr8::Family;
|
||||
using Targets::TargetPhysicalInterface;
|
||||
|
||||
if (avrFamily == Family::MEGA || avrFamily == Family::TINY) {
|
||||
switch (physicalInterface) {
|
||||
case TargetPhysicalInterface::JTAG: {
|
||||
return Avr8ConfigVariant::MEGAJTAG;
|
||||
}
|
||||
case TargetPhysicalInterface::DEBUG_WIRE: {
|
||||
return Avr8ConfigVariant::DEBUG_WIRE;
|
||||
}
|
||||
case TargetPhysicalInterface::UPDI: {
|
||||
return Avr8ConfigVariant::UPDI;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (avrFamily == Family::XMEGA) {
|
||||
switch (physicalInterface) {
|
||||
case TargetPhysicalInterface::JTAG:
|
||||
case TargetPhysicalInterface::PDI: {
|
||||
return Avr8ConfigVariant::XMEGA;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
avrFamily == Family::DA
|
||||
|| avrFamily == Family::DB
|
||||
|| avrFamily == Family::DD
|
||||
|| avrFamily == Family::DU
|
||||
|| avrFamily == Family::EA
|
||||
|| avrFamily == Family::EB
|
||||
) {
|
||||
switch (physicalInterface) {
|
||||
case TargetPhysicalInterface::UPDI: {
|
||||
return Avr8ConfigVariant::UPDI;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/TargetDescriptionFile.hpp"
|
||||
#include "src/Targets/Microchip/Avr8/Avr8TargetConfig.hpp"
|
||||
#include "src/Targets/TargetMemorySegmentDescriptor.hpp"
|
||||
#include "src/Targets/TargetRegisterDescriptor.hpp"
|
||||
|
||||
#include "Avr8Generic.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
/**
|
||||
* This struct holds all required target info for an EDBG AVR8 session.
|
||||
*/
|
||||
struct EdbgAvr8Session
|
||||
{
|
||||
/**
|
||||
* AVR8 TDF, from which we extract all target info to configure the EDBG debug tool.
|
||||
*/
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile;
|
||||
|
||||
/**
|
||||
* Project's AVR8 target configuration.
|
||||
*/
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig;
|
||||
|
||||
/**
|
||||
* The EDBG config variant parameter.
|
||||
*
|
||||
* See the "AVR8_CONFIG_VARIANT" parameter in section 7.1.3.1 of Microchip's "EDBG-based Tools Protocols"
|
||||
* document for more.
|
||||
*/
|
||||
Avr8ConfigVariant configVariant = Avr8ConfigVariant::NONE;
|
||||
|
||||
const Targets::TargetDescription::AddressSpace& programAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& registerFileAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& dataAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& eepromAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& ioAddressSpace;
|
||||
const Targets::TargetDescription::AddressSpace& signatureAddressSpace;
|
||||
|
||||
const Targets::TargetDescription::MemorySegment& programMemorySegment;
|
||||
const Targets::TargetDescription::MemorySegment& ramMemorySegment;
|
||||
const Targets::TargetDescription::MemorySegment& eepromMemorySegment;
|
||||
const Targets::TargetDescription::MemorySegment& ioMemorySegment;
|
||||
const Targets::TargetDescription::MemorySegment& fuseMemorySegment;
|
||||
const Targets::TargetDescription::MemorySegment& signatureMemorySegment;
|
||||
|
||||
const std::optional<
|
||||
std::reference_wrapper<const Targets::TargetDescription::MemorySegmentSection>
|
||||
> programAppSection;
|
||||
|
||||
const std::optional<
|
||||
std::reference_wrapper<const Targets::TargetDescription::MemorySegmentSection>
|
||||
> programBootSection;
|
||||
|
||||
std::optional<std::uint8_t> ocdDataRegister;
|
||||
|
||||
EdbgAvr8Session(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile,
|
||||
const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig
|
||||
);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Attempts to determine the EDBG config variant for a given AVR family and physical interface.
|
||||
*
|
||||
* See the "AVR8_CONFIG_VARIANT" parameter in section 7.1.3.1 of Microchip's "EDBG-based Tools Protocols"
|
||||
* document for more.
|
||||
*
|
||||
* @return
|
||||
* The resolved config variant, or std::nullopt if the given AVR family and physical interface do not map to
|
||||
* any particular EDBG config variant.
|
||||
*/
|
||||
static std::optional<Avr8ConfigVariant> tryResolveConfigVariant(
|
||||
Targets::Microchip::Avr8::Family avrFamily,
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "EdbgAvrIspInterface.hpp"
|
||||
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.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::Avr8;
|
||||
|
||||
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,
|
||||
const TargetDescriptionFile& targetDescriptionFile
|
||||
)
|
||||
: edbgInterface(edbgInterface)
|
||||
, ispParameters(IspParameters{targetDescriptionFile})
|
||||
{}
|
||||
|
||||
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 {this->readSignatureByte(0), this->readSignatureByte(1), this->readSignatureByte(2)};
|
||||
}
|
||||
|
||||
FuseValue EdbgAvrIspInterface::readFuse(const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ReadFuse{this->resolveFuseType(fuseRegisterDescriptor), 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 static_cast<FuseValue>(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(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor,
|
||||
FuseValue value
|
||||
) {
|
||||
const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(
|
||||
ProgramFuse{this->resolveFuseType(fuseRegisterDescriptor), 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];
|
||||
}
|
||||
|
||||
FuseType EdbgAvrIspInterface::resolveFuseType(const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor) {
|
||||
if (fuseRegisterDescriptor.key == "low") {
|
||||
return FuseType::LOW;
|
||||
}
|
||||
|
||||
if (fuseRegisterDescriptor.key == "high") {
|
||||
return FuseType::HIGH;
|
||||
}
|
||||
|
||||
if (fuseRegisterDescriptor.key == "extended") {
|
||||
return FuseType::EXTENDED;
|
||||
}
|
||||
|
||||
throw Exceptions::InternalFatalErrorException{
|
||||
"Could not resolve fuse type from register descriptor (key: \"" + fuseRegisterDescriptor.key + "\")"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cassert>
|
||||
|
||||
#include "src/DebugToolDrivers/TargetInterfaces/Microchip/Avr8/AvrIspInterface.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/EdbgInterface.hpp"
|
||||
#include "src/Targets/Microchip/Avr8/TargetDescriptionFile.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::Avr8::AvrIspInterface
|
||||
{
|
||||
public:
|
||||
explicit EdbgAvrIspInterface(
|
||||
EdbgInterface* edbgInterface,
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile
|
||||
);
|
||||
|
||||
/**
|
||||
* 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::Avr8::TargetSignature getDeviceId() override;
|
||||
|
||||
/**
|
||||
* Reads a particular fuse byte from the AVR target.
|
||||
*
|
||||
* @param fuseRegisterDescriptor
|
||||
* @return
|
||||
*/
|
||||
Targets::Microchip::Avr8::FuseValue readFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor
|
||||
) override;
|
||||
|
||||
/**
|
||||
* Reads the lock bit byte from the AVR target.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
unsigned char readLockBitByte() override;
|
||||
|
||||
/**
|
||||
* Programs a particular fuse on the AVR target.
|
||||
*
|
||||
* @param fuseRegisterDescriptor
|
||||
* @param value
|
||||
*/
|
||||
void programFuse(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor,
|
||||
Targets::Microchip::Avr8::FuseValue value
|
||||
) 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::Avr8::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);
|
||||
|
||||
Targets::Microchip::Avr8::FuseType resolveFuseType(
|
||||
const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "BreakEvent.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
using Targets::TargetBreakCause;
|
||||
|
||||
BreakEvent::BreakEvent(const AvrEvent& event)
|
||||
: AvrEvent(event)
|
||||
{
|
||||
if (this->eventData.size() < 8) {
|
||||
/*
|
||||
* All BreakEvent packets must consist of at least 8 bytes:
|
||||
* 1 byte for event ID
|
||||
* 4 bytes for program counter
|
||||
* 1 byte for break cause
|
||||
* 2 bytes for extended info
|
||||
*/
|
||||
throw Exception{"Failed to process BreakEvent from AvrEvent - unexpected packet size."};
|
||||
}
|
||||
|
||||
// Program counter consists of 4 bytes
|
||||
this->programCounter = static_cast<std::uint32_t>(
|
||||
(this->eventData[4] << 24) | (this->eventData[3] << 16) | (this->eventData[2] << 8) | this->eventData[1]
|
||||
) * 2;
|
||||
|
||||
// Break cause is 1 byte, where 0x01 is 'program breakpoint' and 0x00 'unspecified'
|
||||
this->breakCause = this->eventData[7] == 0x01 ? TargetBreakCause::BREAKPOINT : TargetBreakCause::UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/AvrEvent.hpp"
|
||||
#include "src/Targets/TargetBreakpoint.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class BreakEvent: public AvrEvent
|
||||
{
|
||||
public:
|
||||
std::uint32_t programCounter;
|
||||
Targets::TargetBreakCause breakCause;
|
||||
|
||||
explicit BreakEvent(const AvrEvent& event);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "src/TargetController/Exceptions/TargetOperationFailure.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/Avr8Generic/Avr8GenericResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
enum class Avr8CommandFailureCode: std::uint8_t
|
||||
{
|
||||
DEBUGWIRE_PHYSICAL_ERROR = 0x10,
|
||||
JTAGM_FAILED_TO_INITIALISE = 0x11,
|
||||
UNKNOWN_JTAG_ERROR = 0x12,
|
||||
JTAG_LOW_LEVEL_ERROR = 0x13,
|
||||
UNSUPPORTED_JTAGM_VERSION = 0x14,
|
||||
JTAG_MASTER_TIMED_OUT = 0x15,
|
||||
JTAG_BIT_BANGER_TIMED_OUT = 0x16,
|
||||
PARITY_ERROR_IN_RECEIVED_DATA = 0x17,
|
||||
MISSING_EMPTY_BYTE = 0x18,
|
||||
PDI_PHYSICAL_TIMED_OUT = 0x19,
|
||||
PHYSICAL_LEVEL_COLLISION = 0x1A,
|
||||
PDI_ENABLE_FAILED = 0x1B,
|
||||
TARGET_NOT_FOUND = 0x20,
|
||||
BAUD_INCREASE_FAILURE = 0x21,
|
||||
TARGET_POWER_NOT_DETECTED = 0x22,
|
||||
MUST_RUN_ATTACH_COMMAND_FIRST = 0x23,
|
||||
DEVICES_EXCEED_31 = 0x24,
|
||||
CONFIGURED_DEVICE_BITS_DO_NOT_ADD_UP_TO_DETECTED_BITS = 0x25,
|
||||
PHYSICAL_NOT_ACTIVATED = 0x31,
|
||||
ILLEGAL_RUN_OR_STOPPED_STATE = 0x32,
|
||||
INVALID_CONFIG_FOR_ACTIVATE_PHYSICAL = 0x33,
|
||||
NOT_A_VALID_MEMTYPE = 0x34,
|
||||
TOO_MANY_OR_TOO_FEW_BYTES = 0x35,
|
||||
ASKED_FOR_A_BAD_ADDRESS = 0x36,
|
||||
ASKED_FOR_BADLY_ALIGNED_DATA = 0x37,
|
||||
ADDRESS_NOT_WITHIN_LEGAL_RANGE = 0x38,
|
||||
ILLEGAL_VALUE_GIVEN = 0x39,
|
||||
ILLEGAL_TARGET_ID = 0x3A,
|
||||
CLOCK_VALUE_OUT_OF_RANGE = 0x3B,
|
||||
TIMEOUT_OCCURRED = 0x3C,
|
||||
READ_AN_ILLEGAL_OCD_STATUS = 0x3D,
|
||||
NVM_FAILED_TO_BE_ENABLED = 0x40,
|
||||
NVM_FAILED_TO_BE_DISABLED = 0x41,
|
||||
ILLEGAL_CONTROL_OR_STATUS_BITS = 0x42,
|
||||
CRC_MISMATCH = 0x43,
|
||||
FAILED_TO_ENABLE_OCD = 0x44,
|
||||
DEVICE_NOT_UNDER_CONTROL = 0x50,
|
||||
ERROR_WHEN_READING_PC = 0x60,
|
||||
ERROR_WHEN_READING_REGISTER = 0x61,
|
||||
ERROR_WHILE_READING = 0x70,
|
||||
ERROR_WHILE_WRITING = 0x71,
|
||||
TIMEOUT_WHILE_READING = 0x72,
|
||||
INVALID_BREAKPOINT_CONFIGURATION = 0x80,
|
||||
NOT_ENOUGH_AVAILABLE_RESOURCES = 0x81,
|
||||
FEATURE_NOT_AVAILABLE = 0x90,
|
||||
UNKNOWN_COMMAND = 0x91,
|
||||
UNKNOWN_ERROR = 0xFF,
|
||||
};
|
||||
|
||||
class Avr8CommandFailure: public Exceptions::TargetOperationFailure
|
||||
{
|
||||
public:
|
||||
std::optional<Avr8CommandFailureCode> code;
|
||||
|
||||
explicit Avr8CommandFailure(const std::string& message): TargetOperationFailure(message) {
|
||||
this->message = message;
|
||||
}
|
||||
|
||||
explicit Avr8CommandFailure(const char* message): TargetOperationFailure(message) {
|
||||
this->message = std::string{message};
|
||||
}
|
||||
|
||||
explicit Avr8CommandFailure(
|
||||
const std::string& message,
|
||||
const ResponseFrames::Avr8Generic::Avr8GenericResponseFrame& responseFrame
|
||||
)
|
||||
: TargetOperationFailure(message)
|
||||
{
|
||||
this->message = message;
|
||||
|
||||
if (responseFrame.payload.size() == 3) {
|
||||
/*
|
||||
* The response includes a failure code - lookup the corresponding description and append it to the
|
||||
* exception message.
|
||||
*/
|
||||
const auto failureCode = static_cast<Avr8CommandFailureCode>(responseFrame.payload[2]);
|
||||
const auto failureCodeDescriptionIt = this->failureCodeToDescription.find(failureCode);
|
||||
|
||||
if (failureCodeDescriptionIt != this->failureCodeToDescription.end()) {
|
||||
this->code = failureCode;
|
||||
this->message += " - Failure reason: " + failureCodeDescriptionIt->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const inline auto failureCodeToDescription = std::map<Avr8CommandFailureCode, std::string>{
|
||||
{Avr8CommandFailureCode::DEBUGWIRE_PHYSICAL_ERROR, "debugWIRE physical error"},
|
||||
{Avr8CommandFailureCode::JTAGM_FAILED_TO_INITIALISE, "JTAGM failed to initialise"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_JTAG_ERROR, "JTAGM did something strange"},
|
||||
{Avr8CommandFailureCode::JTAG_LOW_LEVEL_ERROR, "JTAG low level error"},
|
||||
{Avr8CommandFailureCode::UNSUPPORTED_JTAGM_VERSION, "Unsupported version of JTAGM"},
|
||||
{Avr8CommandFailureCode::JTAG_MASTER_TIMED_OUT, "JTAG master timed out"},
|
||||
{Avr8CommandFailureCode::JTAG_BIT_BANGER_TIMED_OUT, "JTAG bit banger timed out"},
|
||||
{Avr8CommandFailureCode::PARITY_ERROR_IN_RECEIVED_DATA, "Parity error in received data"},
|
||||
{Avr8CommandFailureCode::MISSING_EMPTY_BYTE, "Did not receive EMPTY byte"},
|
||||
{Avr8CommandFailureCode::PDI_PHYSICAL_TIMED_OUT, "PDI physical timed out"},
|
||||
{Avr8CommandFailureCode::PHYSICAL_LEVEL_COLLISION, "Collision on physical level"},
|
||||
{Avr8CommandFailureCode::PDI_ENABLE_FAILED, "PDI enable failed"},
|
||||
{Avr8CommandFailureCode::TARGET_NOT_FOUND, "Target not found"},
|
||||
{Avr8CommandFailureCode::BAUD_INCREASE_FAILURE, "Failure when increasing baud"},
|
||||
{Avr8CommandFailureCode::TARGET_POWER_NOT_DETECTED, "Target power not detected"},
|
||||
{Avr8CommandFailureCode::MUST_RUN_ATTACH_COMMAND_FIRST, "Must run attach command first"},
|
||||
{Avr8CommandFailureCode::DEVICES_EXCEED_31, "Devices > 31"},
|
||||
{Avr8CommandFailureCode::CONFIGURED_DEVICE_BITS_DO_NOT_ADD_UP_TO_DETECTED_BITS, "Configured device bits do not add up to detected bits"},
|
||||
{Avr8CommandFailureCode::PHYSICAL_NOT_ACTIVATED, "Physical not activated"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_RUN_OR_STOPPED_STATE, "Illegal run / stopped state"},
|
||||
{Avr8CommandFailureCode::INVALID_CONFIG_FOR_ACTIVATE_PHYSICAL, "Invalid config for activate physical"},
|
||||
{Avr8CommandFailureCode::NOT_A_VALID_MEMTYPE, "Not a valid memtype"},
|
||||
{Avr8CommandFailureCode::TOO_MANY_OR_TOO_FEW_BYTES, "Too many or too few bytes"},
|
||||
{Avr8CommandFailureCode::ASKED_FOR_A_BAD_ADDRESS, "Asked for a bad address"},
|
||||
{Avr8CommandFailureCode::ASKED_FOR_BADLY_ALIGNED_DATA, "Asked for badly aligned data"},
|
||||
{Avr8CommandFailureCode::ADDRESS_NOT_WITHIN_LEGAL_RANGE, "Address not within legal range"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_VALUE_GIVEN, "Illegal value given"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_TARGET_ID, "Illegal target ID"},
|
||||
{Avr8CommandFailureCode::CLOCK_VALUE_OUT_OF_RANGE, "Clock value out of range"},
|
||||
{Avr8CommandFailureCode::TIMEOUT_OCCURRED, "A timeout occurred"},
|
||||
{Avr8CommandFailureCode::READ_AN_ILLEGAL_OCD_STATUS, "Read an illegal OCD status - check OCDEN fuse bit (if applicable)"},
|
||||
{Avr8CommandFailureCode::NVM_FAILED_TO_BE_ENABLED, "NVM failed to be enabled"},
|
||||
{Avr8CommandFailureCode::NVM_FAILED_TO_BE_DISABLED, "NVM failed to be disabled"},
|
||||
{Avr8CommandFailureCode::ILLEGAL_CONTROL_OR_STATUS_BITS, "Illegal control/status bits"},
|
||||
{Avr8CommandFailureCode::CRC_MISMATCH, "CRC mismatch"},
|
||||
{Avr8CommandFailureCode::FAILED_TO_ENABLE_OCD, "Failed to enable OCD"},
|
||||
{Avr8CommandFailureCode::DEVICE_NOT_UNDER_CONTROL, "Device not under control - check OCDEN fuse bit (if applicable)"},
|
||||
{Avr8CommandFailureCode::ERROR_WHEN_READING_PC, "Error when reading PC"},
|
||||
{Avr8CommandFailureCode::ERROR_WHEN_READING_REGISTER, "Error when reading register"},
|
||||
{Avr8CommandFailureCode::ERROR_WHILE_READING, "Error while reading"},
|
||||
{Avr8CommandFailureCode::ERROR_WHILE_WRITING, "Error while writing"},
|
||||
{Avr8CommandFailureCode::TIMEOUT_WHILE_READING, "Timeout while reading"},
|
||||
{Avr8CommandFailureCode::INVALID_BREAKPOINT_CONFIGURATION, "Invalid breakpoint configuration"},
|
||||
{Avr8CommandFailureCode::NOT_ENOUGH_AVAILABLE_RESOURCES, "Not enough available resources"},
|
||||
{Avr8CommandFailureCode::FEATURE_NOT_AVAILABLE, "Feature not available"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_COMMAND, "Command has not been implemented"},
|
||||
{Avr8CommandFailureCode::UNKNOWN_ERROR, "Unknown error reported by EDBG device"},
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "DebugWireJtagParameters.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
#include "src/Exceptions/InternalFatalErrorException.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
DebugWireJtagParameters::DebugWireJtagParameters(
|
||||
const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile
|
||||
) {
|
||||
using Services::StringService;
|
||||
|
||||
const auto& programMemorySegment = targetDescriptionFile.getProgramMemorySegment();
|
||||
const auto& ramMemorySegment = targetDescriptionFile.getRamMemorySegment();
|
||||
const auto& eepromMemorySegment = targetDescriptionFile.getEepromMemorySegment();
|
||||
|
||||
this->flashPageSize = static_cast<std::uint16_t>(programMemorySegment.pageSize.value());
|
||||
this->flashSize = programMemorySegment.size;
|
||||
this->flashStartWordAddress = static_cast<std::uint32_t>(programMemorySegment.startAddress / 2);
|
||||
|
||||
const auto firstBootSectionOptionGroup = targetDescriptionFile.tryGetPropertyGroup(
|
||||
"boot_section_options.boot_section_1"
|
||||
);
|
||||
if (firstBootSectionOptionGroup.has_value()) {
|
||||
this->bootSectionStartWordAddress = static_cast<std::uint32_t>(
|
||||
StringService::toUint32(firstBootSectionOptionGroup->get().getProperty("start_address").value) / 2
|
||||
);
|
||||
}
|
||||
|
||||
this->ramStartAddress = static_cast<std::uint16_t>(ramMemorySegment.startAddress);
|
||||
this->eepromSize = static_cast<std::uint16_t>(eepromMemorySegment.size);
|
||||
this->eepromPageSize = static_cast<std::uint8_t>(eepromMemorySegment.pageSize.value());
|
||||
|
||||
const auto& ocdPropertyGroup = targetDescriptionFile.getPropertyGroup("ocd");
|
||||
this->ocdRevision = StringService::toUint8(ocdPropertyGroup.getProperty("ocd_revision").value);
|
||||
this->ocdDataRegisterAddress = StringService::toUint8(ocdPropertyGroup.getProperty("ocd_datareg").value);
|
||||
|
||||
const auto eepromPeripheralDescriptor = targetDescriptionFile.getTargetPeripheralDescriptor("eeprom");
|
||||
const auto& eepromRegisterGroupDescriptor = eepromPeripheralDescriptor.getRegisterGroupDescriptor("eeprom");
|
||||
|
||||
const auto& eearDescriptor = eepromRegisterGroupDescriptor.tryGetRegisterDescriptor("eear");
|
||||
if (eearDescriptor.has_value()) {
|
||||
const auto startAddress = eearDescriptor->get().startAddress;
|
||||
this->eearAddressLow = static_cast<std::uint8_t>(startAddress);
|
||||
|
||||
/*
|
||||
* If the target doesn't have a high byte in the `EEAR` address, the `eearAddressHigh` parameter should be
|
||||
* equal to the `eearAddressLow` parameter, as stated in the "EDBG-based Tools Protocols" document.
|
||||
*/
|
||||
this->eearAddressHigh = static_cast<std::uint8_t>(startAddress + (eearDescriptor->get().size - 1));
|
||||
|
||||
} else {
|
||||
const auto& eearlDescriptor = eepromRegisterGroupDescriptor.getRegisterDescriptor("eearl");
|
||||
this->eearAddressLow = static_cast<std::uint8_t>(eearlDescriptor.startAddress);
|
||||
|
||||
/*
|
||||
* Some debugWIRE targets only have a single-byte `EEARL` register.
|
||||
*
|
||||
* In the absence of an `EEARH` register, and if there is no high byte in the `EEARL` register, the
|
||||
* `eearAddressHigh` parameter should be equal to the `eearAddressLow` parameter, as stated in the
|
||||
* "EDBG-based Tools Protocols" document.
|
||||
*/
|
||||
const auto eearhDescriptor = eepromRegisterGroupDescriptor.tryGetRegisterDescriptor("eearh");
|
||||
this->eearAddressHigh = static_cast<std::uint8_t>(
|
||||
eearhDescriptor.has_value()
|
||||
? eearhDescriptor->get().startAddress
|
||||
: eearlDescriptor.startAddress + (eearlDescriptor.size - 1)
|
||||
);
|
||||
}
|
||||
|
||||
this->eedrAddress = static_cast<std::uint8_t>(
|
||||
eepromRegisterGroupDescriptor.getRegisterDescriptor("eedr").startAddress
|
||||
);
|
||||
|
||||
this->eecrAddress = static_cast<std::uint8_t>(
|
||||
eepromRegisterGroupDescriptor.getRegisterDescriptor("eecr").startAddress
|
||||
);
|
||||
|
||||
const auto cpuPeripheralDescriptor = targetDescriptionFile.getTargetPeripheralDescriptor("cpu");
|
||||
const auto& cpuRegisterGroupDescriptor = cpuPeripheralDescriptor.getRegisterGroupDescriptor("cpu");
|
||||
|
||||
const auto spmcsrDescriptor = cpuRegisterGroupDescriptor.tryGetFirstRegisterDescriptor({"spmcsr", "spmcr"});
|
||||
|
||||
if (spmcsrDescriptor.has_value()) {
|
||||
this->spmcrAddress = static_cast<std::uint8_t>(spmcsrDescriptor->get().startAddress);
|
||||
|
||||
} else {
|
||||
const auto bootLoadPeripheral = targetDescriptionFile.getTargetPeripheralDescriptor("boot_load");
|
||||
const auto& bootLoaderRegisterGroupDescriptor = bootLoadPeripheral.getRegisterGroupDescriptor("boot_load");
|
||||
|
||||
const auto spmcsrDescriptor = bootLoaderRegisterGroupDescriptor.tryGetFirstRegisterDescriptor(
|
||||
{"spmcsr", "spmcr"}
|
||||
);
|
||||
|
||||
if (!spmcsrDescriptor.has_value()) {
|
||||
throw Exceptions::InternalFatalErrorException{"Could not extract SPMCS register from TDF"};
|
||||
}
|
||||
|
||||
this->spmcrAddress = static_cast<std::uint8_t>(spmcsrDescriptor->get().startAddress);
|
||||
}
|
||||
|
||||
const auto osccalDescriptor = cpuRegisterGroupDescriptor.tryGetFirstRegisterDescriptor({
|
||||
"osccal",
|
||||
"osccal0",
|
||||
"osccal1",
|
||||
"fosccal",
|
||||
"sosccala",
|
||||
});
|
||||
|
||||
if (!osccalDescriptor.has_value()) {
|
||||
throw Exceptions::InternalFatalErrorException{"Could not extract OSCCAL register from TDF"};
|
||||
}
|
||||
|
||||
this->osccalAddress = static_cast<std::uint8_t>(osccalDescriptor->get().startAddress);
|
||||
|
||||
/*
|
||||
* All addresses for registers that reside in the IO memory segment include the IO segment offset
|
||||
* (start address). But the EDBG protocol requires *some* of these addresses to be stripped of this offset
|
||||
* before sending them as target parameters.
|
||||
*
|
||||
* This applies to the following addresses:
|
||||
*
|
||||
* - OSCALL Address
|
||||
* - EEARL Address
|
||||
* - EEARH Address
|
||||
* - EECR Address
|
||||
* - EEDR Address
|
||||
*
|
||||
* It does *not* seem to apply to the SPMCR or OCDDR address.
|
||||
*/
|
||||
const auto& ioSegmentStartAddress = static_cast<std::uint8_t>(
|
||||
targetDescriptionFile.getIoMemorySegment().startAddress
|
||||
);
|
||||
|
||||
// This is enforced in TDF validation
|
||||
assert(this->osccalAddress >= ioSegmentStartAddress);
|
||||
assert(this->eearAddressLow >= ioSegmentStartAddress);
|
||||
assert(this->eearAddressHigh >= ioSegmentStartAddress);
|
||||
assert(this->eecrAddress >= ioSegmentStartAddress);
|
||||
assert(this->eedrAddress >= ioSegmentStartAddress);
|
||||
|
||||
this->osccalAddress -= ioSegmentStartAddress;
|
||||
this->eearAddressLow -= ioSegmentStartAddress;
|
||||
this->eearAddressHigh -= ioSegmentStartAddress;
|
||||
this->eecrAddress -= ioSegmentStartAddress;
|
||||
this->eedrAddress -= ioSegmentStartAddress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
/**
|
||||
* EDBG parameters for debugWIRE and JTAG AVR targets.
|
||||
*
|
||||
* See Microchip's "EDBG-based Tools Protocols" document for more on these parameters.
|
||||
*/
|
||||
struct DebugWireJtagParameters
|
||||
{
|
||||
std::uint16_t flashPageSize;
|
||||
std::uint32_t flashSize;
|
||||
std::uint32_t flashStartWordAddress;
|
||||
std::optional<std::uint32_t> bootSectionStartWordAddress;
|
||||
std::uint16_t ramStartAddress;
|
||||
std::uint16_t eepromSize;
|
||||
std::uint8_t eepromPageSize;
|
||||
std::uint8_t ocdRevision;
|
||||
std::uint8_t ocdDataRegisterAddress;
|
||||
std::uint8_t eearAddressHigh;
|
||||
std::uint8_t eearAddressLow;
|
||||
std::uint8_t eedrAddress;
|
||||
std::uint8_t eecrAddress;
|
||||
std::uint8_t spmcrAddress;
|
||||
std::uint8_t osccalAddress;
|
||||
|
||||
DebugWireJtagParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "PdiParameters.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
PdiParameters::PdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile) {
|
||||
using Services::StringService;
|
||||
|
||||
const auto& pdiGroup = targetDescriptionFile.getPropertyGroup("pdi_interface");
|
||||
|
||||
this->appSectionPdiOffset = StringService::toUint32(pdiGroup.getProperty("app_section_offset").value);
|
||||
this->bootSectionPdiOffset = StringService::toUint32(pdiGroup.getProperty("boot_section_offset").value);
|
||||
this->eepromPdiOffset = StringService::toUint32(pdiGroup.getProperty("eeprom_offset").value);
|
||||
this->fuseRegistersPdiOffset = StringService::toUint32(pdiGroup.getProperty("fuse_registers_offset").value);
|
||||
this->lockRegistersPdiOffset = StringService::toUint32(pdiGroup.getProperty("lock_registers_offset").value);
|
||||
this->userSignaturesPdiOffset = StringService::toUint32(pdiGroup.getProperty("user_signatures_offset").value);
|
||||
this->prodSignaturesPdiOffset = StringService::toUint32(pdiGroup.getProperty("prod_signatures_offset").value);
|
||||
this->ramPdiOffset = StringService::toUint32(pdiGroup.getProperty("datamem_offset").value);
|
||||
this->signaturesPdiOffset = StringService::toUint16(pdiGroup.getProperty("signature_offset").value);
|
||||
|
||||
const auto& programMemorySegment = targetDescriptionFile.getProgramMemorySegment();
|
||||
const auto& eepromMemorySegment = targetDescriptionFile.getEepromMemorySegment();
|
||||
|
||||
this->appSectionSize = programMemorySegment.getSection("app_section").size;
|
||||
this->bootSectionSize = static_cast<std::uint16_t>(programMemorySegment.getSection("boot_section").size);
|
||||
this->flashPageSize = static_cast<std::uint16_t>(programMemorySegment.pageSize.value());
|
||||
this->eepromSize = static_cast<std::uint16_t>(eepromMemorySegment.size);
|
||||
this->eepromPageSize = static_cast<std::uint8_t>(eepromMemorySegment.pageSize.value());
|
||||
|
||||
const auto nvmPeripheralDescriptor = targetDescriptionFile.getTargetPeripheralDescriptor("nvm");
|
||||
this->nvmModuleBaseAddress = static_cast<std::uint16_t>(
|
||||
nvmPeripheralDescriptor.getRegisterGroupDescriptor("nvm").startAddress()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
/**
|
||||
* EDBG parameters for PDI-enabled (XMega) AVR targets.
|
||||
*
|
||||
* See Microchip's "EDBG-based Tools Protocols" document for more on these parameters.
|
||||
*/
|
||||
struct PdiParameters
|
||||
{
|
||||
std::uint32_t appSectionPdiOffset;
|
||||
std::uint32_t bootSectionPdiOffset;
|
||||
std::uint32_t eepromPdiOffset;
|
||||
std::uint32_t fuseRegistersPdiOffset;
|
||||
std::uint32_t lockRegistersPdiOffset;
|
||||
std::uint32_t userSignaturesPdiOffset;
|
||||
std::uint32_t prodSignaturesPdiOffset;
|
||||
std::uint32_t ramPdiOffset;
|
||||
std::uint32_t appSectionSize;
|
||||
std::uint16_t bootSectionSize;
|
||||
std::uint16_t flashPageSize;
|
||||
std::uint16_t eepromSize;
|
||||
std::uint8_t eepromPageSize;
|
||||
std::uint16_t nvmModuleBaseAddress;
|
||||
std::uint16_t signaturesPdiOffset;
|
||||
|
||||
PdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "UpdiParameters.hpp"
|
||||
|
||||
#include "src/Services/StringService.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
UpdiParameters::UpdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile) {
|
||||
using Services::StringService;
|
||||
|
||||
const auto& updiGroup = targetDescriptionFile.getPropertyGroup("updi_interface");
|
||||
|
||||
this->programMemoryStartAddress = StringService::toUint32(updiGroup.getProperty("progmem_offset").value);
|
||||
this->ocdModuleAddress = StringService::toUint16(updiGroup.getProperty("ocd_base_addr").value);
|
||||
|
||||
const auto& programMemorySegment = targetDescriptionFile.getProgramMemorySegment();
|
||||
const auto& eepromMemorySegment = targetDescriptionFile.getEepromMemorySegment();
|
||||
const auto& signatureMemorySegment = targetDescriptionFile.getSignatureMemorySegment();
|
||||
const auto& fuseMemorySegment = targetDescriptionFile.getFuseMemorySegment();
|
||||
const auto& lockbitMemorySegment = targetDescriptionFile.getLockbitMemorySegment();
|
||||
|
||||
this->flashSize = programMemorySegment.size;
|
||||
this->flashPageSize = static_cast<std::uint16_t>(programMemorySegment.pageSize.value());
|
||||
this->eepromStartAddress = static_cast<std::uint16_t>(eepromMemorySegment.startAddress);
|
||||
this->eepromSize = static_cast<std::uint16_t>(eepromMemorySegment.size);
|
||||
this->eepromPageSize = static_cast<std::uint8_t>(eepromMemorySegment.pageSize.value());
|
||||
this->signatureSegmentStartAddress = static_cast<std::uint16_t>(signatureMemorySegment.startAddress);
|
||||
this->fuseSegmentSize = static_cast<std::uint16_t>(fuseMemorySegment.size);
|
||||
this->fuseSegmentStartAddress = static_cast<std::uint16_t>(fuseMemorySegment.startAddress);
|
||||
this->lockbitSegmentStartAddress = static_cast<std::uint16_t>(lockbitMemorySegment.startAddress);
|
||||
|
||||
const auto nvmCtrlPeripheralDescriptor = targetDescriptionFile.getTargetPeripheralDescriptor("nvmctrl");
|
||||
this->nvmModuleBaseAddress = static_cast<std::uint16_t>(
|
||||
nvmCtrlPeripheralDescriptor.getRegisterGroupDescriptor("nvmctrl").startAddress()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/TargetDescriptionFile.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic
|
||||
{
|
||||
/**
|
||||
* EDBG parameters for UPDI-enabled AVR targets.
|
||||
*
|
||||
* See Microchip's "EDBG-based Tools Protocols" document for more on these parameters.
|
||||
* BTW that document seems to be a little outdated. It doesn't list most of these parameters. I discovered the
|
||||
* unlisted ones by looking at other open-source codebases and reverse engineering.
|
||||
*/
|
||||
struct UpdiParameters
|
||||
{
|
||||
std::uint32_t programMemoryStartAddress;
|
||||
std::uint16_t flashPageSize;
|
||||
std::uint8_t eepromPageSize;
|
||||
std::uint16_t nvmModuleBaseAddress;
|
||||
std::uint16_t ocdModuleAddress;
|
||||
std::uint32_t flashSize;
|
||||
std::uint16_t eepromSize;
|
||||
std::uint16_t fuseSegmentSize;
|
||||
std::uint16_t eepromStartAddress;
|
||||
std::uint16_t signatureSegmentStartAddress;
|
||||
std::uint16_t fuseSegmentStartAddress;
|
||||
std::uint16_t lockbitSegmentStartAddress;
|
||||
|
||||
UpdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
Avr8GenericResponseFrame::Avr8GenericResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception{"Response ID missing from AVR8 Generic response frame payload."};
|
||||
}
|
||||
|
||||
this->id = static_cast<Avr8ResponseId>(this->payload[0]);
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Avr8GenericResponseFrame::getPayloadData() const {
|
||||
/*
|
||||
* AVR8 data payloads are in little endian form and include two bytes before the data (response ID and
|
||||
* version byte) as well as an additional byte after the data, known as the 'status code'.
|
||||
*/
|
||||
auto data = std::vector<unsigned char>{this->payload.begin() + 2, this->payload.end() - 1};
|
||||
std::reverse(data.begin(), data.end());
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/AvrResponseFrame.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/Avr8Generic.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class Avr8GenericResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
Avr8ResponseId id;
|
||||
|
||||
explicit Avr8GenericResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
[[nodiscard]] std::vector<unsigned char> getPayloadData() const;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "GetDeviceId.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
GetDeviceId::GetDeviceId(const std::vector<AvrResponse>& AvrResponses)
|
||||
: Avr8GenericResponseFrame(AvrResponses)
|
||||
{}
|
||||
|
||||
Targets::Microchip::Avr8::TargetSignature GetDeviceId::extractSignature(
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
) const {
|
||||
using Targets::TargetPhysicalInterface;
|
||||
using Targets::Microchip::Avr8::TargetSignature;
|
||||
|
||||
const auto payloadData = this->getPayloadData();
|
||||
|
||||
switch (physicalInterface) {
|
||||
case TargetPhysicalInterface::DEBUG_WIRE: {
|
||||
/*
|
||||
* When using the DebugWire physical interface, the get device ID command will return
|
||||
* four bytes, where the first can be ignored.
|
||||
*/
|
||||
return TargetSignature{payloadData[1], payloadData[2], payloadData[3]};
|
||||
}
|
||||
case TargetPhysicalInterface::PDI:
|
||||
case TargetPhysicalInterface::UPDI: {
|
||||
/*
|
||||
* When using the PDI physical interface, the signature is returned in LSB format.
|
||||
*/
|
||||
return TargetSignature{payloadData[3], payloadData[2], payloadData[1]};
|
||||
}
|
||||
case TargetPhysicalInterface::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 TargetSignature{
|
||||
0x1E,
|
||||
static_cast<unsigned char>((jtagId << 4) >> 24),
|
||||
static_cast<unsigned char>((jtagId << 12) >> 24)
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return TargetSignature{payloadData[0], payloadData[1], payloadData[2]};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Targets/Microchip/Avr8/TargetSignature.hpp"
|
||||
#include "src/Targets/TargetPhysicalInterface.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class GetDeviceId: public Avr8GenericResponseFrame
|
||||
{
|
||||
public:
|
||||
explicit GetDeviceId(const std::vector<AvrResponse>& AvrResponses);
|
||||
|
||||
Targets::Microchip::Avr8::TargetSignature extractSignature(
|
||||
Targets::TargetPhysicalInterface physicalInterface
|
||||
) const;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Avr8GenericResponseFrame.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic
|
||||
{
|
||||
class GetProgramCounter: public Avr8GenericResponseFrame
|
||||
{
|
||||
public:
|
||||
explicit GetProgramCounter(const std::vector<AvrResponse>& avrResponses)
|
||||
: Avr8GenericResponseFrame(avrResponses)
|
||||
{}
|
||||
|
||||
Targets::TargetMemoryAddress extractProgramCounter() const {
|
||||
/*
|
||||
* The payload for the PC Read command should always consist of six bytes. Thr first two being the
|
||||
* command ID and version, the other four being the PC. The four PC bytes are little-endian.
|
||||
*/
|
||||
if (this->payload.size() != 6) {
|
||||
throw Exceptions::Exception{
|
||||
"Failed to extract PC from payload of PC read command response "
|
||||
"frame - unexpected payload size."
|
||||
};
|
||||
}
|
||||
|
||||
return static_cast<Targets::TargetMemoryAddress>(
|
||||
this->payload[5] << 24 | this->payload[4] << 16 | this->payload[3] << 8 | this->payload[2]
|
||||
) * 2;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#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 {this->payload.begin() + 2, this->payload.end() - 1};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "AvrIspResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::AvrIsp
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
AvrIspResponseFrame::AvrIspResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.size() < 2) {
|
||||
throw Exception{"Status code missing from AVRISP response frame payload."};
|
||||
}
|
||||
|
||||
this->statusCode = static_cast<StatusCode>(this->payload[1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::AvrIsp
|
||||
{
|
||||
enum class StatusCode: unsigned char
|
||||
{
|
||||
OK = 0x00,
|
||||
TIMEOUT = 0x80,
|
||||
FAILED = 0xC0,
|
||||
};
|
||||
|
||||
class AvrIspResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
StatusCode statusCode;
|
||||
|
||||
explicit AvrIspResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "AvrResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
void AvrResponseFrame::initFromAvrResponses(const std::vector<AvrResponse>& avrResponses) {
|
||||
// Build a raw frame buffer from the AvrResponse objects and just call initFromRawFrame()
|
||||
auto rawFrame = std::vector<unsigned char>{};
|
||||
|
||||
for (const auto& avrResponse : avrResponses) {
|
||||
rawFrame.insert(rawFrame.end(), avrResponse.responsePacket.begin(), avrResponse.responsePacket.end());
|
||||
}
|
||||
|
||||
return this->initFromRawFrame(rawFrame);
|
||||
}
|
||||
|
||||
void AvrResponseFrame::initFromRawFrame(const std::vector<unsigned char>& rawFrame) {
|
||||
if (rawFrame.size() < 4) {
|
||||
/*
|
||||
* All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and
|
||||
* a protocol handler ID).
|
||||
*/
|
||||
throw Exception{"Failed to construct AvrResponseFrame - unexpected end to raw frame"};
|
||||
}
|
||||
|
||||
if (rawFrame[0] != 0x0E) {
|
||||
// Invalid SOF byte value
|
||||
throw Exception{"Failed to construct AvrResponseFrame - unexpected SOF byte value in raw frame"};
|
||||
}
|
||||
|
||||
this->sequenceId = static_cast<std::uint16_t>((rawFrame[2] << 8) + rawFrame[1]);
|
||||
this->protocolHandlerId = static_cast<ProtocolHandlerId>(rawFrame[3]);
|
||||
|
||||
this->payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Edbg.hpp"
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/AvrResponse.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr
|
||||
{
|
||||
class AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Incrementing from 0x00
|
||||
*/
|
||||
std::uint16_t sequenceId = 0;
|
||||
|
||||
/**
|
||||
* Destination sub-protocol handler ID
|
||||
*/
|
||||
ProtocolHandlerId protocolHandlerId = ProtocolHandlerId::AVR8_GENERIC;
|
||||
|
||||
std::vector<unsigned char> payload;
|
||||
|
||||
explicit AvrResponseFrame(const std::vector<AvrResponse>& avrResponses) {
|
||||
this->initFromAvrResponses(avrResponses);
|
||||
}
|
||||
|
||||
virtual ~AvrResponseFrame() = default;
|
||||
|
||||
AvrResponseFrame(const AvrResponseFrame& other) = default;
|
||||
AvrResponseFrame(AvrResponseFrame&& other) = default;
|
||||
|
||||
AvrResponseFrame& operator = (const AvrResponseFrame& other) = default;
|
||||
AvrResponseFrame& operator = (AvrResponseFrame&& other) = default;
|
||||
|
||||
/**
|
||||
* An AvrResponse contains a single fragment of an AvrResponseFrame.
|
||||
*
|
||||
* This method will construct an AvrResponseFrame from a vector of AvrResponse.
|
||||
*
|
||||
* @param avrResponses
|
||||
*/
|
||||
void initFromAvrResponses(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
private:
|
||||
virtual void initFromRawFrame(const std::vector<unsigned char>& rawFrame);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#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 {this->payload.begin() + 2, this->payload.end()};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Discovery
|
||||
{
|
||||
/**
|
||||
* Discovery commands can only return two responses; A LIST response and a failure.
|
||||
*/
|
||||
enum class ResponseId: unsigned char
|
||||
{
|
||||
/*
|
||||
* According to the protocol docs, response ID 0x81 is for a LIST response, but this doesn't seem to be
|
||||
* well-defined. So just going to use a generic name.
|
||||
*/
|
||||
OK = 0x81,
|
||||
FAILED = 0xA0,
|
||||
};
|
||||
|
||||
class DiscoveryResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
ResponseId id;
|
||||
|
||||
explicit DiscoveryResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
std::vector<unsigned char> getPayloadData() const;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#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 {this->payload.begin() + 2, this->payload.end() - 1};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::EdbgControl
|
||||
{
|
||||
enum class EdbgControlResponseId: unsigned char
|
||||
{
|
||||
OK = 0x80,
|
||||
DATA = 0x84,
|
||||
FAILED = 0xA0,
|
||||
};
|
||||
|
||||
class EdbgControlResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
EdbgControlResponseId id;
|
||||
|
||||
explicit EdbgControlResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
|
||||
[[nodiscard]] std::vector<unsigned char> getPayloadData();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "HouseKeepingResponseFrame.hpp"
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::HouseKeeping
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
HouseKeepingResponseFrame::HouseKeepingResponseFrame(const std::vector<AvrResponse>& avrResponses)
|
||||
: AvrResponseFrame(avrResponses)
|
||||
{
|
||||
if (this->payload.empty()) {
|
||||
throw Exception{"Response ID missing from HOUSEKEEPING response frame payload."};
|
||||
}
|
||||
|
||||
this->id = static_cast<ResponseId>(this->payload[0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/DebugToolDrivers/Microchip/Protocols/Edbg/Avr/ResponseFrames/AvrResponseFrame.hpp"
|
||||
|
||||
namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::HouseKeeping
|
||||
{
|
||||
enum class ResponseId: unsigned char
|
||||
{
|
||||
OK = 0x80,
|
||||
LIST = 0x81,
|
||||
DATA = 0x84,
|
||||
FAILED = 0xA0,
|
||||
FAILED_WITH_DATA = 0xA1,
|
||||
};
|
||||
|
||||
class HouseKeepingResponseFrame: public AvrResponseFrame
|
||||
{
|
||||
public:
|
||||
ResponseId id;
|
||||
|
||||
explicit HouseKeepingResponseFrame(const std::vector<AvrResponse>& avrResponses);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user