diff --git a/src/Makefile b/src/Makefile index b618bfc4a7e..97f1d54b697 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,6 +20,9 @@ SRC = \ adiv5.c \ adiv5_jtagdp.c \ adiv5_swdp.c \ + atxmega.c \ + avr_jtagdp.c \ + avr_pdi.c \ command.c \ cortexa.c \ cortexm.c \ diff --git a/src/platforms/hosted/platform.c b/src/platforms/hosted/platform.c index ff6865a3e29..c10666963dc 100644 --- a/src/platforms/hosted/platform.c +++ b/src/platforms/hosted/platform.c @@ -25,6 +25,7 @@ #include "target.h" #include "target_internal.h" #include "adiv5.h" +#include "avr.h" #include "timing.h" #include "cl_utils.h" #include "gdb_if.h" @@ -252,6 +253,12 @@ int platform_jtag_dp_init(ADIv5_DP_t *dp) return 0; } +int platform_avr_jtag_pdi_init(avr_pdi_t *pdi) +{ + (void)pdi; + return 0; +} + char *platform_ident(void) { switch (info.bmp_type) { diff --git a/src/target/adiv5.c b/src/target/adiv5.c index af6251a7612..89c11c6a18c 100644 --- a/src/target/adiv5.c +++ b/src/target/adiv5.c @@ -683,9 +683,8 @@ void adiv5_dp_init(ADIv5_DP_t *dp) return; } DEBUG_INFO("DPIDR 0x%08" PRIx32 " (v%d %srev%d)\n", dp->idcode, - (uint8_t)((dp->idcode >> 12) & 0xf), - (dp->idcode & ADIV5_MINDP) ? "MINDP " : "", - (uint16_t)(dp->idcode >> 28)); + (uint8_t)((dp->idcode >> 12U) & 0xfU), + (dp->idcode & ADIV5_MINDP) ? "MINDP " : "", (uint16_t)(dp->idcode >> 28U)); volatile uint32_t ctrlstat = 0; #if PC_HOSTED == 1 platform_adiv5_dp_defaults(dp); diff --git a/src/target/atxmega.c b/src/target/atxmega.c new file mode 100644 index 00000000000..8131fc254fe --- /dev/null +++ b/src/target/atxmega.c @@ -0,0 +1,74 @@ +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "avr.h" + +#define IDCODE_XMEGA256A3U 0x9842U + +static const char tdesc_xmega6[] = + "" + "" + "" + " avr:106" + /* As it turns out, GDB doesn't acctually support this (despite asking for it!) yet */ +#if 0 + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " +#endif + ""; + +bool atxmega_probe(target *t) +{ + avr_pdi_t *pdi = t->priv; + + switch (t->idcode) { + case IDCODE_XMEGA256A3U: + t->core = "ATXMega256A3U"; + // RAM is actually at 0x01002000, but this is done to keep things right for GDB + // - internally we add 0x00800000 to get to the PDI mapped address. + target_add_ram(t, 0x00802000, 0x800); + // These are mapped here to make things make sense to GDB + // - internally we add 0x00800000 to get to the PDI mapped address. + avr_add_flash(t, 0x00000000, 0x40000); + avr_add_flash(t, 0x00040000, 0x2000); + t->tdesc = tdesc_xmega6; + pdi->hw_breakpoints_max = 2; + return true; + } + return false; +} diff --git a/src/target/avr.h b/src/target/avr.h new file mode 100644 index 00000000000..c23675c41f8 --- /dev/null +++ b/src/target/avr.h @@ -0,0 +1,41 @@ +#ifndef ATMEL___H +#define ATMEL___H + +#include "jtag_scan.h" +#include "target.h" + +enum avr_error_e +{ + pdi_ok, + pdi_failure, +}; + +#define AVR_MAX_BREAKPOINTS 2 + +typedef struct avr_pdi_s { + uint32_t idcode; + + uint8_t dp_jd_index; + enum target_halt_reason halt_reason; + enum avr_error_e error_state; + target_addr programCounter; + + target_addr hw_breakpoint[AVR_MAX_BREAKPOINTS]; + uint32_t hw_breakpoints_enabled; + uint32_t hw_breakpoints_max; +} avr_pdi_t; + +bool avr_pdi_init(avr_pdi_t *pdi); + +void avr_jtag_pdi_handler(uint8_t jd_index); +int platform_avr_jtag_pdi_init(avr_pdi_t *pdi); + +bool avr_jtag_shift_dr(jtag_proc_t *jp, uint8_t jd_index, uint8_t *dout, const uint8_t din); +bool avr_pdi_reg_write(avr_pdi_t *pdi, uint8_t reg, uint8_t value); +uint8_t avr_pdi_reg_read(avr_pdi_t *pdi, uint8_t reg); + +bool avr_attach(target *t); +void avr_detach(target *t); +void avr_add_flash(target *t, uint32_t start, size_t length); + +#endif /*ATMEL___H*/ diff --git a/src/target/avr_jtagdp.c b/src/target/avr_jtagdp.c new file mode 100644 index 00000000000..a762522cebc --- /dev/null +++ b/src/target/avr_jtagdp.c @@ -0,0 +1,55 @@ +#include "general.h" +#include "exception.h" +#include "avr.h" +#include "jtag_scan.h" +#include "jtagtap.h" + +#define PDI_DELAY 0xDBU + +void avr_jtag_pdi_handler(uint8_t jd_index) +{ + avr_pdi_t *pdi = calloc(1, sizeof(*pdi)); + if (!pdi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + pdi->dp_jd_index = jd_index; + pdi->idcode = jtag_devs[jd_index].jd_idcode; + pdi->error_state = pdi_ok; + pdi->hw_breakpoints_enabled = 0; + if ((PC_HOSTED == 0) || (!platform_avr_jtag_pdi_init(pdi))) { + // + } + avr_pdi_init(pdi); +} + +bool avr_jtag_shift_dr(jtag_proc_t *jp, uint8_t jd_index, uint8_t *dout, const uint8_t din) +{ + jtag_dev_t *d = &jtag_devs[jd_index]; + uint8_t result = 0; + uint16_t request = 0, response = 0; + uint8_t *data; + if (!dout) + return false; + do + { + data = (uint8_t *)&request; + jtagtap_shift_dr(); + jp->jtagtap_tdi_seq(0, ones, d->dr_prescan); + data[0] = din; + // Calculate the parity bit + for (uint8_t i = 0; i < 8; ++i) + data[1] ^= (din >> i) & 1U; + jp->jtagtap_tdi_tdo_seq((uint8_t *)&response, 1, (uint8_t *)&request, 9); + jp->jtagtap_tdi_seq(1, ones, d->dr_postscan); + jtagtap_return_idle(); + data = (uint8_t *)&response; + } + while (data[0] == PDI_DELAY && data[1] == 1); + // Calculate the parity bit + for (uint8_t i = 0; i < 8; ++i) + result ^= (data[0] >> i) & 1U; + *dout = data[0]; + DEBUG_INFO("Sent 0x%02x to target, response was 0x%02x (0x%x)\n", din, data[0], data[1]); + return result == data[1]; +} diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c new file mode 100644 index 00000000000..55c87e15f47 --- /dev/null +++ b/src/target/avr_pdi.c @@ -0,0 +1,701 @@ +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "avr.h" +#include "exception.h" +#include "gdb_packet.h" + +#define IR_PDI 0x7U +#define IR_BYPASS 0xFU + +#define PDI_BREAK 0xBBU +#define PDI_DELAY 0xDBU +#define PDI_EMPTY 0xEBU + +#define PDI_LDS 0x00U +#define PDI_LD 0x20U +#define PDI_STS 0x40U +#define PDI_ST 0x60U +#define PDI_LDCS 0x80U +#define PDI_REPEAT 0xA0U +#define PDI_STCS 0xC0U +#define PDI_KEY 0xE0U + +#define PDI_DATA_8 0x00U +#define PDI_DATA_16 0x01U +#define PDI_DATA_24 0x02U +#define PDI_DATA_32 0x03U + +#define PDI_ADDR_8 0x00U +#define PDI_ADDR_16 0x04U +#define PDI_ADDR_24 0x08U +#define PDI_ADDR_32 0x0CU + +#define PDI_MODE_IND_PTR 0x00U +#define PDI_MODE_IND_INCPTR 0x04U +#define PDI_MODE_DIR_PTR 0x08U +#define PDI_MODE_DIR_INCPTR 0x0CU // "Reserved" +#define PDI_MODE_MASK 0xF3U + +#define PDI_REG_STATUS 0U +#define PDI_REG_RESET 1U +#define PDI_REG_CTRL 2U +#define PDI_REG_R3 3U +#define PDI_REG_R4 4U + +#define PDI_RESET 0x59U + +#define PDI_FLASH_OFFSET 0x00800000U + +#define AVR_ADDR_DBG_CTR 0x00000000U +#define AVR_ADDR_DBG_PC 0x00000004U +#define AVR_ADDR_DBG_CTRL 0x0000000AU +#define AVR_ADDR_DBG_SPECIAL 0x0000000CU + +#define AVR_ADDR_DBG_BREAK_BASE 0x00000020U +#define AVR_ADDR_DBG_BREAK_UNK1 0x00000040U +#define AVR_ADDR_DBG_BREAK_UNK2 0x00000044U +#define AVR_ADDR_DBG_BREAK_UNK3 0x00000048U + +#define AVR_DBG_BREAK_ENABLED 0x80000000U +#define AVR_DBG_BREAK_MASK 0x00FFFFFFU + +#define AVR_DBG_READ_REGS 0x11U +#define AVR_NUM_REGS 32 + +#define AVR_ADDR_CPU_SPL 0x0100003DU + +#define AVR_ADDR_NVM 0x010001C0U +#define AVR_ADDR_NVM_DATA 0x010001C4U +#define AVR_ADDR_NVM_CMD 0x010001CAU +#define AVR_ADDR_NVM_STATUS 0x010001CFU + +#define AVR_NVM_CMD_NOP 0x00U +#define AVR_NVM_CMD_ERASE_FLASH_BUFFER 0x26U +#define AVR_NVM_CMD_WRITE_FLASH_BUFFER 0x23U +#define AVR_NVM_CMD_ERASE_FLASH_PAGE 0x2BU +#define AVR_NVM_CMD_WRITE_FLASH_PAGE 0x2EU +#define AVR_NVM_CMD_READ_NVM 0x43U + +#define AVR_NVM_BUSY 0x80U +#define AVR_NVM_FBUSY 0x40U + +typedef enum +{ + PDI_NVM = 0x02U, + PDI_DEBUG = 0x04U, +} pdi_key_e; + +static const char pdi_key_nvm[] = {0xff, 0x88, 0xd8, 0xcd, 0x45, 0xab, 0x89, 0x12}; +static const char pdi_key_debug[] = {0x21, 0x81, 0x7c, 0x9f, 0xd4, 0x2d, 0x21, 0x3a}; + +static bool avr_erase(target *t, int argc, char **argv); + +const struct command_s avr_cmd_list[] = { + {"erase", (cmd_handler)avr_erase, "Erase (part of) a device"}, + {NULL, NULL, NULL} +}; + +static void avr_reset(target *t); +static void avr_halt_request(target *t); +static void avr_halt_resume(target *t, bool step); +static enum target_halt_reason avr_halt_poll(target *t, target_addr *watch); + +static bool avr_check_error(target *t); +static void avr_mem_read(target *t, void *dest, target_addr src, size_t len); + +static void avr_regs_read(target *t, void *data); + +static int avr_flash_erase(struct target_flash *f, target_addr addr, size_t len); +static int avr_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len); + +static int avr_breakwatch_set(target *t, struct breakwatch *bw); +static int avr_breakwatch_clear(target *t, struct breakwatch *bw); + +typedef struct __attribute__((packed)) +{ + uint8_t general[32]; // r0-r31 + uint8_t sreg; // r32 + uint16_t sp; // r33 + uint32_t pc; // r34 +} avr_regs; + +bool avr_pdi_init(avr_pdi_t *pdi) +{ + target *t; + + /* Check for a valid part number in the IDCode */ + if ((pdi->idcode & 0x0FFFF000) == 0) { + DEBUG_WARN("Invalid PDI idcode %08" PRIx32 "\n", pdi->idcode); + free(pdi); + return false; + } + DEBUG_INFO("AVR ID 0x%08" PRIx32 " (v%d)\n", pdi->idcode, + (uint8_t)((pdi->idcode >> 28U) & 0xfU)); + jtag_dev_write_ir(&jtag_proc, pdi->dp_jd_index, IR_BYPASS); + + t = target_new(); + if (!t) + return false; + + t->cpuid = pdi->idcode; + t->idcode = (pdi->idcode >> 12) & 0xFFFFU; + t->driver = "Atmel AVR"; + t->core = "AVR"; + t->priv = pdi; + t->priv_free = free; + + t->attach = avr_attach; + t->detach = avr_detach; + + t->check_error = avr_check_error; + t->mem_read = avr_mem_read; + + t->regs_read = avr_regs_read; + + t->reset = avr_reset; + t->halt_request = avr_halt_request; + t->halt_resume = avr_halt_resume; + t->halt_poll = avr_halt_poll; + // Unlike on an ARM processor, where this is the length of a table, here we return the size of + // a suitable registers structure. + t->regs_size = sizeof(avr_regs); + + t->breakwatch_set = avr_breakwatch_set; + t->breakwatch_clear = avr_breakwatch_clear; + + target_add_commands(t, avr_cmd_list, "Atmel AVR"); + + if (atxmega_probe(t)) + return true; + pdi->halt_reason = TARGET_HALT_RUNNING; + return true; +} + +bool avr_pdi_reg_write(avr_pdi_t *pdi, uint8_t reg, uint8_t value) +{ + uint8_t result = 0, command = PDI_STCS | reg; + if (reg >= 16 || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, value)) + return false; + return result == PDI_EMPTY; +} + +uint8_t avr_pdi_reg_read(avr_pdi_t *pdi, uint8_t reg) +{ + uint8_t result = 0, command = PDI_LDCS | reg; + if (reg >= 16 || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) || result != PDI_EMPTY || + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, 0)) + return 0xFFU; // TODO - figure out a better way to indicate failure. + return result; +} + +static bool avr_pdi_write(avr_pdi_t *pdi, uint8_t bytes, uint32_t reg, uint32_t value) +{ + uint8_t result = 0; + uint8_t command = PDI_STS | PDI_ADDR_32 | bytes; + uint8_t data_bytes[4] = { + value & 0xffU, + (value >> 8U) & 0xffU, + (value >> 16U) & 0xffU, + (value >> 24U) & 0xffU + }; + + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, reg & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (reg >> 8U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (reg >> 16U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (reg >> 24U) & 0xffU) || result != PDI_EMPTY) + return false; + // This is intentionally <= to avoid `bytes + 1` silliness + for (uint8_t i = 0; i <= bytes; ++i) + { + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, data_bytes[i]) || result != PDI_EMPTY) + return false; + } + return true; +} + +static bool avr_pdi_read(avr_pdi_t *pdi, uint8_t bytes, uint32_t reg, uint32_t *value) +{ + uint8_t result = 0; + uint8_t command = PDI_LDS | PDI_ADDR_32 | bytes; + uint8_t data_bytes[4]; + uint32_t data = 0xffffffffU; + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, reg & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (reg >> 8U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (reg >> 16U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (reg >> 24U) & 0xffU) || result != PDI_EMPTY) + return false; + for (uint8_t i = 0; i <= bytes; ++i) + { + if (!avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &data_bytes[i], 0)) + return false; + } + data = data_bytes[0]; + if (bytes > PDI_DATA_8) + data |= data_bytes[1] << 8U; + if (bytes > PDI_DATA_16) + data |= data_bytes[2] << 16U; + if (bytes > PDI_DATA_24) + data |= data_bytes[3] << 24U; + *value = data; + return true; +} + +static inline bool avr_pdi_read8(avr_pdi_t *pdi, uint32_t reg, uint8_t *value) +{ + uint32_t data; + const bool result = avr_pdi_read(pdi, PDI_DATA_8, reg, &data); + if (result) + *value = data; + return result; +} + +static inline bool avr_pdi_read16(avr_pdi_t *pdi, uint32_t reg, uint16_t *value) +{ + uint32_t data; + const bool result = avr_pdi_read(pdi, PDI_DATA_16, reg, &data); + if (result) + *value = data; + return result; +} + +static inline bool avr_pdi_read24(avr_pdi_t *pdi, uint32_t reg, uint32_t *value) + { return avr_pdi_read(pdi, PDI_DATA_24, reg, value); } + +static inline bool avr_pdi_read32(avr_pdi_t *pdi, uint32_t reg, uint32_t *value) + { return avr_pdi_read(pdi, PDI_DATA_32, reg, value); } + +// Runs `st ptr ` +static bool avr_pdi_write_ptr(const avr_pdi_t *const pdi, const uint32_t addr) +{ + const uint8_t command = PDI_ST | PDI_MODE_DIR_PTR | PDI_DATA_32; + uint8_t result = 0; + return !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, addr & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (addr >> 8U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (addr >> 16U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (addr >> 24U) & 0xffU) && result == PDI_EMPTY; +} + +// Runs `repeat ` +static bool avr_pdi_repeat(const avr_pdi_t *const pdi, const uint32_t count) +{ + const uint32_t repeat = count - 1U; + const uint8_t command = PDI_REPEAT | PDI_DATA_32; + uint8_t result = 0; + return !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, repeat & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (repeat >> 8U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (repeat >> 16U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, (repeat >> 24U) & 0xffU) && result == PDI_EMPTY; +} + +static bool avr_pdi_read_ind(const avr_pdi_t *const pdi, const uint32_t addr, const uint8_t ptr_mode, + void *const dst, const uint32_t count) +{ + const uint8_t command = PDI_LD | ptr_mode; + uint8_t result = 0; + uint8_t *const data = (uint8_t *)dst; + if ((ptr_mode & PDI_MODE_MASK) || !count || + !avr_pdi_write_ptr(pdi, addr) || + !avr_pdi_repeat(pdi, count)) + return false; + // Run `ld ` + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) || result != PDI_EMPTY) + return false; + for (uint32_t i = 0; i < count; ++i) + { + if (!avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, data + i, 0)) + return false; + } + return true; +} + +static bool avr_pdi_write_ind(const avr_pdi_t *const pdi, const uint32_t addr, const uint8_t ptr_mode, + const void *const src, const uint32_t count) +{ + const uint8_t command = PDI_ST | ptr_mode; + uint8_t result = 0; + const uint8_t *const data = (const uint8_t *)src; + if ((ptr_mode & PDI_MODE_MASK) || !count || + !avr_pdi_write_ptr(pdi, addr) || + !avr_pdi_repeat(pdi, count)) + return false; + // Run `st ` + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, command) || result != PDI_EMPTY) + return false; + for (uint32_t i = 0; i < count; ++i) + { + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, data[i]) || result != PDI_EMPTY) + return false; + } + return true; +} + +static bool avr_enable(avr_pdi_t *pdi, pdi_key_e what) +{ + const char *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; + uint8_t result = 0; + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, PDI_KEY) || result != PDI_EMPTY) + return false; + for (uint8_t i = 0; i < 8; ++i) + { + if (avr_jtag_shift_dr(&jtag_proc, pdi->dp_jd_index, &result, key[i]) || result != PDI_EMPTY) + return false; + } + if (what == PDI_NVM) + { + while ((avr_pdi_reg_read(pdi, PDI_REG_STATUS) & what) != what) + continue; + return true; + } + else + return (avr_pdi_reg_read(pdi, PDI_REG_STATUS) & what) == what; +} + +static bool avr_disable(avr_pdi_t *pdi, pdi_key_e what) +{ + return avr_pdi_reg_write(pdi, PDI_REG_STATUS, ~what); +} + +void avr_add_flash(target *t, uint32_t start, size_t length) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + if (!f) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + f->start = start; + f->length = length; + f->blocksize = 0x100; + f->erase = avr_flash_erase; + f->write = avr_flash_write; + f->erased = 0xff; + target_add_flash(t, f); +} + +static bool avr_ensure_nvm_idle(avr_pdi_t *pdi) +{ + return avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_NVM_CMD, 0) && + avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_NVM_DATA, 0xFFU); +} + +static bool avr_config_breakpoints(avr_pdi_t *pdi, const bool step) +{ + const uint32_t addr_breakpoint_counter = AVR_ADDR_DBG_BREAK_BASE + (pdi->hw_breakpoints_max * 4); + const uint16_t breakpoint_count = step ? 0U : (pdi->hw_breakpoints_enabled << 8U); + if (step) + { + for (uint8_t i = 0; i < pdi->hw_breakpoints_max; ++i) + { + if (!avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_BREAK_BASE + (i * 4), 0U)) + return false; + } + } + else + { + for (uint8_t i = 0; i < pdi->hw_breakpoints_max; ++i) + { + if (!avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_BREAK_BASE + (i * 4), + pdi->hw_breakpoint[i] & AVR_DBG_BREAK_MASK)) + return false; + } + } + if (!avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_DBG_BREAK_UNK1, 0) || + !avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_DBG_BREAK_UNK2, 0) || + !avr_pdi_write(pdi, PDI_DATA_16, addr_breakpoint_counter, breakpoint_count) || + !avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_DBG_BREAK_UNK3, 0)) + return false; + return true; +} + +bool avr_attach(target *t) +{ + avr_pdi_t *pdi = t->priv; + volatile struct exception e; + jtag_dev_write_ir(&jtag_proc, pdi->dp_jd_index, IR_PDI); + + TRY_CATCH (e, EXCEPTION_ALL) { + target_reset(t); + if (!avr_enable(pdi, PDI_DEBUG)) + return false; + target_halt_request(t); + if (!avr_enable(pdi, PDI_NVM) || + !avr_ensure_nvm_idle(pdi) || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U) + return false; + } + return !e.type; +} + +void avr_detach(target *t) +{ + avr_pdi_t *pdi = t->priv; + + avr_disable(pdi, PDI_NVM); + avr_disable(pdi, PDI_DEBUG); + jtag_dev_write_ir(&jtag_proc, pdi->dp_jd_index, IR_BYPASS); +} + +static void avr_reset(target *t) +{ + avr_pdi_t *pdi = t->priv; + // We only actually want to do this if the target is not presently attached as this resets the NVM and debug enables + if (target_attached(t)) + return; + if (!avr_pdi_reg_write(pdi, PDI_REG_RESET, PDI_RESET)) + raise_exception(EXCEPTION_ERROR, "Error resetting device, device in incorrect state"); + if (avr_pdi_reg_read(pdi, PDI_REG_STATUS) != 0x00) + { + avr_disable(pdi, PDI_NVM); + avr_disable(pdi, PDI_DEBUG); + } + pdi->programCounter = 0; +} + +static void avr_halt_request(target *t) +{ + avr_pdi_t *pdi = t->priv; + /* To halt the processor we go through a few really specific steps: + * Write r4 to 1 to indicate we want to put the processor into debug-based pause + * Read r3 and check it's 0x10 which indicates the processor is held in reset and no debugging is active + * Releae reset + * Read r3 twice more, the first time should respond 0x14 to indicate the processor is still reset + * but that debug pause is requested, and the second should respond 0x04 to indicate the processor is now + * in debug pause state (halted) + */ + if (!avr_pdi_reg_write(pdi, PDI_REG_R4, 1) || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x10U || + !avr_pdi_reg_write(pdi, PDI_REG_RESET, 0) || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x14U || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U) + raise_exception(EXCEPTION_ERROR, "Error halting device, device in incorrect state"); + pdi->halt_reason = TARGET_HALT_REQUEST; +} + +static void avr_halt_resume(target *t, const bool step) +{ + avr_pdi_t *pdi = t->priv; + if (step) + { + const target_addr currentPC = pdi->programCounter; + const target_addr nextPC = currentPC + 1U; + /* To do a single step, we run the following steps: + * Write the debug control register to 4, which puts the processor in a temporary breakpoint mode + * Write the debug counter register with the address to stop execution on + * Write the program counter with the address to resume execution on + */ + if (avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + !avr_config_breakpoints(pdi, step) || + !avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_DBG_CTRL, 4) || + !avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_CTR, nextPC) || + !avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_PC, currentPC) || + !avr_pdi_reg_write(pdi, PDI_REG_R4, 1) || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + avr_pdi_reg_read(pdi, PDI_REG_STATUS) != 0x06U) + raise_exception(EXCEPTION_ERROR, "Error stepping device, device in incorrect state"); + pdi->halt_reason = TARGET_HALT_STEPPING; + } + else + { + /* To resume the processor we go through the following specific steps: + * Write the program counter to ensure we start where we expect + * Then we release the externally (PDI) applied reset + * We then poke the debug control register to indicate debug-supervised run + * Ensure that PDI is still in debug mode (r4 = 1) + * Read r3 to see that the processor is resuming + */ + if (avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + !avr_config_breakpoints(pdi, step) || + !avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_PC, pdi->programCounter) || + !avr_pdi_reg_write(pdi, PDI_REG_RESET, 0) || + !avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_DBG_CTRL, 0) || + !avr_pdi_reg_write(pdi, PDI_REG_R4, 1)) + raise_exception(EXCEPTION_ERROR, "Error resuming device, device in incorrect state"); + pdi->halt_reason = TARGET_HALT_RUNNING; + } +} + +static enum target_halt_reason avr_halt_poll(target *t, target_addr *watch) +{ + avr_pdi_t *pdi = t->priv; + if (pdi->halt_reason == TARGET_HALT_RUNNING && + avr_pdi_reg_read(pdi, PDI_REG_R3) == 0x04U) + pdi->halt_reason = TARGET_HALT_BREAKPOINT; + (void)watch; + return pdi->halt_reason; +} + +static bool avr_check_error(target *t) +{ + avr_pdi_t *pdi = t->priv; + return pdi->error_state != pdi_ok; +} + +static void avr_mem_read(target *t, void *dest, target_addr src, size_t len) +{ + avr_pdi_t *pdi = t->priv; + if (target_flash_for_addr(t, src) != NULL) + { + // This presently assumes src is a Flash address. + if (!avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_NVM_CMD, AVR_NVM_CMD_READ_NVM) || + !avr_pdi_read_ind(pdi, src | PDI_FLASH_OFFSET, PDI_MODE_IND_INCPTR, dest, len) || + !avr_ensure_nvm_idle(pdi)) + pdi->error_state = pdi_failure; + } + else if (src >= 0x00800000U) + { + if (!avr_pdi_read_ind(pdi, src + 0x00800000U, PDI_MODE_IND_INCPTR, dest, len)) + pdi->error_state = pdi_failure; + } +} + +static void avr_regs_read(target *t, void *data) +{ + avr_pdi_t *pdi = t->priv; + avr_regs *regs = (avr_regs *)data; + uint8_t status[3]; + uint32_t pc = 0; + if (!avr_pdi_read32(pdi, AVR_ADDR_DBG_PC, &pc) || + !avr_pdi_read_ind(pdi, AVR_ADDR_CPU_SPL, PDI_MODE_IND_INCPTR, status, 3) || + !avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_PC, 0) || + !avr_pdi_write(pdi, PDI_DATA_32, AVR_ADDR_DBG_CTR, AVR_NUM_REGS) || + !avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_DBG_CTRL, AVR_DBG_READ_REGS) || + !avr_pdi_reg_write(pdi, PDI_REG_R4, 1) || + !avr_pdi_read_ind(pdi, AVR_ADDR_DBG_SPECIAL, PDI_MODE_IND_PTR, regs->general, 32) || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U) + raise_exception(EXCEPTION_ERROR, "Error reading registers"); + // These aren't in the reads above because regs is a packed struct, which results in compiler errors + // Additionally, the program counter is stored in words and points to the next instruction to be executed + // So we substract 1 and double. + regs->pc = (pc - 1) << 1; + regs->sp = status[0] | (status[1] << 8); + regs->sreg = status[2]; + pdi->programCounter = pc - 1; +} + +static int avr_flash_erase(struct target_flash *f, target_addr addr, size_t len) +{ + avr_pdi_t *pdi = f->t->priv; + for (size_t i = 0; i < len; i += f->blocksize) + { + uint8_t status = 0; + if (!avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_NVM_CMD, AVR_NVM_CMD_ERASE_FLASH_PAGE) || + !avr_pdi_write(pdi, PDI_DATA_8, (addr + i) | PDI_FLASH_OFFSET, 0x55U)) + return 1; + + while (avr_pdi_read8(pdi, AVR_ADDR_NVM_STATUS, &status) && + (status & (AVR_NVM_BUSY | AVR_NVM_FBUSY)) == (AVR_NVM_BUSY | AVR_NVM_FBUSY)) + continue; + + if (!avr_ensure_nvm_idle(pdi) || + // This can only happen if the read failed. + (status & (AVR_NVM_BUSY | AVR_NVM_FBUSY)) != 0) + return 1; + } + return 0; +} + +static int avr_flash_write(struct target_flash *f, target_addr dest, const void *src, size_t len) +{ + avr_pdi_t *pdi = f->t->priv; + const uint8_t *const buffer = (const uint8_t *)src; + for (size_t i = 0; i < len; i += f->blocksize) + { + uint8_t status = 0; + const size_t amount = MIN(f->blocksize, len - i); + const uint32_t addr = (dest + i) | PDI_FLASH_OFFSET; + if (!avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_NVM_CMD, AVR_NVM_CMD_WRITE_FLASH_BUFFER) || + !avr_pdi_write_ind(pdi, addr, PDI_MODE_IND_INCPTR, buffer + i, amount) || + !avr_pdi_write(pdi, PDI_DATA_8, AVR_ADDR_NVM_CMD, AVR_NVM_CMD_WRITE_FLASH_PAGE) || + !avr_pdi_write(pdi, PDI_DATA_8, addr, 0xFFU)) + return 1; + + while (avr_pdi_read8(pdi, AVR_ADDR_NVM_STATUS, &status) && + (status & (AVR_NVM_BUSY | AVR_NVM_FBUSY)) == (AVR_NVM_BUSY | AVR_NVM_FBUSY)) + continue; + + if (!avr_ensure_nvm_idle(pdi) || + // This can only happen if the read failed. + (status & (AVR_NVM_BUSY | AVR_NVM_FBUSY)) != 0) + return 1; + } + return 0; +} + +static int avr_breakwatch_set(target *t, struct breakwatch *bw) +{ + avr_pdi_t *pdi = t->priv; + switch (bw->type) + { + case TARGET_BREAK_HARD: + { + const uint8_t bp = pdi->hw_breakpoints_enabled; + if (bp == pdi->hw_breakpoints_max) + return -1; + pdi->hw_breakpoint[bp] = AVR_DBG_BREAK_ENABLED | bw->addr; + bw->reserved[0] = pdi->hw_breakpoint[bp]; + ++pdi->hw_breakpoints_enabled; + return 0; + } + default: + return 1; + } +} + +static int avr_breakwatch_clear(target *t, struct breakwatch *bw) +{ + avr_pdi_t *pdi = t->priv; + switch (bw->type) + { + case TARGET_BREAK_HARD: + { + uint8_t bp = 0; + // Locate the breakpoint + for (; bp < pdi->hw_breakpoints_max; ++bp) + { + if (pdi->hw_breakpoint[bp] == bw->reserved[0]) + break; + } + // Fail if we cannot find it + if (bp == pdi->hw_breakpoints_max) + return -1; + // Shuffle the remaining breakpoints. + for (; bp < (pdi->hw_breakpoints_enabled - 1U); ++bp) + pdi->hw_breakpoint[bp] = pdi->hw_breakpoint[bp + 1]; + // Cleanup by disabling the breakpoint and fixing the count + pdi->hw_breakpoint[bp] = 0; + bw->reserved[0] = 0; + --pdi->hw_breakpoints_enabled; + return 0; + } + default: + return 1; + } +} + +static bool avr_erase(target *t, int argc, char **argv) +{ + if (argc < 2) + tc_printf(t, "usage: monitor erase () \n"); + else + { + target_addr begin = 0; + size_t length = 0; + if (argc >= 3) + { + begin = atol(argv[1]); + length = atol(argv[2]); + } + else + length = atol(argv[1]); + target_flash_erase(t, begin, length); + } + return true; +} diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index f0ddea43df6..d3fdb4b4540 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -22,6 +22,7 @@ #include "general.h" #include "jtag_scan.h" #include "adiv5.h" +#include "avr.h" #include "jtag_devs.h" jtag_dev_descr_t dev_descr[] = { @@ -62,6 +63,9 @@ jtag_dev_descr_t dev_descr[] = { .descr = "Xambala: RVDBG013."}, {.idcode = 0x000007A3, .idmask = 0x00000FFF, .descr = "Gigadevice BSD."}, + {.idcode = 0x0000003F, .idmask = 0x00000FFF, + .descr = "Atmel Limited: AVR JTAG-PDI port.", + .handler = avr_jtag_pdi_handler}, /* Just for fun, unsupported */ {.idcode = 0x8940303F, .idmask = 0xFFFFFFFF, .descr = "ATMega16."}, {.idcode = 0x0792603F, .idmask = 0xFFFFFFFF, .descr = "AT91SAM9261."}, diff --git a/src/target/jtag_scan.c b/src/target/jtag_scan.c index 4addc4921e3..42966c15c39 100644 --- a/src/target/jtag_scan.c +++ b/src/target/jtag_scan.c @@ -35,7 +35,7 @@ struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1]; int jtag_dev_count; /* bucket of ones for don't care TDI */ -static const uint8_t ones[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +const uint8_t ones[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; #if PC_HOSTED == 0 void jtag_add_device(const int dev_index, const jtag_dev_t *jtag_dev) @@ -207,8 +207,8 @@ int jtag_scan(const uint8_t *irlens) #endif /* Check for known devices and handle accordingly */ - for(i = 0; i < jtag_dev_count; i++) - for(j = 0; dev_descr[j].idcode; j++) + for(i = 0; i < jtag_dev_count; i++) { + for(j = 0; dev_descr[j].idcode; j++) { if((jtag_devs[i].jd_idcode & dev_descr[j].idmask) == dev_descr[j].idcode) { jtag_devs[i].current_ir = -1; @@ -219,6 +219,8 @@ int jtag_scan(const uint8_t *irlens) dev_descr[j].handler(i); break; } + } + } return jtag_dev_count; } diff --git a/src/target/jtag_scan.h b/src/target/jtag_scan.h index cc6d361a2c2..67449dd0eee 100644 --- a/src/target/jtag_scan.h +++ b/src/target/jtag_scan.h @@ -42,6 +42,7 @@ typedef struct jtag_dev_s { extern struct jtag_dev_s jtag_devs[JTAG_MAX_DEVS+1]; extern int jtag_dev_count; +extern const uint8_t ones[9]; void jtag_dev_write_ir(jtag_proc_t *jp, uint8_t jd_index, uint32_t ir); void jtag_dev_shift_dr(jtag_proc_t *jp, uint8_t jd_index, uint8_t *dout, const uint8_t *din, int ticks); diff --git a/src/target/target.c b/src/target/target.c index dd4208a85f7..eae1b5bbdb4 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -239,7 +239,7 @@ bool target_mem_map(target *t, char *tmp, size_t len) return true; } -static struct target_flash *flash_for_addr(target *t, uint32_t addr) +struct target_flash *target_flash_for_addr(target *t, uint32_t addr) { for (struct target_flash *f = t->flash; f; f = f->next) if ((f->start <= addr) && @@ -252,7 +252,7 @@ int target_flash_erase(target *t, target_addr addr, size_t len) { int ret = 0; while (len) { - struct target_flash *f = flash_for_addr(t, addr); + struct target_flash *f = target_flash_for_addr(t, addr); if (!f) { DEBUG_WARN("Erase stopped at 0x%06" PRIx32 "\n", addr); return ret; @@ -271,7 +271,7 @@ int target_flash_write(target *t, { int ret = 0; while (len) { - struct target_flash *f = flash_for_addr(t, dest); + struct target_flash *f = target_flash_for_addr(t, dest); if (!f) return 1; size_t tmptarget = MIN(dest + len, f->start + f->length); diff --git a/src/target/target_internal.h b/src/target/target_internal.h index 883d3b57313..bb9eab2abc7 100644 --- a/src/target/target_internal.h +++ b/src/target/target_internal.h @@ -143,6 +143,8 @@ void target_add_commands(target *t, const struct command_s *cmds, const char *na void target_add_ram(target *t, target_addr start, uint32_t len); void target_add_flash(target *t, struct target_flash *f); +struct target_flash *target_flash_for_addr(target *t, uint32_t addr); + /* Convenience function for MMIO access */ uint32_t target_mem_read32(target *t, uint32_t addr); uint16_t target_mem_read16(target *t, uint32_t addr); @@ -201,4 +203,6 @@ bool efm32_probe(target *t); bool msp432_probe(target *t); bool ke04_probe(target *t); bool rp_probe(target *t); + +bool atxmega_probe(target *t); #endif