From aad7b431303e56952a87dbe63f316378482f00e1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 20:25:48 +0000 Subject: [PATCH 01/42] avr_pdi: Begun building a scan handler for JTAG-PDI devices --- meson_options.txt | 1 + src/target/avr_pdi.c | 48 ++++++++++++++++++++++++++++++++++++++++++ src/target/avr_pdi.h | 47 +++++++++++++++++++++++++++++++++++++++++ src/target/jtag_devs.c | 11 ++++++++++ src/target/meson.build | 7 ++++++ 5 files changed, 114 insertions(+) create mode 100644 src/target/avr_pdi.c create mode 100644 src/target/avr_pdi.h diff --git a/meson_options.txt b/meson_options.txt index 612c2f53ec9..b8729eaef61 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -30,6 +30,7 @@ option( 'cortexm', 'riscv32', 'riscv64', + 'avr', 'apollo3', 'at32f4', 'ch32', diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c new file mode 100644 index 00000000000..2728907333e --- /dev/null +++ b/src/target/avr_pdi.c @@ -0,0 +1,48 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "jtag_scan.h" +#include "avr_pdi.h" + +void avr_jtag_pdi_handler(const uint8_t dev_index) +{ + avr_pdi_s *pdi = calloc(1, sizeof(*pdi)); + if (!pdi) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + pdi->dev_index = dev_index; + pdi->idcode = jtag_devs[dev_index].jd_idcode; +} diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h new file mode 100644 index 00000000000..89b819396b9 --- /dev/null +++ b/src/target/avr_pdi.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TARGET_AVR_PDI_H +#define TARGET_AVR_PDI_H + +#include + +typedef struct avr_pdi { + uint32_t idcode; + + uint8_t dev_index; +} avr_pdi_s; + +void avr_jtag_pdi_handler(uint8_t dev_index); + +#endif /*TARGET_AVR_PDI_H*/ diff --git a/src/target/jtag_devs.c b/src/target/jtag_devs.c index ecd6eca761a..ac71ebf3300 100644 --- a/src/target/jtag_devs.c +++ b/src/target/jtag_devs.c @@ -23,6 +23,7 @@ #include "general.h" #include "jtag_scan.h" #include "adiv5.h" +#include "avr_pdi.h" #include "riscv_debug.h" #include "jtag_devs.h" @@ -423,6 +424,16 @@ const jtag_dev_descr_s dev_descr[] = { #endif }, #endif +#ifdef ENABLE_AVR + { + .idcode = 0x0000003fU, + .idmask = 0x00000fffU, +#if ENABLE_DEBUG == 1 + .descr = "AVR JTAG-PDI port.", +#endif + .handler = avr_jtag_pdi_handler, + }, +#endif #if ENABLE_DEBUG == 1 { .idcode = 0x000007a3U, diff --git a/src/target/meson.build b/src/target/meson.build index 4e11cb99ef0..566e7565386 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -75,6 +75,7 @@ if is_firmware_build 'cortexm': 'Cortex-M support', 'riscv32': 'RISC-V 32-bit support', 'riscv64': 'RISC-V 64-bit support', + 'avr': '8-bit AVR support', 'at32f4': 'Arterytek parts', 'apollo3': 'Ambiq Apollo3 parts', 'ch32': 'WinChipHead CH32 parts', @@ -162,6 +163,11 @@ target_riscv64 = declare_dependency( dependencies: target_riscv, ) +target_avr = declare_dependency( + sources: files('avr_pdi.c'), + compile_args: ['-DENABLE_AVR=1'], +) + target_apollo3 = declare_dependency( sources: files('apollo3.c'), dependencies: target_cortexm, @@ -378,6 +384,7 @@ libbmd_target_deps = [ target_cortexm, target_riscv32, target_riscv64, + target_avr, # Enable all targets for libbmd target_apollo3, target_at32f4, From f08602ccd039617f9501403fe1d5fbd3c0d13e75 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 20:38:40 +0000 Subject: [PATCH 02/42] avr_pdi: Implemented a low-level routine for exchanging PDI messages with the part over JTAG --- src/target/avr_pdi.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 2728907333e..5ecadf9a8a4 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -35,6 +35,10 @@ #include "jtag_scan.h" #include "avr_pdi.h" +#define PDI_BREAK 0xbbU +#define PDI_DELAY 0xdbU +#define PDI_EMPTY 0xebU + void avr_jtag_pdi_handler(const uint8_t dev_index) { avr_pdi_s *pdi = calloc(1, sizeof(*pdi)); @@ -46,3 +50,38 @@ void avr_jtag_pdi_handler(const uint8_t dev_index) pdi->dev_index = dev_index; pdi->idcode = jtag_devs[dev_index].jd_idcode; } + +/* + * This is a PDI-specific DR manipulation function that handles PDI_DELAY responses + * transparently to the caller. It also does parity validation, returning true for + * valid parity. + */ +static bool avr_jtag_shift_dr(uint8_t dev_index, uint8_t *data_out, const uint8_t data_in) +{ + jtag_dev_s *dev = &jtag_devs[dev_index]; + uint8_t result = 0; + uint16_t request = 0; + uint16_t response = 0; + uint8_t *data; + if (!data_out) + return false; + do { + data = (uint8_t *)&request; + jtagtap_shift_dr(); + jtag_proc.jtagtap_tdi_seq(false, ones, dev->dr_prescan); + data[0] = data_in; + /* Calculate the parity bit */ + for (uint8_t i = 0; i < 8; ++i) + data[1] ^= (data_in >> i) & 1U; + jtag_proc.jtagtap_tdi_tdo_seq((uint8_t *)&response, !dev->dr_postscan, (uint8_t *)&request, 9); + jtag_proc.jtagtap_tdi_seq(true, ones, dev->dr_postscan); + jtagtap_return_idle(0); + 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; + *data_out = data[0]; + DEBUG_WARN("Sent 0x%02x to target, response was 0x%02x (0x%x)\n", data_in, data[0], data[1]); + return result == data[1]; +} From f5f9233647cf79cdc73d0b3be9c945a4b2dfaa9a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 20:39:43 +0000 Subject: [PATCH 03/42] avr_pdi: Added validation for a valid part to the JTAG-PDI handler and transitioned the part into PDI mode --- src/target/avr_pdi.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 5ecadf9a8a4..656a73be9f0 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -33,8 +33,11 @@ #include "general.h" #include "jtag_scan.h" +#include "jtagtap.h" #include "avr_pdi.h" +#define IR_PDI 0x7U + #define PDI_BREAK 0xbbU #define PDI_DELAY 0xdbU #define PDI_EMPTY 0xebU @@ -49,6 +52,16 @@ void avr_jtag_pdi_handler(const uint8_t dev_index) pdi->dev_index = dev_index; pdi->idcode = jtag_devs[dev_index].jd_idcode; + + /* Check for a valid part number in the JTAG ID code */ + if ((pdi->idcode & 0x0ffff000U) == 0) { + DEBUG_WARN("Invalid PDI idcode %08" PRIx32 "\n", pdi->idcode); + free(pdi); + return; + } + DEBUG_INFO("AVR ID 0x%08" PRIx32 " (v%u)\n", pdi->idcode, (uint8_t)((pdi->idcode >> 28U) & 0xfU)); + /* Transition the part into PDI mode */ + jtag_dev_write_ir(pdi->dev_index, IR_PDI); } /* From 1d9cc8623c1782394e33cd7df8b7215d1842b24a Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 21:12:34 +0000 Subject: [PATCH 04/42] avr_pdi: Implemented PDI register access functions --- src/target/avr_pdi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 656a73be9f0..606d2d1523b 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -42,6 +42,9 @@ #define PDI_DELAY 0xdbU #define PDI_EMPTY 0xebU +#define PDI_LDCS 0x80U +#define PDI_STCS 0xc0U + void avr_jtag_pdi_handler(const uint8_t dev_index) { avr_pdi_s *pdi = calloc(1, sizeof(*pdi)); @@ -98,3 +101,23 @@ static bool avr_jtag_shift_dr(uint8_t dev_index, uint8_t *data_out, const uint8_ DEBUG_WARN("Sent 0x%02x to target, response was 0x%02x (0x%x)\n", data_in, data[0], data[1]); return result == data[1]; } + +bool avr_pdi_reg_write(const avr_pdi_s *const pdi, const uint8_t reg, const uint8_t value) +{ + uint8_t result = 0; + const uint8_t command = PDI_STCS | reg; + if (reg >= 16 || avr_jtag_shift_dr(pdi->dev_index, &result, command) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, value)) + return false; + return result == PDI_EMPTY; +} + +uint8_t avr_pdi_reg_read(const avr_pdi_s *const pdi, const uint8_t reg) +{ + uint8_t result = 0; + const uint8_t command = PDI_LDCS | reg; + if (reg >= 16 || avr_jtag_shift_dr(pdi->dev_index, &result, command) || result != PDI_EMPTY || + !avr_jtag_shift_dr(pdi->dev_index, &result, 0)) + return 0xffU; // TODO - figure out a better way to indicate failure. + return result; +} From d70a9a0e2e3014dafb9f27da371c5338c23575e3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 22:03:05 +0000 Subject: [PATCH 05/42] atxmega: Implemented a probe routine for some of the ATXMega parts --- meson_options.txt | 1 + src/target/atxmega.c | 142 ++++++++++++++++++++++++++++++++++++++ src/target/meson.build | 7 ++ src/target/target_probe.c | 2 + src/target/target_probe.h | 2 + 5 files changed, 154 insertions(+) create mode 100644 src/target/atxmega.c diff --git a/meson_options.txt b/meson_options.txt index b8729eaef61..f94d06db885 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -32,6 +32,7 @@ option( 'riscv64', 'avr', 'apollo3', + 'atxmega', 'at32f4', 'ch32', 'ch579', diff --git a/src/target/atxmega.c b/src/target/atxmega.c new file mode 100644 index 00000000000..a8a9d0e6e63 --- /dev/null +++ b/src/target/atxmega.c @@ -0,0 +1,142 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2022 1BitSquared + * Written by Rachel Mant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "general.h" +#include "target.h" +#include "target_internal.h" +#include "target_probe.h" +#include "avr_pdi.h" + +#define IDCODE_XMEGA64A3U 0x9642U +#define IDCODE_XMEGA128A3U 0x9742U +#define IDCODE_XMEGA192A3U 0x9744U +#define IDCODE_XMEGA256A3U 0x9842U + +void avr_add_flash(target_s *const target, const uint32_t start, const size_t length, const uint16_t block_size) +{ + target_flash_s *flash = calloc(1, sizeof(*flash)); + if (!flash) { /* calloc failed: heap exhaustion */ + DEBUG_WARN("calloc: failed in %s\n", __func__); + return; + } + + flash->start = start; + flash->length = length; + flash->blocksize = block_size; + flash->erased = 0xffU; + target_add_flash(target, flash); +} + +bool atxmega_probe(target_s *const target) +{ + uint32_t application_flash = 0; + uint32_t application_table_flash = 0; + uint32_t bootloader_flash = 0; + uint16_t flash_block_size = 0; + uint32_t sram = 0; + + switch (target->part_id) { + case IDCODE_XMEGA64A3U: + /* + * The 64A3U has: + * 60kiB of normal Flash + * 4kiB of application table Flash + * 4kiB of bootloader Flash + * 16kiB of internal SRAM + */ + application_flash = 0xf000U; + application_table_flash = 0x1000U; + bootloader_flash = 0x1000U; + flash_block_size = 128; + target->core = "ATXMega64A3U"; + break; + case IDCODE_XMEGA128A3U: + /* + * The 128A3U has: + * 120kiB of normal Flash + * 8kiB of application table Flash + * 8kiB of bootloader Flash + * 16kiB of internal SRAM + */ + application_flash = 0x1e000U; + application_table_flash = 0x2000U; + bootloader_flash = 0x2000U; + flash_block_size = 256; + target->core = "ATXMega128A3U"; + break; + case IDCODE_XMEGA192A3U: + /* + * The 192A3U has: + * 184kiB of normal Flash + * 8kiB of application table Flash + * 8kiB of bootloader Flash + * 16kiB of internal SRAM + */ + application_flash = 0x2e000U; + application_table_flash = 0x2000U; + bootloader_flash = 0x2000U; + flash_block_size = 256; + target->core = "ATXMega192A3U"; + break; + case IDCODE_XMEGA256A3U: + /* + * The 256A3U has: + * 248kiB of normal Flash + * 8kiB of application table Flash + * 8kiB of bootloader Flash + * 16kiB of internal SRAM + */ + application_flash = 0x3e000U; + application_table_flash = 0x2000U; + bootloader_flash = 0x2000U; + flash_block_size = 256; + sram = 0x800U; + target->core = "ATXMega256A3U"; + break; + default: + return false; + } + + /* + * RAM is actually at 0x01002000 in the 24-bit linearised PDI address space however, because GDB/GCC, + * internally we have to map at 0x00800000 to get a suitable mapping for the host + */ + target_add_ram32(target, 0x00802000U, sram); + uint32_t flash_base_address = 0x00000000; + avr_add_flash(target, flash_base_address, application_flash, flash_block_size); + flash_base_address += application_flash; + avr_add_flash(target, flash_base_address, application_table_flash, flash_block_size); + flash_base_address += application_table_flash; + avr_add_flash(target, flash_base_address, bootloader_flash, flash_block_size); + return true; +} diff --git a/src/target/meson.build b/src/target/meson.build index 566e7565386..64eb3f16ec2 100644 --- a/src/target/meson.build +++ b/src/target/meson.build @@ -77,6 +77,7 @@ if is_firmware_build 'riscv64': 'RISC-V 64-bit support', 'avr': '8-bit AVR support', 'at32f4': 'Arterytek parts', + 'atxmega': 'ATXMega support', 'apollo3': 'Ambiq Apollo3 parts', 'ch32': 'WinChipHead CH32 parts', 'ch579': 'WinChipHead CH579 parts', @@ -173,6 +174,11 @@ target_apollo3 = declare_dependency( dependencies: target_cortexm, ) +target_atxmega = declare_dependency( + sources: files('atxmega.c'), + dependencies: target_avr, +) + target_ch579 = declare_dependency( sources: files('ch579.c'), dependencies: target_cortexm, @@ -387,6 +393,7 @@ libbmd_target_deps = [ target_avr, # Enable all targets for libbmd target_apollo3, + target_atxmega, target_at32f4, target_ch32, target_ch579, diff --git a/src/target/target_probe.c b/src/target/target_probe.c index 2fee2a324e1..c5fbc723674 100644 --- a/src/target/target_probe.c +++ b/src/target/target_probe.c @@ -156,4 +156,6 @@ TARGET_PROBE_WEAK_NOP(zynq7_probe) LPC55_DP_PREPARE_WEAK_NOP(lpc55_dp_prepare) +TARGET_PROBE_WEAK_NOP(atxmega_probe) + #endif /* _WIN32 */ diff --git a/src/target/target_probe.h b/src/target/target_probe.h index a68693dea8c..d3f7da2452e 100644 --- a/src/target/target_probe.h +++ b/src/target/target_probe.h @@ -107,4 +107,6 @@ bool zynq7_probe(target_s *target); void lpc55_dp_prepare(adiv5_debug_port_s *dp); +bool atxmega_probe(target_s *t); + #endif /* TARGET_TARGET_PROBE_H */ From 00e5ad3e31dbc5e432dc2493f02675a0ea4effa3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 22:33:53 +0000 Subject: [PATCH 06/42] avr_pdi: Implement the logic to create the target, populate it, and dispatch to a suitable probe routine --- src/target/avr_pdi.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 606d2d1523b..de333733604 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -32,11 +32,15 @@ */ #include "general.h" +#include "target_probe.h" +#include "target_internal.h" #include "jtag_scan.h" #include "jtagtap.h" #include "avr_pdi.h" +#include "gdb_packet.h" -#define IR_PDI 0x7U +#define IR_PDI 0x7U +#define IR_BYPASS 0xfU #define PDI_BREAK 0xbbU #define PDI_DELAY 0xdbU @@ -45,6 +49,8 @@ #define PDI_LDCS 0x80U #define PDI_STCS 0xc0U +static bool avr_pdi_init(avr_pdi_s *pdi); + void avr_jtag_pdi_handler(const uint8_t dev_index) { avr_pdi_s *pdi = calloc(1, sizeof(*pdi)); @@ -55,16 +61,42 @@ void avr_jtag_pdi_handler(const uint8_t dev_index) pdi->dev_index = dev_index; pdi->idcode = jtag_devs[dev_index].jd_idcode; + if (!avr_pdi_init(pdi)) + free(pdi); + jtag_dev_write_ir(dev_index, IR_BYPASS); +} +static bool avr_pdi_init(avr_pdi_s *const pdi) +{ /* Check for a valid part number in the JTAG ID code */ if ((pdi->idcode & 0x0ffff000U) == 0) { DEBUG_WARN("Invalid PDI idcode %08" PRIx32 "\n", pdi->idcode); - free(pdi); - return; + return false; } DEBUG_INFO("AVR ID 0x%08" PRIx32 " (v%u)\n", pdi->idcode, (uint8_t)((pdi->idcode >> 28U) & 0xfU)); /* Transition the part into PDI mode */ jtag_dev_write_ir(pdi->dev_index, IR_PDI); + + target_s *target = target_new(); + if (!target) + return false; + + target->cpuid = pdi->idcode; + target->part_id = (pdi->idcode >> 12U) & 0xffffU; + target->driver = "Atmel AVR"; + target->core = "AVR"; + target->priv = pdi; + target->priv_free = free; + + /* Try probing for various known AVR parts */ + PROBE(atxmega_probe); + +#if PC_HOSTED == 0 + gdb_outf("Please report unknown AVR device with Part ID 0x%x\n", target->part_id); +#else + DEBUG_WARN("Please report unknown AVR device with Part ID 0x%x\n", target->part_id); +#endif + return true; } /* From 983b7db33b2dcb180f9e852d9b74a669b0888cf1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 22:52:15 +0000 Subject: [PATCH 07/42] avr_pdi: Implemented support for target_halt_request --- src/target/avr_pdi.c | 28 ++++++++++++++++++++++++++++ src/target/avr_pdi.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index de333733604..fa0acf5d38c 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -38,6 +38,7 @@ #include "jtagtap.h" #include "avr_pdi.h" #include "gdb_packet.h" +#include "exception.h" #define IR_PDI 0x7U #define IR_BYPASS 0xfU @@ -49,7 +50,14 @@ #define PDI_LDCS 0x80U #define PDI_STCS 0xc0U +#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 + static bool avr_pdi_init(avr_pdi_s *pdi); +static void avr_halt_request(target_s *target); void avr_jtag_pdi_handler(const uint8_t dev_index) { @@ -88,6 +96,8 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) target->priv = pdi; target->priv_free = free; + target->halt_request = avr_halt_request; + /* Try probing for various known AVR parts */ PROBE(atxmega_probe); @@ -153,3 +163,21 @@ uint8_t avr_pdi_reg_read(const avr_pdi_s *const pdi, const uint8_t reg) return 0xffU; // TODO - figure out a better way to indicate failure. return result; } + +static void avr_halt_request(target_s *const target) +{ + avr_pdi_s *const pdi = target->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 + * Release 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; +} diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 89b819396b9..896c7498a66 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -35,11 +35,13 @@ #define TARGET_AVR_PDI_H #include +#include "target.h" typedef struct avr_pdi { uint32_t idcode; uint8_t dev_index; + target_halt_reason_e halt_reason; } avr_pdi_s; void avr_jtag_pdi_handler(uint8_t dev_index); From 6b9ed2d52ba3be4da792b51a86d77c7497981277 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 22:57:15 +0000 Subject: [PATCH 08/42] avr_pdi: Begun implementing a function for target_reset --- src/target/avr_pdi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index fa0acf5d38c..2c2692c477f 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -56,7 +56,10 @@ #define PDI_REG_R3 3U #define PDI_REG_R4 4U +#define PDI_RESET 0x59U + static bool avr_pdi_init(avr_pdi_s *pdi); +static void avr_reset(target_s *target); static void avr_halt_request(target_s *target); void avr_jtag_pdi_handler(const uint8_t dev_index) @@ -97,6 +100,7 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) target->priv_free = free; target->halt_request = avr_halt_request; + target->reset = avr_reset; /* Try probing for various known AVR parts */ PROBE(atxmega_probe); @@ -164,6 +168,13 @@ uint8_t avr_pdi_reg_read(const avr_pdi_s *const pdi, const uint8_t reg) return result; } +static void avr_reset(target_s *const target) +{ + avr_pdi_s *const pdi = target->priv; + if (!avr_pdi_reg_write(pdi, PDI_REG_RESET, PDI_RESET)) + raise_exception(EXCEPTION_ERROR, "Error resetting device, device in incorrect state"); +} + static void avr_halt_request(target_s *const target) { avr_pdi_s *const pdi = target->priv; From 7af7c590a6806ca3a3fa500dfc9e54e5fb766449 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:03:14 +0000 Subject: [PATCH 09/42] avr_pdi: Implemented functions for enabling and disabling PDI controller features --- src/target/avr_pdi.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 2c2692c477f..06e1a50d212 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -49,6 +49,7 @@ #define PDI_LDCS 0x80U #define PDI_STCS 0xc0U +#define PDI_KEY 0xe0U #define PDI_REG_STATUS 0U #define PDI_REG_RESET 1U @@ -58,6 +59,14 @@ #define PDI_RESET 0x59U +typedef enum pdi_key { + PDI_NVM = 0x02U, + PDI_DEBUG = 0x04U, +} pdi_key_e; + +static const uint8_t pdi_key_nvm[] = {0xff, 0x88, 0xd8, 0xcd, 0x45, 0xab, 0x89, 0x12}; +static const uint8_t pdi_key_debug[] = {0x21, 0x81, 0x7c, 0x9f, 0xd4, 0x2d, 0x21, 0x3a}; + static bool avr_pdi_init(avr_pdi_s *pdi); static void avr_reset(target_s *target); static void avr_halt_request(target_s *target); @@ -168,6 +177,24 @@ uint8_t avr_pdi_reg_read(const avr_pdi_s *const pdi, const uint8_t reg) return result; } +static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) +{ + const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; + uint8_t result = 0; + if (avr_jtag_shift_dr(pdi->dev_index, &result, PDI_KEY) || result != PDI_EMPTY) + return false; + for (uint8_t i = 0; i < 8; ++i) { + if (avr_jtag_shift_dr(pdi->dev_index, &result, key[i]) || result != PDI_EMPTY) + return false; + } + return (avr_pdi_reg_read(pdi, PDI_REG_STATUS) & what) == what; +} + +static bool avr_disable(const avr_pdi_s *const pdi, const pdi_key_e what) +{ + return avr_pdi_reg_write(pdi, PDI_REG_STATUS, ~what); +} + static void avr_reset(target_s *const target) { avr_pdi_s *const pdi = target->priv; From e482bc96a9e2b8aeea81a9a4fdb0eec6cb52690c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:07:48 +0000 Subject: [PATCH 10/42] avr_pdi: Impelemented a function for writing the processor address space --- src/target/avr_pdi.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 06e1a50d212..7ebbeb465df 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -47,10 +47,21 @@ #define PDI_DELAY 0xdbU #define PDI_EMPTY 0xebU +#define PDI_STS 0x40U #define PDI_LDCS 0x80U #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_REG_STATUS 0U #define PDI_REG_RESET 1U #define PDI_REG_CTRL 2U @@ -177,6 +188,31 @@ uint8_t avr_pdi_reg_read(const avr_pdi_s *const pdi, const uint8_t reg) return result; } +static bool avr_pdi_write(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, const 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(pdi->dev_index, &result, command) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, reg & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, (reg >> 8U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, (reg >> 16U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_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(pdi->dev_index, &result, data_bytes[i]) || result != PDI_EMPTY) + return false; + } + return true; +} + static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; From 0069e9f0e0195bbe941ae405fe1f00c78ae55501 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:09:59 +0000 Subject: [PATCH 11/42] avr_pdi: Impelemented a function for reading the processor address space --- src/target/avr_pdi.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 7ebbeb465df..d1e347ff4be 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -47,6 +47,7 @@ #define PDI_DELAY 0xdbU #define PDI_EMPTY 0xebU +#define PDI_LDS 0x00U #define PDI_STS 0x40U #define PDI_LDCS 0x80U #define PDI_STCS 0xc0U @@ -213,6 +214,33 @@ static bool avr_pdi_write(const avr_pdi_s *const pdi, const uint8_t bytes, const return true; } +static bool avr_pdi_read(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, uint32_t *const 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(pdi->dev_index, &result, command) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, reg & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, (reg >> 8U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, (reg >> 16U) & 0xffU) || result != PDI_EMPTY || + avr_jtag_shift_dr(pdi->dev_index, &result, (reg >> 24U) & 0xffU) || result != PDI_EMPTY) + return false; + for (uint8_t i = 0; i <= bytes; ++i) { + if (!avr_jtag_shift_dr(pdi->dev_index, &data_bytes[i], 0)) + return false; + } + data = data_bytes[0]; + if (bytes > PDI_DATA_8) + data |= (uint32_t)data_bytes[1] << 8U; + if (bytes > PDI_DATA_16) + data |= (uint32_t)data_bytes[2] << 16U; + if (bytes > PDI_DATA_24) + data |= (uint32_t)data_bytes[3] << 24U; + *value = data; + return true; +} + static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; From 6b1e1f649a2af4b9eb9f08f5a99966b4044c0758 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:36:39 +0000 Subject: [PATCH 12/42] avr_pdi: Made the processor address space manipulation functions available in the header --- src/target/avr_pdi.c | 9 ++------- src/target/avr_pdi.h | 8 ++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index d1e347ff4be..0f73e76fa8e 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -53,11 +53,6 @@ #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 @@ -189,7 +184,7 @@ uint8_t avr_pdi_reg_read(const avr_pdi_s *const pdi, const uint8_t reg) return result; } -static bool avr_pdi_write(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, const uint32_t value) +bool avr_pdi_write(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, const uint32_t value) { uint8_t result = 0; uint8_t command = PDI_STS | PDI_ADDR_32 | bytes; @@ -214,7 +209,7 @@ static bool avr_pdi_write(const avr_pdi_s *const pdi, const uint8_t bytes, const return true; } -static bool avr_pdi_read(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, uint32_t *const value) +bool avr_pdi_read(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, uint32_t *const value) { uint8_t result = 0; uint8_t command = PDI_LDS | PDI_ADDR_32 | bytes; diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 896c7498a66..b115e36703f 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -44,6 +44,14 @@ typedef struct avr_pdi { target_halt_reason_e halt_reason; } avr_pdi_s; +#define PDI_DATA_8 0x00U +#define PDI_DATA_16 0x01U +#define PDI_DATA_24 0x02U +#define PDI_DATA_32 0x03U + void avr_jtag_pdi_handler(uint8_t dev_index); +bool avr_pdi_write(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t value); +bool avr_pdi_read(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t *value); + #endif /*TARGET_AVR_PDI_H*/ From ccb532932f7b00edcece272a9eb510d9afb80123 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:37:32 +0000 Subject: [PATCH 13/42] avr_pdi: Implemented a function for accessing a target's avr_pdi_s --- src/target/avr_pdi.c | 5 +++++ src/target/avr_pdi.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 0f73e76fa8e..1727e84740f 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -129,6 +129,11 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) return true; } +avr_pdi_s *avr_pdi_struct(target_s *const target) +{ + return (avr_pdi_s *)target->priv; +} + /* * This is a PDI-specific DR manipulation function that handles PDI_DELAY responses * transparently to the caller. It also does parity validation, returning true for diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index b115e36703f..e292f90035d 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -50,6 +50,7 @@ typedef struct avr_pdi { #define PDI_DATA_32 0x03U void avr_jtag_pdi_handler(uint8_t dev_index); +avr_pdi_s *avr_pdi_struct(target_s *target); bool avr_pdi_write(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t value); bool avr_pdi_read(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t *value); From de5b2af1ab3ae2794d6c5d080712620026266a6e Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:40:44 +0000 Subject: [PATCH 14/42] avr_pdi: Implemented a function for asking the target to ensure the NVM controller is idle --- src/target/avr_pdi.c | 7 +++++++ src/target/avr_pdi.h | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 1727e84740f..5eb524613ba 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -259,6 +259,13 @@ static bool avr_disable(const avr_pdi_s *const pdi, const pdi_key_e what) return avr_pdi_reg_write(pdi, PDI_REG_STATUS, ~what); } +static bool avr_ensure_nvm_idle(const avr_pdi_s *const pdi) +{ + if (pdi->ensure_nvm_idle) + return pdi->ensure_nvm_idle(pdi); + return true; +} + static void avr_reset(target_s *const target) { avr_pdi_s *const pdi = target->priv; diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index e292f90035d..ddca580bea4 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -37,12 +37,16 @@ #include #include "target.h" -typedef struct avr_pdi { +typedef struct avr_pdi avr_pdi_s; + +struct avr_pdi { uint32_t idcode; uint8_t dev_index; target_halt_reason_e halt_reason; -} avr_pdi_s; + + bool (*ensure_nvm_idle)(const avr_pdi_s *pdi); +}; #define PDI_DATA_8 0x00U #define PDI_DATA_16 0x01U From 6ee64c0700835807e952884ee705645624b43b7d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:40:54 +0000 Subject: [PATCH 15/42] atxmega: Implemented a function for ensuring the NVM controller is idle --- src/target/atxmega.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index a8a9d0e6e63..e21f4c76c01 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -42,6 +42,23 @@ #define IDCODE_XMEGA192A3U 0x9744U #define IDCODE_XMEGA256A3U 0x9842U +#define ATXMEGA_NVM_BASE 0x010001c0U +#define ATXMEGA_NVM_DATA (ATXMEGA_NVM_BASE + 0x4U) +#define ATXMEGA_NVM_CMD (ATXMEGA_NVM_BASE + 0xaU) +#define ATXMEGA_NVM_STATUS (ATXMEGA_NVM_BASE + 0xfU) + +#define ATXMEGA_NVM_CMD_NOP 0x00U +#define ATXMEGA_NVM_CMD_ERASE_FLASH_BUFFER 0x26U +#define ATXMEGA_NVM_CMD_WRITE_FLASH_BUFFER 0x23U +#define ATXMEGA_NVM_CMD_ERASE_FLASH_PAGE 0x2bU +#define ATXMEGA_NVM_CMD_WRITE_FLASH_PAGE 0x2eU +#define ATXMEGA_NVM_CMD_READ_NVM 0x43U + +#define ATXMEGA_NVM_STATUS_BUSY 0x80U +#define ATXMEGA_NVM_STATUS_FBUSY 0x40U + +static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); + void avr_add_flash(target_s *const target, const uint32_t start, const size_t length, const uint16_t block_size) { target_flash_s *flash = calloc(1, sizeof(*flash)); @@ -138,5 +155,14 @@ bool atxmega_probe(target_s *const target) avr_add_flash(target, flash_base_address, application_table_flash, flash_block_size); flash_base_address += application_table_flash; avr_add_flash(target, flash_base_address, bootloader_flash, flash_block_size); + + avr_pdi_s *const pdi = avr_pdi_struct(target); + pdi->ensure_nvm_idle = atxmega_ensure_nvm_idle; return true; } + +static bool atxmega_ensure_nvm_idle(const avr_pdi_s *const pdi) +{ + return avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_CMD, ATXMEGA_NVM_CMD_NOP) && + avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_DATA, 0xffU); +} From 819e42e33efbbb88d0634d42af7173ff25cff441 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:48:14 +0000 Subject: [PATCH 16/42] avr_pdi: Implemented functions for attaching and detaching to the target --- src/target/avr_pdi.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 5eb524613ba..3149a896db0 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -75,6 +75,8 @@ static const uint8_t pdi_key_nvm[] = {0xff, 0x88, 0xd8, 0xcd, 0x45, 0xab, 0x89, static const uint8_t pdi_key_debug[] = {0x21, 0x81, 0x7c, 0x9f, 0xd4, 0x2d, 0x21, 0x3a}; static bool avr_pdi_init(avr_pdi_s *pdi); +static bool avr_attach(target_s *target); +static void avr_detach(target_s *target); static void avr_reset(target_s *target); static void avr_halt_request(target_s *target); @@ -115,6 +117,9 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) target->priv = pdi; target->priv_free = free; + target->attach = avr_attach; + target->detach = avr_detach; + target->halt_request = avr_halt_request; target->reset = avr_reset; @@ -266,6 +271,35 @@ static bool avr_ensure_nvm_idle(const avr_pdi_s *const pdi) return true; } +static bool avr_attach(target_s *const target) +{ + const avr_pdi_s *const pdi = target->priv; + jtag_dev_write_ir(pdi->dev_index, IR_PDI); + + TRY (EXCEPTION_ALL) { + target_reset(target); + if (!avr_enable(pdi, PDI_DEBUG)) + return false; + target_halt_request(target); + if (!avr_enable(pdi, PDI_NVM) || !avr_ensure_nvm_idle(pdi) || avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U) + return false; + } + CATCH () { + default: + return !exception_frame.type; + } + return true; +} + +static void avr_detach(target_s *const target) +{ + const avr_pdi_s *const pdi = target->priv; + + avr_disable(pdi, PDI_NVM); + avr_disable(pdi, PDI_DEBUG); + jtag_dev_write_ir(pdi->dev_index, IR_BYPASS); +} + static void avr_reset(target_s *const target) { avr_pdi_s *const pdi = target->priv; From 9b2c77dad53c08fc6fe7a7d1784b3828f0cb2d66 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sat, 31 Dec 2022 23:51:37 +0000 Subject: [PATCH 17/42] avr_pdi: Added handling in avr_reset() for when the PDI controller has things enabled already --- src/target/avr_pdi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 3149a896db0..06b78c65e82 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -303,8 +303,18 @@ static void avr_detach(target_s *const target) static void avr_reset(target_s *const target) { avr_pdi_s *const pdi = target->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) + 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); + } } static void avr_halt_request(target_s *const target) From 7802713ee80095a34944a1bb0fd6cbf99d0544b0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:03:39 +0000 Subject: [PATCH 18/42] avr_pdi: Implemented type-safe read helpers for the processor address space --- src/target/avr_pdi.c | 30 +++++++++++++++++++++++++++++- src/target/avr_pdi.h | 5 ++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 06b78c65e82..d2465751950 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -219,7 +219,7 @@ bool avr_pdi_write(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32 return true; } -bool avr_pdi_read(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, uint32_t *const value) +static bool avr_pdi_read(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_t reg, uint32_t *const value) { uint8_t result = 0; uint8_t command = PDI_LDS | PDI_ADDR_32 | bytes; @@ -246,6 +246,34 @@ bool avr_pdi_read(const avr_pdi_s *const pdi, const uint8_t bytes, const uint32_ return true; } +bool avr_pdi_read8(const avr_pdi_s *const pdi, const uint32_t reg, uint8_t *const value) +{ + uint32_t data; + const bool result = avr_pdi_read(pdi, PDI_DATA_8, reg, &data); + if (result) + *value = data; + return result; +} + +bool avr_pdi_read16(const avr_pdi_s *const pdi, const uint32_t reg, uint16_t *const value) +{ + uint32_t data; + const bool result = avr_pdi_read(pdi, PDI_DATA_16, reg, &data); + if (result) + *value = data; + return result; +} + +bool avr_pdi_read24(const avr_pdi_s *const pdi, const uint32_t reg, uint32_t *const value) +{ + return avr_pdi_read(pdi, PDI_DATA_24, reg, value); +} + +bool avr_pdi_read32(const avr_pdi_s *const pdi, const uint32_t reg, uint32_t *const value) +{ + return avr_pdi_read(pdi, PDI_DATA_32, reg, value); +} + static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index ddca580bea4..b8b2c60b0e1 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -57,6 +57,9 @@ void avr_jtag_pdi_handler(uint8_t dev_index); avr_pdi_s *avr_pdi_struct(target_s *target); bool avr_pdi_write(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t value); -bool avr_pdi_read(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t *value); +bool avr_pdi_read8(const avr_pdi_s *pdi, uint32_t reg, uint8_t *value); +bool avr_pdi_read16(const avr_pdi_s *pdi, uint32_t reg, uint16_t *value); +bool avr_pdi_read24(const avr_pdi_s *pdi, uint32_t reg, uint32_t *value); +bool avr_pdi_read32(const avr_pdi_s *pdi, uint32_t reg, uint32_t *value); #endif /*TARGET_AVR_PDI_H*/ From 0ec719e452e563c4946ef257e8edb01531cae406 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:06:28 +0000 Subject: [PATCH 19/42] avr_pdi: Implemented a function for writing the PDI pointer register and another for writing the PDI repeat counter --- src/target/avr_pdi.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index d2465751950..158e27ed89e 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -47,11 +47,13 @@ #define PDI_DELAY 0xdbU #define PDI_EMPTY 0xebU -#define PDI_LDS 0x00U -#define PDI_STS 0x40U -#define PDI_LDCS 0x80U -#define PDI_STCS 0xc0U -#define PDI_KEY 0xe0U +#define PDI_LDS 0x00U +#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_ADDR_8 0x00U #define PDI_ADDR_16 0x04U @@ -274,6 +276,31 @@ bool avr_pdi_read32(const avr_pdi_s *const pdi, const uint32_t reg, uint32_t *co return avr_pdi_read(pdi, PDI_DATA_32, reg, value); } +// Runs `st ptr ` +static bool avr_pdi_write_ptr(const avr_pdi_s *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(pdi->dev_index, &result, command) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, addr & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, (addr >> 8U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, (addr >> 16U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, (addr >> 24U) & 0xffU) && result == PDI_EMPTY; +} + +// Runs `repeat ` +static bool avr_pdi_repeat(const avr_pdi_s *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(pdi->dev_index, &result, command) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, repeat & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, (repeat >> 8U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, (repeat >> 16U) & 0xffU) && result == PDI_EMPTY && + !avr_jtag_shift_dr(pdi->dev_index, &result, (repeat >> 24U) & 0xffU) && result == PDI_EMPTY; +} + static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; From 303c99e5257471bdc921faaecb8bbf8ca6006678 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:08:36 +0000 Subject: [PATCH 20/42] avr_pdi: Implemented a function for doing an indirect write to the processor address space --- src/target/avr_pdi.c | 20 ++++++++++++++++++++ src/target/avr_pdi.h | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 158e27ed89e..39639652558 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -60,6 +60,8 @@ #define PDI_ADDR_24 0x08U #define PDI_ADDR_32 0x0cU +#define PDI_MODE_MASK 0xf3U + #define PDI_REG_STATUS 0U #define PDI_REG_RESET 1U #define PDI_REG_CTRL 2U @@ -301,6 +303,24 @@ static bool avr_pdi_repeat(const avr_pdi_s *const pdi, const uint32_t count) !avr_jtag_shift_dr(pdi->dev_index, &result, (repeat >> 24U) & 0xffU) && result == PDI_EMPTY; } +bool avr_pdi_write_ind(const avr_pdi_s *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(pdi->dev_index, &result, command) || result != PDI_EMPTY) + return false; + for (uint32_t i = 0; i < count; ++i) { + if (avr_jtag_shift_dr(pdi->dev_index, &result, data[i]) || result != PDI_EMPTY) + return false; + } + return true; +} + static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index b8b2c60b0e1..6e93328d262 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -53,6 +53,11 @@ struct avr_pdi { #define PDI_DATA_24 0x02U #define PDI_DATA_32 0x03U +#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" */ + void avr_jtag_pdi_handler(uint8_t dev_index); avr_pdi_s *avr_pdi_struct(target_s *target); @@ -61,5 +66,6 @@ bool avr_pdi_read8(const avr_pdi_s *pdi, uint32_t reg, uint8_t *value); bool avr_pdi_read16(const avr_pdi_s *pdi, uint32_t reg, uint16_t *value); bool avr_pdi_read24(const avr_pdi_s *pdi, uint32_t reg, uint32_t *value); bool avr_pdi_read32(const avr_pdi_s *pdi, uint32_t reg, uint32_t *value); +bool avr_pdi_write_ind(const avr_pdi_s *pdi, uint32_t addr, uint8_t ptr_mode, const void *src, uint32_t count); #endif /*TARGET_AVR_PDI_H*/ From 15beb3c8067caffe55ad7682a70b151179bd52c9 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:08:54 +0000 Subject: [PATCH 21/42] avr_pdi: Implemented a function for doing an indirect read from the processor address space --- src/target/avr_pdi.c | 19 +++++++++++++++++++ src/target/avr_pdi.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 39639652558..386d96adeec 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -48,6 +48,7 @@ #define PDI_EMPTY 0xebU #define PDI_LDS 0x00U +#define PDI_LD 0x20U #define PDI_STS 0x40U #define PDI_ST 0x60U #define PDI_LDCS 0x80U @@ -321,6 +322,24 @@ bool avr_pdi_write_ind(const avr_pdi_s *const pdi, const uint32_t addr, const ui return true; } +bool avr_pdi_read_ind( + const avr_pdi_s *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(pdi->dev_index, &result, command) || result != PDI_EMPTY) + return false; + for (uint32_t i = 0; i < count; ++i) { + if (!avr_jtag_shift_dr(pdi->dev_index, data + i, 0)) + return false; + } + return true; +} + static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 6e93328d262..f91b669e91c 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -67,5 +67,6 @@ bool avr_pdi_read16(const avr_pdi_s *pdi, uint32_t reg, uint16_t *value); bool avr_pdi_read24(const avr_pdi_s *pdi, uint32_t reg, uint32_t *value); bool avr_pdi_read32(const avr_pdi_s *pdi, uint32_t reg, uint32_t *value); bool avr_pdi_write_ind(const avr_pdi_s *pdi, uint32_t addr, uint8_t ptr_mode, const void *src, uint32_t count); +bool avr_pdi_read_ind(const avr_pdi_s *pdi, uint32_t addr, uint8_t ptr_mode, void *dst, uint32_t count); #endif /*TARGET_AVR_PDI_H*/ From 0d80ec31f2a8410044cd3670b1c5b4dd2861d821 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:19:51 +0000 Subject: [PATCH 22/42] atxmega: Implemented a Flash erase routine --- src/target/atxmega.c | 32 ++++++++++++++++++++++++++++++++ src/target/avr_pdi.h | 2 ++ 2 files changed, 34 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index e21f4c76c01..793c72d7c92 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -57,6 +57,9 @@ #define ATXMEGA_NVM_STATUS_BUSY 0x80U #define ATXMEGA_NVM_STATUS_FBUSY 0x40U +static bool atxmega_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); +static bool atxmega_flash_done(target_flash_s *flash); + static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); void avr_add_flash(target_s *const target, const uint32_t start, const size_t length, const uint16_t block_size) @@ -70,6 +73,8 @@ void avr_add_flash(target_s *const target, const uint32_t start, const size_t le flash->start = start; flash->length = length; flash->blocksize = block_size; + flash->erase = atxmega_flash_erase; + flash->done = atxmega_flash_done; flash->erased = 0xffU; target_add_flash(target, flash); } @@ -166,3 +171,30 @@ static bool atxmega_ensure_nvm_idle(const avr_pdi_s *const pdi) return avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_CMD, ATXMEGA_NVM_CMD_NOP) && avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_DATA, 0xffU); } + +static bool atxmega_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t len) +{ + const avr_pdi_s *const pdi = avr_pdi_struct(flash->t); + for (size_t i = 0; i < len; i += flash->blocksize) { + if (!avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_CMD, ATXMEGA_NVM_CMD_ERASE_FLASH_PAGE) || + !avr_pdi_write(pdi, PDI_DATA_8, (addr + i) | PDI_FLASH_OFFSET, 0x55U)) + return false; + + uint8_t status = 0; + while (avr_pdi_read8(pdi, ATXMEGA_NVM_STATUS, &status) && + (status & (ATXMEGA_NVM_STATUS_BUSY | ATXMEGA_NVM_STATUS_FBUSY)) == + (ATXMEGA_NVM_STATUS_BUSY | ATXMEGA_NVM_STATUS_FBUSY)) + continue; + + /* Check if the read status failed */ + if (status & (ATXMEGA_NVM_STATUS_BUSY | ATXMEGA_NVM_STATUS_FBUSY)) + return false; + } + return true; +} + +static bool atxmega_flash_done(target_flash_s *const flash) +{ + const avr_pdi_s *const pdi = avr_pdi_struct(flash->t); + return atxmega_ensure_nvm_idle(pdi); +} diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index f91b669e91c..66cd40c2ec8 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -58,6 +58,8 @@ struct avr_pdi { #define PDI_MODE_DIR_PTR 0x08U #define PDI_MODE_DIR_INCPTR 0x0cU /* "Reserved" */ +#define PDI_FLASH_OFFSET 0x00800000U + void avr_jtag_pdi_handler(uint8_t dev_index); avr_pdi_s *avr_pdi_struct(target_s *target); From 477e3251aeb85c67fd9c3c06b01cb524750a59b0 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:19:57 +0000 Subject: [PATCH 23/42] atxmega: Implemented a Flash write routine --- src/target/atxmega.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 793c72d7c92..8f293e6db25 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -58,6 +58,7 @@ #define ATXMEGA_NVM_STATUS_FBUSY 0x40U static bool atxmega_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); +static bool atxmega_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); static bool atxmega_flash_done(target_flash_s *flash); static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); @@ -74,6 +75,7 @@ void avr_add_flash(target_s *const target, const uint32_t start, const size_t le flash->length = length; flash->blocksize = block_size; flash->erase = atxmega_flash_erase; + flash->write = atxmega_flash_write; flash->done = atxmega_flash_done; flash->erased = 0xffU; target_add_flash(target, flash); @@ -193,6 +195,33 @@ static bool atxmega_flash_erase(target_flash_s *const flash, const target_addr_t return true; } +static bool atxmega_flash_write( + target_flash_s *const flash, target_addr_t dest, const void *const src, const size_t len) +{ + const avr_pdi_s *const pdi = avr_pdi_struct(flash->t); + const uint8_t *const buffer = (const uint8_t *)src; + for (size_t i = 0; i < len; i += flash->blocksize) { + const size_t amount = MIN(flash->blocksize, len - i); + const uint32_t addr = (dest + i) | PDI_FLASH_OFFSET; + if (!avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_CMD, ATXMEGA_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, ATXMEGA_NVM_CMD, ATXMEGA_NVM_CMD_WRITE_FLASH_PAGE) || + !avr_pdi_write(pdi, PDI_DATA_8, addr, 0xffU)) + return false; + + uint8_t status = 0; + while (avr_pdi_read8(pdi, ATXMEGA_NVM_STATUS, &status) && + (status & (ATXMEGA_NVM_STATUS_BUSY | ATXMEGA_NVM_STATUS_FBUSY)) == + (ATXMEGA_NVM_STATUS_BUSY | ATXMEGA_NVM_STATUS_FBUSY)) + continue; + + /* Check if the read status failed */ + if (status & (ATXMEGA_NVM_STATUS_BUSY | ATXMEGA_NVM_STATUS_FBUSY)) + return false; + } + return true; +} + static bool atxmega_flash_done(target_flash_s *const flash) { const avr_pdi_s *const pdi = avr_pdi_struct(flash->t); From 5ba8fc8f1334dd0489e9d776e9f01b05b5cac2b3 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:28:21 +0000 Subject: [PATCH 24/42] avr_pdi: Implemented target_halt_poll --- src/target/avr_pdi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 386d96adeec..183d051d55a 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -82,8 +82,10 @@ static const uint8_t pdi_key_debug[] = {0x21, 0x81, 0x7c, 0x9f, 0xd4, 0x2d, 0x21 static bool avr_pdi_init(avr_pdi_s *pdi); static bool avr_attach(target_s *target); static void avr_detach(target_s *target); + static void avr_reset(target_s *target); static void avr_halt_request(target_s *target); +static target_halt_reason_e avr_halt_poll(target_s *target, target_addr_t *watch); void avr_jtag_pdi_handler(const uint8_t dev_index) { @@ -126,6 +128,7 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) target->detach = avr_detach; target->halt_request = avr_halt_request; + target->halt_poll = avr_halt_poll; target->reset = avr_reset; /* Try probing for various known AVR parts */ @@ -428,3 +431,14 @@ static void avr_halt_request(target_s *const target) raise_exception(EXCEPTION_ERROR, "Error halting device, device in incorrect state"); pdi->halt_reason = TARGET_HALT_REQUEST; } + +static target_halt_reason_e avr_halt_poll(target_s *const target, target_addr_t *const watch) +{ + avr_pdi_s *const pdi = target->priv; + (void)watch; + + /* If we're running but the processor stops because it's hit a breakpoint, update */ + if (pdi->halt_reason == TARGET_HALT_RUNNING && avr_pdi_reg_read(pdi, PDI_REG_R3) == 0x04U) + pdi->halt_reason = TARGET_HALT_BREAKPOINT; + return pdi->halt_reason; +} From 9dc09344f382ba7fd95ede0fe28debed36310e49 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 00:32:13 +0000 Subject: [PATCH 25/42] avr_pdi: Implemented the AVR registers structure --- src/target/avr_pdi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 183d051d55a..1ec3accf9f6 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -76,6 +76,13 @@ typedef enum pdi_key { PDI_DEBUG = 0x04U, } pdi_key_e; +typedef struct __attribute__((packed)) avr_regs { + uint8_t general[32]; /* r0-r31 */ + uint8_t sreg; /* r32 */ + uint16_t sp; /* r33 */ + uint32_t pc; /* r34 */ +} avr_regs_s; + static const uint8_t pdi_key_nvm[] = {0xff, 0x88, 0xd8, 0xcd, 0x45, 0xab, 0x89, 0x12}; static const uint8_t pdi_key_debug[] = {0x21, 0x81, 0x7c, 0x9f, 0xd4, 0x2d, 0x21, 0x3a}; @@ -130,6 +137,11 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) target->halt_request = avr_halt_request; target->halt_poll = avr_halt_poll; target->reset = avr_reset; + /* + * Unlike on an ARM processor, where this is the length of a table, here + * we return the size of a sutable registers structure. + */ + target->regs_size = sizeof(avr_regs_s); /* Try probing for various known AVR parts */ PROBE(atxmega_probe); From 81232d35636fc5ce7f4569a07a157ce5268dd702 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 01:12:05 +0000 Subject: [PATCH 26/42] atxmega: Implemented building of the target register description XML --- src/target/atxmega.c | 134 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 8f293e6db25..a0b9b3de1ff 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -35,8 +35,11 @@ #include "target.h" #include "target_internal.h" #include "target_probe.h" +#include "gdb_reg.h" #include "avr_pdi.h" +#include + #define IDCODE_XMEGA64A3U 0x9642U #define IDCODE_XMEGA128A3U 0x9742U #define IDCODE_XMEGA192A3U 0x9744U @@ -57,10 +60,43 @@ #define ATXMEGA_NVM_STATUS_BUSY 0x80U #define ATXMEGA_NVM_STATUS_FBUSY 0x40U +/* Special-purpose register name strings */ +static const char *const avr_spr_names[] = { + "sreg", + "sp", + "pc", +}; + +/* Special-purpose register types */ +static const gdb_reg_type_e avr_spr_types[] = { + GDB_TYPE_UNSPECIFIED, /* sreg */ + GDB_TYPE_DATA_PTR, /* sp */ + GDB_TYPE_CODE_PTR, /* pc */ +}; + +/* Special-purpose register bitsizes */ +static const uint8_t avr_spr_bitsizes[] = { + 8, /* sreg */ + 16, /* sp */ + 32, /* pc */ +}; + +// clang-format off +static_assert(ARRAY_LENGTH(avr_spr_types) == ARRAY_LENGTH(avr_spr_names), + "SPR array length mismatch! SPR type array should have the same length as SPR name array." +); + +static_assert(ARRAY_LENGTH(avr_spr_bitsizes) == ARRAY_LENGTH(avr_spr_names), + "SPR array length mismatch! SPR bitsize array should have the same length as SPR name array." +); +// clang-format on + static bool atxmega_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len); static bool atxmega_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len); static bool atxmega_flash_done(target_flash_s *flash); +static const char *atxmega_target_description(target_s *target); + static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); void avr_add_flash(target_s *const target, const uint32_t start, const size_t length, const uint16_t block_size) @@ -151,6 +187,8 @@ bool atxmega_probe(target_s *const target) return false; } + target->regs_description = atxmega_target_description; + /* * RAM is actually at 0x01002000 in the 24-bit linearised PDI address space however, because GDB/GCC, * internally we have to map at 0x00800000 to get a suitable mapping for the host @@ -227,3 +265,99 @@ static bool atxmega_flash_done(target_flash_s *const flash) const avr_pdi_s *const pdi = avr_pdi_struct(flash->t); return atxmega_ensure_nvm_idle(pdi); } + +/* + * This function creates the target description XML string for an ATXMega6 part. + * This is done this way to decrease string duplication and thus code size, making it + * unfortunately much less readable than the string literal it is equivilent to. + * + * This string it creates is the XML-equivalent to the following: + * "" + * "" + * "" + * " avr:106" + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * " " + * "" + */ +static size_t atxmega_build_target_description(char *buffer, size_t max_len) +{ + size_t print_size = max_len; + /* Start with the "preamble" chunks, which are mostly common across targets save for 2 words. */ + int offset = snprintf(buffer, print_size, "%s target %savr:106%s ", + gdb_xml_preamble_first, gdb_xml_preamble_second, gdb_xml_preamble_third); + + /* Then build the general purpose register descriptions which have names r0 through r31 and the same bitsize */ + for (uint8_t i = 0; i < 32; ++i) { + if (max_len != 0) + print_size = max_len - (size_t)offset; + offset += snprintf( + buffer + offset, print_size, "", i, i == 0 ? " regnum=\"0\"" : ""); + } + + /* Then finally build the special-purpose register descriptions using the arrays at top of file. */ + for (size_t i = 0; i < ARRAY_LENGTH(avr_spr_names); ++i) { + if (max_len != 0) + print_size = max_len - (size_t)offset; + + const char *const name = avr_spr_names[i]; + const uint8_t bitsize = avr_spr_bitsizes[i]; + const gdb_reg_type_e type = avr_spr_types[i]; + + offset += snprintf(buffer + offset, print_size, "", name, bitsize, + gdb_reg_type_strings[type]); + } + + /* Add the closing tags required */ + if (max_len != 0) + print_size = max_len - (size_t)offset; + + offset += snprintf(buffer + offset, print_size, ""); + /* offset is now the total length of the string created, discard the sign and return it. */ + return (size_t)offset; +} + +static const char *atxmega_target_description(target_s *const target) +{ + (void)target; + const size_t description_length = atxmega_build_target_description(NULL, 0) + 1U; + char *const description = malloc(description_length); + if (description) + atxmega_build_target_description(description, description_length); + return description; +} From 1259f8dd1e7838fbe305a85eff90b93acaf65121 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 01:50:04 +0000 Subject: [PATCH 27/42] avr_pdi: Added the PDI controller register access functions into the header --- src/target/avr_pdi.c | 2 -- src/target/avr_pdi.h | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 1ec3accf9f6..4ae2b7c3e17 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -66,8 +66,6 @@ #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 diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 66cd40c2ec8..3646935678f 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -53,6 +53,9 @@ struct avr_pdi { #define PDI_DATA_24 0x02U #define PDI_DATA_32 0x03U +#define PDI_REG_R3 3U +#define PDI_REG_R4 4U + #define PDI_MODE_IND_PTR 0x00U #define PDI_MODE_IND_INCPTR 0x04U #define PDI_MODE_DIR_PTR 0x08U @@ -63,6 +66,9 @@ struct avr_pdi { void avr_jtag_pdi_handler(uint8_t dev_index); avr_pdi_s *avr_pdi_struct(target_s *target); +bool avr_pdi_reg_write(const avr_pdi_s *pdi, uint8_t reg, uint8_t value); +uint8_t avr_pdi_reg_read(const avr_pdi_s *pdi, uint8_t reg); + bool avr_pdi_write(const avr_pdi_s *pdi, uint8_t bytes, uint32_t reg, uint32_t value); bool avr_pdi_read8(const avr_pdi_s *pdi, uint32_t reg, uint8_t *value); bool avr_pdi_read16(const avr_pdi_s *pdi, uint32_t reg, uint16_t *value); From 562890191b38e44509baea603da5c885b584b79d Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 01:51:04 +0000 Subject: [PATCH 28/42] avr_pdi: Moved the target register structure into the header --- src/target/avr_pdi.c | 7 ------- src/target/avr_pdi.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 4ae2b7c3e17..81da58a2fd5 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -74,13 +74,6 @@ typedef enum pdi_key { PDI_DEBUG = 0x04U, } pdi_key_e; -typedef struct __attribute__((packed)) avr_regs { - uint8_t general[32]; /* r0-r31 */ - uint8_t sreg; /* r32 */ - uint16_t sp; /* r33 */ - uint32_t pc; /* r34 */ -} avr_regs_s; - static const uint8_t pdi_key_nvm[] = {0xff, 0x88, 0xd8, 0xcd, 0x45, 0xab, 0x89, 0x12}; static const uint8_t pdi_key_debug[] = {0x21, 0x81, 0x7c, 0x9f, 0xd4, 0x2d, 0x21, 0x3a}; diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 3646935678f..19cf50b3765 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -48,6 +48,13 @@ struct avr_pdi { bool (*ensure_nvm_idle)(const avr_pdi_s *pdi); }; +typedef struct __attribute__((packed)) avr_regs { + uint8_t general[32]; /* r0-r31 */ + uint8_t sreg; /* r32 */ + uint16_t sp; /* r33 */ + uint32_t pc; /* r34 */ +} avr_regs_s; + #define PDI_DATA_8 0x00U #define PDI_DATA_16 0x01U #define PDI_DATA_24 0x02U From 3cf33cfa63ecb35489b584bf03b855d631cec5db Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 1 Jan 2023 01:53:08 +0000 Subject: [PATCH 29/42] atxmega: Implemented a function for reading the ATXMega processor registers --- src/target/atxmega.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index a0b9b3de1ff..336711528da 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -36,6 +36,7 @@ #include "target_internal.h" #include "target_probe.h" #include "gdb_reg.h" +#include "exception.h" #include "avr_pdi.h" #include @@ -45,6 +46,20 @@ #define IDCODE_XMEGA192A3U 0x9744U #define IDCODE_XMEGA256A3U 0x9842U +#define ATXMEGA_DBG_BASE 0x00000000U +#define ATXMEGA_DBG_CTR (ATXMEGA_DBG_BASE + 0x0U) +#define ATXMEGA_DBG_PC (ATXMEGA_DBG_BASE + 0x4U) +#define ATXMEGA_DBG_CTRL (ATXMEGA_DBG_BASE + 0xaU) +#define ATXMEGA_DBG_SPECIAL (ATXMEGA_DBG_BASE + 0xcU) + +#define AVR_DBG_READ_REGS 0x11U +#define AVR_NUM_REGS 32 + +#define ATXMEGA_CPU_BASE 0x01000030U +/* Address of the low byte of the stack pointer */ +#define ATXMEGA_CPU_SPL (ATXMEGA_CPU_BASE + 0xdU) +/* This is followed by the high byte and SREG */ + #define ATXMEGA_NVM_BASE 0x010001c0U #define ATXMEGA_NVM_DATA (ATXMEGA_NVM_BASE + 0x4U) #define ATXMEGA_NVM_CMD (ATXMEGA_NVM_BASE + 0xaU) @@ -97,6 +112,8 @@ static bool atxmega_flash_done(target_flash_s *flash); static const char *atxmega_target_description(target_s *target); +static void atxmega_regs_read(target_s *target, void *data); + static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); void avr_add_flash(target_s *const target, const uint32_t start, const size_t length, const uint16_t block_size) @@ -188,6 +205,7 @@ bool atxmega_probe(target_s *const target) } target->regs_description = atxmega_target_description; + target->regs_read = atxmega_regs_read; /* * RAM is actually at 0x01002000 in the 24-bit linearised PDI address space however, because GDB/GCC, @@ -361,3 +379,28 @@ static const char *atxmega_target_description(target_s *const target) atxmega_build_target_description(description, description_length); return description; } + +static void atxmega_regs_read(target_s *const target, void *const data) +{ + const avr_pdi_s *const pdi = avr_pdi_struct(target); + avr_regs_s *regs = (avr_regs_s *)data; + uint8_t status[3]; + uint32_t pc = 0; + if (!avr_pdi_read32(pdi, ATXMEGA_DBG_PC, &pc) || + !avr_pdi_read_ind(pdi, ATXMEGA_CPU_SPL, PDI_MODE_IND_INCPTR, status, 3) || + !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_PC, 0) || + !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_CTR, AVR_NUM_REGS) || + !avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_DBG_CTRL, AVR_DBG_READ_REGS) || + !avr_pdi_reg_write(pdi, PDI_REG_R4, 1) || + !avr_pdi_read_ind(pdi, ATXMEGA_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 have to adjust it by 1 and make it bytes. + */ + regs->pc = (pc - 1) << 1U; + regs->sp = status[0] | (status[1] << 8); + regs->sreg = status[2]; +} From ab5340e7a249a517a071548eae604c83ecdea279 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 15:00:47 +0000 Subject: [PATCH 30/42] avr_pdi: Implemented an error state tracker for the PDI structure --- src/target/avr_pdi.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 19cf50b3765..f0b113a3eef 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -37,6 +37,11 @@ #include #include "target.h" +typedef enum avr_error { + pdi_ok, + pdi_failure, +} avr_error_e; + typedef struct avr_pdi avr_pdi_s; struct avr_pdi { @@ -44,6 +49,7 @@ struct avr_pdi { uint8_t dev_index; target_halt_reason_e halt_reason; + avr_error_e error_state; bool (*ensure_nvm_idle)(const avr_pdi_s *pdi); }; From 881fc4e42721c8dfc7728bf55420658397ba7729 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 15:03:56 +0000 Subject: [PATCH 31/42] atxmega: Implemented support for reading out memory from the target --- src/target/atxmega.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 336711528da..08ea20eac4d 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -113,6 +113,7 @@ static bool atxmega_flash_done(target_flash_s *flash); static const char *atxmega_target_description(target_s *target); static void atxmega_regs_read(target_s *target, void *data); +static void atxmega_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); @@ -206,6 +207,7 @@ bool atxmega_probe(target_s *const target) target->regs_description = atxmega_target_description; target->regs_read = atxmega_regs_read; + target->mem_read = atxmega_mem_read; /* * RAM is actually at 0x01002000 in the 24-bit linearised PDI address space however, because GDB/GCC, @@ -380,6 +382,20 @@ static const char *atxmega_target_description(target_s *const target) return description; } +static void atxmega_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) +{ + avr_pdi_s *const pdi = avr_pdi_struct(target); + const target_addr32_t translated_src = (target_addr32_t)src + PDI_FLASH_OFFSET; + if (target_flash_for_addr(target, src)) { + if (!avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_NVM_CMD, ATXMEGA_NVM_CMD_READ_NVM) || + !avr_pdi_read_ind(pdi, translated_src, PDI_MODE_IND_INCPTR, dest, len) || !atxmega_ensure_nvm_idle(pdi)) + pdi->error_state = pdi_failure; + } else if (translated_src < PDI_FLASH_OFFSET) { + if (!avr_pdi_read_ind(pdi, translated_src, PDI_MODE_IND_INCPTR, dest, len)) + pdi->error_state = pdi_failure; + } +} + static void atxmega_regs_read(target_s *const target, void *const data) { const avr_pdi_s *const pdi = avr_pdi_struct(target); From 9a2acc0c484a3b2b4d877f08b35f2eb07de80f12 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 15:09:54 +0000 Subject: [PATCH 32/42] atxmega: Implemented target check_error --- src/target/atxmega.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 08ea20eac4d..e586f88bfcd 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -112,6 +112,7 @@ static bool atxmega_flash_done(target_flash_s *flash); static const char *atxmega_target_description(target_s *target); +static bool atxmega_check_error(target_s *target); static void atxmega_regs_read(target_s *target, void *data); static void atxmega_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); @@ -206,6 +207,8 @@ bool atxmega_probe(target_s *const target) } target->regs_description = atxmega_target_description; + target->check_error = atxmega_check_error; + target->regs_read = atxmega_regs_read; target->mem_read = atxmega_mem_read; @@ -382,6 +385,12 @@ static const char *atxmega_target_description(target_s *const target) return description; } +static bool atxmega_check_error(target_s *const target) +{ + const avr_pdi_s *const pdi = avr_pdi_struct(target); + return pdi->error_state != pdi_ok; +} + static void atxmega_mem_read(target_s *const target, void *const dest, const target_addr64_t src, const size_t len) { avr_pdi_s *const pdi = avr_pdi_struct(target); From 632802fc1fac32926282cbdc0950795d4512ccb7 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 15:52:41 +0000 Subject: [PATCH 33/42] avr_pdi: Added PDI structure definitions for breakpoint handling --- src/target/avr_pdi.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index f0b113a3eef..921df48ccfd 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -42,6 +42,10 @@ typedef enum avr_error { pdi_failure, } avr_error_e; +#define AVR_MAX_BREAKPOINTS 2U +#define AVR_BREAKPOINT_ENABLED 0x80000000U +#define AVR_BREAKPOINT_MASK 0x00ffffffU + typedef struct avr_pdi avr_pdi_s; struct avr_pdi { @@ -51,6 +55,11 @@ struct avr_pdi { target_halt_reason_e halt_reason; avr_error_e error_state; + /* Storage slots for the current breakpoints configuration */ + uint32_t breakpoints[AVR_MAX_BREAKPOINTS]; + /* A count of the total available breakpoints on the target */ + uint8_t breakpoints_available; + bool (*ensure_nvm_idle)(const avr_pdi_s *pdi); }; From 23451382acf6fbdf341e7120ffa090de13bb5d4f Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 15:53:09 +0000 Subject: [PATCH 34/42] atxmega: Defined the number of breakpoints available on a ATXMega --- src/target/atxmega.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index e586f88bfcd..e17b06e0b4b 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -226,6 +226,9 @@ bool atxmega_probe(target_s *const target) avr_pdi_s *const pdi = avr_pdi_struct(target); pdi->ensure_nvm_idle = atxmega_ensure_nvm_idle; + + /* This is unfortunately hard-coded as we don't currently have a way to "learn" this from the target. */ + pdi->breakpoints_available = 2; return true; } From 168fc7c4a6c2d7d8d65e51544eed0c5b3b7e98dd Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 15:54:05 +0000 Subject: [PATCH 35/42] avr_pdi: Implemented the debugger-facing part of the breakwatch system --- src/target/avr_pdi.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 81da58a2fd5..7d1cd64a6bd 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -85,6 +85,9 @@ static void avr_reset(target_s *target); static void avr_halt_request(target_s *target); static target_halt_reason_e avr_halt_poll(target_s *target, target_addr_t *watch); +static int avr_breakwatch_set(target_s *target, breakwatch_s *breakwatch); +static int avr_breakwatch_clear(target_s *target, breakwatch_s *breakwatch); + void avr_jtag_pdi_handler(const uint8_t dev_index) { avr_pdi_s *pdi = calloc(1, sizeof(*pdi)); @@ -134,6 +137,9 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) */ target->regs_size = sizeof(avr_regs_s); + target->breakwatch_set = avr_breakwatch_set; + target->breakwatch_clear = avr_breakwatch_clear; + /* Try probing for various known AVR parts */ PROBE(atxmega_probe); @@ -445,3 +451,47 @@ static target_halt_reason_e avr_halt_poll(target_s *const target, target_addr_t pdi->halt_reason = TARGET_HALT_BREAKPOINT; return pdi->halt_reason; } + +/* + * The following can be used as a key for understanding the various return results from the breakwatch functions: + * 0 -> success + * 1 -> not supported + * -1 -> an error occured + */ + +static int avr_breakwatch_set(target_s *const target, breakwatch_s *const breakwatch) +{ + avr_pdi_s *const pdi = avr_pdi_struct(target); + switch (breakwatch->type) { + case TARGET_BREAK_HARD: { + /* First try and find a unused breakpoint slot */ + size_t breakpoint = 0; + for (; breakpoint < pdi->breakpoints_available; ++breakpoint) { + /* Check if the slot is presently in use, breaking if it is not */ + if (!(pdi->breakpoints[breakpoint] & AVR_BREAKPOINT_ENABLED)) + break; + } + /* If none was available, return an error */ + if (breakpoint == pdi->breakpoints_available) + return -1; + + /* Store the address to set the breakpoint, and store the index of the slot used in the breakwatch structure */ + pdi->breakpoints[breakpoint] = AVR_BREAKPOINT_ENABLED | (breakwatch->addr & AVR_BREAKPOINT_MASK); + breakwatch->reserved[0] = breakpoint; + /* Tell the debugger that it was successfully able to "set" the breakpoint */ + return 0; + } + default: + /* If the breakwatch type is not one of the above, tell the debugger we don't support it */ + return 1; + } +} + +static int avr_breakwatch_clear(target_s *const target, breakwatch_s *const breakwatch) +{ + avr_pdi_s *const pdi = avr_pdi_struct(target); + /* Clear the breakpoint slot this used */ + pdi->breakpoints[breakwatch->reserved[0]] = 0U; + /* Tell the debugger that it was successfully able to "clear" the breakpoint */ + return 0; +} From 257ca868f572b05f1ef43b0c5c6baa90e06930e1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 16:25:00 +0000 Subject: [PATCH 36/42] avr_pdi: Added documentation for how various parts of the support works --- src/target/avr_pdi.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 7d1cd64a6bd..c70b4d5930a 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -96,10 +96,13 @@ void avr_jtag_pdi_handler(const uint8_t dev_index) return; } + /* Setup and try to initialise the PDI controller */ pdi->dev_index = dev_index; pdi->idcode = jtag_devs[dev_index].jd_idcode; + /* If we failed, free the structure */ if (!avr_pdi_init(pdi)) free(pdi); + /* Reset the JTAG machinary back to bypass to scan the next device in the chain */ jtag_dev_write_ir(dev_index, IR_BYPASS); } @@ -114,10 +117,12 @@ static bool avr_pdi_init(avr_pdi_s *const pdi) /* Transition the part into PDI mode */ jtag_dev_write_ir(pdi->dev_index, IR_PDI); + /* Allocate a new target */ target_s *target = target_new(); if (!target) return false; + /* Do preliminary setup of the target structure for an AVR target */ target->cpuid = pdi->idcode; target->part_id = (pdi->idcode >> 12U) & 0xffffU; target->driver = "Atmel AVR"; @@ -170,24 +175,30 @@ static bool avr_jtag_shift_dr(uint8_t dev_index, uint8_t *data_out, const uint8_ uint8_t *data; if (!data_out) return false; + /* Try to perform a PDI access, with retry when the reply is a delay packet */ do { data = (uint8_t *)&request; + /* Begin the data register transaction, handling any devices in bypass before us in the chain */ jtagtap_shift_dr(); jtag_proc.jtagtap_tdi_seq(false, ones, dev->dr_prescan); + /* Build the PDI packet to send */ data[0] = data_in; - /* Calculate the parity bit */ + /* Calculate the parity bit of the request */ for (uint8_t i = 0; i < 8; ++i) data[1] ^= (data_in >> i) & 1U; + /* Then send it and handle any devices in bypass after us in the chain */ jtag_proc.jtagtap_tdi_tdo_seq((uint8_t *)&response, !dev->dr_postscan, (uint8_t *)&request, 9); jtag_proc.jtagtap_tdi_seq(true, ones, dev->dr_postscan); + /* Then return the JTAG bus to idle */ jtagtap_return_idle(0); data = (uint8_t *)&response; } while (data[0] == PDI_DELAY && data[1] == 1); - /* Calculate the parity bit */ + /* Calculate the parity bit from the reply */ for (uint8_t i = 0; i < 8; ++i) result ^= (data[0] >> i) & 1U; *data_out = data[0]; - DEBUG_WARN("Sent 0x%02x to target, response was 0x%02x (0x%x)\n", data_in, data[0], data[1]); + DEBUG_TARGET("Sent 0x%02x to target, response was 0x%02x (0x%x)\n", data_in, data[0], data[1]); + /* Return if the calculated parity matches the received parity */ return result == data[1]; } @@ -352,6 +363,11 @@ bool avr_pdi_read_ind( return true; } +/* + * Enable a PDI feature based on the `what` argument. + * This sends a PDI `KEY` instruction with the appropriate enablement key and + * returns if the PDI controller did actually enable the requested feature. + */ static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) { const uint8_t *const key = what == PDI_DEBUG ? pdi_key_debug : pdi_key_nvm; @@ -365,6 +381,7 @@ static bool avr_enable(const avr_pdi_s *const pdi, const pdi_key_e what) return (avr_pdi_reg_read(pdi, PDI_REG_STATUS) & what) == what; } +/* Disables the requested PDI feature based on the `what` argument */ static bool avr_disable(const avr_pdi_s *const pdi, const pdi_key_e what) { return avr_pdi_reg_write(pdi, PDI_REG_STATUS, ~what); @@ -380,13 +397,21 @@ static bool avr_ensure_nvm_idle(const avr_pdi_s *const pdi) static bool avr_attach(target_s *const target) { const avr_pdi_s *const pdi = target->priv; + /* Put the target back in PDI comms mode */ jtag_dev_write_ir(pdi->dev_index, IR_PDI); TRY (EXCEPTION_ALL) { + /* Attempt to reset the target */ target_reset(target); + /* Then enable the debug unit in the PDI controller */ if (!avr_enable(pdi, PDI_DEBUG)) return false; + /* Ask the PDI controller to halt the processor */ target_halt_request(target); + /* + * And now finish by enabling the NVM unit in the PDI controller and + * check that the processor is in administrative halt + */ if (!avr_enable(pdi, PDI_NVM) || !avr_ensure_nvm_idle(pdi) || avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U) return false; } @@ -401,6 +426,7 @@ static void avr_detach(target_s *const target) { const avr_pdi_s *const pdi = target->priv; + /* Disable all enabled PDI controller units to detach from the target */ avr_disable(pdi, PDI_NVM); avr_disable(pdi, PDI_DEBUG); jtag_dev_write_ir(pdi->dev_index, IR_BYPASS); @@ -415,8 +441,10 @@ static void avr_reset(target_s *const target) */ if (target->attached) return; + /* Write the reset register to initiate reset */ if (!avr_pdi_reg_write(pdi, PDI_REG_RESET, PDI_RESET)) raise_exception(EXCEPTION_ERROR, "Error resetting device, device in incorrect state"); + /* If any PDI controller units are presently enabled, disable them */ if (avr_pdi_reg_read(pdi, PDI_REG_STATUS) != 0x00) { avr_disable(pdi, PDI_NVM); avr_disable(pdi, PDI_DEBUG); @@ -426,7 +454,8 @@ static void avr_reset(target_s *const target) static void avr_halt_request(target_s *const target) { avr_pdi_s *const pdi = target->priv; - /* To halt the processor we go through a few really specific steps: + /* + * 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 * Release reset From 22686efe8668e61c811e4bf6c0ebb221cfa07590 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 16:49:11 +0000 Subject: [PATCH 37/42] atxmega: Implemented the config breakpoints hook for the AVR support --- src/target/atxmega.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index e17b06e0b4b..6176698e9b1 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -55,6 +55,12 @@ #define AVR_DBG_READ_REGS 0x11U #define AVR_NUM_REGS 32 +#define ATXMEGA_BRK_BASE 0x00000020U +#define ATXMEGA_BRK_COUNTER 0x00000028U +#define ATXMEGA_BRK_UNKNOWN1 0x00000040U +#define ATXMEGA_BRK_UNKNOWN2 0x00000046U +#define ATXMEGA_BRK_UNKNOWN3 0x00000048U + #define ATXMEGA_CPU_BASE 0x01000030U /* Address of the low byte of the stack pointer */ #define ATXMEGA_CPU_SPL (ATXMEGA_CPU_BASE + 0xdU) @@ -117,6 +123,7 @@ static void atxmega_regs_read(target_s *target, void *data); static void atxmega_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); static bool atxmega_ensure_nvm_idle(const avr_pdi_s *pdi); +static bool atxmega_config_breakpoints(const avr_pdi_s *pdi, bool step); void avr_add_flash(target_s *const target, const uint32_t start, const size_t length, const uint16_t block_size) { @@ -432,3 +439,32 @@ static void atxmega_regs_read(target_s *const target, void *const data) regs->sp = status[0] | (status[1] << 8); regs->sreg = status[2]; } + +static bool atxmega_config_breakpoints(const avr_pdi_s *const pdi, const bool step) +{ + uint8_t breakpoint_count = 0U; + if (step) { + /* If we are single stepping, clear all enabled breakpoints */ + for (uint8_t idx = 0; idx < pdi->breakpoints_available; ++idx) { + if (!avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_BRK_BASE + (idx * 4U), 0U)) + return false; + } + } else { + /* We are not single stepping, so configure the breakpoints as defined in the PDI structure */ + for (uint8_t idx = 0; idx < pdi->breakpoints_available; ++idx) { + const uint32_t breakpoint = pdi->breakpoints[idx]; + /* If the breakpoint is enabled, increment breakpoint_count */ + if (breakpoint & AVR_BREAKPOINT_ENABLED) + ++breakpoint_count; + /* Try to write the address of the breakpoint */ + /* XXX: Need to first collect all the breakpoints on the stack, then write all of them used first */ + if (!avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_BRK_BASE + (idx * 4U), breakpoint & AVR_BREAKPOINT_MASK)) + return false; + } + } + /* Tell the breakpoint unit how many breakpoints are enabled */ + return avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_BRK_UNKNOWN1, 0) && + avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_BRK_UNKNOWN2, 0) && + avr_pdi_write(pdi, PDI_DATA_16, ATXMEGA_BRK_COUNTER, (uint16_t)breakpoint_count << 8U) && + avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_BRK_UNKNOWN3, 0); +} From 79b2e890e3106f3b4be46ac8590be432d8b1399c Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 17:27:43 +0000 Subject: [PATCH 38/42] avr_pdi: Cleaned up use of `target->priv` --- src/target/avr_pdi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index c70b4d5930a..9aac28cd929 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -396,7 +396,7 @@ static bool avr_ensure_nvm_idle(const avr_pdi_s *const pdi) static bool avr_attach(target_s *const target) { - const avr_pdi_s *const pdi = target->priv; + const avr_pdi_s *const pdi = avr_pdi_struct(target); /* Put the target back in PDI comms mode */ jtag_dev_write_ir(pdi->dev_index, IR_PDI); @@ -424,7 +424,7 @@ static bool avr_attach(target_s *const target) static void avr_detach(target_s *const target) { - const avr_pdi_s *const pdi = target->priv; + const avr_pdi_s *const pdi = avr_pdi_struct(target); /* Disable all enabled PDI controller units to detach from the target */ avr_disable(pdi, PDI_NVM); @@ -434,7 +434,7 @@ static void avr_detach(target_s *const target) static void avr_reset(target_s *const target) { - avr_pdi_s *const pdi = target->priv; + avr_pdi_s *const pdi = avr_pdi_struct(target); /* * We only actually want to do this if the target is not presently attached as * this resets the NVM and debug enables @@ -453,7 +453,7 @@ static void avr_reset(target_s *const target) static void avr_halt_request(target_s *const target) { - avr_pdi_s *const pdi = target->priv; + avr_pdi_s *const pdi = avr_pdi_struct(target); /* * 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 @@ -472,7 +472,7 @@ static void avr_halt_request(target_s *const target) static target_halt_reason_e avr_halt_poll(target_s *const target, target_addr_t *const watch) { - avr_pdi_s *const pdi = target->priv; + avr_pdi_s *const pdi = avr_pdi_struct(target); (void)watch; /* If we're running but the processor stops because it's hit a breakpoint, update */ From 7a505cf3983c2f6c25c798d83616e5392b0a37cf Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 17:28:45 +0000 Subject: [PATCH 39/42] atxmega: Implemented storage of the program counter from atxmega_regs_read() --- src/target/atxmega.c | 12 +++++++----- src/target/avr_pdi.h | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 6176698e9b1..1ad9afb5114 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -417,11 +417,11 @@ static void atxmega_mem_read(target_s *const target, void *const dest, const tar static void atxmega_regs_read(target_s *const target, void *const data) { - const avr_pdi_s *const pdi = avr_pdi_struct(target); + avr_pdi_s *const pdi = avr_pdi_struct(target); avr_regs_s *regs = (avr_regs_s *)data; uint8_t status[3]; - uint32_t pc = 0; - if (!avr_pdi_read32(pdi, ATXMEGA_DBG_PC, &pc) || + uint32_t program_counter = 0; + if (!avr_pdi_read32(pdi, ATXMEGA_DBG_PC, &program_counter) || !avr_pdi_read_ind(pdi, ATXMEGA_CPU_SPL, PDI_MODE_IND_INCPTR, status, 3) || !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_PC, 0) || !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_CTR, AVR_NUM_REGS) || @@ -430,13 +430,15 @@ static void atxmega_regs_read(target_s *const target, void *const data) !avr_pdi_read_ind(pdi, ATXMEGA_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"); + /* Store the newly read program counter */ + pdi->program_counter = program_counter - 1U; /* * 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 have to adjust it by 1 and make it bytes. */ - regs->pc = (pc - 1) << 1U; - regs->sp = status[0] | (status[1] << 8); + regs->pc = pdi->program_counter << 1U; + regs->sp = status[0] | ((uint16_t)status[1] << 8); regs->sreg = status[2]; } diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 921df48ccfd..2379dc654be 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -54,6 +54,7 @@ struct avr_pdi { uint8_t dev_index; target_halt_reason_e halt_reason; avr_error_e error_state; + uint32_t program_counter; /* Storage slots for the current breakpoints configuration */ uint32_t breakpoints[AVR_MAX_BREAKPOINTS]; From 67577dbf4c021407ff5abd640a170c0a30723857 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 17:29:05 +0000 Subject: [PATCH 40/42] atxmega: Implemented target halt_resume for single stepping --- src/target/atxmega.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 1ad9afb5114..12ec34fe00c 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -119,6 +119,8 @@ static bool atxmega_flash_done(target_flash_s *flash); static const char *atxmega_target_description(target_s *target); static bool atxmega_check_error(target_s *target); +static void atxmega_halt_resume(target_s *target, bool step); + static void atxmega_regs_read(target_s *target, void *data); static void atxmega_mem_read(target_s *target, void *dest, target_addr64_t src, size_t len); @@ -215,6 +217,7 @@ bool atxmega_probe(target_s *const target) target->regs_description = atxmega_target_description; target->check_error = atxmega_check_error; + target->halt_resume = atxmega_halt_resume; target->regs_read = atxmega_regs_read; target->mem_read = atxmega_mem_read; @@ -470,3 +473,36 @@ static bool atxmega_config_breakpoints(const avr_pdi_s *const pdi, const bool st avr_pdi_write(pdi, PDI_DATA_16, ATXMEGA_BRK_COUNTER, (uint16_t)breakpoint_count << 8U) && avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_BRK_UNKNOWN3, 0); } + +static void atxmega_halt_resume(target_s *const target, const bool step) +{ + avr_pdi_s *const pdi = avr_pdi_struct(target); + if (step) { + const uint32_t current_pc = pdi->program_counter; + const uint32_t next_pc = current_pc + 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 + */ + /* Check that we are in administrative halt */ + if (avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + /* Try to configure (clear) the breakpoints */ + !atxmega_config_breakpoints(pdi, step) || avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + /* Configure the debug controller */ + !avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_DBG_CTRL, 4U) || + !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_CTR, next_pc) || + !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_PC, current_pc) || + avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + /* And try to execute the request*/ + !avr_pdi_reg_write(pdi, PDI_REG_R4, 1U)) + raise_exception(EXCEPTION_ERROR, "Error stepping device, device in incorrect state"); + /* Then spin waiting to see the processor stop back in administrative halt */ + while (avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U) + continue; + pdi->halt_reason = TARGET_HALT_STEPPING; + } else { + pdi->halt_reason = TARGET_HALT_RUNNING; + } +} From 2d998499cc135c95ed5ec6f5ab0f47ac272bc8d1 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 17:49:58 +0000 Subject: [PATCH 41/42] avr_pdi: Moved all the PDI registers definitions to the header --- src/target/avr_pdi.c | 4 ---- src/target/avr_pdi.h | 7 +++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/target/avr_pdi.c b/src/target/avr_pdi.c index 9aac28cd929..3c60275f472 100644 --- a/src/target/avr_pdi.c +++ b/src/target/avr_pdi.c @@ -63,10 +63,6 @@ #define PDI_MODE_MASK 0xf3U -#define PDI_REG_STATUS 0U -#define PDI_REG_RESET 1U -#define PDI_REG_CTRL 2U - #define PDI_RESET 0x59U typedef enum pdi_key { diff --git a/src/target/avr_pdi.h b/src/target/avr_pdi.h index 2379dc654be..578d108fdee 100644 --- a/src/target/avr_pdi.h +++ b/src/target/avr_pdi.h @@ -76,8 +76,11 @@ typedef struct __attribute__((packed)) avr_regs { #define PDI_DATA_24 0x02U #define PDI_DATA_32 0x03U -#define PDI_REG_R3 3U -#define PDI_REG_R4 4U +#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_MODE_IND_PTR 0x00U #define PDI_MODE_IND_INCPTR 0x04U From f992b374455f3807e081fb945c90d7d672194777 Mon Sep 17 00:00:00 2001 From: dragonmux Date: Sun, 12 Feb 2023 17:50:27 +0000 Subject: [PATCH 42/42] atxmega: Created an initial implementation of breakpoint-based halt_resume --- src/target/atxmega.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/target/atxmega.c b/src/target/atxmega.c index 12ec34fe00c..9b2bc109311 100644 --- a/src/target/atxmega.c +++ b/src/target/atxmega.c @@ -503,6 +503,26 @@ static void atxmega_halt_resume(target_s *const target, const bool step) continue; 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 + */ + /* Check that we are in administrative halt */ + if (avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + /* Try to configure the breakpoints */ + !atxmega_config_breakpoints(pdi, step) || avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + /* Set the program counter and release reset */ + !avr_pdi_write(pdi, PDI_DATA_32, ATXMEGA_DBG_PC, pdi->program_counter) || + !avr_pdi_reg_write(pdi, PDI_REG_RESET, 0U) || + /* Configure the debug controller */ + !avr_pdi_write(pdi, PDI_DATA_8, ATXMEGA_DBG_CTRL, 0U) || avr_pdi_reg_read(pdi, PDI_REG_R3) != 0x04U || + /* And try to execute the request */ + !avr_pdi_reg_write(pdi, PDI_REG_R4, 1U)) + raise_exception(EXCEPTION_ERROR, "Error resuming device, device in incorrect state"); pdi->halt_reason = TARGET_HALT_RUNNING; } }