aboutsummaryrefslogtreecommitdiff
path: root/src/target
diff options
context:
space:
mode:
authorErhan Kurubas <erhan.kurubas@espressif.com>2022-04-21 07:53:54 +0200
committerAntonio Borneo <borneo.antonio@gmail.com>2022-06-04 08:18:44 +0000
commit78c87f5e81f8b3ee2a72aa546f87985596cb2b9f (patch)
treed4507e98ff15487c2ffb6a2890f23cd650bc3c6e /src/target
parentb470b664ca7ba3f21684848c3819d696fb3c890a (diff)
downloadriscv-openocd-78c87f5e81f8b3ee2a72aa546f87985596cb2b9f.zip
riscv-openocd-78c87f5e81f8b3ee2a72aa546f87985596cb2b9f.tar.gz
riscv-openocd-78c87f5e81f8b3ee2a72aa546f87985596cb2b9f.tar.bz2
target: add Espressif ESP32-S2 basic support
ESP32-S2 is a single core Xtensa chip. Not full featured yet. Some of the missing functionality: -Semihosting -Flash breakpoints -Flash loader -Apptrace -FreeRTOS Signed-off-by: Erhan Kurubas <erhan.kurubas@espressif.com> Change-Id: I2fb32978e801af5aa21616c581691406ad7cd6bb Reviewed-on: https://review.openocd.org/c/openocd/+/6940 Reviewed-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-by: Ian Thompson <ianst@cadence.com> Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com> Tested-by: jenkins
Diffstat (limited to 'src/target')
-rw-r--r--src/target/Makefile.am6
-rw-r--r--src/target/espressif/Makefile.am6
-rw-r--r--src/target/espressif/esp32s2.c715
-rw-r--r--src/target/espressif/esp32s2.h40
-rw-r--r--src/target/espressif/esp_xtensa.c71
-rw-r--r--src/target/espressif/esp_xtensa.h48
-rw-r--r--src/target/target.c2
-rw-r--r--src/target/xtensa/Makefile.am7
-rw-r--r--src/target/xtensa/xtensa.c2731
-rw-r--r--src/target/xtensa/xtensa.h309
-rw-r--r--src/target/xtensa/xtensa_debug_module.c359
-rw-r--r--src/target/xtensa/xtensa_debug_module.h385
-rw-r--r--src/target/xtensa/xtensa_regs.h278
13 files changed, 4956 insertions, 1 deletions
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 49e882f..799c3dd 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -1,5 +1,7 @@
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
- %D%/riscv/libriscv.la
+ %D%/riscv/libriscv.la \
+ %D%/xtensa/libxtensa.la \
+ %D%/espressif/libespressif.la
%C%_libtarget_la_CPPFLAGS = $(AM_CPPFLAGS)
@@ -260,3 +262,5 @@ ARC_SRC = \
include %D%/openrisc/Makefile.am
include %D%/riscv/Makefile.am
+include %D%/xtensa/Makefile.am
+include %D%/espressif/Makefile.am \ No newline at end of file
diff --git a/src/target/espressif/Makefile.am b/src/target/espressif/Makefile.am
new file mode 100644
index 0000000..c681e09
--- /dev/null
+++ b/src/target/espressif/Makefile.am
@@ -0,0 +1,6 @@
+noinst_LTLIBRARIES += %D%/libespressif.la
+%C%_libespressif_la_SOURCES = \
+ %D%/esp_xtensa.c \
+ %D%/esp_xtensa.h \
+ %D%/esp32s2.c \
+ %D%/esp32s2.h
diff --git a/src/target/espressif/esp32s2.c b/src/target/espressif/esp32s2.c
new file mode 100644
index 0000000..212533f
--- /dev/null
+++ b/src/target/espressif/esp32s2.c
@@ -0,0 +1,715 @@
+/***************************************************************************
+ * ESP32-S2 target for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "assert.h"
+#include <target/target.h>
+#include <target/target_type.h>
+#include "esp_xtensa.h"
+#include "esp32s2.h"
+
+/* Overall memory map
+ * TODO: read memory configuration from target registers */
+#define ESP32_S2_IROM_MASK_LOW 0x40000000
+#define ESP32_S2_IROM_MASK_HIGH 0x40020000
+#define ESP32_S2_IRAM_LOW 0x40020000
+#define ESP32_S2_IRAM_HIGH 0x40070000
+#define ESP32_S2_DRAM_LOW 0x3ffb0000
+#define ESP32_S2_DRAM_HIGH 0x40000000
+#define ESP32_S2_RTC_IRAM_LOW 0x40070000
+#define ESP32_S2_RTC_IRAM_HIGH 0x40072000
+#define ESP32_S2_RTC_DRAM_LOW 0x3ff9e000
+#define ESP32_S2_RTC_DRAM_HIGH 0x3ffa0000
+#define ESP32_S2_RTC_DATA_LOW 0x50000000
+#define ESP32_S2_RTC_DATA_HIGH 0x50002000
+#define ESP32_S2_EXTRAM_DATA_LOW 0x3f500000
+#define ESP32_S2_EXTRAM_DATA_HIGH 0x3ff80000
+#define ESP32_S2_DR_REG_LOW 0x3f400000
+#define ESP32_S2_DR_REG_HIGH 0x3f4d3FFC
+#define ESP32_S2_SYS_RAM_LOW 0x60000000UL
+#define ESP32_S2_SYS_RAM_HIGH (ESP32_S2_SYS_RAM_LOW + 0x20000000UL)
+/* ESP32-S2 DROM mapping is not contiguous. */
+/* IDF declares this as 0x3F000000..0x3FF80000, but there are peripheral registers mapped to
+ * 0x3f400000..0x3f4d3FFC. */
+#define ESP32_S2_DROM0_LOW ESP32_S2_DROM_LOW
+#define ESP32_S2_DROM0_HIGH ESP32_S2_DR_REG_LOW
+#define ESP32_S2_DROM1_LOW ESP32_S2_DR_REG_HIGH
+#define ESP32_S2_DROM1_HIGH ESP32_S2_DROM_HIGH
+
+/* ESP32 WDT */
+#define ESP32_S2_WDT_WKEY_VALUE 0x50d83aa1
+#define ESP32_S2_TIMG0_BASE 0x3f41F000
+#define ESP32_S2_TIMG1_BASE 0x3f420000
+#define ESP32_S2_TIMGWDT_CFG0_OFF 0x48
+#define ESP32_S2_TIMGWDT_PROTECT_OFF 0x64
+#define ESP32_S2_TIMG0WDT_CFG0 (ESP32_S2_TIMG0_BASE + ESP32_S2_TIMGWDT_CFG0_OFF)
+#define ESP32_S2_TIMG1WDT_CFG0 (ESP32_S2_TIMG1_BASE + ESP32_S2_TIMGWDT_CFG0_OFF)
+#define ESP32_S2_TIMG0WDT_PROTECT (ESP32_S2_TIMG0_BASE + ESP32_S2_TIMGWDT_PROTECT_OFF)
+#define ESP32_S2_TIMG1WDT_PROTECT (ESP32_S2_TIMG1_BASE + ESP32_S2_TIMGWDT_PROTECT_OFF)
+#define ESP32_S2_RTCCNTL_BASE 0x3f408000
+#define ESP32_S2_RTCWDT_CFG_OFF 0x94
+#define ESP32_S2_RTCWDT_PROTECT_OFF 0xAC
+#define ESP32_S2_SWD_CONF_OFF 0xB0
+#define ESP32_S2_SWD_WPROTECT_OFF 0xB4
+#define ESP32_S2_RTC_CNTL_DIG_PWC_REG_OFF 0x8C
+#define ESP32_S2_RTC_CNTL_DIG_PWC_REG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_RTC_CNTL_DIG_PWC_REG_OFF)
+#define ESP32_S2_RTCWDT_CFG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_RTCWDT_CFG_OFF)
+#define ESP32_S2_RTCWDT_PROTECT (ESP32_S2_RTCCNTL_BASE + ESP32_S2_RTCWDT_PROTECT_OFF)
+#define ESP32_S2_SWD_CONF_REG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_SWD_CONF_OFF)
+#define ESP32_S2_SWD_WPROTECT_REG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_SWD_WPROTECT_OFF)
+#define ESP32_S2_SWD_AUTO_FEED_EN_M BIT(31)
+#define ESP32_S2_SWD_WKEY_VALUE 0x8F1D312AU
+#define ESP32_S2_OPTIONS0 (ESP32_S2_RTCCNTL_BASE + 0x0000)
+#define ESP32_S2_SW_SYS_RST_M 0x80000000
+#define ESP32_S2_SW_SYS_RST_V 0x1
+#define ESP32_S2_SW_SYS_RST_S 31
+#define ESP32_S2_SW_STALL_PROCPU_C0_M ((ESP32_S2_SW_STALL_PROCPU_C0_V) << (ESP32_S2_SW_STALL_PROCPU_C0_S))
+#define ESP32_S2_SW_STALL_PROCPU_C0_V 0x3
+#define ESP32_S2_SW_STALL_PROCPU_C0_S 2
+#define ESP32_S2_SW_CPU_STALL (ESP32_S2_RTCCNTL_BASE + 0x00B8)
+#define ESP32_S2_SW_STALL_PROCPU_C1_M ((ESP32_S2_SW_STALL_PROCPU_C1_V) << (ESP32_S2_SW_STALL_PROCPU_C1_S))
+#define ESP32_S2_SW_STALL_PROCPU_C1_V 0x3FU
+#define ESP32_S2_SW_STALL_PROCPU_C1_S 26
+#define ESP32_S2_CLK_CONF (ESP32_S2_RTCCNTL_BASE + 0x0074)
+#define ESP32_S2_CLK_CONF_DEF 0x1583218
+#define ESP32_S2_STORE4 (ESP32_S2_RTCCNTL_BASE + 0x00BC)
+#define ESP32_S2_STORE5 (ESP32_S2_RTCCNTL_BASE + 0x00C0)
+#define ESP32_S2_DPORT_PMS_OCCUPY_3 0x3F4C10E0
+
+#define ESP32_S2_TRACEMEM_BLOCK_SZ 0x4000
+
+#define ESP32_S2_DR_REG_UART_BASE 0x3f400000
+#define ESP32_S2_REG_UART_BASE(i) (ESP32_S2_DR_REG_UART_BASE + (i) * 0x10000)
+#define ESP32_S2_UART_DATE_REG(i) (ESP32_S2_REG_UART_BASE(i) + 0x74)
+
+/* this should map local reg IDs to GDB reg mapping as defined in xtensa-config.c 'rmap' in
+ * xtensa-overlay */
+static const unsigned int esp32s2_gdb_regs_mapping[ESP32_S2_NUM_REGS] = {
+ XT_REG_IDX_PC,
+ XT_REG_IDX_AR0, XT_REG_IDX_AR1, XT_REG_IDX_AR2, XT_REG_IDX_AR3,
+ XT_REG_IDX_AR4, XT_REG_IDX_AR5, XT_REG_IDX_AR6, XT_REG_IDX_AR7,
+ XT_REG_IDX_AR8, XT_REG_IDX_AR9, XT_REG_IDX_AR10, XT_REG_IDX_AR11,
+ XT_REG_IDX_AR12, XT_REG_IDX_AR13, XT_REG_IDX_AR14, XT_REG_IDX_AR15,
+ XT_REG_IDX_AR16, XT_REG_IDX_AR17, XT_REG_IDX_AR18, XT_REG_IDX_AR19,
+ XT_REG_IDX_AR20, XT_REG_IDX_AR21, XT_REG_IDX_AR22, XT_REG_IDX_AR23,
+ XT_REG_IDX_AR24, XT_REG_IDX_AR25, XT_REG_IDX_AR26, XT_REG_IDX_AR27,
+ XT_REG_IDX_AR28, XT_REG_IDX_AR29, XT_REG_IDX_AR30, XT_REG_IDX_AR31,
+ XT_REG_IDX_AR32, XT_REG_IDX_AR33, XT_REG_IDX_AR34, XT_REG_IDX_AR35,
+ XT_REG_IDX_AR36, XT_REG_IDX_AR37, XT_REG_IDX_AR38, XT_REG_IDX_AR39,
+ XT_REG_IDX_AR40, XT_REG_IDX_AR41, XT_REG_IDX_AR42, XT_REG_IDX_AR43,
+ XT_REG_IDX_AR44, XT_REG_IDX_AR45, XT_REG_IDX_AR46, XT_REG_IDX_AR47,
+ XT_REG_IDX_AR48, XT_REG_IDX_AR49, XT_REG_IDX_AR50, XT_REG_IDX_AR51,
+ XT_REG_IDX_AR52, XT_REG_IDX_AR53, XT_REG_IDX_AR54, XT_REG_IDX_AR55,
+ XT_REG_IDX_AR56, XT_REG_IDX_AR57, XT_REG_IDX_AR58, XT_REG_IDX_AR59,
+ XT_REG_IDX_AR60, XT_REG_IDX_AR61, XT_REG_IDX_AR62, XT_REG_IDX_AR63,
+ XT_REG_IDX_SAR,
+ XT_REG_IDX_WINDOWBASE, XT_REG_IDX_WINDOWSTART, XT_REG_IDX_CONFIGID0, XT_REG_IDX_CONFIGID1,
+ XT_REG_IDX_PS, XT_REG_IDX_THREADPTR,
+ ESP32_S2_REG_IDX_GPIOOUT,
+ XT_REG_IDX_MMID, XT_REG_IDX_IBREAKENABLE, XT_REG_IDX_OCD_DDR,
+ XT_REG_IDX_IBREAKA0, XT_REG_IDX_IBREAKA1, XT_REG_IDX_DBREAKA0, XT_REG_IDX_DBREAKA1,
+ XT_REG_IDX_DBREAKC0, XT_REG_IDX_DBREAKC1,
+ XT_REG_IDX_EPC1, XT_REG_IDX_EPC2, XT_REG_IDX_EPC3, XT_REG_IDX_EPC4,
+ XT_REG_IDX_EPC5, XT_REG_IDX_EPC6, XT_REG_IDX_EPC7, XT_REG_IDX_DEPC,
+ XT_REG_IDX_EPS2, XT_REG_IDX_EPS3, XT_REG_IDX_EPS4, XT_REG_IDX_EPS5,
+ XT_REG_IDX_EPS6, XT_REG_IDX_EPS7,
+ XT_REG_IDX_EXCSAVE1, XT_REG_IDX_EXCSAVE2, XT_REG_IDX_EXCSAVE3, XT_REG_IDX_EXCSAVE4,
+ XT_REG_IDX_EXCSAVE5, XT_REG_IDX_EXCSAVE6, XT_REG_IDX_EXCSAVE7, XT_REG_IDX_CPENABLE,
+ XT_REG_IDX_INTERRUPT, XT_REG_IDX_INTSET, XT_REG_IDX_INTCLEAR, XT_REG_IDX_INTENABLE,
+ XT_REG_IDX_VECBASE, XT_REG_IDX_EXCCAUSE, XT_REG_IDX_DEBUGCAUSE, XT_REG_IDX_CCOUNT,
+ XT_REG_IDX_PRID, XT_REG_IDX_ICOUNT, XT_REG_IDX_ICOUNTLEVEL, XT_REG_IDX_EXCVADDR,
+ XT_REG_IDX_CCOMPARE0, XT_REG_IDX_CCOMPARE1, XT_REG_IDX_CCOMPARE2,
+ XT_REG_IDX_MISC0, XT_REG_IDX_MISC1, XT_REG_IDX_MISC2, XT_REG_IDX_MISC3,
+ XT_REG_IDX_A0, XT_REG_IDX_A1, XT_REG_IDX_A2, XT_REG_IDX_A3,
+ XT_REG_IDX_A4, XT_REG_IDX_A5, XT_REG_IDX_A6, XT_REG_IDX_A7,
+ XT_REG_IDX_A8, XT_REG_IDX_A9, XT_REG_IDX_A10, XT_REG_IDX_A11,
+ XT_REG_IDX_A12, XT_REG_IDX_A13, XT_REG_IDX_A14, XT_REG_IDX_A15,
+ XT_REG_IDX_PWRCTL, XT_REG_IDX_PWRSTAT, XT_REG_IDX_ERISTAT,
+ XT_REG_IDX_CS_ITCTRL, XT_REG_IDX_CS_CLAIMSET, XT_REG_IDX_CS_CLAIMCLR,
+ XT_REG_IDX_CS_LOCKACCESS, XT_REG_IDX_CS_LOCKSTATUS, XT_REG_IDX_CS_AUTHSTATUS,
+ XT_REG_IDX_FAULT_INFO,
+ XT_REG_IDX_TRAX_ID, XT_REG_IDX_TRAX_CTRL, XT_REG_IDX_TRAX_STAT,
+ XT_REG_IDX_TRAX_DATA, XT_REG_IDX_TRAX_ADDR, XT_REG_IDX_TRAX_PCTRIGGER,
+ XT_REG_IDX_TRAX_PCMATCH, XT_REG_IDX_TRAX_DELAY, XT_REG_IDX_TRAX_MEMSTART,
+ XT_REG_IDX_TRAX_MEMEND,
+ XT_REG_IDX_PMG, XT_REG_IDX_PMPC, XT_REG_IDX_PM0, XT_REG_IDX_PM1,
+ XT_REG_IDX_PMCTRL0, XT_REG_IDX_PMCTRL1, XT_REG_IDX_PMSTAT0, XT_REG_IDX_PMSTAT1,
+ XT_REG_IDX_OCD_ID, XT_REG_IDX_OCD_DCRCLR, XT_REG_IDX_OCD_DCRSET, XT_REG_IDX_OCD_DSR,
+};
+
+static const struct xtensa_user_reg_desc esp32s2_user_regs[ESP32_S2_NUM_REGS - XT_NUM_REGS] = {
+ { "gpio_out", 0x00, 0, 32, &xtensa_user_reg_u32_type },
+};
+
+static const struct xtensa_config esp32s2_xtensa_cfg = {
+ .density = true,
+ .aregs_num = XT_AREGS_NUM_MAX,
+ .windowed = true,
+ .coproc = true,
+ .miscregs_num = 4,
+ .reloc_vec = true,
+ .proc_id = true,
+ .threadptr = true,
+ .user_regs_num = ARRAY_SIZE(esp32s2_user_regs),
+ .user_regs = esp32s2_user_regs,
+ .fetch_user_regs = xtensa_fetch_user_regs_u32,
+ .queue_write_dirty_user_regs = xtensa_queue_write_dirty_user_regs_u32,
+ .gdb_general_regs_num = ESP32_S2_NUM_REGS_G_COMMAND,
+ .gdb_regs_mapping = esp32s2_gdb_regs_mapping,
+ .irom = {
+ .count = 2,
+ .regions = {
+ {
+ .base = ESP32_S2_IROM_LOW,
+ .size = ESP32_S2_IROM_HIGH - ESP32_S2_IROM_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ {
+ .base = ESP32_S2_IROM_MASK_LOW,
+ .size = ESP32_S2_IROM_MASK_HIGH - ESP32_S2_IROM_MASK_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ }
+ },
+ .iram = {
+ .count = 2,
+ .regions = {
+ {
+ .base = ESP32_S2_IRAM_LOW,
+ .size = ESP32_S2_IRAM_HIGH - ESP32_S2_IRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_RTC_IRAM_LOW,
+ .size = ESP32_S2_RTC_IRAM_HIGH - ESP32_S2_RTC_IRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ }
+ },
+ .drom = {
+ .count = 2,
+ .regions = {
+ {
+ .base = ESP32_S2_DROM0_LOW,
+ .size = ESP32_S2_DROM0_HIGH - ESP32_S2_DROM0_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ {
+ .base = ESP32_S2_DROM1_LOW,
+ .size = ESP32_S2_DROM1_HIGH - ESP32_S2_DROM1_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ }
+ },
+ .dram = {
+ .count = 6,
+ .regions = {
+ {
+ .base = ESP32_S2_DRAM_LOW,
+ .size = ESP32_S2_DRAM_HIGH - ESP32_S2_DRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_RTC_DRAM_LOW,
+ .size = ESP32_S2_RTC_DRAM_HIGH - ESP32_S2_RTC_DRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_RTC_DATA_LOW,
+ .size = ESP32_S2_RTC_DATA_HIGH - ESP32_S2_RTC_DATA_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_EXTRAM_DATA_LOW,
+ .size = ESP32_S2_EXTRAM_DATA_HIGH - ESP32_S2_EXTRAM_DATA_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_DR_REG_LOW,
+ .size = ESP32_S2_DR_REG_HIGH - ESP32_S2_DR_REG_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_SYS_RAM_LOW,
+ .size = ESP32_S2_SYS_RAM_HIGH - ESP32_S2_SYS_RAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ }
+ },
+ .exc = {
+ .enabled = true,
+ },
+ .irq = {
+ .enabled = true,
+ .irq_num = 32,
+ },
+ .high_irq = {
+ .enabled = true,
+ .excm_level = 3,
+ .nmi_num = 1,
+ },
+ .tim_irq = {
+ .enabled = true,
+ .comp_num = 3,
+ },
+ .debug = {
+ .enabled = true,
+ .irq_level = 6,
+ .ibreaks_num = 2,
+ .dbreaks_num = 2,
+ .icount_sz = 32,
+ },
+ .trace = {
+ .enabled = true,
+ .mem_sz = ESP32_S2_TRACEMEM_BLOCK_SZ,
+ },
+};
+
+struct esp32s2_common {
+ struct esp_xtensa_common esp_xtensa;
+};
+
+static int esp32s2_soc_reset(struct target *target);
+static int esp32s2_disable_wdts(struct target *target);
+
+static int esp32s2_assert_reset(struct target *target)
+{
+ return ERROR_OK;
+}
+
+static int esp32s2_deassert_reset(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "begin");
+
+ int res = xtensa_deassert_reset(target);
+ if (res != ERROR_OK)
+ return res;
+
+ /* restore configured value
+ esp32s2_soc_reset() modified it, but can not restore just after SW reset for some reason (???) */
+ res = xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to restore smpbreak (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+int esp32s2_soft_reset_halt(struct target *target)
+{
+ LOG_TARGET_DEBUG(target, "begin");
+
+ /* Reset the SoC first */
+ int res = esp32s2_soc_reset(target);
+ if (res != ERROR_OK)
+ return res;
+ return xtensa_assert_reset(target);
+}
+
+static int esp32s2_set_peri_reg_mask(struct target *target,
+ target_addr_t addr,
+ uint32_t mask,
+ uint32_t val)
+{
+ uint32_t reg_val;
+ int res = target_read_u32(target, addr, &reg_val);
+ if (res != ERROR_OK)
+ return res;
+ reg_val = (reg_val & (~mask)) | val;
+ res = target_write_u32(target, addr, reg_val);
+ if (res != ERROR_OK)
+ return res;
+
+ return ERROR_OK;
+}
+
+static int esp32s2_stall_set(struct target *target, bool stall)
+{
+ LOG_TARGET_DEBUG(target, "begin");
+
+ int res = esp32s2_set_peri_reg_mask(target,
+ ESP32_S2_SW_CPU_STALL,
+ ESP32_S2_SW_STALL_PROCPU_C1_M,
+ stall ? 0x21U << ESP32_S2_SW_STALL_PROCPU_C1_S : 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_SW_CPU_STALL (%d)!", res);
+ return res;
+ }
+ res = esp32s2_set_peri_reg_mask(target,
+ ESP32_S2_OPTIONS0,
+ ESP32_S2_SW_STALL_PROCPU_C0_M,
+ stall ? 0x2 << ESP32_S2_SW_STALL_PROCPU_C0_S : 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_OPTIONS0 (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+static inline int esp32s2_stall(struct target *target)
+{
+ return esp32s2_stall_set(target, true);
+}
+
+static inline int esp32s2_unstall(struct target *target)
+{
+ return esp32s2_stall_set(target, false);
+}
+
+/* Reset ESP32-S2's peripherals.
+Postconditions: all peripherals except RTC_CNTL are reset, CPU's PC is undefined, PRO CPU is halted, APP CPU is in reset
+How this works:
+0. make sure target is halted; if not, try to halt it; if that fails, try to reset it (via OCD) and then halt
+1. Resets clock related registers
+2. Stalls CPU
+3. trigger SoC reset using RTC_CNTL_SW_SYS_RST bit
+4. CPU is reset and stalled at the first reset vector instruction
+5. wait for the OCD to be reset
+6. halt the target
+7. Unstalls CPU
+8. Disables WDTs and trace memory mapping
+*/
+static int esp32s2_soc_reset(struct target *target)
+{
+ int res;
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_DEBUG("start");
+
+ /* In order to write to peripheral registers, target must be halted first */
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target, "Target not halted before SoC reset, trying to halt it first");
+ xtensa_halt(target);
+ res = target_wait_state(target, TARGET_HALTED, 1000);
+ if (res != ERROR_OK) {
+ LOG_TARGET_DEBUG(target, "Couldn't halt target before SoC reset, trying to do reset-halt");
+ res = xtensa_assert_reset(target);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(
+ target,
+ "Couldn't halt target before SoC reset! (xtensa_assert_reset returned %d)",
+ res);
+ return res;
+ }
+ alive_sleep(10);
+ xtensa_poll(target);
+ int reset_halt_save = target->reset_halt;
+ target->reset_halt = 1;
+ res = xtensa_deassert_reset(target);
+ target->reset_halt = reset_halt_save;
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(
+ target,
+ "Couldn't halt target before SoC reset! (xtensa_deassert_reset returned %d)",
+ res);
+ return res;
+ }
+ alive_sleep(10);
+ xtensa_poll(target);
+ xtensa_halt(target);
+ res = target_wait_state(target, TARGET_HALTED, 1000);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Couldn't halt target before SoC reset");
+ return res;
+ }
+ }
+ }
+
+ assert(target->state == TARGET_HALTED);
+
+ /* Set some clock-related RTC registers to the default values */
+ res = target_write_u32(target, ESP32_S2_STORE4, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_STORE4 (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_STORE5, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_STORE5 (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_RTC_CNTL_DIG_PWC_REG, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_RTC_CNTL_DIG_PWC_REG (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_CLK_CONF, ESP32_S2_CLK_CONF_DEF);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_CLK_CONF (%d)!", res);
+ return res;
+ }
+ /* Stall CPU */
+ res = esp32s2_stall(target);
+ if (res != ERROR_OK)
+ return res;
+ /* enable stall */
+ res = xtensa_smpbreak_write(xtensa, OCDDCR_RUNSTALLINEN);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to set smpbreak (%d)!", res);
+ return res;
+ }
+ /* Reset CPU */
+ xtensa->suppress_dsr_errors = true;
+ res = esp32s2_set_peri_reg_mask(target,
+ ESP32_S2_OPTIONS0,
+ ESP32_S2_SW_SYS_RST_M,
+ 1U << ESP32_S2_SW_SYS_RST_S);
+ xtensa->suppress_dsr_errors = false;
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_OPTIONS0 (%d)!", res);
+ return res;
+ }
+ /* Wait for SoC to reset */
+ alive_sleep(100);
+ int timeout = 100;
+ while (target->state != TARGET_RESET && target->state != TARGET_RUNNING && --timeout > 0) {
+ alive_sleep(10);
+ xtensa_poll(target);
+ }
+ if (timeout == 0) {
+ LOG_ERROR("Timed out waiting for CPU to be reset, target->state=%d", target->state);
+ return ERROR_TARGET_TIMEOUT;
+ }
+ xtensa_halt(target);
+ res = target_wait_state(target, TARGET_HALTED, 1000);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Couldn't halt target before SoC reset");
+ return res;
+ }
+ /* Unstall CPU */
+ res = esp32s2_unstall(target);
+ if (res != ERROR_OK)
+ return res;
+ /* Disable WDTs */
+ res = esp32s2_disable_wdts(target);
+ if (res != ERROR_OK)
+ return res;
+ /* Disable trace memory mapping */
+ res = target_write_u32(target, ESP32_S2_DPORT_PMS_OCCUPY_3, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_DPORT_PMS_OCCUPY_3 (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+static int esp32s2_disable_wdts(struct target *target)
+{
+ /* TIMG1 WDT */
+ int res = target_write_u32(target, ESP32_S2_TIMG0WDT_PROTECT, ESP32_S2_WDT_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG0WDT_PROTECT (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_TIMG0WDT_CFG0, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG0WDT_CFG0 (%d)!", res);
+ return res;
+ }
+ /* TIMG2 WDT */
+ res = target_write_u32(target, ESP32_S2_TIMG1WDT_PROTECT, ESP32_S2_WDT_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG1WDT_PROTECT (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_TIMG1WDT_CFG0, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG1WDT_CFG0 (%d)!", res);
+ return res;
+ }
+ /* RTC WDT */
+ res = target_write_u32(target, ESP32_S2_RTCWDT_PROTECT, ESP32_S2_WDT_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_RTCWDT_PROTECT (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_RTCWDT_CFG, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_RTCWDT_CFG (%d)!", res);
+ return res;
+ }
+ /* Enable SWD auto-feed */
+ res = target_write_u32(target, ESP32_S2_SWD_WPROTECT_REG, ESP32_S2_SWD_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_SWD_WPROTECT_REG (%d)!", res);
+ return res;
+ }
+ uint32_t swd_conf_reg = 0;
+ res = target_read_u32(target, ESP32_S2_SWD_CONF_REG, &swd_conf_reg);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to read ESP32_S2_SWD_CONF_REG (%d)!", res);
+ return res;
+ }
+ swd_conf_reg |= ESP32_S2_SWD_AUTO_FEED_EN_M;
+ res = target_write_u32(target, ESP32_S2_SWD_CONF_REG, swd_conf_reg);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_SWD_CONF_REG (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+static int esp32s2_arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+static int esp32s2_on_halt(struct target *target)
+{
+ return esp32s2_disable_wdts(target);
+}
+
+static int esp32s2_step(struct target *target, int current, target_addr_t address, int handle_breakpoints)
+{
+ int ret = xtensa_step(target, current, address, handle_breakpoints);
+ if (ret == ERROR_OK) {
+ esp32s2_on_halt(target);
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ return ret;
+}
+
+static int esp32s2_poll(struct target *target)
+{
+ enum target_state old_state = target->state;
+ int ret = esp_xtensa_poll(target);
+
+ if (old_state != TARGET_HALTED && target->state == TARGET_HALTED) {
+ /* Call any event callbacks that are applicable */
+ if (old_state == TARGET_DEBUG_RUNNING) {
+ target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
+ } else {
+ esp32s2_on_halt(target);
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ }
+
+ return ret;
+}
+
+static int esp32s2_virt2phys(struct target *target,
+ target_addr_t virtual, target_addr_t *physical)
+{
+ *physical = virtual;
+ return ERROR_OK;
+}
+
+static int esp32s2_target_init(struct command_context *cmd_ctx, struct target *target)
+{
+ return esp_xtensa_target_init(cmd_ctx, target);
+}
+
+static const struct xtensa_debug_ops esp32s2_dbg_ops = {
+ .queue_enable = xtensa_dm_queue_enable,
+ .queue_reg_read = xtensa_dm_queue_reg_read,
+ .queue_reg_write = xtensa_dm_queue_reg_write
+};
+
+static const struct xtensa_power_ops esp32s2_pwr_ops = {
+ .queue_reg_read = xtensa_dm_queue_pwr_reg_read,
+ .queue_reg_write = xtensa_dm_queue_pwr_reg_write
+};
+
+static int esp32s2_target_create(struct target *target, Jim_Interp *interp)
+{
+ struct xtensa_debug_module_config esp32s2_dm_cfg = {
+ .dbg_ops = &esp32s2_dbg_ops,
+ .pwr_ops = &esp32s2_pwr_ops,
+ .tap = target->tap,
+ .queue_tdi_idle = NULL,
+ .queue_tdi_idle_arg = NULL
+ };
+
+ /* creates xtensa object */
+ struct esp32s2_common *esp32 = calloc(1, sizeof(*esp32));
+ if (!esp32) {
+ LOG_ERROR("Failed to alloc memory for arch info!");
+ return ERROR_FAIL;
+ }
+
+ int ret = esp_xtensa_init_arch_info(target, &esp32->esp_xtensa, &esp32s2_xtensa_cfg, &esp32s2_dm_cfg);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to init arch info!");
+ free(esp32);
+ return ret;
+ }
+
+ /* Assume running target. If different, the first poll will fix this */
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ return ERROR_OK;
+}
+
+static const struct command_registration esp32s2_command_handlers[] = {
+ {
+ .name = "xtensa",
+ .mode = COMMAND_ANY,
+ .help = "Xtensa commands group",
+ .usage = "",
+ .chain = xtensa_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+/* Holds methods for Xtensa targets. */
+struct target_type esp32s2_target = {
+ .name = "esp32s2",
+
+ .poll = esp32s2_poll,
+ .arch_state = esp32s2_arch_state,
+
+ .halt = xtensa_halt,
+ .resume = xtensa_resume,
+ .step = esp32s2_step,
+
+ .assert_reset = esp32s2_assert_reset,
+ .deassert_reset = esp32s2_deassert_reset,
+ .soft_reset_halt = esp32s2_soft_reset_halt,
+
+ .virt2phys = esp32s2_virt2phys,
+ .mmu = xtensa_mmu_is_enabled,
+ .read_memory = xtensa_read_memory,
+ .write_memory = xtensa_write_memory,
+
+ .read_buffer = xtensa_read_buffer,
+ .write_buffer = xtensa_write_buffer,
+
+ .checksum_memory = xtensa_checksum_memory,
+
+ .get_gdb_arch = xtensa_get_gdb_arch,
+ .get_gdb_reg_list = xtensa_get_gdb_reg_list,
+
+ .add_breakpoint = esp_xtensa_breakpoint_add,
+ .remove_breakpoint = esp_xtensa_breakpoint_remove,
+
+ .add_watchpoint = xtensa_watchpoint_add,
+ .remove_watchpoint = xtensa_watchpoint_remove,
+
+ .target_create = esp32s2_target_create,
+ .init_target = esp32s2_target_init,
+ .examine = xtensa_examine,
+ .deinit_target = esp_xtensa_target_deinit,
+
+ .commands = esp32s2_command_handlers,
+};
diff --git a/src/target/espressif/esp32s2.h b/src/target/espressif/esp32s2.h
new file mode 100644
index 0000000..2c43e3e
--- /dev/null
+++ b/src/target/espressif/esp32s2.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * ESP32-S2 target for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESP32S2_H
+#define OPENOCD_TARGET_ESP32S2_H
+
+#include <target/xtensa/xtensa_regs.h>
+
+#define ESP32_S2_DROM_LOW 0x3f000000
+#define ESP32_S2_DROM_HIGH 0x3ff80000
+#define ESP32_S2_IROM_LOW 0x40080000
+#define ESP32_S2_IROM_HIGH 0x40800000
+
+/* Number of registers returned directly by the G command
+ * Corresponds to the amount of regs listed in regformats/reg-xtensa.dat in the gdb source */
+#define ESP32_S2_NUM_REGS_G_COMMAND 72
+
+enum esp32s2_reg_id {
+ /* chip specific registers that extend ISA go after ISA-defined ones */
+ ESP32_S2_REG_IDX_GPIOOUT = XT_USR_REG_START,
+ ESP32_S2_NUM_REGS,
+};
+
+#endif /* OPENOCD_TARGET_ESP32S2_H */
diff --git a/src/target/espressif/esp_xtensa.c b/src/target/espressif/esp_xtensa.c
new file mode 100644
index 0000000..89393f4
--- /dev/null
+++ b/src/target/espressif/esp_xtensa.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Espressif Xtensa target API for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <target/smp.h>
+#include "esp_xtensa.h"
+#include <target/register.h>
+
+int esp_xtensa_init_arch_info(struct target *target,
+ struct esp_xtensa_common *esp_xtensa,
+ const struct xtensa_config *xtensa_cfg,
+ struct xtensa_debug_module_config *dm_cfg)
+{
+ return xtensa_init_arch_info(target, &esp_xtensa->xtensa, xtensa_cfg, dm_cfg);
+}
+
+int esp_xtensa_target_init(struct command_context *cmd_ctx, struct target *target)
+{
+ return xtensa_target_init(cmd_ctx, target);
+}
+
+void esp_xtensa_target_deinit(struct target *target)
+{
+ LOG_DEBUG("start");
+
+ xtensa_target_deinit(target);
+ free(target_to_esp_xtensa(target)); /* same as free(xtensa) */
+}
+
+int esp_xtensa_arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+int esp_xtensa_poll(struct target *target)
+{
+ return xtensa_poll(target);
+}
+
+int esp_xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint)
+{
+ return xtensa_breakpoint_add(target, breakpoint);
+ /* flash breakpoints will be handled in another patch */
+}
+
+int esp_xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint)
+{
+ return xtensa_breakpoint_remove(target, breakpoint);
+ /* flash breakpoints will be handled in another patch */
+}
diff --git a/src/target/espressif/esp_xtensa.h b/src/target/espressif/esp_xtensa.h
new file mode 100644
index 0000000..6badb1b
--- /dev/null
+++ b/src/target/espressif/esp_xtensa.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Generic ESP xtensa target implementation for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESP_XTENSA_H
+#define OPENOCD_TARGET_ESP_XTENSA_H
+
+#include <helper/command.h>
+#include <target/target.h>
+#include <target/xtensa/xtensa.h>
+
+struct esp_xtensa_common {
+ struct xtensa xtensa; /* must be the first element */
+};
+
+static inline struct esp_xtensa_common *target_to_esp_xtensa(struct target *target)
+{
+ return container_of(target->arch_info, struct esp_xtensa_common, xtensa);
+}
+
+int esp_xtensa_init_arch_info(struct target *target,
+ struct esp_xtensa_common *esp_xtensa,
+ const struct xtensa_config *xtensa_cfg,
+ struct xtensa_debug_module_config *dm_cfg);
+int esp_xtensa_target_init(struct command_context *cmd_ctx, struct target *target);
+void esp_xtensa_target_deinit(struct target *target);
+int esp_xtensa_arch_state(struct target *target);
+void esp_xtensa_queue_tdi_idle(struct target *target);
+int esp_xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint);
+int esp_xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint);
+int esp_xtensa_poll(struct target *target);
+
+#endif /* OPENOCD_TARGET_ESP_XTENSA_H */
diff --git a/src/target/target.c b/src/target/target.c
index 76327b1..e2004e4 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -105,6 +105,7 @@ extern struct target_type hla_target;
extern struct target_type nds32_v2_target;
extern struct target_type nds32_v3_target;
extern struct target_type nds32_v3m_target;
+extern struct target_type esp32s2_target;
extern struct target_type or1k_target;
extern struct target_type quark_x10xx_target;
extern struct target_type quark_d20xx_target;
@@ -141,6 +142,7 @@ static struct target_type *target_types[] = {
&nds32_v2_target,
&nds32_v3_target,
&nds32_v3m_target,
+ &esp32s2_target,
&or1k_target,
&quark_x10xx_target,
&quark_d20xx_target,
diff --git a/src/target/xtensa/Makefile.am b/src/target/xtensa/Makefile.am
new file mode 100644
index 0000000..f6cee99
--- /dev/null
+++ b/src/target/xtensa/Makefile.am
@@ -0,0 +1,7 @@
+noinst_LTLIBRARIES += %D%/libxtensa.la
+%C%_libxtensa_la_SOURCES = \
+ %D%/xtensa.c \
+ %D%/xtensa.h \
+ %D%/xtensa_debug_module.c \
+ %D%/xtensa_debug_module.h \
+ %D%/xtensa_regs.h
diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c
new file mode 100644
index 0000000..a955960
--- /dev/null
+++ b/src/target/xtensa/xtensa.c
@@ -0,0 +1,2731 @@
+/***************************************************************************
+ * Generic Xtensa target API for OpenOCD *
+ * Copyright (C) 2016-2019 Espressif Systems Ltd. *
+ * Derived from esp108.c *
+ * Author: Angus Gratton gus@projectgus.com *
+ * Author: Jeroen Domburg <jeroen@espressif.com> *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * Author: Andrey Gramakov <andrei.gramakov@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <helper/time_support.h>
+#include <helper/align.h>
+#include <target/register.h>
+
+#include "xtensa.h"
+
+
+#define _XT_INS_FORMAT_RSR(OPCODE, SR, T) ((OPCODE) \
+ | (((SR) & 0xFF) << 8) \
+ | (((T) & 0x0F) << 4))
+
+#define _XT_INS_FORMAT_RRR(OPCODE, ST, R) ((OPCODE) \
+ | (((ST) & 0xFF) << 4) \
+ | (((R) & 0x0F) << 12))
+
+#define _XT_INS_FORMAT_RRRN(OPCODE, S, T, IMM4) ((OPCODE) \
+ | (((T) & 0x0F) << 4) \
+ | (((S) & 0x0F) << 8) \
+ | (((IMM4) & 0x0F) << 12))
+
+#define _XT_INS_FORMAT_RRI8(OPCODE, R, S, T, IMM8) ((OPCODE) \
+ | (((IMM8) & 0xFF) << 16) \
+ | (((R) & 0x0F) << 12) \
+ | (((S) & 0x0F) << 8) \
+ | (((T) & 0x0F) << 4))
+
+#define _XT_INS_FORMAT_RRI4(OPCODE, IMM4, R, S, T) ((OPCODE) \
+ | (((IMM4) & 0x0F) << 20) \
+ | (((R) & 0x0F) << 12) \
+ | (((S) & 0x0F) << 8) \
+ | (((T) & 0x0F) << 4))
+
+/* Xtensa processor instruction opcodes
+ * "Return From Debug Operation" to Normal */
+#define XT_INS_RFDO 0xf1e000
+/* "Return From Debug and Dispatch" - allow sw debugging stuff to take over */
+#define XT_INS_RFDD 0xf1e010
+
+/* Load to DDR register, increase addr register */
+#define XT_INS_LDDR32P(S) (0x0070E0 | ((S) << 8))
+/* Store from DDR register, increase addr register */
+#define XT_INS_SDDR32P(S) (0x0070F0 | ((S) << 8))
+
+/* Load 32-bit Indirect from A(S) + 4 * IMM8 to A(T) */
+#define XT_INS_L32I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x002002, 0, S, T, IMM8)
+/* Load 16-bit Unsigned from A(S) + 2 * IMM8 to A(T) */
+#define XT_INS_L16UI(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x001002, 0, S, T, IMM8)
+/* Load 8-bit Unsigned from A(S) + IMM8 to A(T) */
+#define XT_INS_L8UI(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x000002, 0, S, T, IMM8)
+
+/* Store 32-bit Indirect to A(S) + 4 * IMM8 from A(T) */
+#define XT_INS_S32I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x006002, 0, S, T, IMM8)
+/* Store 16-bit to A(S) + 2 * IMM8 from A(T) */
+#define XT_INS_S16I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x005002, 0, S, T, IMM8)
+/* Store 8-bit to A(S) + IMM8 from A(T) */
+#define XT_INS_S8I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x004002, 0, S, T, IMM8)
+
+/* Read Special Register */
+#define XT_INS_RSR(SR, T) _XT_INS_FORMAT_RSR(0x030000, SR, T)
+/* Write Special Register */
+#define XT_INS_WSR(SR, T) _XT_INS_FORMAT_RSR(0x130000, SR, T)
+/* Swap Special Register */
+#define XT_INS_XSR(SR, T) _XT_INS_FORMAT_RSR(0x610000, SR, T)
+
+/* Rotate Window by (-8..7) */
+#define XT_INS_ROTW(N) ((0x408000) | (((N) & 15) << 4))
+
+/* Read User Register */
+#define XT_INS_RUR(UR, T) _XT_INS_FORMAT_RRR(0xE30000, UR, T)
+/* Write User Register */
+#define XT_INS_WUR(UR, T) _XT_INS_FORMAT_RSR(0xF30000, UR, T)
+
+/* Read Floating-Point Register */
+#define XT_INS_RFR(FR, T) _XT_INS_FORMAT_RRR(0xFA0000, (((FR) << 4) | 0x4), T)
+/* Write Floating-Point Register */
+#define XT_INS_WFR(FR, T) _XT_INS_FORMAT_RRR(0xFA0000, (((FR) << 4) | 0x5), T)
+
+/* 32-bit break */
+#define XT_INS_BREAK(IMM1, IMM2) _XT_INS_FORMAT_RRR(0x000000, \
+ (((IMM1) & 0x0F) << 4) | ((IMM2) & 0x0F), 0x4)
+/* 16-bit break */
+#define XT_INS_BREAKN(IMM4) _XT_INS_FORMAT_RRRN(0x00000D, IMM4, 0x2, 0xF)
+
+#define XT_INS_L32E(R, S, T) _XT_INS_FORMAT_RRI4(0x90000, 0, R, S, T)
+#define XT_INS_S32E(R, S, T) _XT_INS_FORMAT_RRI4(0x490000, 0, R, S, T)
+#define XT_INS_L32E_S32E_MASK 0xFF000F
+
+#define XT_INS_RFWO 0x3400
+#define XT_INS_RFWU 0x3500
+#define XT_INS_RFWO_RFWU_MASK 0xFFFFFF
+
+#define XT_WATCHPOINTS_NUM_MAX 2
+
+/* Special register number macro for DDR register.
+* this gets used a lot so making a shortcut to it is
+* useful.
+*/
+#define XT_SR_DDR (xtensa_regs[XT_REG_IDX_OCD_DDR].reg_num)
+
+/*Same thing for A3/A4 */
+#define XT_REG_A3 (xtensa_regs[XT_REG_IDX_AR3].reg_num)
+#define XT_REG_A4 (xtensa_regs[XT_REG_IDX_AR4].reg_num)
+
+#define XT_PC_REG_NUM_BASE (176)
+#define XT_SW_BREAKPOINTS_MAX_NUM 32
+
+const struct xtensa_reg_desc xtensa_regs[XT_NUM_REGS] = {
+ { "pc", XT_PC_REG_NUM_BASE /*+XT_DEBUGLEVEL*/, XT_REG_SPECIAL, 0 }, /* actually epc[debuglevel] */
+ { "ar0", 0x00, XT_REG_GENERAL, 0 },
+ { "ar1", 0x01, XT_REG_GENERAL, 0 },
+ { "ar2", 0x02, XT_REG_GENERAL, 0 },
+ { "ar3", 0x03, XT_REG_GENERAL, 0 },
+ { "ar4", 0x04, XT_REG_GENERAL, 0 },
+ { "ar5", 0x05, XT_REG_GENERAL, 0 },
+ { "ar6", 0x06, XT_REG_GENERAL, 0 },
+ { "ar7", 0x07, XT_REG_GENERAL, 0 },
+ { "ar8", 0x08, XT_REG_GENERAL, 0 },
+ { "ar9", 0x09, XT_REG_GENERAL, 0 },
+ { "ar10", 0x0A, XT_REG_GENERAL, 0 },
+ { "ar11", 0x0B, XT_REG_GENERAL, 0 },
+ { "ar12", 0x0C, XT_REG_GENERAL, 0 },
+ { "ar13", 0x0D, XT_REG_GENERAL, 0 },
+ { "ar14", 0x0E, XT_REG_GENERAL, 0 },
+ { "ar15", 0x0F, XT_REG_GENERAL, 0 },
+ { "ar16", 0x10, XT_REG_GENERAL, 0 },
+ { "ar17", 0x11, XT_REG_GENERAL, 0 },
+ { "ar18", 0x12, XT_REG_GENERAL, 0 },
+ { "ar19", 0x13, XT_REG_GENERAL, 0 },
+ { "ar20", 0x14, XT_REG_GENERAL, 0 },
+ { "ar21", 0x15, XT_REG_GENERAL, 0 },
+ { "ar22", 0x16, XT_REG_GENERAL, 0 },
+ { "ar23", 0x17, XT_REG_GENERAL, 0 },
+ { "ar24", 0x18, XT_REG_GENERAL, 0 },
+ { "ar25", 0x19, XT_REG_GENERAL, 0 },
+ { "ar26", 0x1A, XT_REG_GENERAL, 0 },
+ { "ar27", 0x1B, XT_REG_GENERAL, 0 },
+ { "ar28", 0x1C, XT_REG_GENERAL, 0 },
+ { "ar29", 0x1D, XT_REG_GENERAL, 0 },
+ { "ar30", 0x1E, XT_REG_GENERAL, 0 },
+ { "ar31", 0x1F, XT_REG_GENERAL, 0 },
+ { "ar32", 0x20, XT_REG_GENERAL, 0 },
+ { "ar33", 0x21, XT_REG_GENERAL, 0 },
+ { "ar34", 0x22, XT_REG_GENERAL, 0 },
+ { "ar35", 0x23, XT_REG_GENERAL, 0 },
+ { "ar36", 0x24, XT_REG_GENERAL, 0 },
+ { "ar37", 0x25, XT_REG_GENERAL, 0 },
+ { "ar38", 0x26, XT_REG_GENERAL, 0 },
+ { "ar39", 0x27, XT_REG_GENERAL, 0 },
+ { "ar40", 0x28, XT_REG_GENERAL, 0 },
+ { "ar41", 0x29, XT_REG_GENERAL, 0 },
+ { "ar42", 0x2A, XT_REG_GENERAL, 0 },
+ { "ar43", 0x2B, XT_REG_GENERAL, 0 },
+ { "ar44", 0x2C, XT_REG_GENERAL, 0 },
+ { "ar45", 0x2D, XT_REG_GENERAL, 0 },
+ { "ar46", 0x2E, XT_REG_GENERAL, 0 },
+ { "ar47", 0x2F, XT_REG_GENERAL, 0 },
+ { "ar48", 0x30, XT_REG_GENERAL, 0 },
+ { "ar49", 0x31, XT_REG_GENERAL, 0 },
+ { "ar50", 0x32, XT_REG_GENERAL, 0 },
+ { "ar51", 0x33, XT_REG_GENERAL, 0 },
+ { "ar52", 0x34, XT_REG_GENERAL, 0 },
+ { "ar53", 0x35, XT_REG_GENERAL, 0 },
+ { "ar54", 0x36, XT_REG_GENERAL, 0 },
+ { "ar55", 0x37, XT_REG_GENERAL, 0 },
+ { "ar56", 0x38, XT_REG_GENERAL, 0 },
+ { "ar57", 0x39, XT_REG_GENERAL, 0 },
+ { "ar58", 0x3A, XT_REG_GENERAL, 0 },
+ { "ar59", 0x3B, XT_REG_GENERAL, 0 },
+ { "ar60", 0x3C, XT_REG_GENERAL, 0 },
+ { "ar61", 0x3D, XT_REG_GENERAL, 0 },
+ { "ar62", 0x3E, XT_REG_GENERAL, 0 },
+ { "ar63", 0x3F, XT_REG_GENERAL, 0 },
+ { "lbeg", 0x00, XT_REG_SPECIAL, 0 },
+ { "lend", 0x01, XT_REG_SPECIAL, 0 },
+ { "lcount", 0x02, XT_REG_SPECIAL, 0 },
+ { "sar", 0x03, XT_REG_SPECIAL, 0 },
+ { "windowbase", 0x48, XT_REG_SPECIAL, 0 },
+ { "windowstart", 0x49, XT_REG_SPECIAL, 0 },
+ { "configid0", 0xB0, XT_REG_SPECIAL, 0 },
+ { "configid1", 0xD0, XT_REG_SPECIAL, 0 },
+ { "ps", 0xC6, XT_REG_SPECIAL, 0 }, /* actually EPS[debuglevel] */
+ { "threadptr", 0xE7, XT_REG_USER, 0 },
+ { "br", 0x04, XT_REG_SPECIAL, 0 },
+ { "scompare1", 0x0C, XT_REG_SPECIAL, 0 },
+ { "acclo", 0x10, XT_REG_SPECIAL, 0 },
+ { "acchi", 0x11, XT_REG_SPECIAL, 0 },
+ { "m0", 0x20, XT_REG_SPECIAL, 0 },
+ { "m1", 0x21, XT_REG_SPECIAL, 0 },
+ { "m2", 0x22, XT_REG_SPECIAL, 0 },
+ { "m3", 0x23, XT_REG_SPECIAL, 0 },
+ { "f0", 0x00, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f1", 0x01, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f2", 0x02, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f3", 0x03, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f4", 0x04, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f5", 0x05, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f6", 0x06, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f7", 0x07, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f8", 0x08, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f9", 0x09, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f10", 0x0A, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f11", 0x0B, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f12", 0x0C, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f13", 0x0D, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f14", 0x0E, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f15", 0x0F, XT_REG_FR, XT_REGF_COPROC0 },
+ { "fcr", 0xE8, XT_REG_USER, XT_REGF_COPROC0 },
+ { "fsr", 0xE9, XT_REG_USER, XT_REGF_COPROC0 },
+ { "mmid", 0x59, XT_REG_SPECIAL, XT_REGF_NOREAD },
+ { "ibreakenable", 0x60, XT_REG_SPECIAL, 0 },
+ { "memctl", 0x61, XT_REG_SPECIAL, 0 },
+ { "atomctl", 0x63, XT_REG_SPECIAL, 0 },
+ { "ibreaka0", 0x80, XT_REG_SPECIAL, 0 },
+ { "ibreaka1", 0x81, XT_REG_SPECIAL, 0 },
+ { "dbreaka0", 0x90, XT_REG_SPECIAL, 0 },
+ { "dbreaka1", 0x91, XT_REG_SPECIAL, 0 },
+ { "dbreakc0", 0xA0, XT_REG_SPECIAL, 0 },
+ { "dbreakc1", 0xA1, XT_REG_SPECIAL, 0 },
+ { "epc1", 0xB1, XT_REG_SPECIAL, 0 },
+ { "epc2", 0xB2, XT_REG_SPECIAL, 0 },
+ { "epc3", 0xB3, XT_REG_SPECIAL, 0 },
+ { "epc4", 0xB4, XT_REG_SPECIAL, 0 },
+ { "epc5", 0xB5, XT_REG_SPECIAL, 0 },
+ { "epc6", 0xB6, XT_REG_SPECIAL, 0 },
+ { "epc7", 0xB7, XT_REG_SPECIAL, 0 },
+ { "depc", 0xC0, XT_REG_SPECIAL, 0 },
+ { "eps2", 0xC2, XT_REG_SPECIAL, 0 },
+ { "eps3", 0xC3, XT_REG_SPECIAL, 0 },
+ { "eps4", 0xC4, XT_REG_SPECIAL, 0 },
+ { "eps5", 0xC5, XT_REG_SPECIAL, 0 },
+ { "eps6", 0xC6, XT_REG_SPECIAL, 0 },
+ { "eps7", 0xC7, XT_REG_SPECIAL, 0 },
+ { "excsave1", 0xD1, XT_REG_SPECIAL, 0 },
+ { "excsave2", 0xD2, XT_REG_SPECIAL, 0 },
+ { "excsave3", 0xD3, XT_REG_SPECIAL, 0 },
+ { "excsave4", 0xD4, XT_REG_SPECIAL, 0 },
+ { "excsave5", 0xD5, XT_REG_SPECIAL, 0 },
+ { "excsave6", 0xD6, XT_REG_SPECIAL, 0 },
+ { "excsave7", 0xD7, XT_REG_SPECIAL, 0 },
+ { "cpenable", 0xE0, XT_REG_SPECIAL, 0 },
+ { "interrupt", 0xE2, XT_REG_SPECIAL, 0 },
+ { "intset", 0xE2, XT_REG_SPECIAL, XT_REGF_NOREAD },
+ { "intclear", 0xE3, XT_REG_SPECIAL, XT_REGF_NOREAD },
+ { "intenable", 0xE4, XT_REG_SPECIAL, 0 },
+ { "vecbase", 0xE7, XT_REG_SPECIAL, 0 },
+ { "exccause", 0xE8, XT_REG_SPECIAL, 0 },
+ { "debugcause", 0xE9, XT_REG_SPECIAL, 0 },
+ { "ccount", 0xEA, XT_REG_SPECIAL, 0 },
+ { "prid", 0xEB, XT_REG_SPECIAL, 0 },
+ { "icount", 0xEC, XT_REG_SPECIAL, 0 },
+ { "icountlevel", 0xED, XT_REG_SPECIAL, 0 },
+ { "excvaddr", 0xEE, XT_REG_SPECIAL, 0 },
+ { "ccompare0", 0xF0, XT_REG_SPECIAL, 0 },
+ { "ccompare1", 0xF1, XT_REG_SPECIAL, 0 },
+ { "ccompare2", 0xF2, XT_REG_SPECIAL, 0 },
+ { "misc0", 0xF4, XT_REG_SPECIAL, 0 },
+ { "misc1", 0xF5, XT_REG_SPECIAL, 0 },
+ { "misc2", 0xF6, XT_REG_SPECIAL, 0 },
+ { "misc3", 0xF7, XT_REG_SPECIAL, 0 },
+ { "litbase", 0x05, XT_REG_SPECIAL, 0 },
+ { "ptevaddr", 0x53, XT_REG_SPECIAL, 0 },
+ { "rasid", 0x5A, XT_REG_SPECIAL, 0 },
+ { "itlbcfg", 0x5B, XT_REG_SPECIAL, 0 },
+ { "dtlbcfg", 0x5C, XT_REG_SPECIAL, 0 },
+ { "mepc", 0x6A, XT_REG_SPECIAL, 0 },
+ { "meps", 0x6B, XT_REG_SPECIAL, 0 },
+ { "mesave", 0x6C, XT_REG_SPECIAL, 0 },
+ { "mesr", 0x6D, XT_REG_SPECIAL, 0 },
+ { "mecr", 0x6E, XT_REG_SPECIAL, 0 },
+ { "mevaddr", 0x6F, XT_REG_SPECIAL, 0 },
+ { "a0", XT_REG_IDX_AR0, XT_REG_RELGEN, 0 }, /* WARNING: For these registers, regnum points to the */
+ { "a1", XT_REG_IDX_AR1, XT_REG_RELGEN, 0 }, /* index of the corresponding ARxregisters, NOT to */
+ { "a2", XT_REG_IDX_AR2, XT_REG_RELGEN, 0 }, /* the processor register number! */
+ { "a3", XT_REG_IDX_AR3, XT_REG_RELGEN, 0 },
+ { "a4", XT_REG_IDX_AR4, XT_REG_RELGEN, 0 },
+ { "a5", XT_REG_IDX_AR5, XT_REG_RELGEN, 0 },
+ { "a6", XT_REG_IDX_AR6, XT_REG_RELGEN, 0 },
+ { "a7", XT_REG_IDX_AR7, XT_REG_RELGEN, 0 },
+ { "a8", XT_REG_IDX_AR8, XT_REG_RELGEN, 0 },
+ { "a9", XT_REG_IDX_AR9, XT_REG_RELGEN, 0 },
+ { "a10", XT_REG_IDX_AR10, XT_REG_RELGEN, 0 },
+ { "a11", XT_REG_IDX_AR11, XT_REG_RELGEN, 0 },
+ { "a12", XT_REG_IDX_AR12, XT_REG_RELGEN, 0 },
+ { "a13", XT_REG_IDX_AR13, XT_REG_RELGEN, 0 },
+ { "a14", XT_REG_IDX_AR14, XT_REG_RELGEN, 0 },
+ { "a15", XT_REG_IDX_AR15, XT_REG_RELGEN, 0 },
+
+ { "pwrctl", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pwrstat", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "eristat", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_itctrl", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_claimset", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_claimclr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_lockaccess", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_lockstatus", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_authstatus", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "fault_info", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_id", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_ctrl", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_stat", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_data", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_addr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_pctrigger", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_pcmatch", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_delay", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_memstart", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_memend", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmg", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmoc", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pm0", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pm1", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmctrl0", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmctrl1", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmstat0", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmstat1", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_id", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_dcrclr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_dcrset", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_dsr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ddr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+};
+
+
+/**
+ * Types of memory used at xtensa target
+ */
+enum xtensa_mem_region_type {
+ XTENSA_MEM_REG_IROM = 0x0,
+ XTENSA_MEM_REG_IRAM,
+ XTENSA_MEM_REG_DROM,
+ XTENSA_MEM_REG_DRAM,
+ XTENSA_MEM_REG_URAM,
+ XTENSA_MEM_REG_XLMI,
+ XTENSA_MEM_REGS_NUM
+};
+
+/**
+ * Gets a config for the specific mem type
+ */
+static inline const struct xtensa_local_mem_config *xtensa_get_mem_config(
+ struct xtensa *xtensa,
+ enum xtensa_mem_region_type type)
+{
+ switch (type) {
+ case XTENSA_MEM_REG_IROM:
+ return &xtensa->core_config->irom;
+ case XTENSA_MEM_REG_IRAM:
+ return &xtensa->core_config->iram;
+ case XTENSA_MEM_REG_DROM:
+ return &xtensa->core_config->drom;
+ case XTENSA_MEM_REG_DRAM:
+ return &xtensa->core_config->dram;
+ case XTENSA_MEM_REG_URAM:
+ return &xtensa->core_config->uram;
+ case XTENSA_MEM_REG_XLMI:
+ return &xtensa->core_config->xlmi;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Extracts an exact xtensa_local_mem_region_config from xtensa_local_mem_config
+ * for a given address
+ * Returns NULL if nothing found
+ */
+static inline const struct xtensa_local_mem_region_config *xtensa_memory_region_find(
+ const struct xtensa_local_mem_config *mem,
+ target_addr_t address)
+{
+ for (unsigned int i = 0; i < mem->count; i++) {
+ const struct xtensa_local_mem_region_config *region = &mem->regions[i];
+ if (address >= region->base && address < (region->base + region->size))
+ return region;
+ }
+ return NULL;
+}
+
+/**
+ * Returns a corresponding xtensa_local_mem_region_config from the xtensa target
+ * for a given address
+ * Returns NULL if nothing found
+ */
+static inline const struct xtensa_local_mem_region_config *xtensa_target_memory_region_find(
+ struct xtensa *xtensa,
+ target_addr_t address)
+{
+ const struct xtensa_local_mem_region_config *result;
+ const struct xtensa_local_mem_config *mcgf;
+ for (unsigned int mtype = 0; mtype < XTENSA_MEM_REGS_NUM; mtype++) {
+ mcgf = xtensa_get_mem_config(xtensa, mtype);
+ result = xtensa_memory_region_find(mcgf, address);
+ if (result)
+ return result;
+ }
+ return NULL;
+}
+
+static int xtensa_core_reg_get(struct reg *reg)
+{
+ /*We don't need this because we read all registers on halt anyway. */
+ struct xtensa *xtensa = (struct xtensa *)reg->arch_info;
+ struct target *target = xtensa->target;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+ return ERROR_OK;
+}
+
+static int xtensa_core_reg_set(struct reg *reg, uint8_t *buf)
+{
+ struct xtensa *xtensa = (struct xtensa *)reg->arch_info;
+ struct target *target = xtensa->target;
+
+ assert(reg->size <= 64 && "up to 64-bit regs are supported only!");
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ buf_cpy(buf, reg->value, reg->size);
+ reg->dirty = true;
+ reg->valid = true;
+
+ return ERROR_OK;
+}
+
+static const struct reg_arch_type xtensa_reg_type = {
+ .get = xtensa_core_reg_get,
+ .set = xtensa_core_reg_set,
+};
+
+const struct reg_arch_type xtensa_user_reg_u32_type = {
+ .get = xtensa_core_reg_get,
+ .set = xtensa_core_reg_set,
+};
+
+const struct reg_arch_type xtensa_user_reg_u128_type = {
+ .get = xtensa_core_reg_get,
+ .set = xtensa_core_reg_set,
+};
+
+static inline size_t xtensa_insn_size_get(uint32_t insn)
+{
+ return insn & BIT(3) ? 2 : XT_ISNS_SZ_MAX;
+}
+
+/* Convert a register index that's indexed relative to windowbase, to the real address. */
+static enum xtensa_reg_id xtensa_windowbase_offset_to_canonical(enum xtensa_reg_id reg_idx, int windowbase)
+{
+ unsigned int idx;
+ if (reg_idx >= XT_REG_IDX_AR0 && reg_idx <= XT_REG_IDX_AR63) {
+ idx = reg_idx - XT_REG_IDX_AR0;
+ } else if (reg_idx >= XT_REG_IDX_A0 && reg_idx <= XT_REG_IDX_A15) {
+ idx = reg_idx - XT_REG_IDX_A0;
+ } else {
+ LOG_ERROR("Error: can't convert register %d to non-windowbased register!", reg_idx);
+ return -1;
+ }
+ return ((idx + windowbase * 4) & 63) + XT_REG_IDX_AR0;
+}
+
+static enum xtensa_reg_id xtensa_canonical_to_windowbase_offset(enum xtensa_reg_id reg_idx, int windowbase)
+{
+ return xtensa_windowbase_offset_to_canonical(reg_idx, -windowbase);
+}
+
+static void xtensa_mark_register_dirty(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ reg_list[reg_idx].dirty = true;
+}
+
+static int xtensa_queue_dbg_reg_read(struct xtensa *xtensa, unsigned int reg, uint8_t *data)
+{
+ struct xtensa_debug_module *dm = &xtensa->dbg_mod;
+
+ if (!xtensa->core_config->trace.enabled &&
+ (reg <= NARADR_MEMADDREND || (reg >= NARADR_PMG && reg <= NARADR_PMSTAT7))) {
+ LOG_ERROR("Can not access %u reg when Trace Port option disabled!", reg);
+ return ERROR_FAIL;
+ }
+ return dm->dbg_ops->queue_reg_read(dm, reg, data);
+}
+
+static int xtensa_queue_dbg_reg_write(struct xtensa *xtensa, unsigned int reg, uint32_t data)
+{
+ struct xtensa_debug_module *dm = &xtensa->dbg_mod;
+
+ if (!xtensa->core_config->trace.enabled &&
+ (reg <= NARADR_MEMADDREND || (reg >= NARADR_PMG && reg <= NARADR_PMSTAT7))) {
+ LOG_ERROR("Can not access %u reg when Trace Port option disabled!", reg);
+ return ERROR_FAIL;
+ }
+ return dm->dbg_ops->queue_reg_write(dm, reg, data);
+}
+
+static void xtensa_queue_exec_ins(struct xtensa *xtensa, uint32_t ins)
+{
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DIR0EXEC, ins);
+}
+
+static bool xtensa_reg_is_readable(enum xtensa_reg_flags flags, xtensa_reg_val_t cpenable)
+{
+ if (flags & XT_REGF_NOREAD)
+ return false;
+ if ((flags & XT_REGF_COPROC0) && (cpenable & BIT(0)) == 0)
+ return false;
+ return true;
+}
+
+static int xtensa_queue_pwr_reg_write(struct xtensa *xtensa, unsigned int reg, uint32_t data)
+{
+ struct xtensa_debug_module *dm = &xtensa->dbg_mod;
+ return dm->pwr_ops->queue_reg_write(dm, reg, data);
+}
+
+static bool xtensa_special_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ /* TODO: array of size XT_NUM_REGS can be used here to map special register ID to
+ * corresponding config option 'enabled' flag */
+ if (reg_idx >= XT_REG_IDX_LBEG && reg_idx <= XT_REG_IDX_LCOUNT)
+ return xtensa->core_config->loop;
+ else if (reg_idx == XT_REG_IDX_BR)
+ return xtensa->core_config->boolean;
+ else if (reg_idx == XT_REG_IDX_LITBASE)
+ return xtensa->core_config->ext_l32r;
+ else if (reg_idx == XT_REG_IDX_SCOMPARE1 || reg_idx == XT_REG_IDX_ATOMCTL)
+ return xtensa->core_config->cond_store;
+ else if (reg_idx >= XT_REG_IDX_ACCLO && reg_idx <= XT_REG_IDX_M3)
+ return xtensa->core_config->mac16;
+ else if (reg_idx == XT_REG_IDX_WINDOWBASE || reg_idx == XT_REG_IDX_WINDOWSTART)
+ return xtensa->core_config->windowed;
+ else if (reg_idx >= XT_REG_IDX_PTEVADDR && reg_idx <= XT_REG_IDX_DTLBCFG)
+ return xtensa->core_config->mmu.enabled;
+ else if (reg_idx == XT_REG_IDX_MMID)
+ return xtensa->core_config->trace.enabled;
+ else if (reg_idx >= XT_REG_IDX_MEPC && reg_idx <= XT_REG_IDX_MEVADDR)
+ return xtensa->core_config->mem_err_check;
+ else if (reg_idx == XT_REG_IDX_CPENABLE)
+ return xtensa->core_config->coproc;
+ else if (reg_idx == XT_REG_IDX_VECBASE)
+ return xtensa->core_config->reloc_vec;
+ else if (reg_idx == XT_REG_IDX_CCOUNT)
+ return xtensa->core_config->tim_irq.enabled;
+ else if (reg_idx >= XT_REG_IDX_CCOMPARE0 && reg_idx <= XT_REG_IDX_CCOMPARE2)
+ return xtensa->core_config->tim_irq.enabled &&
+ (reg_idx - XT_REG_IDX_CCOMPARE0 < xtensa->core_config->tim_irq.comp_num);
+ else if (reg_idx == XT_REG_IDX_PRID)
+ return xtensa->core_config->proc_id;
+ else if (reg_idx >= XT_REG_IDX_MISC0 && reg_idx <= XT_REG_IDX_MISC3)
+ return reg_idx - XT_REG_IDX_MISC0 < xtensa->core_config->miscregs_num;
+ return true;
+}
+
+static bool xtensa_user_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ if (reg_idx == XT_REG_IDX_THREADPTR)
+ return xtensa->core_config->threadptr;
+ if (reg_idx == XT_REG_IDX_FCR || reg_idx == XT_REG_IDX_FSR)
+ return xtensa->core_config->fp_coproc;
+ return false;
+}
+
+static inline bool xtensa_fp_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ return xtensa->core_config->fp_coproc;
+}
+
+static inline bool xtensa_regular_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ if (reg_idx >= XT_REG_IDX_AR0 && reg_idx <= XT_REG_IDX_AR63)
+ return reg_idx - XT_REG_IDX_AR0 < xtensa->core_config->aregs_num;
+ return true;
+}
+
+static int xtensa_write_dirty_registers(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res;
+ xtensa_reg_val_t regval, windowbase = 0;
+ bool scratch_reg_dirty = false;
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ /*We need to write the dirty registers in the cache list back to the processor.
+ *Start by writing the SFR/user registers. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (reg_list[i].dirty) {
+ if (xtensa_regs[i].type == XT_REG_SPECIAL ||
+ xtensa_regs[i].type == XT_REG_USER ||
+ xtensa_regs[i].type == XT_REG_FR) {
+ scratch_reg_dirty = true;
+ regval = xtensa_reg_get(target, i);
+ LOG_TARGET_DEBUG(target, "Writing back reg %s val %08" PRIX32,
+ xtensa_regs[i].name,
+ regval);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, regval);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ if (xtensa_regs[i].type == XT_REG_USER) {
+ if (reg_list[i].exist)
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WUR(xtensa_regs[i].reg_num,
+ XT_REG_A3));
+ } else if (xtensa_regs[i].type == XT_REG_FR) {
+ if (reg_list[i].exist)
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WFR(xtensa_regs[i].reg_num,
+ XT_REG_A3));
+ } else {/*SFR */
+ if (reg_list[i].exist) {
+ unsigned int reg_num = xtensa_regs[i].reg_num;
+ if (reg_num == XT_PC_REG_NUM_BASE)
+ /* reg number of PC for debug interrupt
+ * depends on NDEBUGLEVEL */
+ reg_num += xtensa->core_config->debug.irq_level;
+
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WSR(reg_num, XT_REG_A3));
+ }
+ }
+ reg_list[i].dirty = false;
+ }
+ }
+ }
+ if (scratch_reg_dirty)
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+
+ if (xtensa->core_config->user_regs_num > 0 &&
+ xtensa->core_config->queue_write_dirty_user_regs)
+ xtensa->core_config->queue_write_dirty_user_regs(target);
+
+ if (xtensa->core_config->windowed) {
+ /*Grab the windowbase, we need it. */
+ windowbase = xtensa_reg_get(target, XT_REG_IDX_WINDOWBASE);
+ /*Check if there are problems with both the ARx as well as the corresponding Rx
+ * registers set and dirty. */
+ /*Warn the user if this happens, not much else we can do... */
+ for (unsigned int i = XT_REG_IDX_A0; i <= XT_REG_IDX_A15; i++) {
+ unsigned int j = xtensa_windowbase_offset_to_canonical(i, windowbase);
+ if (reg_list[i].dirty && reg_list[j].dirty) {
+ if (memcmp(reg_list[i].value, reg_list[j].value,
+ sizeof(xtensa_reg_val_t)) != 0)
+ LOG_WARNING(
+ "Warning: Both A%d as well as the physical register it points to (AR%d) are dirty and differs in value. Results are undefined!",
+ i - XT_REG_IDX_A0,
+ j - XT_REG_IDX_AR0);
+ }
+ }
+ }
+
+ /*Write A0-A16 */
+ for (unsigned int i = 0; i < 16; i++) {
+ if (reg_list[XT_REG_IDX_A0 + i].dirty) {
+ regval = xtensa_reg_get(target, XT_REG_IDX_A0 + i);
+ LOG_TARGET_DEBUG(target, "Writing back reg %s value %08" PRIX32 ", num =%i",
+ xtensa_regs[XT_REG_IDX_A0 + i].name,
+ regval,
+ xtensa_regs[XT_REG_IDX_A0 + i].reg_num);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, regval);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, i));
+ reg_list[XT_REG_IDX_A0 + i].dirty = false;
+ }
+ }
+
+ if (xtensa->core_config->windowed) {
+ /*Now write AR0-AR63. */
+ for (unsigned int j = 0; j < 64; j += 16) {
+ /*Write the 16 registers we can see */
+ for (unsigned int i = 0; i < 16; i++) {
+ if (i + j < xtensa->core_config->aregs_num) {
+ enum xtensa_reg_id realadr =
+ xtensa_windowbase_offset_to_canonical(XT_REG_IDX_AR0 + i + j,
+ windowbase);
+ /*Write back any dirty un-windowed registers */
+ if (reg_list[realadr].dirty) {
+ regval = xtensa_reg_get(target, realadr);
+ LOG_TARGET_DEBUG(
+ target,
+ "Writing back reg %s value %08" PRIX32 ", num =%i",
+ xtensa_regs[realadr].name,
+ regval,
+ xtensa_regs[realadr].reg_num);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, regval);
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_RSR(XT_SR_DDR, xtensa_regs[XT_REG_IDX_AR0 + i].reg_num));
+ reg_list[realadr].dirty = false;
+ }
+ }
+ }
+ /*Now rotate the window so we'll see the next 16 registers. The final rotate
+ * will wraparound, */
+ /*leaving us in the state we were. */
+ xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(4));
+ }
+ }
+ res = jtag_execute_queue();
+ xtensa_core_status_check(target);
+
+ return res;
+}
+
+int xtensa_queue_write_dirty_user_regs_u32(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ xtensa_reg_val_t reg_val;
+ bool scratch_reg_dirty = false;
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ /* We need to write the dirty registers in the cache list back to the processor.
+ * Start by writing the SFR/user registers. */
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (!reg_list[XT_USR_REG_START + i].dirty)
+ continue;
+ scratch_reg_dirty = true;
+ reg_val = xtensa_reg_get(target, XT_USR_REG_START + i);
+ LOG_TARGET_DEBUG(target, "Writing back reg %s val %08" PRIX32,
+ xtensa->core_config->user_regs[i].name,
+ reg_val);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, reg_val);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WUR(xtensa->core_config->user_regs[i].reg_num,
+ XT_REG_A3));
+ reg_list[XT_USR_REG_START + i].dirty = false;
+ }
+ if (scratch_reg_dirty)
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+
+ return ERROR_OK;
+}
+
+static inline bool xtensa_is_stopped(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ return xtensa->dbg_mod.core_status.dsr & OCDDSR_STOPPED;
+}
+
+int xtensa_examine(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int cmd = PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP;
+
+ LOG_DEBUG("coreid = %d", target->coreid);
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd);
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE);
+ xtensa_dm_queue_enable(&xtensa->dbg_mod);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ if (!xtensa_dm_is_online(&xtensa->dbg_mod)) {
+ LOG_ERROR("Unexpected OCD_ID = %08" PRIx32, xtensa->dbg_mod.device_id);
+ return ERROR_TARGET_FAILURE;
+ }
+ LOG_DEBUG("OCD_ID = %08" PRIx32, xtensa->dbg_mod.device_id);
+ if (!target_was_examined(target))
+ target_set_examined(target);
+ xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ return ERROR_OK;
+}
+
+int xtensa_wakeup(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int cmd = PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP;
+
+ if (xtensa->reset_asserted)
+ cmd |= PWRCTL_CORERESET;
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd);
+ /* TODO: can we join this with the write above? */
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ return jtag_execute_queue();
+}
+
+int xtensa_smpbreak_write(struct xtensa *xtensa, uint32_t set)
+{
+ uint32_t dsr_data = 0x00110000;
+ uint32_t clear = (set | OCDDCR_ENABLEOCD) ^
+ (OCDDCR_BREAKINEN | OCDDCR_BREAKOUTEN | OCDDCR_RUNSTALLINEN |
+ OCDDCR_DEBUGMODEOUTEN | OCDDCR_ENABLEOCD);
+
+ LOG_TARGET_DEBUG(xtensa->target, "write smpbreak set=0x%" PRIx32 " clear=0x%" PRIx32, set, clear);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRSET, set | OCDDCR_ENABLEOCD);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRCLR, clear);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DSR, dsr_data);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ return jtag_execute_queue();
+}
+
+int xtensa_smpbreak_set(struct target *target, uint32_t set)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res = ERROR_OK;
+
+ xtensa->smp_break = set;
+ if (target_was_examined(target))
+ res = xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ LOG_TARGET_DEBUG(target, "set smpbreak=%" PRIx32 ", state=%i", set, target->state);
+ return res;
+}
+
+int xtensa_smpbreak_read(struct xtensa *xtensa, uint32_t *val)
+{
+ uint8_t dcr_buf[sizeof(uint32_t)];
+
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DCRSET, dcr_buf);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ *val = buf_get_u32(dcr_buf, 0, 32);
+
+ return res;
+}
+
+int xtensa_smpbreak_get(struct target *target, uint32_t *val)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ *val = xtensa->smp_break;
+ return ERROR_OK;
+}
+
+static inline xtensa_reg_val_t xtensa_reg_get_value(struct reg *reg)
+{
+ return buf_get_u32(reg->value, 0, 32);
+}
+
+static inline void xtensa_reg_set_value(struct reg *reg, xtensa_reg_val_t value)
+{
+ buf_set_u32(reg->value, 0, 32, value);
+ reg->dirty = true;
+}
+
+int xtensa_core_status_check(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res, needclear = 0;
+
+ xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ xtensa_dsr_t dsr = xtensa_dm_core_status_get(&xtensa->dbg_mod);
+ LOG_TARGET_DEBUG(target, "DSR (%08" PRIX32 ")", dsr);
+ if (dsr & OCDDSR_EXECBUSY) {
+ if (!xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target, "DSR (%08" PRIX32 ") indicates target still busy!", dsr);
+ needclear = 1;
+ }
+ if (dsr & OCDDSR_EXECEXCEPTION) {
+ if (!xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target,
+ "DSR (%08" PRIX32 ") indicates DIR instruction generated an exception!",
+ dsr);
+ needclear = 1;
+ }
+ if (dsr & OCDDSR_EXECOVERRUN) {
+ if (!xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target,
+ "DSR (%08" PRIX32 ") indicates DIR instruction generated an overrun!",
+ dsr);
+ needclear = 1;
+ }
+ if (needclear) {
+ res = xtensa_dm_core_status_clear(&xtensa->dbg_mod,
+ OCDDSR_EXECEXCEPTION | OCDDSR_EXECOVERRUN);
+ if (res != ERROR_OK && !xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target, "clearing DSR failed!");
+ return xtensa->suppress_dsr_errors ? ERROR_OK : ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+xtensa_reg_val_t xtensa_reg_get(struct target *target, enum xtensa_reg_id reg_id)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg = &xtensa->core_cache->reg_list[reg_id];
+ assert(reg_id < xtensa->core_cache->num_regs && "Attempt to access non-existing reg!");
+ return xtensa_reg_get_value(reg);
+}
+
+void xtensa_reg_set(struct target *target, enum xtensa_reg_id reg_id, xtensa_reg_val_t value)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg = &xtensa->core_cache->reg_list[reg_id];
+ assert(reg_id < xtensa->core_cache->num_regs && "Attempt to access non-existing reg!");
+ if (xtensa_reg_get_value(reg) == value)
+ return;
+ xtensa_reg_set_value(reg, value);
+}
+
+int xtensa_assert_reset(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "target_number=%i, begin", target->target_number);
+ target->state = TARGET_RESET;
+ xtensa_queue_pwr_reg_write(xtensa,
+ DMREG_PWRCTL,
+ PWRCTL_JTAGDEBUGUSE | PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP |
+ PWRCTL_CORERESET);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ xtensa->reset_asserted = true;
+ return res;
+}
+
+int xtensa_deassert_reset(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "halt=%d", target->reset_halt);
+ if (target->reset_halt)
+ xtensa_queue_dbg_reg_write(xtensa,
+ NARADR_DCRSET,
+ OCDDCR_ENABLEOCD | OCDDCR_DEBUGINTERRUPT);
+ xtensa_queue_pwr_reg_write(xtensa,
+ DMREG_PWRCTL,
+ PWRCTL_JTAGDEBUGUSE | PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ target->state = TARGET_RUNNING;
+ xtensa->reset_asserted = false;
+ return res;
+}
+
+int xtensa_fetch_all_regs(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ xtensa_reg_val_t cpenable = 0, windowbase = 0;
+ uint8_t regvals[XT_NUM_REGS][sizeof(xtensa_reg_val_t)];
+ uint8_t dsrs[XT_NUM_REGS][sizeof(xtensa_dsr_t)];
+ bool debug_dsrs = !xtensa->regs_fetched || LOG_LEVEL_IS(LOG_LVL_DEBUG);
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ /* Assume the CPU has just halted. We now want to fill the register cache with all the
+ * register contents GDB needs. For speed, we pipeline all the read operations, execute them
+ * in one go, then sort everything out from the regvals variable. */
+
+ /* Start out with AREGS; we can reach those immediately. Grab them per 16 registers. */
+ for (unsigned int j = 0; j < XT_AREGS_NUM_MAX; j += 16) {
+ /*Grab the 16 registers we can see */
+ for (unsigned int i = 0; i < 16; i++) {
+ if (i + j < xtensa->core_config->aregs_num) {
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WSR(XT_SR_DDR, xtensa_regs[XT_REG_IDX_AR0 + i].reg_num));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[XT_REG_IDX_AR0 + i + j]);
+ if (debug_dsrs)
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DSR, dsrs[XT_REG_IDX_AR0 + i + j]);
+ }
+ }
+ if (xtensa->core_config->windowed) {
+ /* Now rotate the window so we'll see the next 16 registers. The final rotate
+ * will wraparound, */
+ /* leaving us in the state we were. */
+ xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(4));
+ }
+ }
+ if (xtensa->core_config->coproc) {
+ /* As the very first thing after AREGS, go grab the CPENABLE registers. It indicates
+ * if we can also grab the FP */
+ /* (and theoretically other coprocessor) registers, or if this is a bad thing to do.*/
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa_regs[XT_REG_IDX_CPENABLE].reg_num, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[XT_REG_IDX_CPENABLE]);
+ }
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to read ARs (%d)!", res);
+ return res;
+ }
+ xtensa_core_status_check(target);
+
+ if (xtensa->core_config->coproc)
+ cpenable = buf_get_u32(regvals[XT_REG_IDX_CPENABLE], 0, 32);
+ /* We're now free to use any of A0-A15 as scratch registers
+ * Grab the SFRs and user registers first. We use A3 as a scratch register. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (xtensa_reg_is_readable(xtensa_regs[i].flags, cpenable) && reg_list[i].exist &&
+ (xtensa_regs[i].type == XT_REG_SPECIAL ||
+ xtensa_regs[i].type == XT_REG_USER || xtensa_regs[i].type == XT_REG_FR)) {
+ if (xtensa_regs[i].type == XT_REG_USER) {
+ xtensa_queue_exec_ins(xtensa, XT_INS_RUR(xtensa_regs[i].reg_num, XT_REG_A3));
+ } else if (xtensa_regs[i].type == XT_REG_FR) {
+ xtensa_queue_exec_ins(xtensa, XT_INS_RFR(xtensa_regs[i].reg_num, XT_REG_A3));
+ } else { /*SFR */
+ unsigned int reg_num = xtensa_regs[i].reg_num;
+ if (reg_num == XT_PC_REG_NUM_BASE) {
+ /* reg number of PC for debug interrupt depends on NDEBUGLEVEL */
+ reg_num += xtensa->core_config->debug.irq_level;
+ }
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(reg_num, XT_REG_A3));
+ }
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[i]);
+ if (debug_dsrs)
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DSR, dsrs[i]);
+ }
+ }
+ /* Ok, send the whole mess to the CPU. */
+ res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to fetch AR regs!");
+ return res;
+ }
+ xtensa_core_status_check(target);
+
+ if (debug_dsrs) {
+ /* DSR checking: follows order in which registers are requested. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (xtensa_reg_is_readable(xtensa_regs[i].flags, cpenable) && reg_list[i].exist &&
+ (xtensa_regs[i].type == XT_REG_SPECIAL || xtensa_regs[i].type == XT_REG_USER ||
+ xtensa_regs[i].type == XT_REG_FR)) {
+ if (buf_get_u32(dsrs[i], 0, 32) & OCDDSR_EXECEXCEPTION) {
+ LOG_ERROR("Exception reading %s!", xtensa_regs[i].name);
+ return ERROR_FAIL;
+ }
+ }
+ }
+ }
+
+ if (xtensa->core_config->user_regs_num > 0 && xtensa->core_config->fetch_user_regs) {
+ res = xtensa->core_config->fetch_user_regs(target);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ if (xtensa->core_config->windowed) {
+ /* We need the windowbase to decode the general addresses. */
+ windowbase = buf_get_u32(regvals[XT_REG_IDX_WINDOWBASE], 0, 32);
+ }
+ /* Decode the result and update the cache. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (xtensa_reg_is_readable(xtensa_regs[i].flags, cpenable) && reg_list[i].exist) {
+ if (xtensa_regs[i].type == XT_REG_GENERAL) {
+ /* TODO: add support for non-windowed configs */
+ assert(
+ xtensa->core_config->windowed &&
+ "Regs fetch is not supported for non-windowed configs!");
+ /* The 64-value general register set is read from (windowbase) on down.
+ * We need to get the real register address by subtracting windowbase and
+ * wrapping around. */
+ int realadr = xtensa_canonical_to_windowbase_offset(i, windowbase);
+ buf_cpy(regvals[realadr], reg_list[i].value, reg_list[i].size);
+ } else if (xtensa_regs[i].type == XT_REG_RELGEN) {
+ buf_cpy(regvals[xtensa_regs[i].reg_num], reg_list[i].value, reg_list[i].size);
+ } else {
+ buf_cpy(regvals[i], reg_list[i].value, reg_list[i].size);
+ }
+ reg_list[i].valid = true;
+ } else {
+ reg_list[i].valid = false;
+ }
+ }
+ /* We have used A3 as a scratch register and we will need to write that back. */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+ xtensa->regs_fetched = true;
+
+ return ERROR_OK;
+}
+
+int xtensa_fetch_user_regs_u32(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ xtensa_reg_val_t cpenable = 0;
+ uint8_t regvals[XT_USER_REGS_NUM_MAX][sizeof(xtensa_reg_val_t)];
+ uint8_t dsrs[XT_USER_REGS_NUM_MAX][sizeof(xtensa_dsr_t)];
+ bool debug_dsrs = !xtensa->regs_fetched || LOG_LEVEL_IS(LOG_LVL_DEBUG);
+
+ assert(xtensa->core_config->user_regs_num < XT_USER_REGS_NUM_MAX && "Too many user regs configured!");
+ if (xtensa->core_config->coproc)
+ cpenable = xtensa_reg_get(target, XT_REG_IDX_CPENABLE);
+
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (!xtensa_reg_is_readable(xtensa->core_config->user_regs[i].flags, cpenable))
+ continue;
+ xtensa_queue_exec_ins(xtensa, XT_INS_RUR(xtensa->core_config->user_regs[i].reg_num, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[i]);
+ if (debug_dsrs)
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DSR, dsrs[i]);
+ }
+ /* Ok, send the whole mess to the CPU. */
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to fetch AR regs!");
+ return res;
+ }
+ xtensa_core_status_check(target);
+
+ if (debug_dsrs) {
+ /* DSR checking: follows order in which registers are requested. */
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (!xtensa_reg_is_readable(xtensa->core_config->user_regs[i].flags, cpenable))
+ continue;
+ if (buf_get_u32(dsrs[i], 0, 32) & OCDDSR_EXECEXCEPTION) {
+ LOG_ERROR("Exception reading %s!", xtensa->core_config->user_regs[i].name);
+ return ERROR_FAIL;
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (xtensa_reg_is_readable(xtensa->core_config->user_regs[i].flags, cpenable)) {
+ buf_cpy(regvals[i], reg_list[XT_USR_REG_START + i].value, reg_list[XT_USR_REG_START + i].size);
+ reg_list[XT_USR_REG_START + i].valid = true;
+ } else {
+ reg_list[XT_USR_REG_START + i].valid = false;
+ }
+ }
+
+ /* We have used A3 as a scratch register and we will need to write that back. */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+ return ERROR_OK;
+}
+
+int xtensa_get_gdb_reg_list(struct target *target,
+ struct reg **reg_list[],
+ int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int num_regs = xtensa->core_config->gdb_general_regs_num;
+
+ if (reg_class == REG_CLASS_ALL)
+ num_regs = xtensa->regs_num;
+
+ LOG_DEBUG("reg_class=%i, num_regs=%d", reg_class, num_regs);
+
+ *reg_list = malloc(num_regs * sizeof(struct reg *));
+ if (!*reg_list)
+ return ERROR_FAIL;
+
+ for (unsigned int k = 0; k < num_regs; k++) {
+ unsigned int reg_id = xtensa->core_config->gdb_regs_mapping[k];
+ (*reg_list)[k] = &xtensa->core_cache->reg_list[reg_id];
+ }
+
+ *reg_list_size = num_regs;
+
+ return ERROR_OK;
+}
+
+int xtensa_mmu_is_enabled(struct target *target, int *enabled)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ *enabled = xtensa->core_config->mmu.itlb_entries_count > 0 ||
+ xtensa->core_config->mmu.dtlb_entries_count > 0;
+ return ERROR_OK;
+}
+
+int xtensa_halt(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "start");
+ if (target->state == TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target, "target was already halted");
+ return ERROR_OK;
+ }
+ /* First we have to read dsr and check if the target stopped */
+ int res = xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read core status!");
+ return res;
+ }
+ LOG_TARGET_DEBUG(target, "Core status 0x%" PRIx32, xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ if (!xtensa_is_stopped(target)) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRSET, OCDDCR_ENABLEOCD | OCDDCR_DEBUGINTERRUPT);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set OCDDCR_DEBUGINTERRUPT. Can't halt.");
+ }
+
+ return res;
+}
+
+int xtensa_prepare_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ uint32_t bpena = 0;
+
+ LOG_TARGET_DEBUG(target,
+ "current=%d address=" TARGET_ADDR_FMT ", handle_breakpoints=%i, debug_execution=%i)",
+ current,
+ address,
+ handle_breakpoints,
+ debug_execution);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (address && !current) {
+ xtensa_reg_set(target, XT_REG_IDX_PC, address);
+ } else {
+ xtensa_reg_val_t cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
+ if (cause & DEBUGCAUSE_DB) {
+ /* We stopped due to a watchpoint. We can't just resume executing the
+ * instruction again because */
+ /* that would trigger the watchpoint again. To fix this, we single-step,
+ * which ignores watchpoints. */
+ xtensa_do_step(target, current, address, handle_breakpoints);
+ }
+ if (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN)) {
+ /* We stopped due to a break instruction. We can't just resume executing the
+ * instruction again because */
+ /* that would trigger the break again. To fix this, we single-step, which
+ * ignores break. */
+ xtensa_do_step(target, current, address, handle_breakpoints);
+ }
+ }
+
+ /* Write back hw breakpoints. Current FreeRTOS SMP code can set a hw breakpoint on an
+ * exception; we need to clear that and return to the breakpoints gdb has set on resume. */
+ for (unsigned int slot = 0; slot < xtensa->core_config->debug.ibreaks_num; slot++) {
+ if (xtensa->hw_brps[slot]) {
+ /* Write IBREAKA[slot] and set bit #slot in IBREAKENABLE */
+ xtensa_reg_set(target, XT_REG_IDX_IBREAKA0 + slot, xtensa->hw_brps[slot]->address);
+ bpena |= BIT(slot);
+ }
+ }
+ xtensa_reg_set(target, XT_REG_IDX_IBREAKENABLE, bpena);
+
+ /* Here we write all registers to the targets */
+ int res = xtensa_write_dirty_registers(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to write back register cache.");
+ return res;
+}
+
+int xtensa_do_resume(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ xtensa_queue_exec_ins(xtensa, XT_INS_RFDO);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to exec RFDO %d!", res);
+ return res;
+ }
+ xtensa_core_status_check(target);
+ return ERROR_OK;
+}
+
+int xtensa_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution)
+{
+ LOG_TARGET_DEBUG(target, "start");
+ int res = xtensa_prepare_resume(target, current, address, handle_breakpoints, debug_execution);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to prepare for resume!");
+ return res;
+ }
+ res = xtensa_do_resume(target);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to resume!");
+ return res;
+ }
+
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ if (!debug_execution)
+ target->state = TARGET_RUNNING;
+ else
+ target->state = TARGET_DEBUG_RUNNING;
+
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+
+ return ERROR_OK;
+}
+
+static bool xtensa_pc_in_winexc(struct target *target, target_addr_t pc)
+{
+ uint8_t insn_buf[XT_ISNS_SZ_MAX];
+ int err = xtensa_read_buffer(target, pc, sizeof(insn_buf), insn_buf);
+ if (err != ERROR_OK)
+ return false;
+
+ xtensa_insn_t insn = buf_get_u32(insn_buf, 0, 24);
+ xtensa_insn_t masked = insn & XT_INS_L32E_S32E_MASK;
+ if (masked == XT_INS_L32E(0, 0, 0) || masked == XT_INS_S32E(0, 0, 0))
+ return true;
+
+ masked = insn & XT_INS_RFWO_RFWU_MASK;
+ if (masked == XT_INS_RFWO || masked == XT_INS_RFWU)
+ return true;
+
+ return false;
+}
+
+int xtensa_do_step(struct target *target, int current, target_addr_t address, int handle_breakpoints)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res;
+ const uint32_t icount_val = -2; /* ICOUNT value to load for 1 step */
+ xtensa_reg_val_t dbreakc[XT_WATCHPOINTS_NUM_MAX];
+ xtensa_reg_val_t icountlvl, cause;
+ xtensa_reg_val_t oldps, newps, oldpc, cur_pc;
+
+ LOG_TARGET_DEBUG(target, "current=%d, address=" TARGET_ADDR_FMT ", handle_breakpoints=%i",
+ current, address, handle_breakpoints);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (xtensa->core_config->debug.icount_sz != 32) {
+ LOG_TARGET_WARNING(target, "stepping for ICOUNT less then 32 bits is not implemented!");
+ return ERROR_FAIL;
+ }
+
+ /* Save old ps/pc */
+ oldps = xtensa_reg_get(target, XT_REG_IDX_PS);
+ oldpc = xtensa_reg_get(target, XT_REG_IDX_PC);
+
+ cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
+ LOG_TARGET_DEBUG(target, "oldps=%" PRIx32 ", oldpc=%" PRIx32 " dbg_cause=%" PRIx32 " exc_cause=%" PRIx32,
+ oldps,
+ oldpc,
+ cause,
+ xtensa_reg_get(target, XT_REG_IDX_EXCCAUSE));
+ if (handle_breakpoints && (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN))) {
+ /* handle hard-coded SW breakpoints (e.g. syscalls) */
+ LOG_TARGET_DEBUG(target, "Increment PC to pass break instruction...");
+ xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0); /* so we don't recurse into the same routine */
+ xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
+ /* pretend that we have stepped */
+ if (cause & DEBUGCAUSE_BI)
+ xtensa_reg_set(target, XT_REG_IDX_PC, oldpc + 3); /* PC = PC+3 */
+ else
+ xtensa_reg_set(target, XT_REG_IDX_PC, oldpc + 2); /* PC = PC+2 */
+ return ERROR_OK;
+ }
+
+ /* Xtensa has an ICOUNTLEVEL register which sets the maximum interrupt level at which the
+ * instructions are to be counted while stepping.
+ * For example, if we need to step by 2 instructions, and an interrupt occurs inbetween,
+ * the processor will execute the interrupt, return, and halt after the 2nd instruction.
+ * However, sometimes we don't want the interrupt handlers to be executed at all, while
+ * stepping through the code. In this case (XT_STEPPING_ISR_OFF), PS.INTLEVEL can be raised
+ * to only allow Debug and NMI interrupts.
+ */
+ if (xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF) {
+ if (!xtensa->core_config->high_irq.enabled) {
+ LOG_TARGET_WARNING(
+ target,
+ "disabling IRQs while stepping is not implemented w/o high prio IRQs option!");
+ return ERROR_FAIL;
+ }
+ /* Mask all interrupts below Debug, i.e. PS.INTLEVEL = DEBUGLEVEL - 1 */
+ xtensa_reg_val_t temp_ps = (oldps & ~0xF) | (xtensa->core_config->debug.irq_level - 1);
+ xtensa_reg_set(target, XT_REG_IDX_PS, temp_ps);
+ }
+ /* Regardless of ISRs masking mode we need to count instructions at any CINTLEVEL during step.
+ So set `icountlvl` to DEBUGLEVEL.
+ If ISRs are masked they are disabled in PS (see above), so having `icountlvl` set to DEBUGLEVEL
+ will allow to step through any type of the code, e.g. 'high int level' ISR.
+ If ISRs are not masked With `icountlvl` set to DEBUGLEVEL, we can step into any ISR
+ which can happen (enabled in PS).
+ */
+ icountlvl = xtensa->core_config->debug.irq_level;
+
+ if (cause & DEBUGCAUSE_DB) {
+ /* We stopped due to a watchpoint. We can't just resume executing the instruction again because
+ * that would trigger the watchpoint again. To fix this, we remove watchpoints,single-step and
+ * re-enable the watchpoint. */
+ LOG_TARGET_DEBUG(
+ target,
+ "Single-stepping to get past instruction that triggered the watchpoint...");
+ xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0); /*so we don't recurse into
+ * the same routine */
+ xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
+ /*Save all DBREAKCx registers and set to 0 to disable watchpoints */
+ for (unsigned int slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++) {
+ dbreakc[slot] = xtensa_reg_get(target, XT_REG_IDX_DBREAKC0 + slot);
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, 0);
+ }
+ }
+
+ if (!handle_breakpoints && (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN))) {
+ /* handle normal SW breakpoint */
+ xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0); /*so we don't recurse into
+ * the same routine */
+ xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
+ }
+ do {
+ xtensa_reg_set(target, XT_REG_IDX_ICOUNTLEVEL, icountlvl);
+ xtensa_reg_set(target, XT_REG_IDX_ICOUNT, icount_val);
+
+ /* Now ICOUNT is set, we can resume as if we were going to run */
+ res = xtensa_prepare_resume(target, current, address, 0, 0);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to prepare resume for single step");
+ return res;
+ }
+ res = xtensa_do_resume(target);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to resume after setting up single step");
+ return res;
+ }
+
+ /* Wait for stepping to complete */
+ long long start = timeval_ms();
+ while (timeval_ms() < start + 500) {
+ /* Do not use target_poll here, it also triggers other things... just manually read the DSR
+ *until stepping is complete. */
+ usleep(1000);
+ res = xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read core status!");
+ return res;
+ }
+ if (xtensa_is_stopped(target))
+ break;
+ usleep(1000);
+ }
+ LOG_TARGET_DEBUG(target, "Finish stepping. dsr=0x%08" PRIx32,
+ xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ if (!xtensa_is_stopped(target)) {
+ LOG_TARGET_WARNING(
+ target,
+ "Timed out waiting for target to finish stepping. dsr=0x%08" PRIx32,
+ xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ target->state = TARGET_RUNNING;
+ return ERROR_FAIL;
+ }
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ target->state = TARGET_HALTED;
+
+ xtensa_fetch_all_regs(target);
+
+ cur_pc = xtensa_reg_get(target, XT_REG_IDX_PC);
+
+ LOG_TARGET_DEBUG(target,
+ "cur_ps=%" PRIx32 ", cur_pc=%" PRIx32 " dbg_cause=%" PRIx32 " exc_cause=%" PRIx32,
+ xtensa_reg_get(target, XT_REG_IDX_PS),
+ cur_pc,
+ xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE),
+ xtensa_reg_get(target, XT_REG_IDX_EXCCAUSE));
+
+ /* Do not step into WindowOverflow if ISRs are masked.
+ If we stop in WindowOverflow at breakpoint with masked ISRs and
+ try to do a step it will get us out of that handler */
+ if (xtensa->core_config->windowed &&
+ xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF &&
+ xtensa_pc_in_winexc(target, cur_pc)) {
+ /* isrmask = on, need to step out of the window exception handler */
+ LOG_DEBUG("Stepping out of window exception, PC=%" PRIX32, cur_pc);
+ oldpc = cur_pc;
+ address = oldpc + 3;
+ continue;
+ }
+
+ if (oldpc == cur_pc)
+ LOG_TARGET_WARNING(target, "Stepping doesn't seem to change PC! dsr=0x%08" PRIx32,
+ xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ else
+ LOG_DEBUG("Stepped from %" PRIX32 " to %" PRIX32, oldpc, cur_pc);
+ break;
+ } while (true);
+ LOG_DEBUG("Done stepping, PC=%" PRIX32, cur_pc);
+
+ if (cause & DEBUGCAUSE_DB) {
+ LOG_TARGET_DEBUG(target, "...Done, re-installing watchpoints.");
+ /* Restore the DBREAKCx registers */
+ for (unsigned int slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++)
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, dbreakc[slot]);
+ }
+
+ /* Restore int level */
+ /* TODO: Theoretically, this can mess up stepping over an instruction that modifies
+ * ps.intlevel by itself. TODO: Look into this. */
+ if (xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF) {
+ newps = xtensa_reg_get(target, XT_REG_IDX_PS);
+ newps = (newps & ~0xF) | (oldps & 0xf);
+ xtensa_reg_set(target, XT_REG_IDX_PS, newps);
+ }
+
+ /* write ICOUNTLEVEL back to zero */
+ xtensa_reg_set(target, XT_REG_IDX_ICOUNTLEVEL, 0);
+ /* TODO: can we skip writing dirty registers and re-fetching them? */
+ res = xtensa_write_dirty_registers(target);
+ xtensa_fetch_all_regs(target);
+ return res;
+}
+
+int xtensa_step(struct target *target, int current, target_addr_t address, int handle_breakpoints)
+{
+ return xtensa_do_step(target, current, address, handle_breakpoints);
+}
+
+/**
+ * Returns true if two ranges are overlapping
+ */
+static inline bool xtensa_memory_regions_overlap(target_addr_t r1_start,
+ target_addr_t r1_end,
+ target_addr_t r2_start,
+ target_addr_t r2_end)
+{
+ if ((r2_start >= r1_start) && (r2_start < r1_end))
+ return true; /* r2_start is in r1 region */
+ if ((r2_end > r1_start) && (r2_end <= r1_end))
+ return true; /* r2_end is in r1 region */
+ return false;
+}
+
+/**
+ * Returns a size of overlapped region of two ranges.
+ */
+static inline target_addr_t xtensa_get_overlap_size(target_addr_t r1_start,
+ target_addr_t r1_end,
+ target_addr_t r2_start,
+ target_addr_t r2_end)
+{
+ if (xtensa_memory_regions_overlap(r1_start, r1_end, r2_start, r2_end)) {
+ target_addr_t ov_start = r1_start < r2_start ? r2_start : r1_start;
+ target_addr_t ov_end = r1_end > r2_end ? r2_end : r1_end;
+ return ov_end - ov_start;
+ }
+ return 0;
+}
+
+/**
+ * Check if the address gets to memory regions, and it's access mode
+ */
+static bool xtensa_memory_op_validate_range(struct xtensa *xtensa, target_addr_t address, size_t size, int access)
+{
+ target_addr_t adr_pos = address; /* address cursor set to the beginning start */
+ target_addr_t adr_end = address + size; /* region end */
+ target_addr_t overlap_size;
+ const struct xtensa_local_mem_region_config *cm; /* current mem region */
+
+ while (adr_pos < adr_end) {
+ cm = xtensa_target_memory_region_find(xtensa, adr_pos);
+ if (!cm) /* address is not belong to anything */
+ return false;
+ if ((cm->access & access) != access) /* access check */
+ return false;
+ overlap_size = xtensa_get_overlap_size(cm->base, (cm->base + cm->size), adr_pos, adr_end);
+ assert(overlap_size != 0);
+ adr_pos += overlap_size;
+ }
+ return true;
+}
+
+int xtensa_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ /* We are going to read memory in 32-bit increments. This may not be what the calling
+ * function expects, so we may need to allocate a temp buffer and read into that first. */
+ target_addr_t addrstart_al = ALIGN_DOWN(address, 4);
+ target_addr_t addrend_al = ALIGN_UP(address + size * count, 4);
+ target_addr_t adr = addrstart_al;
+ uint8_t *albuff;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!xtensa->permissive_mode) {
+ if (!xtensa_memory_op_validate_range(xtensa, address, (size * count),
+ XT_MEM_ACCESS_READ)) {
+ LOG_DEBUG("address " TARGET_ADDR_FMT " not readable", address);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (addrstart_al == address && addrend_al == address + (size * count)) {
+ albuff = buffer;
+ } else {
+ albuff = malloc(addrend_al - addrstart_al);
+ if (!albuff) {
+ LOG_TARGET_ERROR(target, "Out of memory allocating %" TARGET_PRIdADDR " bytes!",
+ addrend_al - addrstart_al);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+
+ /* We're going to use A3 here */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+ /* Write start address to A3 */
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrstart_al);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ /* Now we can safely read data from addrstart_al up to addrend_al into albuff */
+ for (unsigned int i = 0; adr != addrend_al; i += sizeof(uint32_t), adr += sizeof(uint32_t)) {
+ xtensa_queue_exec_ins(xtensa, XT_INS_LDDR32P(XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, &albuff[i]);
+ }
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK)
+ res = xtensa_core_status_check(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_WARNING(target, "Failed reading %d bytes at address " TARGET_ADDR_FMT,
+ count * size, address);
+
+ if (albuff != buffer) {
+ memcpy(buffer, albuff + (address & 3), (size * count));
+ free(albuff);
+ }
+
+ return res;
+}
+
+int xtensa_read_buffer(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer)
+{
+ /* xtensa_read_memory can also read unaligned stuff. Just pass through to that routine. */
+ return xtensa_read_memory(target, address, 1, count, buffer);
+}
+
+int xtensa_write_memory(struct target *target,
+ target_addr_t address,
+ uint32_t size,
+ uint32_t count,
+ const uint8_t *buffer)
+{
+ /* This memory write function can get thrown nigh everything into it, from
+ * aligned uint32 writes to unaligned uint8ths. The Xtensa memory doesn't always
+ * accept anything but aligned uint32 writes, though. That is why we convert
+ * everything into that. */
+ struct xtensa *xtensa = target_to_xtensa(target);
+ target_addr_t addrstart_al = ALIGN_DOWN(address, 4);
+ target_addr_t addrend_al = ALIGN_UP(address + size * count, 4);
+ target_addr_t adr = addrstart_al;
+ int res;
+ uint8_t *albuff;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!xtensa->permissive_mode) {
+ if (!xtensa_memory_op_validate_range(xtensa, address, (size * count), XT_MEM_ACCESS_WRITE)) {
+ LOG_WARNING("address " TARGET_ADDR_FMT " not writable", address);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (size == 0 || count == 0 || !buffer)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ /* Allocate a temporary buffer to put the aligned bytes in, if needed. */
+ if (addrstart_al == address && addrend_al == address + (size * count)) {
+ /* We discard the const here because albuff can also be non-const */
+ albuff = (uint8_t *)buffer;
+ } else {
+ albuff = malloc(addrend_al - addrstart_al);
+ if (!albuff) {
+ LOG_TARGET_ERROR(target, "Out of memory allocating %" TARGET_PRIdADDR " bytes!",
+ addrend_al - addrstart_al);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+
+ /* We're going to use A3 here */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+
+ /* If we're using a temp aligned buffer, we need to fill the head and/or tail bit of it. */
+ if (albuff != buffer) {
+ /* See if we need to read the first and/or last word. */
+ if (address & 3) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrstart_al);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_LDDR32P(XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, &albuff[0]);
+ }
+ if ((address + (size * count)) & 3) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrend_al - 4);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_LDDR32P(XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR,
+ &albuff[addrend_al - addrstart_al - 4]);
+ }
+ /* Grab bytes */
+ res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Error issuing unaligned memory write context instruction(s): %d", res);
+ if (albuff != buffer)
+ free(albuff);
+ return res;
+ }
+ xtensa_core_status_check(target);
+ /* Copy data to be written into the aligned buffer */
+ memcpy(&albuff[address & 3], buffer, size * count);
+ /* Now we can write albuff in aligned uint32s. */
+ }
+
+ /* Write start address to A3 */
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrstart_al);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ /* Write the aligned buffer */
+ for (unsigned int i = 0; adr != addrend_al; i += sizeof(uint32_t), adr += sizeof(uint32_t)) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, buf_get_u32(&albuff[i], 0, 32));
+ xtensa_queue_exec_ins(xtensa, XT_INS_SDDR32P(XT_REG_A3));
+ }
+ res = jtag_execute_queue();
+ if (res == ERROR_OK)
+ res = xtensa_core_status_check(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_WARNING(target, "Failed writing %d bytes at address " TARGET_ADDR_FMT, count * size, address);
+ if (albuff != buffer)
+ free(albuff);
+
+ return res;
+}
+
+int xtensa_write_buffer(struct target *target, target_addr_t address, uint32_t count, const uint8_t *buffer)
+{
+ /* xtensa_write_memory can handle everything. Just pass on to that. */
+ return xtensa_write_memory(target, address, 1, count, buffer);
+}
+
+int xtensa_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum)
+{
+ LOG_WARNING("not implemented yet");
+ return ERROR_FAIL;
+}
+
+int xtensa_poll(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ int res = xtensa_dm_power_status_read(&xtensa->dbg_mod, PWRSTAT_DEBUGWASRESET | PWRSTAT_COREWASRESET);
+ if (res != ERROR_OK)
+ return res;
+
+ if (xtensa_dm_tap_was_reset(&xtensa->dbg_mod)) {
+ LOG_TARGET_INFO(target, "Debug controller was reset.");
+ res = xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ if (res != ERROR_OK)
+ return res;
+ }
+ if (xtensa_dm_core_was_reset(&xtensa->dbg_mod))
+ LOG_TARGET_INFO(target, "Core was reset.");
+ xtensa_dm_power_status_cache(&xtensa->dbg_mod);
+ /* Enable JTAG, set reset if needed */
+ res = xtensa_wakeup(target);
+ if (res != ERROR_OK)
+ return res;
+
+ res = xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ if (res != ERROR_OK)
+ return res;
+ if (xtensa->dbg_mod.power_status.stath & PWRSTAT_COREWASRESET) {
+ /* if RESET state is persitent */
+ target->state = TARGET_RESET;
+ } else if (!xtensa_dm_is_powered(&xtensa->dbg_mod)) {
+ LOG_TARGET_DEBUG(target, "not powered 0x%" PRIX32 "%ld",
+ xtensa->dbg_mod.core_status.dsr,
+ xtensa->dbg_mod.core_status.dsr & OCDDSR_STOPPED);
+ target->state = TARGET_UNKNOWN;
+ if (xtensa->come_online_probes_num == 0)
+ target->examined = false;
+ else
+ xtensa->come_online_probes_num--;
+ } else if (xtensa_is_stopped(target)) {
+ if (target->state != TARGET_HALTED) {
+ enum target_state oldstate = target->state;
+ target->state = TARGET_HALTED;
+ /* Examine why the target has been halted */
+ target->debug_reason = DBG_REASON_DBGRQ;
+ xtensa_fetch_all_regs(target);
+ /* When setting debug reason DEBUGCAUSE events have the following
+ * priorities: watchpoint == breakpoint > single step > debug interrupt. */
+ /* Watchpoint and breakpoint events at the same time results in special
+ * debug reason: DBG_REASON_WPTANDBKPT. */
+ xtensa_reg_val_t halt_cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
+ /* TODO: Add handling of DBG_REASON_EXC_CATCH */
+ if (halt_cause & DEBUGCAUSE_IC)
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ if (halt_cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BN | DEBUGCAUSE_BI)) {
+ if (halt_cause & DEBUGCAUSE_DB)
+ target->debug_reason = DBG_REASON_WPTANDBKPT;
+ else
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ } else if (halt_cause & DEBUGCAUSE_DB) {
+ target->debug_reason = DBG_REASON_WATCHPOINT;
+ }
+ LOG_TARGET_DEBUG(target, "Target halted, pc=0x%08" PRIX32 ", debug_reason=%08x, oldstate=%08x",
+ xtensa_reg_get(target, XT_REG_IDX_PC),
+ target->debug_reason,
+ oldstate);
+ LOG_TARGET_DEBUG(target, "Halt reason=0x%08" PRIX32 ", exc_cause=%" PRId32 ", dsr=0x%08" PRIx32,
+ halt_cause,
+ xtensa_reg_get(target, XT_REG_IDX_EXCCAUSE),
+ xtensa->dbg_mod.core_status.dsr);
+ LOG_TARGET_INFO(target, "Target halted, PC=0x%08" PRIX32 ", debug_reason=%08x",
+ xtensa_reg_get(target, XT_REG_IDX_PC), target->debug_reason);
+ xtensa_dm_core_status_clear(
+ &xtensa->dbg_mod,
+ OCDDSR_DEBUGPENDBREAK | OCDDSR_DEBUGINTBREAK | OCDDSR_DEBUGPENDTRAX |
+ OCDDSR_DEBUGINTTRAX |
+ OCDDSR_DEBUGPENDHOST | OCDDSR_DEBUGINTHOST);
+ }
+ } else {
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ if (target->state != TARGET_RUNNING && target->state != TARGET_DEBUG_RUNNING) {
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ }
+ }
+ if (xtensa->trace_active) {
+ /* Detect if tracing was active but has stopped. */
+ struct xtensa_trace_status trace_status;
+ res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res == ERROR_OK) {
+ if (!(trace_status.stat & TRAXSTAT_TRACT)) {
+ LOG_INFO("Detected end of trace.");
+ if (trace_status.stat & TRAXSTAT_PCMTG)
+ LOG_TARGET_INFO(target, "Trace stop triggered by PC match");
+ if (trace_status.stat & TRAXSTAT_PTITG)
+ LOG_TARGET_INFO(target, "Trace stop triggered by Processor Trigger Input");
+ if (trace_status.stat & TRAXSTAT_CTITG)
+ LOG_TARGET_INFO(target, "Trace stop triggered by Cross-trigger Input");
+ xtensa->trace_active = false;
+ }
+ }
+ }
+ return ERROR_OK;
+}
+
+static int xtensa_sw_breakpoint_add(struct target *target,
+ struct breakpoint *breakpoint,
+ struct xtensa_sw_breakpoint *sw_bp)
+{
+ int ret = target_read_buffer(target, breakpoint->address, XT_ISNS_SZ_MAX, sw_bp->insn);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read original instruction (%d)!", ret);
+ return ret;
+ }
+
+ sw_bp->insn_sz = xtensa_insn_size_get(buf_get_u32(sw_bp->insn, 0, 24));
+ sw_bp->oocd_bp = breakpoint;
+
+ uint32_t break_insn = sw_bp->insn_sz == XT_ISNS_SZ_MAX ? XT_INS_BREAK(0, 0) : XT_INS_BREAKN(0);
+ /* convert to target endianness */
+ uint8_t break_insn_buff[4];
+ target_buffer_set_u32(target, break_insn_buff, break_insn);
+
+ ret = target_write_buffer(target, breakpoint->address, sw_bp->insn_sz, break_insn_buff);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to write breakpoint instruction (%d)!", ret);
+ return ret;
+ }
+
+ return ERROR_OK;
+}
+
+static int xtensa_sw_breakpoint_remove(struct target *target, struct xtensa_sw_breakpoint *sw_bp)
+{
+ int ret = target_write_buffer(target, sw_bp->oocd_bp->address, sw_bp->insn_sz, sw_bp->insn);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read insn (%d)!", ret);
+ return ret;
+ }
+ sw_bp->oocd_bp = NULL;
+ return ERROR_OK;
+}
+
+int xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+
+ if (breakpoint->type == BKPT_SOFT) {
+ for (slot = 0; slot < XT_SW_BREAKPOINTS_MAX_NUM; slot++) {
+ if (!xtensa->sw_brps[slot].oocd_bp ||
+ xtensa->sw_brps[slot].oocd_bp == breakpoint)
+ break;
+ }
+ if (slot == XT_SW_BREAKPOINTS_MAX_NUM) {
+ LOG_TARGET_WARNING(target, "No free slots to add SW breakpoint!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ int ret = xtensa_sw_breakpoint_add(target, breakpoint, &xtensa->sw_brps[slot]);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to add SW breakpoint!");
+ return ret;
+ }
+ LOG_TARGET_DEBUG(target, "placed SW breakpoint %u @ " TARGET_ADDR_FMT,
+ slot,
+ breakpoint->address);
+ return ERROR_OK;
+ }
+
+ for (slot = 0; slot < xtensa->core_config->debug.ibreaks_num; slot++) {
+ if (!xtensa->hw_brps[slot] || xtensa->hw_brps[slot] == breakpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.ibreaks_num) {
+ LOG_TARGET_ERROR(target, "No free slots to add HW breakpoint!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ xtensa->hw_brps[slot] = breakpoint;
+ /* We will actually write the breakpoints when we resume the target. */
+ LOG_TARGET_DEBUG(target, "placed HW breakpoint @ " TARGET_ADDR_FMT,
+ breakpoint->address);
+
+ return ERROR_OK;
+}
+
+int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+
+ if (breakpoint->type == BKPT_SOFT) {
+ for (slot = 0; slot < XT_SW_BREAKPOINTS_MAX_NUM; slot++) {
+ if (xtensa->sw_brps[slot].oocd_bp && xtensa->sw_brps[slot].oocd_bp == breakpoint)
+ break;
+ }
+ if (slot == XT_SW_BREAKPOINTS_MAX_NUM) {
+ LOG_TARGET_WARNING(target, "Max SW breakpoints slot reached, slot=%u!", slot);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ int ret = xtensa_sw_breakpoint_remove(target, &xtensa->sw_brps[slot]);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to remove SW breakpoint (%d)!", ret);
+ return ret;
+ }
+ LOG_TARGET_DEBUG(target, "cleared SW breakpoint %u @ " TARGET_ADDR_FMT, slot, breakpoint->address);
+ return ERROR_OK;
+ }
+
+ for (slot = 0; slot < xtensa->core_config->debug.ibreaks_num; slot++) {
+ if (xtensa->hw_brps[slot] == breakpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.ibreaks_num) {
+ LOG_TARGET_ERROR(target, "HW breakpoint not found!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ xtensa->hw_brps[slot] = NULL;
+ LOG_TARGET_DEBUG(target, "cleared HW breakpoint %u @ " TARGET_ADDR_FMT, slot, breakpoint->address);
+ return ERROR_OK;
+}
+
+int xtensa_watchpoint_add(struct target *target, struct watchpoint *watchpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+ xtensa_reg_val_t dbreakcval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (watchpoint->mask != ~(uint32_t)0) {
+ LOG_TARGET_ERROR(target, "watchpoint value masks not supported");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ for (slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++) {
+ if (!xtensa->hw_wps[slot] || xtensa->hw_wps[slot] == watchpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.dbreaks_num) {
+ LOG_TARGET_WARNING(target, "No free slots to add HW watchpoint!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ /* Figure out value for dbreakc5..0
+ * It's basically 0x3F with an incremental bit removed from the LSB for each extra length power of 2. */
+ if (watchpoint->length < 1 || watchpoint->length > 64 ||
+ !IS_PWR_OF_2(watchpoint->length) ||
+ !IS_ALIGNED(watchpoint->address, watchpoint->length)) {
+ LOG_TARGET_WARNING(
+ target,
+ "Watchpoint with length %d on address " TARGET_ADDR_FMT
+ " not supported by hardware.",
+ watchpoint->length,
+ watchpoint->address);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ dbreakcval = ALIGN_DOWN(0x3F, watchpoint->length);
+
+ if (watchpoint->rw == WPT_READ)
+ dbreakcval |= BIT(30);
+ if (watchpoint->rw == WPT_WRITE)
+ dbreakcval |= BIT(31);
+ if (watchpoint->rw == WPT_ACCESS)
+ dbreakcval |= BIT(30) | BIT(31);
+
+ /* Write DBREAKA[slot] and DBCREAKC[slot] */
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKA0 + slot, watchpoint->address);
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, dbreakcval);
+ xtensa->hw_wps[slot] = watchpoint;
+ LOG_TARGET_DEBUG(target, "placed HW watchpoint @ " TARGET_ADDR_FMT,
+ watchpoint->address);
+ return ERROR_OK;
+}
+
+int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+
+ for (slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++) {
+ if (xtensa->hw_wps[slot] == watchpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.dbreaks_num) {
+ LOG_TARGET_WARNING(target, "HW watchpoint " TARGET_ADDR_FMT " not found!", watchpoint->address);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, 0);
+ xtensa->hw_wps[slot] = NULL;
+ LOG_TARGET_DEBUG(target, "cleared HW watchpoint @ " TARGET_ADDR_FMT,
+ watchpoint->address);
+ return ERROR_OK;
+}
+
+static int xtensa_build_reg_cache(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+ struct reg_cache *reg_cache = calloc(1, sizeof(struct reg_cache));
+
+ if (!reg_cache) {
+ LOG_ERROR("Failed to alloc reg cache!");
+ return ERROR_FAIL;
+ }
+ reg_cache->name = "Xtensa registers";
+ reg_cache->next = NULL;
+ reg_cache->num_regs = XT_NUM_REGS + xtensa->core_config->user_regs_num;
+ /* Init reglist */
+ struct reg *reg_list = calloc(reg_cache->num_regs, sizeof(struct reg));
+ if (!reg_list) {
+ LOG_ERROR("Failed to alloc reg list!");
+ goto fail;
+ }
+ xtensa->regs_num = 0;
+
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ reg_list[i].exist = false;
+ if (xtensa_regs[i].type == XT_REG_USER) {
+ if (xtensa_user_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("User reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ } else if (xtensa_regs[i].type == XT_REG_FR) {
+ if (xtensa_fp_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("FP reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ } else if (xtensa_regs[i].type == XT_REG_SPECIAL) {
+ if (xtensa_special_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("Special reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ } else {
+ if (xtensa_regular_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("Regular reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ }
+ reg_list[i].name = xtensa_regs[i].name;
+ reg_list[i].size = 32;
+ reg_list[i].value = calloc(1, 4 /*XT_REG_LEN*/);/* make Clang Static Analyzer happy */
+ if (!reg_list[i].value) {
+ LOG_ERROR("Failed to alloc reg list value!");
+ goto fail;
+ }
+ reg_list[i].dirty = false;
+ reg_list[i].valid = false;
+ reg_list[i].type = &xtensa_reg_type;
+ reg_list[i].arch_info = xtensa;
+ if (reg_list[i].exist)
+ xtensa->regs_num++;
+ }
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ reg_list[XT_USR_REG_START + i].exist = true;
+ reg_list[XT_USR_REG_START + i].name = xtensa->core_config->user_regs[i].name;
+ reg_list[XT_USR_REG_START + i].size = xtensa->core_config->user_regs[i].size;
+ reg_list[XT_USR_REG_START + i].value = calloc(1, reg_list[XT_USR_REG_START + i].size / 8);
+ if (!reg_list[XT_USR_REG_START + i].value) {
+ LOG_ERROR("Failed to alloc user reg list value!");
+ goto fail;
+ }
+ reg_list[XT_USR_REG_START + i].dirty = false;
+ reg_list[XT_USR_REG_START + i].valid = false;
+ reg_list[XT_USR_REG_START + i].type = xtensa->core_config->user_regs[i].type;
+ reg_list[XT_USR_REG_START + i].arch_info = xtensa;
+ xtensa->regs_num++;
+ }
+ if (xtensa->core_config->gdb_general_regs_num >= xtensa->regs_num) {
+ LOG_ERROR("Regs number less then GDB general regs number!");
+ goto fail;
+ }
+
+ /* assign GDB reg numbers to registers */
+ for (unsigned int gdb_reg_id = 0; gdb_reg_id < xtensa->regs_num; gdb_reg_id++) {
+ unsigned int reg_id = xtensa->core_config->gdb_regs_mapping[gdb_reg_id];
+ if (reg_id >= reg_cache->num_regs) {
+ LOG_ERROR("Invalid GDB map!");
+ goto fail;
+ }
+ if (!reg_list[reg_id].exist) {
+ LOG_ERROR("Non-existing reg in GDB map!");
+ goto fail;
+ }
+ reg_list[reg_id].number = gdb_reg_id;
+ }
+ reg_cache->reg_list = reg_list;
+
+ xtensa->algo_context_backup = calloc(reg_cache->num_regs, sizeof(void *));
+ if (!xtensa->algo_context_backup) {
+ LOG_ERROR("Failed to alloc mem for algorithm context backup!");
+ goto fail;
+ }
+ for (unsigned int i = 0; i < reg_cache->num_regs; i++) {
+ struct reg *reg = &reg_cache->reg_list[i];
+ xtensa->algo_context_backup[i] = calloc(1, reg->size / 8);
+ if (!xtensa->algo_context_backup[i]) {
+ LOG_ERROR("Failed to alloc mem for algorithm context!");
+ goto fail;
+ }
+ }
+
+ xtensa->core_cache = reg_cache;
+ if (cache_p)
+ *cache_p = reg_cache;
+ return ERROR_OK;
+
+fail:
+ if (reg_list) {
+ for (unsigned int i = 0; i < reg_cache->num_regs; i++)
+ free(reg_list[i].value);
+ free(reg_list);
+ }
+ if (xtensa->algo_context_backup) {
+ for (unsigned int i = 0; i < reg_cache->num_regs; i++)
+ free(xtensa->algo_context_backup[i]);
+ free(xtensa->algo_context_backup);
+ }
+ free(reg_cache);
+
+ return ERROR_FAIL;
+}
+
+int xtensa_init_arch_info(struct target *target, struct xtensa *xtensa,
+ const struct xtensa_config *xtensa_config,
+ const struct xtensa_debug_module_config *dm_cfg)
+{
+ target->arch_info = xtensa;
+ xtensa->common_magic = XTENSA_COMMON_MAGIC;
+ xtensa->target = target;
+ xtensa->core_config = xtensa_config;
+ xtensa->stepping_isr_mode = XT_STEPPING_ISR_ON;
+
+ if (!xtensa->core_config->exc.enabled || !xtensa->core_config->irq.enabled ||
+ !xtensa->core_config->high_irq.enabled || !xtensa->core_config->debug.enabled) {
+ LOG_ERROR("Xtensa configuration does not support debugging!");
+ return ERROR_FAIL;
+ }
+ return xtensa_dm_init(&xtensa->dbg_mod, dm_cfg);
+}
+
+void xtensa_set_permissive_mode(struct target *target, bool state)
+{
+ target_to_xtensa(target)->permissive_mode = state;
+}
+
+int xtensa_target_init(struct command_context *cmd_ctx, struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ xtensa->come_online_probes_num = 3;
+ xtensa->hw_brps = calloc(xtensa->core_config->debug.ibreaks_num, sizeof(struct breakpoint *));
+ if (!xtensa->hw_brps) {
+ LOG_ERROR("Failed to alloc memory for HW breakpoints!");
+ return ERROR_FAIL;
+ }
+ xtensa->hw_wps = calloc(xtensa->core_config->debug.dbreaks_num, sizeof(struct watchpoint *));
+ if (!xtensa->hw_wps) {
+ free(xtensa->hw_brps);
+ LOG_ERROR("Failed to alloc memory for HW watchpoints!");
+ return ERROR_FAIL;
+ }
+ xtensa->sw_brps = calloc(XT_SW_BREAKPOINTS_MAX_NUM, sizeof(struct xtensa_sw_breakpoint));
+ if (!xtensa->sw_brps) {
+ free(xtensa->hw_brps);
+ free(xtensa->hw_wps);
+ LOG_ERROR("Failed to alloc memory for SW breakpoints!");
+ return ERROR_FAIL;
+ }
+
+ return xtensa_build_reg_cache(target);
+}
+
+static void xtensa_free_reg_cache(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg_cache *cache = xtensa->core_cache;
+
+ if (cache) {
+ register_unlink_cache(&target->reg_cache, cache);
+ for (unsigned int i = 0; i < cache->num_regs; i++) {
+ free(xtensa->algo_context_backup[i]);
+ free(cache->reg_list[i].value);
+ }
+ free(xtensa->algo_context_backup);
+ free(cache->reg_list);
+ free(cache);
+ }
+ xtensa->core_cache = NULL;
+ xtensa->algo_context_backup = NULL;
+}
+
+void xtensa_target_deinit(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_DEBUG("start");
+
+ if (target_was_examined(target)) {
+ int ret = xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRCLR, OCDDCR_ENABLEOCD);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to queue OCDDCR_ENABLEOCD clear operation!");
+ return;
+ }
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ ret = jtag_execute_queue();
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to clear OCDDCR_ENABLEOCD!");
+ return;
+ }
+ }
+ xtensa_free_reg_cache(target);
+ free(xtensa->hw_brps);
+ free(xtensa->hw_wps);
+ free(xtensa->sw_brps);
+}
+
+const char *xtensa_get_gdb_arch(struct target *target)
+{
+ return "xtensa";
+}
+
+COMMAND_HELPER(xtensa_cmd_permissive_mode_do, struct xtensa *xtensa)
+{
+ return CALL_COMMAND_HANDLER(handle_command_parse_bool,
+ &xtensa->permissive_mode, "xtensa permissive mode");
+}
+
+COMMAND_HANDLER(xtensa_cmd_permissive_mode)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_permissive_mode_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+/* perfmon_enable <counter_id> <select> [mask] [kernelcnt] [tracelevel] */
+COMMAND_HELPER(xtensa_cmd_perfmon_enable_do, struct xtensa *xtensa)
+{
+ struct xtensa_perfmon_config config = {
+ .mask = 0xffff,
+ .kernelcnt = 0,
+ .tracelevel = -1 /* use DEBUGLEVEL by default */
+ };
+
+ if (CMD_ARGC < 2 || CMD_ARGC > 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ unsigned int counter_id = strtoul(CMD_ARGV[0], NULL, 0);
+ if (counter_id >= XTENSA_MAX_PERF_COUNTERS) {
+ command_print(CMD, "counter_id should be < %d", XTENSA_MAX_PERF_COUNTERS);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ config.select = strtoul(CMD_ARGV[1], NULL, 0);
+ if (config.select > XTENSA_MAX_PERF_SELECT) {
+ command_print(CMD, "select should be < %d", XTENSA_MAX_PERF_SELECT);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ if (CMD_ARGC >= 3) {
+ config.mask = strtoul(CMD_ARGV[2], NULL, 0);
+ if (config.mask > XTENSA_MAX_PERF_MASK) {
+ command_print(CMD, "mask should be < %d", XTENSA_MAX_PERF_MASK);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ if (CMD_ARGC >= 4) {
+ config.kernelcnt = strtoul(CMD_ARGV[3], NULL, 0);
+ if (config.kernelcnt > 1) {
+ command_print(CMD, "kernelcnt should be 0 or 1");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ if (CMD_ARGC >= 5) {
+ config.tracelevel = strtoul(CMD_ARGV[4], NULL, 0);
+ if (config.tracelevel > 7) {
+ command_print(CMD, "tracelevel should be <=7");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ if (config.tracelevel == -1)
+ config.tracelevel = xtensa->core_config->debug.irq_level;
+
+ return xtensa_dm_perfmon_enable(&xtensa->dbg_mod, counter_id, &config);
+}
+
+COMMAND_HANDLER(xtensa_cmd_perfmon_enable)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+/* perfmon_dump [counter_id] */
+COMMAND_HELPER(xtensa_cmd_perfmon_dump_do, struct xtensa *xtensa)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ int counter_id = -1;
+ if (CMD_ARGC == 1) {
+ counter_id = strtol(CMD_ARGV[0], NULL, 0);
+ if (counter_id > XTENSA_MAX_PERF_COUNTERS) {
+ command_print(CMD, "counter_id should be < %d", XTENSA_MAX_PERF_COUNTERS);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ unsigned int counter_start = (counter_id < 0) ? 0 : counter_id;
+ unsigned int counter_end = (counter_id < 0) ? XTENSA_MAX_PERF_COUNTERS : counter_id + 1;
+ for (unsigned int counter = counter_start; counter < counter_end; ++counter) {
+ char result_buf[128] = { 0 };
+ size_t result_pos = snprintf(result_buf, sizeof(result_buf), "Counter %d: ", counter);
+ struct xtensa_perfmon_result result;
+ int res = xtensa_dm_perfmon_dump(&xtensa->dbg_mod, counter, &result);
+ if (res != ERROR_OK)
+ return res;
+ snprintf(result_buf + result_pos, sizeof(result_buf) - result_pos,
+ "%-12" PRIu64 "%s",
+ result.value,
+ result.overflow ? " (overflow)" : "");
+ LOG_INFO("%s", result_buf);
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_perfmon_dump)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_mask_interrupts_do, struct xtensa *xtensa)
+{
+ int state = -1;
+
+ if (CMD_ARGC < 1) {
+ const char *st;
+ state = xtensa->stepping_isr_mode;
+ if (state == XT_STEPPING_ISR_ON)
+ st = "OFF";
+ else if (state == XT_STEPPING_ISR_OFF)
+ st = "ON";
+ else
+ st = "UNKNOWN";
+ command_print(CMD, "Current ISR step mode: %s", st);
+ return ERROR_OK;
+ }
+ /* Masking is ON -> interrupts during stepping are OFF, and vice versa */
+ if (!strcasecmp(CMD_ARGV[0], "off"))
+ state = XT_STEPPING_ISR_ON;
+ else if (!strcasecmp(CMD_ARGV[0], "on"))
+ state = XT_STEPPING_ISR_OFF;
+
+ if (state == -1) {
+ command_print(CMD, "Argument unknown. Please pick one of ON, OFF");
+ return ERROR_FAIL;
+ }
+ xtensa->stepping_isr_mode = state;
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_mask_interrupts)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_smpbreak_do, struct target *target)
+{
+ int res = ERROR_OK;
+ uint32_t val = 0;
+
+ if (CMD_ARGC >= 1) {
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ if (!strcasecmp(CMD_ARGV[0], "none")) {
+ val = 0;
+ } else if (!strcasecmp(CMD_ARGV[i], "BreakIn")) {
+ val |= OCDDCR_BREAKINEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "BreakOut")) {
+ val |= OCDDCR_BREAKOUTEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "RunStallIn")) {
+ val |= OCDDCR_RUNSTALLINEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "DebugModeOut")) {
+ val |= OCDDCR_DEBUGMODEOUTEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "BreakInOut")) {
+ val |= OCDDCR_BREAKINEN | OCDDCR_BREAKOUTEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "RunStall")) {
+ val |= OCDDCR_RUNSTALLINEN | OCDDCR_DEBUGMODEOUTEN;
+ } else {
+ command_print(CMD, "Unknown arg %s", CMD_ARGV[i]);
+ command_print(
+ CMD,
+ "use either BreakInOut, None or RunStall as arguments, or any combination of BreakIn, BreakOut, RunStallIn and DebugModeOut.");
+ return ERROR_OK;
+ }
+ }
+ res = xtensa_smpbreak_set(target, val);
+ if (res != ERROR_OK)
+ command_print(CMD, "Failed to set smpbreak config %d", res);
+ } else {
+ struct xtensa *xtensa = target_to_xtensa(target);
+ res = xtensa_smpbreak_read(xtensa, &val);
+ if (res == ERROR_OK) {
+ command_print(CMD, "Current bits set:%s%s%s%s",
+ (val & OCDDCR_BREAKINEN) ? " BreakIn" : "",
+ (val & OCDDCR_BREAKOUTEN) ? " BreakOut" : "",
+ (val & OCDDCR_RUNSTALLINEN) ? " RunStallIn" : "",
+ (val & OCDDCR_DEBUGMODEOUTEN) ? " DebugModeOut" : ""
+ );
+ } else {
+ command_print(CMD, "Failed to get smpbreak config %d", res);
+ }
+ }
+ return res;
+}
+
+COMMAND_HANDLER(xtensa_cmd_smpbreak)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do,
+ get_current_target(CMD_CTX));
+}
+
+COMMAND_HELPER(xtensa_cmd_tracestart_do, struct xtensa *xtensa)
+{
+ struct xtensa_trace_status trace_status;
+ struct xtensa_trace_start_config cfg = {
+ .stoppc = 0,
+ .stopmask = XTENSA_STOPMASK_DISABLED,
+ .after = 0,
+ .after_is_words = false
+ };
+
+ /* Parse arguments */
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ if ((!strcasecmp(CMD_ARGV[i], "pc")) && CMD_ARGC > i) {
+ char *e;
+ i++;
+ cfg.stoppc = strtol(CMD_ARGV[i], &e, 0);
+ cfg.stopmask = 0;
+ if (*e == '/')
+ cfg.stopmask = strtol(e, NULL, 0);
+ } else if ((!strcasecmp(CMD_ARGV[i], "after")) && CMD_ARGC > i) {
+ i++;
+ cfg.after = strtol(CMD_ARGV[i], NULL, 0);
+ } else if (!strcasecmp(CMD_ARGV[i], "ins")) {
+ cfg.after_is_words = 0;
+ } else if (!strcasecmp(CMD_ARGV[i], "words")) {
+ cfg.after_is_words = 1;
+ } else {
+ command_print(CMD, "Did not understand %s", CMD_ARGV[i]);
+ return ERROR_FAIL;
+ }
+ }
+
+ int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+ if (trace_status.stat & TRAXSTAT_TRACT) {
+ LOG_WARNING("Silently stop active tracing!");
+ res = xtensa_dm_trace_stop(&xtensa->dbg_mod, false);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = xtensa_dm_trace_start(&xtensa->dbg_mod, &cfg);
+ if (res != ERROR_OK)
+ return res;
+
+ xtensa->trace_active = true;
+ command_print(CMD, "Trace started.");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_tracestart)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_tracestop_do, struct xtensa *xtensa)
+{
+ struct xtensa_trace_status trace_status;
+
+ int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+
+ if (!(trace_status.stat & TRAXSTAT_TRACT)) {
+ command_print(CMD, "No trace is currently active.");
+ return ERROR_FAIL;
+ }
+
+ res = xtensa_dm_trace_stop(&xtensa->dbg_mod, true);
+ if (res != ERROR_OK)
+ return res;
+
+ xtensa->trace_active = false;
+ command_print(CMD, "Trace stop triggered.");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_tracestop)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_tracedump_do, struct xtensa *xtensa, const char *fname)
+{
+ struct xtensa_trace_config trace_config;
+ struct xtensa_trace_status trace_status;
+ uint32_t memsz, wmem;
+
+ int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+
+ if (trace_status.stat & TRAXSTAT_TRACT) {
+ command_print(CMD, "Tracing is still active. Please stop it first.");
+ return ERROR_FAIL;
+ }
+
+ res = xtensa_dm_trace_config_read(&xtensa->dbg_mod, &trace_config);
+ if (res != ERROR_OK)
+ return res;
+
+ if (!(trace_config.ctrl & TRAXCTRL_TREN)) {
+ command_print(CMD, "No active trace found; nothing to dump.");
+ return ERROR_FAIL;
+ }
+
+ memsz = trace_config.memaddr_end - trace_config.memaddr_start + 1;
+ LOG_INFO("Total trace memory: %d words", memsz);
+ if ((trace_config.addr &
+ ((TRAXADDR_TWRAP_MASK << TRAXADDR_TWRAP_SHIFT) | TRAXADDR_TWSAT)) == 0) {
+ /*Memory hasn't overwritten itself yet. */
+ wmem = trace_config.addr & TRAXADDR_TADDR_MASK;
+ LOG_INFO("...but trace is only %d words", wmem);
+ if (wmem < memsz)
+ memsz = wmem;
+ } else {
+ if (trace_config.addr & TRAXADDR_TWSAT) {
+ LOG_INFO("Real trace is many times longer than that (overflow)");
+ } else {
+ uint32_t trc_sz = (trace_config.addr >> TRAXADDR_TWRAP_SHIFT) & TRAXADDR_TWRAP_MASK;
+ trc_sz = (trc_sz * memsz) + (trace_config.addr & TRAXADDR_TADDR_MASK);
+ LOG_INFO("Real trace is %d words, but the start has been truncated.", trc_sz);
+ }
+ }
+
+ uint8_t *tracemem = malloc(memsz * 4);
+ if (!tracemem) {
+ command_print(CMD, "Failed to alloc memory for trace data!");
+ return ERROR_FAIL;
+ }
+ res = xtensa_dm_trace_data_read(&xtensa->dbg_mod, tracemem, memsz * 4);
+ if (res != ERROR_OK) {
+ free(tracemem);
+ return res;
+ }
+
+ int f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (f <= 0) {
+ free(tracemem);
+ command_print(CMD, "Unable to open file %s", fname);
+ return ERROR_FAIL;
+ }
+ if (write(f, tracemem, memsz * 4) != (int)memsz * 4)
+ command_print(CMD, "Unable to write to file %s", fname);
+ else
+ command_print(CMD, "Written %d bytes of trace data to %s", memsz * 4, fname);
+ close(f);
+
+ bool is_all_zeroes = true;
+ for (unsigned int i = 0; i < memsz * 4; i++) {
+ if (tracemem[i] != 0) {
+ is_all_zeroes = false;
+ break;
+ }
+ }
+ free(tracemem);
+ if (is_all_zeroes)
+ command_print(
+ CMD,
+ "WARNING: File written is all zeroes. Are you sure you enabled trace memory?");
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_tracedump)
+{
+ if (CMD_ARGC != 1) {
+ command_print(CMD, "Command takes exactly 1 parameter.Need filename to dump to as output!");
+ return ERROR_FAIL;
+ }
+
+ return CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do,
+ target_to_xtensa(get_current_target(CMD_CTX)), CMD_ARGV[0]);
+}
+
+const struct command_registration xtensa_command_handlers[] = {
+ {
+ .name = "set_permissive",
+ .handler = xtensa_cmd_permissive_mode,
+ .mode = COMMAND_ANY,
+ .help = "When set to 1, enable Xtensa permissive mode (less client-side checks)",
+ .usage = "[0|1]",
+ },
+ {
+ .name = "maskisr",
+ .handler = xtensa_cmd_mask_interrupts,
+ .mode = COMMAND_ANY,
+ .help = "mask Xtensa interrupts at step",
+ .usage = "['on'|'off']",
+ },
+ {
+ .name = "smpbreak",
+ .handler = xtensa_cmd_smpbreak,
+ .mode = COMMAND_ANY,
+ .help = "Set the way the CPU chains OCD breaks",
+ .usage =
+ "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]",
+ },
+ {
+ .name = "perfmon_enable",
+ .handler = xtensa_cmd_perfmon_enable,
+ .mode = COMMAND_EXEC,
+ .help = "Enable and start performance counter",
+ .usage = "<counter_id> <select> [mask] [kernelcnt] [tracelevel]",
+ },
+ {
+ .name = "perfmon_dump",
+ .handler = xtensa_cmd_perfmon_dump,
+ .mode = COMMAND_EXEC,
+ .help =
+ "Dump performance counter value. If no argument specified, dumps all counters.",
+ .usage = "[counter_id]",
+ },
+ {
+ .name = "tracestart",
+ .handler = xtensa_cmd_tracestart,
+ .mode = COMMAND_EXEC,
+ .help =
+ "Tracing: Set up and start a trace. Optionally set stop trigger address and amount of data captured after.",
+ .usage = "[pc <pcval>/[maskbitcount]] [after <n> [ins|words]]",
+ },
+ {
+ .name = "tracestop",
+ .handler = xtensa_cmd_tracestop,
+ .mode = COMMAND_EXEC,
+ .help = "Tracing: Stop current trace as started by the tracestart command",
+ .usage = "",
+ },
+ {
+ .name = "tracedump",
+ .handler = xtensa_cmd_tracedump,
+ .mode = COMMAND_EXEC,
+ .help = "Tracing: Dump trace memory to a files. One file per core.",
+ .usage = "<outfile>",
+ },
+ COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/xtensa/xtensa.h b/src/target/xtensa/xtensa.h
new file mode 100644
index 0000000..d8b15e1
--- /dev/null
+++ b/src/target/xtensa/xtensa.h
@@ -0,0 +1,309 @@
+/***************************************************************************
+ * Generic Xtensa target *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_XTENSA_H
+#define OPENOCD_TARGET_XTENSA_H
+
+#include "assert.h"
+#include <target/target.h>
+#include <target/breakpoints.h>
+#include "xtensa_regs.h"
+#include "xtensa_debug_module.h"
+
+/**
+ * @file
+ * Holds the interface to Xtensa cores.
+ */
+
+#define XT_ISNS_SZ_MAX 3
+
+#define XT_PS_RING(_v_) ((uint32_t)((_v_) & 0x3) << 6)
+#define XT_PS_RING_MSK (0x3 << 6)
+#define XT_PS_RING_GET(_v_) (((_v_) >> 6) & 0x3)
+#define XT_PS_CALLINC_MSK (0x3 << 16)
+#define XT_PS_OWB_MSK (0xF << 8)
+
+#define XT_LOCAL_MEM_REGIONS_NUM_MAX 8
+
+#define XT_AREGS_NUM_MAX 64
+#define XT_USER_REGS_NUM_MAX 256
+
+#define XT_MEM_ACCESS_NONE 0x0
+#define XT_MEM_ACCESS_READ 0x1
+#define XT_MEM_ACCESS_WRITE 0x2
+
+enum xtensa_mem_err_detect {
+ XT_MEM_ERR_DETECT_NONE,
+ XT_MEM_ERR_DETECT_PARITY,
+ XT_MEM_ERR_DETECT_ECC,
+};
+
+struct xtensa_cache_config {
+ uint8_t way_count;
+ uint8_t line_size;
+ uint16_t size;
+ bool writeback;
+ enum xtensa_mem_err_detect mem_err_check;
+};
+
+struct xtensa_local_mem_region_config {
+ target_addr_t base;
+ uint32_t size;
+ enum xtensa_mem_err_detect mem_err_check;
+ int access;
+};
+
+struct xtensa_local_mem_config {
+ uint16_t count;
+ struct xtensa_local_mem_region_config regions[XT_LOCAL_MEM_REGIONS_NUM_MAX];
+};
+
+struct xtensa_mmu_config {
+ bool enabled;
+ uint8_t itlb_entries_count;
+ uint8_t dtlb_entries_count;
+ bool ivarway56;
+ bool dvarway56;
+};
+
+struct xtensa_exception_config {
+ bool enabled;
+ uint8_t depc_num;
+};
+
+struct xtensa_irq_config {
+ bool enabled;
+ uint8_t irq_num;
+};
+
+struct xtensa_high_prio_irq_config {
+ bool enabled;
+ uint8_t excm_level;
+ uint8_t nmi_num;
+};
+
+struct xtensa_debug_config {
+ bool enabled;
+ uint8_t irq_level;
+ uint8_t ibreaks_num;
+ uint8_t dbreaks_num;
+ uint8_t icount_sz;
+};
+
+struct xtensa_tracing_config {
+ bool enabled;
+ uint32_t mem_sz;
+ bool reversed_mem_access;
+};
+
+struct xtensa_timer_irq_config {
+ bool enabled;
+ uint8_t comp_num;
+};
+
+struct xtensa_config {
+ bool density;
+ uint8_t aregs_num;
+ bool windowed;
+ bool coproc;
+ bool fp_coproc;
+ bool loop;
+ uint8_t miscregs_num;
+ bool threadptr;
+ bool boolean;
+ bool cond_store;
+ bool ext_l32r;
+ bool mac16;
+ bool reloc_vec;
+ bool proc_id;
+ bool mem_err_check;
+ uint16_t user_regs_num;
+ const struct xtensa_user_reg_desc *user_regs;
+ int (*fetch_user_regs)(struct target *target);
+ int (*queue_write_dirty_user_regs)(struct target *target);
+ struct xtensa_cache_config icache;
+ struct xtensa_cache_config dcache;
+ struct xtensa_local_mem_config irom;
+ struct xtensa_local_mem_config iram;
+ struct xtensa_local_mem_config drom;
+ struct xtensa_local_mem_config dram;
+ struct xtensa_local_mem_config uram;
+ struct xtensa_local_mem_config xlmi;
+ struct xtensa_mmu_config mmu;
+ struct xtensa_exception_config exc;
+ struct xtensa_irq_config irq;
+ struct xtensa_high_prio_irq_config high_irq;
+ struct xtensa_timer_irq_config tim_irq;
+ struct xtensa_debug_config debug;
+ struct xtensa_tracing_config trace;
+ unsigned int gdb_general_regs_num;
+ const unsigned int *gdb_regs_mapping;
+};
+
+typedef uint32_t xtensa_insn_t;
+
+enum xtensa_stepping_isr_mode {
+ XT_STEPPING_ISR_OFF, /* interrupts are disabled during stepping */
+ XT_STEPPING_ISR_ON, /* interrupts are enabled during stepping */
+};
+
+/* Only supported in cores with in-CPU MMU. None of Espressif chips as of now. */
+enum xtensa_mode {
+ XT_MODE_RING0,
+ XT_MODE_RING1,
+ XT_MODE_RING2,
+ XT_MODE_RING3,
+ XT_MODE_ANY /* special value to run algorithm in current core mode */
+};
+
+struct xtensa_sw_breakpoint {
+ struct breakpoint *oocd_bp;
+ /* original insn */
+ uint8_t insn[XT_ISNS_SZ_MAX];
+ /* original insn size */
+ uint8_t insn_sz; /* 2 or 3 bytes */
+};
+
+#define XTENSA_COMMON_MAGIC 0x54E4E555U
+
+/**
+ * Represents a generic Xtensa core.
+ */
+struct xtensa {
+ unsigned int common_magic;
+ const struct xtensa_config *core_config;
+ struct xtensa_debug_module dbg_mod;
+ struct reg_cache *core_cache;
+ unsigned int regs_num;
+ /* An array of pointers to buffers to backup registers' values while algo is run on target.
+ * Size is 'regs_num'. */
+ void **algo_context_backup;
+ struct target *target;
+ bool reset_asserted;
+ enum xtensa_stepping_isr_mode stepping_isr_mode;
+ struct breakpoint **hw_brps;
+ struct watchpoint **hw_wps;
+ struct xtensa_sw_breakpoint *sw_brps;
+ bool trace_active;
+ bool permissive_mode; /* bypass memory checks */
+ bool suppress_dsr_errors;
+ uint32_t smp_break;
+ /* Sometimes debug module's 'powered' bit is cleared after reset, but get set after some
+ * time.This is the number of polling periods after which core is considered to be powered
+ * off (marked as unexamined) if the bit retains to be cleared (e.g. if core is disabled by
+ * SW running on target).*/
+ uint8_t come_online_probes_num;
+ bool regs_fetched; /* true after first register fetch completed successfully */
+};
+
+static inline struct xtensa *target_to_xtensa(struct target *target)
+{
+ assert(target);
+ struct xtensa *xtensa = target->arch_info;
+ assert(xtensa->common_magic == XTENSA_COMMON_MAGIC);
+ return xtensa;
+}
+
+int xtensa_init_arch_info(struct target *target,
+ struct xtensa *xtensa,
+ const struct xtensa_config *cfg,
+ const struct xtensa_debug_module_config *dm_cfg);
+int xtensa_target_init(struct command_context *cmd_ctx, struct target *target);
+void xtensa_target_deinit(struct target *target);
+
+static inline bool xtensa_addr_in_mem(const struct xtensa_local_mem_config *mem, uint32_t addr)
+{
+ for (unsigned int i = 0; i < mem->count; i++) {
+ if (addr >= mem->regions[i].base &&
+ addr < mem->regions[i].base + mem->regions[i].size)
+ return true;
+ }
+ return false;
+}
+
+static inline bool xtensa_data_addr_valid(struct target *target, uint32_t addr)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ if (xtensa_addr_in_mem(&xtensa->core_config->drom, addr))
+ return true;
+ if (xtensa_addr_in_mem(&xtensa->core_config->dram, addr))
+ return true;
+ if (xtensa_addr_in_mem(&xtensa->core_config->uram, addr))
+ return true;
+ return false;
+}
+
+int xtensa_core_status_check(struct target *target);
+
+int xtensa_examine(struct target *target);
+int xtensa_wakeup(struct target *target);
+int xtensa_smpbreak_set(struct target *target, uint32_t set);
+int xtensa_smpbreak_get(struct target *target, uint32_t *val);
+int xtensa_smpbreak_write(struct xtensa *xtensa, uint32_t set);
+int xtensa_smpbreak_read(struct xtensa *xtensa, uint32_t *val);
+xtensa_reg_val_t xtensa_reg_get(struct target *target, enum xtensa_reg_id reg_id);
+void xtensa_reg_set(struct target *target, enum xtensa_reg_id reg_id, xtensa_reg_val_t value);
+int xtensa_fetch_all_regs(struct target *target);
+int xtensa_get_gdb_reg_list(struct target *target,
+ struct reg **reg_list[],
+ int *reg_list_size,
+ enum target_register_class reg_class);
+int xtensa_poll(struct target *target);
+void xtensa_on_poll(struct target *target);
+int xtensa_halt(struct target *target);
+int xtensa_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution);
+int xtensa_prepare_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution);
+int xtensa_do_resume(struct target *target);
+int xtensa_step(struct target *target, int current, target_addr_t address, int handle_breakpoints);
+int xtensa_do_step(struct target *target, int current, target_addr_t address, int handle_breakpoints);
+int xtensa_mmu_is_enabled(struct target *target, int *enabled);
+int xtensa_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer);
+int xtensa_read_buffer(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer);
+int xtensa_write_memory(struct target *target,
+ target_addr_t address,
+ uint32_t size,
+ uint32_t count,
+ const uint8_t *buffer);
+int xtensa_write_buffer(struct target *target, target_addr_t address, uint32_t count, const uint8_t *buffer);
+int xtensa_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum);
+int xtensa_assert_reset(struct target *target);
+int xtensa_deassert_reset(struct target *target);
+int xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint);
+int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint);
+int xtensa_watchpoint_add(struct target *target, struct watchpoint *watchpoint);
+int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoint);
+void xtensa_set_permissive_mode(struct target *target, bool state);
+int xtensa_fetch_user_regs_u32(struct target *target);
+int xtensa_queue_write_dirty_user_regs_u32(struct target *target);
+const char *xtensa_get_gdb_arch(struct target *target);
+
+extern const struct reg_arch_type xtensa_user_reg_u32_type;
+extern const struct reg_arch_type xtensa_user_reg_u128_type;
+extern const struct command_registration xtensa_command_handlers[];
+
+#endif /* OPENOCD_TARGET_XTENSA_H */
diff --git a/src/target/xtensa/xtensa_debug_module.c b/src/target/xtensa/xtensa_debug_module.c
new file mode 100644
index 0000000..c6959ae
--- /dev/null
+++ b/src/target/xtensa/xtensa_debug_module.c
@@ -0,0 +1,359 @@
+/***************************************************************************
+ * Generic Xtensa debug module API for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xtensa_debug_module.h"
+
+#define TAPINS_PWRCTL 0x08
+#define TAPINS_PWRSTAT 0x09
+#define TAPINS_NARSEL 0x1C
+#define TAPINS_IDCODE 0x1E
+#define TAPINS_BYPASS 0x1F
+
+#define TAPINS_PWRCTL_LEN 8
+#define TAPINS_PWRSTAT_LEN 8
+#define TAPINS_NARSEL_ADRLEN 8
+#define TAPINS_NARSEL_DATALEN 32
+#define TAPINS_IDCODE_LEN 32
+#define TAPINS_BYPASS_LEN 1
+
+
+static void xtensa_dm_add_set_ir(struct xtensa_debug_module *dm, uint8_t value)
+{
+ struct scan_field field;
+ uint8_t t[4] = { 0 };
+
+ memset(&field, 0, sizeof(field));
+ field.num_bits = dm->tap->ir_length;
+ field.out_value = t;
+ buf_set_u32(t, 0, field.num_bits, value);
+ jtag_add_ir_scan(dm->tap, &field, TAP_IDLE);
+}
+
+static void xtensa_dm_add_dr_scan(struct xtensa_debug_module *dm,
+ int len,
+ const uint8_t *src,
+ uint8_t *dest,
+ tap_state_t endstate)
+{
+ struct scan_field field;
+
+ memset(&field, 0, sizeof(field));
+ field.num_bits = len;
+ field.out_value = src;
+ field.in_value = dest;
+ jtag_add_dr_scan(dm->tap, 1, &field, endstate);
+}
+
+int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_module_config *cfg)
+{
+ if (!dm || !cfg)
+ return ERROR_FAIL;
+
+ dm->pwr_ops = cfg->pwr_ops;
+ dm->dbg_ops = cfg->dbg_ops;
+ dm->tap = cfg->tap;
+ dm->queue_tdi_idle = cfg->queue_tdi_idle;
+ dm->queue_tdi_idle_arg = cfg->queue_tdi_idle_arg;
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_enable(struct xtensa_debug_module *dm)
+{
+ return dm->dbg_ops->queue_reg_write(dm, NARADR_DCRSET, OCDDCR_ENABLEOCD);
+}
+
+int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *value)
+{
+ uint8_t regdata = (reg << 1) | 0;
+ uint8_t dummy[4] = { 0, 0, 0, 0 };
+
+ if (reg > NARADR_MAX) {
+ LOG_ERROR("Invalid DBG reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, &regdata, NULL, TAP_IDLE);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, dummy, value, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint32_t value)
+{
+ uint8_t regdata = (reg << 1) | 1;
+ uint8_t valdata[] = { value, value >> 8, value >> 16, value >> 24 };
+
+ if (reg > NARADR_MAX) {
+ LOG_ERROR("Invalid DBG reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, &regdata, NULL, TAP_IDLE);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, valdata, NULL, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data, uint8_t clear)
+{
+ uint8_t value_clr = clear;
+ uint8_t tap_insn;
+ int tap_insn_sz;
+
+ if (reg == DMREG_PWRCTL) {
+ tap_insn = TAPINS_PWRCTL;
+ tap_insn_sz = TAPINS_PWRCTL_LEN;
+ } else if (reg == DMREG_PWRSTAT) {
+ tap_insn = TAPINS_PWRSTAT;
+ tap_insn_sz = TAPINS_PWRSTAT_LEN;
+ } else {
+ LOG_ERROR("Invalid PWR reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, tap_insn);
+ xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value_clr, data, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data)
+{
+ uint8_t value = data;
+ uint8_t tap_insn;
+ int tap_insn_sz;
+
+ if (reg == DMREG_PWRCTL) {
+ tap_insn = TAPINS_PWRCTL;
+ tap_insn_sz = TAPINS_PWRCTL_LEN;
+ } else if (reg == DMREG_PWRSTAT) {
+ tap_insn = TAPINS_PWRSTAT;
+ tap_insn_sz = TAPINS_PWRSTAT_LEN;
+ } else {
+ LOG_ERROR("Invalid PWR reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, tap_insn);
+ xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value, NULL, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_device_id_read(struct xtensa_debug_module *dm)
+{
+ uint8_t id_buf[sizeof(uint32_t)];
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_OCDID, id_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ dm->device_id = buf_get_u32(id_buf, 0, 32);
+ return ERROR_OK;
+}
+
+int xtensa_dm_power_status_read(struct xtensa_debug_module *dm, uint32_t clear)
+{
+ /* uint8_t id_buf[sizeof(uint32_t)]; */
+
+ /* TODO: JTAG does not work when PWRCTL_JTAGDEBUGUSE is not set.
+ * It is set in xtensa_examine(), need to move reading of NARADR_OCDID out of this function */
+ /* dm->dbg_ops->queue_reg_read(dm, NARADR_OCDID, id_buf);
+ *Read reset state */
+ dm->pwr_ops->queue_reg_read(dm, DMREG_PWRSTAT, &dm->power_status.stat, clear);
+ dm->pwr_ops->queue_reg_read(dm, DMREG_PWRSTAT, &dm->power_status.stath, clear);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_core_status_read(struct xtensa_debug_module *dm)
+{
+ uint8_t dsr_buf[sizeof(uint32_t)];
+
+ xtensa_dm_queue_enable(dm);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_DSR, dsr_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ dm->core_status.dsr = buf_get_u32(dsr_buf, 0, 32);
+ return res;
+}
+
+int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bits)
+{
+ dm->dbg_ops->queue_reg_write(dm, NARADR_DSR, bits);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg)
+{
+ /*Turn off trace unit so we can start a new trace. */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXCTRL, 0);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+
+ /*Set up parameters */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXADDR, 0);
+ if (cfg->stopmask != XTENSA_STOPMASK_DISABLED) {
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PCMATCHCTRL,
+ (cfg->stopmask << PCMATCHCTRL_PCML_SHIFT));
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRIGGERPC, cfg->stoppc);
+ }
+ dm->dbg_ops->queue_reg_write(dm, NARADR_DELAYCNT, cfg->after);
+ /*Options are mostly hardcoded for now. ToDo: make this more configurable. */
+ dm->dbg_ops->queue_reg_write(
+ dm,
+ NARADR_TRAXCTRL,
+ TRAXCTRL_TREN |
+ ((cfg->stopmask != XTENSA_STOPMASK_DISABLED) ? TRAXCTRL_PCMEN : 0) | TRAXCTRL_TMEN |
+ (cfg->after_is_words ? 0 : TRAXCTRL_CNTU) | (0 << TRAXCTRL_SMPER_SHIFT) | TRAXCTRL_PTOWS);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_trace_stop(struct xtensa_debug_module *dm, bool pto_enable)
+{
+ uint8_t traxctl_buf[sizeof(uint32_t)];
+ uint32_t traxctl;
+ struct xtensa_trace_status trace_status;
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXCTRL, traxctl_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ traxctl = buf_get_u32(traxctl_buf, 0, 32);
+
+ if (!pto_enable)
+ traxctl &= ~(TRAXCTRL_PTOWS | TRAXCTRL_PTOWT);
+
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXCTRL, traxctl | TRAXCTRL_TRSTP);
+ xtensa_dm_queue_tdi_idle(dm);
+ res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+
+ /*Check current status of trace hardware */
+ res = xtensa_dm_trace_status_read(dm, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+
+ if (trace_status.stat & TRAXSTAT_TRACT) {
+ LOG_ERROR("Failed to stop tracing (0x%x)!", trace_status.stat);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+int xtensa_dm_trace_status_read(struct xtensa_debug_module *dm, struct xtensa_trace_status *status)
+{
+ uint8_t traxstat_buf[sizeof(uint32_t)];
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXSTAT, traxstat_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK && status)
+ status->stat = buf_get_u32(traxstat_buf, 0, 32);
+ return res;
+}
+
+int xtensa_dm_trace_config_read(struct xtensa_debug_module *dm, struct xtensa_trace_config *config)
+{
+ uint8_t traxctl_buf[sizeof(uint32_t)];
+ uint8_t memadrstart_buf[sizeof(uint32_t)];
+ uint8_t memadrend_buf[sizeof(uint32_t)];
+ uint8_t adr_buf[sizeof(uint32_t)];
+
+ if (!config)
+ return ERROR_FAIL;
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXCTRL, traxctl_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_MEMADDRSTART, memadrstart_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_MEMADDREND, memadrend_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXADDR, adr_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK) {
+ config->ctrl = buf_get_u32(traxctl_buf, 0, 32);
+ config->memaddr_start = buf_get_u32(memadrstart_buf, 0, 32);
+ config->memaddr_end = buf_get_u32(memadrend_buf, 0, 32);
+ config->addr = buf_get_u32(adr_buf, 0, 32);
+ }
+ return res;
+}
+
+int xtensa_dm_trace_data_read(struct xtensa_debug_module *dm, uint8_t *dest, uint32_t size)
+{
+ if (!dest)
+ return ERROR_FAIL;
+
+ for (unsigned int i = 0; i < size / 4; i++)
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXDATA, &dest[i * 4]);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_perfmon_enable(struct xtensa_debug_module *dm, int counter_id,
+ const struct xtensa_perfmon_config *config)
+{
+ if (!config)
+ return ERROR_FAIL;
+
+ uint8_t pmstat_buf[4];
+ uint32_t pmctrl = ((config->tracelevel) << 4) +
+ (config->select << 8) +
+ (config->mask << 16) +
+ (config->kernelcnt << 3);
+
+ /* enable performance monitor */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PMG, 0x1);
+ /* reset counter */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PM0 + counter_id, 0);
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PMCTRL0 + counter_id, pmctrl);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_PMSTAT0 + counter_id, pmstat_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_perfmon_dump(struct xtensa_debug_module *dm, int counter_id,
+ struct xtensa_perfmon_result *out_result)
+{
+ uint8_t pmstat_buf[4];
+ uint8_t pmcount_buf[4];
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_PMSTAT0 + counter_id, pmstat_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_PM0 + counter_id, pmcount_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK) {
+ uint32_t stat = buf_get_u32(pmstat_buf, 0, 32);
+ uint64_t result = buf_get_u32(pmcount_buf, 0, 32);
+
+ /* TODO: if counter # counter_id+1 has 'select' set to 1, use its value as the
+ * high 32 bits of the counter. */
+ if (out_result) {
+ out_result->overflow = ((stat & 1) != 0);
+ out_result->value = result;
+ }
+ }
+
+ return res;
+}
diff --git a/src/target/xtensa/xtensa_debug_module.h b/src/target/xtensa/xtensa_debug_module.h
new file mode 100644
index 0000000..692f0f6
--- /dev/null
+++ b/src/target/xtensa/xtensa_debug_module.h
@@ -0,0 +1,385 @@
+/***************************************************************************
+ * Xtensa debug module API *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * <alexey@espressif.com> *
+ * *
+ * Derived from original ESP8266 target. *
+ * Copyright (C) 2015 by Angus Gratton *
+ * gus@projectgus.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H
+#define OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H
+
+#include <jtag/jtag.h>
+#include <helper/bits.h>
+#include <target/target.h>
+
+/* Virtual IDs for using with xtensa_power_ops API */
+#define DMREG_PWRCTL 0x00
+#define DMREG_PWRSTAT 0x01
+
+/*
+ From the manual:
+ To properly use Debug registers through JTAG, software must ensure that:
+ - Tap is out of reset
+ - Xtensa Debug Module is out of reset
+ - Other bits of PWRCTL are set to their desired values, and finally
+ - JtagDebugUse transitions from 0 to 1
+ The bit must continue to be 1 in order for JTAG accesses to the Debug
+ Module to happen correctly. When it is set, any write to this bit clears it.
+ Either don't access it, or re-write it to 1 so JTAG accesses continue.
+*/
+#define PWRCTL_JTAGDEBUGUSE BIT(7)
+#define PWRCTL_DEBUGRESET BIT(6)
+#define PWRCTL_CORERESET BIT(4)
+#define PWRCTL_DEBUGWAKEUP BIT(2)
+#define PWRCTL_MEMWAKEUP BIT(1)
+#define PWRCTL_COREWAKEUP BIT(0)
+
+#define PWRSTAT_DEBUGWASRESET BIT(6)
+#define PWRSTAT_COREWASRESET BIT(4)
+#define PWRSTAT_CORESTILLNEEDED BIT(3)
+#define PWRSTAT_DEBUGDOMAINON BIT(2)
+#define PWRSTAT_MEMDOMAINON BIT(1)
+#define PWRSTAT_COREDOMAINON BIT(0)
+
+/* *** NAR addresses (also used as IDs for debug registers in xtensa_debug_ops API) ***
+ *TRAX registers */
+#define NARADR_TRAXID 0x00
+#define NARADR_TRAXCTRL 0x01
+#define NARADR_TRAXSTAT 0x02
+#define NARADR_TRAXDATA 0x03
+#define NARADR_TRAXADDR 0x04
+#define NARADR_TRIGGERPC 0x05
+#define NARADR_PCMATCHCTRL 0x06
+#define NARADR_DELAYCNT 0x07
+#define NARADR_MEMADDRSTART 0x08
+#define NARADR_MEMADDREND 0x09
+/*Performance monitor registers */
+#define NARADR_PMG 0x20
+#define NARADR_INTPC 0x24
+#define NARADR_PM0 0x28
+/*... */
+#define NARADR_PM7 0x2F
+#define NARADR_PMCTRL0 0x30
+/*... */
+#define NARADR_PMCTRL7 0x37
+#define NARADR_PMSTAT0 0x38
+/*... */
+#define NARADR_PMSTAT7 0x3F
+/*OCD registers */
+#define NARADR_OCDID 0x40
+#define NARADR_DCRCLR 0x42
+#define NARADR_DCRSET 0x43
+#define NARADR_DSR 0x44
+#define NARADR_DDR 0x45
+#define NARADR_DDREXEC 0x46
+#define NARADR_DIR0EXEC 0x47
+#define NARADR_DIR0 0x48
+#define NARADR_DIR1 0x49
+/*... */
+#define NARADR_DIR7 0x4F
+/*Misc registers */
+#define NARADR_PWRCTL 0x58
+#define NARADR_PWRSTAT 0x59
+#define NARADR_ERISTAT 0x5A
+/*CoreSight registers */
+#define NARADR_ITCTRL 0x60
+#define NARADR_CLAIMSET 0x68
+#define NARADR_CLAIMCLR 0x69
+#define NARADR_LOCKACCESS 0x6c
+#define NARADR_LOCKSTATUS 0x6d
+#define NARADR_AUTHSTATUS 0x6e
+#define NARADR_DEVID 0x72
+#define NARADR_DEVTYPE 0x73
+#define NARADR_PERID4 0x74
+/*... */
+#define NARADR_PERID7 0x77
+#define NARADR_PERID0 0x78
+/*... */
+#define NARADR_PERID3 0x7b
+#define NARADR_COMPID0 0x7c
+/*... */
+#define NARADR_COMPID3 0x7f
+#define NARADR_MAX NARADR_COMPID3
+
+/*OCD registers, bit definitions */
+#define OCDDCR_ENABLEOCD BIT(0)
+#define OCDDCR_DEBUGINTERRUPT BIT(1)
+#define OCDDCR_INTERRUPTALLCONDS BIT(2)
+#define OCDDCR_BREAKINEN BIT(16)
+#define OCDDCR_BREAKOUTEN BIT(17)
+#define OCDDCR_DEBUGSWACTIVE BIT(20)
+#define OCDDCR_RUNSTALLINEN BIT(21)
+#define OCDDCR_DEBUGMODEOUTEN BIT(22)
+#define OCDDCR_BREAKOUTITO BIT(24)
+#define OCDDCR_BREAKACKITO BIT(25)
+
+#define OCDDSR_EXECDONE BIT(0)
+#define OCDDSR_EXECEXCEPTION BIT(1)
+#define OCDDSR_EXECBUSY BIT(2)
+#define OCDDSR_EXECOVERRUN BIT(3)
+#define OCDDSR_STOPPED BIT(4)
+#define OCDDSR_COREWROTEDDR BIT(10)
+#define OCDDSR_COREREADDDR BIT(11)
+#define OCDDSR_HOSTWROTEDDR BIT(14)
+#define OCDDSR_HOSTREADDDR BIT(15)
+#define OCDDSR_DEBUGPENDBREAK BIT(16)
+#define OCDDSR_DEBUGPENDHOST BIT(17)
+#define OCDDSR_DEBUGPENDTRAX BIT(18)
+#define OCDDSR_DEBUGINTBREAK BIT(20)
+#define OCDDSR_DEBUGINTHOST BIT(21)
+#define OCDDSR_DEBUGINTTRAX BIT(22)
+#define OCDDSR_RUNSTALLTOGGLE BIT(23)
+#define OCDDSR_RUNSTALLSAMPLE BIT(24)
+#define OCDDSR_BREACKOUTACKITI BIT(25)
+#define OCDDSR_BREAKINITI BIT(26)
+#define OCDDSR_DBGMODPOWERON BIT(31)
+
+#define DEBUGCAUSE_IC BIT(0) /* ICOUNT exception */
+#define DEBUGCAUSE_IB BIT(1) /* IBREAK exception */
+#define DEBUGCAUSE_DB BIT(2) /* DBREAK exception */
+#define DEBUGCAUSE_BI BIT(3) /* BREAK instruction encountered */
+#define DEBUGCAUSE_BN BIT(4) /* BREAK.N instruction encountered */
+#define DEBUGCAUSE_DI BIT(5) /* Debug Interrupt */
+
+#define TRAXCTRL_TREN BIT(0) /* Trace enable. Tracing starts on 0->1 */
+#define TRAXCTRL_TRSTP BIT(1) /* Trace Stop. Make 1 to stop trace. */
+#define TRAXCTRL_PCMEN BIT(2) /* PC match enable */
+#define TRAXCTRL_PTIEN BIT(4) /* Processor-trigger enable */
+#define TRAXCTRL_CTIEN BIT(5) /* Cross-trigger enable */
+#define TRAXCTRL_TMEN BIT(7) /* Tracemem Enable. Always set. */
+#define TRAXCTRL_CNTU BIT(9) /* Post-stop-trigger countdown units; selects when DelayCount-- happens.
+ *0 - every 32-bit word written to tracemem, 1 - every cpu instruction */
+#define TRAXCTRL_TSEN BIT(11) /* Undocumented/deprecated? */
+#define TRAXCTRL_SMPER_SHIFT 12 /* Send sync every 2^(9-smper) messages. 7=reserved, 0=no sync msg */
+#define TRAXCTRL_SMPER_MASK 0x07 /* Synchronization message period */
+#define TRAXCTRL_PTOWT BIT(16) /* Processor Trigger Out (OCD halt) enabled when stop triggered */
+#define TRAXCTRL_PTOWS BIT(17) /* Processor Trigger Out (OCD halt) enabled when trace stop completes */
+#define TRAXCTRL_CTOWT BIT(20) /* Cross-trigger Out enabled when stop triggered */
+#define TRAXCTRL_CTOWS BIT(21) /* Cross-trigger Out enabled when trace stop completes */
+#define TRAXCTRL_ITCTO BIT(22) /* Integration mode: cross-trigger output */
+#define TRAXCTRL_ITCTIA BIT(23) /* Integration mode: cross-trigger ack */
+#define TRAXCTRL_ITATV BIT(24) /* replaces ATID when in integration mode: ATVALID output */
+#define TRAXCTRL_ATID_MASK 0x7F /* ARB source ID */
+#define TRAXCTRL_ATID_SHIFT 24
+#define TRAXCTRL_ATEN BIT(31) /* ATB interface enable */
+
+#define TRAXSTAT_TRACT BIT(0) /* Trace active flag. */
+#define TRAXSTAT_TRIG BIT(1) /* Trace stop trigger. Clears on TREN 1->0 */
+#define TRAXSTAT_PCMTG BIT(2) /* Stop trigger caused by PC match. Clears on TREN 1->0 */
+#define TRAXSTAT_PJTR BIT(3) /* JTAG transaction result. 1=err in preceding jtag transaction. */
+#define TRAXSTAT_PTITG BIT(4) /* Stop trigger caused by Processor Trigger Input.Clears on TREN 1->0 */
+#define TRAXSTAT_CTITG BIT(5) /* Stop trigger caused by Cross-Trigger Input. Clears on TREN 1->0 */
+#define TRAXSTAT_MEMSZ_SHIFT 8 /* Traceram size inducator. Usable trace ram is 2^MEMSZ bytes. */
+#define TRAXSTAT_MEMSZ_MASK 0x1F
+#define TRAXSTAT_PTO BIT(16) /* Processor Trigger Output: current value */
+#define TRAXSTAT_CTO BIT(17) /* Cross-Trigger Output: current value */
+#define TRAXSTAT_ITCTOA BIT(22) /* Cross-Trigger Out Ack: current value */
+#define TRAXSTAT_ITCTI BIT(23) /* Cross-Trigger Input: current value */
+#define TRAXSTAT_ITATR BIT(24) /* ATREADY Input: current value */
+
+#define TRAXADDR_TADDR_SHIFT 0 /* Trax memory address, in 32-bit words. */
+#define TRAXADDR_TADDR_MASK 0x1FFFFF /* Actually is only as big as the trace buffer size max addr. */
+#define TRAXADDR_TWRAP_SHIFT 21 /* Amount of times TADDR has overflown */
+#define TRAXADDR_TWRAP_MASK 0x3FF
+#define TRAXADDR_TWSAT BIT(31) /* 1 if TWRAP has overflown, clear by disabling tren.*/
+
+#define PCMATCHCTRL_PCML_SHIFT 0 /* Amount of lower bits to ignore in pc trigger register */
+#define PCMATCHCTRL_PCML_MASK 0x1F
+#define PCMATCHCTRL_PCMS BIT(31) /* PC Match Sense, 0-match when procs PC is in-range, 1-match when
+ *out-of-range */
+
+#define XTENSA_MAX_PERF_COUNTERS 2
+#define XTENSA_MAX_PERF_SELECT 32
+#define XTENSA_MAX_PERF_MASK 0xffff
+
+#define XTENSA_STOPMASK_DISABLED UINT32_MAX
+
+struct xtensa_debug_module;
+
+struct xtensa_debug_ops {
+ /** enable operation */
+ int (*queue_enable)(struct xtensa_debug_module *dm);
+ /** register read. */
+ int (*queue_reg_read)(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data);
+ /** register write. */
+ int (*queue_reg_write)(struct xtensa_debug_module *dm, unsigned int reg, uint32_t data);
+};
+
+struct xtensa_power_ops {
+ /** register read. */
+ int (*queue_reg_read)(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data,
+ uint8_t clear);
+ /** register write. */
+ int (*queue_reg_write)(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data);
+};
+
+typedef uint8_t xtensa_pwrstat_t;
+typedef uint32_t xtensa_ocdid_t;
+typedef uint32_t xtensa_dsr_t;
+typedef uint32_t xtensa_traxstat_t;
+
+struct xtensa_power_status {
+ xtensa_pwrstat_t stat;
+ xtensa_pwrstat_t stath;
+ /* TODO: do not need to keep previous status to detect that core or debug module has been
+ * reset, */
+ /* we can clear PWRSTAT_DEBUGWASRESET and PWRSTAT_COREWASRESET after reading will do
+ * the job; */
+ /* upon next reet those bits will be set again. So we can get rid of
+ * xtensa_dm_power_status_cache_reset() and xtensa_dm_power_status_cache(). */
+ xtensa_pwrstat_t prev_stat;
+};
+
+struct xtensa_core_status {
+ xtensa_dsr_t dsr;
+};
+
+struct xtensa_trace_config {
+ uint32_t ctrl;
+ uint32_t memaddr_start;
+ uint32_t memaddr_end;
+ uint32_t addr;
+};
+
+struct xtensa_trace_status {
+ xtensa_traxstat_t stat;
+};
+
+struct xtensa_trace_start_config {
+ uint32_t stoppc;
+ bool after_is_words;
+ uint32_t after;
+ uint32_t stopmask; /* UINT32_MAX: disable PC match option */
+};
+
+struct xtensa_perfmon_config {
+ int select;
+ uint32_t mask;
+ int kernelcnt;
+ int tracelevel;
+};
+
+struct xtensa_perfmon_result {
+ uint64_t value;
+ bool overflow;
+};
+
+struct xtensa_debug_module_config {
+ const struct xtensa_power_ops *pwr_ops;
+ const struct xtensa_debug_ops *dbg_ops;
+ struct jtag_tap *tap;
+ void (*queue_tdi_idle)(struct target *target);
+ void *queue_tdi_idle_arg;
+};
+
+struct xtensa_debug_module {
+ const struct xtensa_power_ops *pwr_ops;
+ const struct xtensa_debug_ops *dbg_ops;
+ struct jtag_tap *tap;
+ void (*queue_tdi_idle)(struct target *target);
+ void *queue_tdi_idle_arg;
+
+ struct xtensa_power_status power_status;
+ struct xtensa_core_status core_status;
+ xtensa_ocdid_t device_id;
+};
+
+int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_module_config *cfg);
+int xtensa_dm_queue_enable(struct xtensa_debug_module *dm);
+int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *value);
+int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint32_t value);
+int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data, uint8_t clear);
+int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data);
+
+static inline void xtensa_dm_queue_tdi_idle(struct xtensa_debug_module *dm)
+{
+ if (dm->queue_tdi_idle)
+ dm->queue_tdi_idle(dm->queue_tdi_idle_arg);
+}
+
+int xtensa_dm_power_status_read(struct xtensa_debug_module *dm, uint32_t clear);
+static inline void xtensa_dm_power_status_cache_reset(struct xtensa_debug_module *dm)
+{
+ dm->power_status.prev_stat = 0;
+}
+static inline void xtensa_dm_power_status_cache(struct xtensa_debug_module *dm)
+{
+ dm->power_status.prev_stat = dm->power_status.stath;
+}
+static inline xtensa_pwrstat_t xtensa_dm_power_status_get(struct xtensa_debug_module *dm)
+{
+ return dm->power_status.stat;
+}
+
+int xtensa_dm_core_status_read(struct xtensa_debug_module *dm);
+int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bits);
+int xtensa_dm_core_status_check(struct xtensa_debug_module *dm);
+static inline xtensa_dsr_t xtensa_dm_core_status_get(struct xtensa_debug_module *dm)
+{
+ return dm->core_status.dsr;
+}
+
+int xtensa_dm_device_id_read(struct xtensa_debug_module *dm);
+static inline xtensa_ocdid_t xtensa_dm_device_id_get(struct xtensa_debug_module *dm)
+{
+ return dm->device_id;
+}
+
+int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg);
+int xtensa_dm_trace_stop(struct xtensa_debug_module *dm, bool pto_enable);
+int xtensa_dm_trace_config_read(struct xtensa_debug_module *dm, struct xtensa_trace_config *config);
+int xtensa_dm_trace_status_read(struct xtensa_debug_module *dm, struct xtensa_trace_status *status);
+int xtensa_dm_trace_data_read(struct xtensa_debug_module *dm, uint8_t *dest, uint32_t size);
+
+static inline bool xtensa_dm_is_online(struct xtensa_debug_module *dm)
+{
+ int res = xtensa_dm_device_id_read(dm);
+ if (res != ERROR_OK)
+ return false;
+ return (dm->device_id != 0xffffffff && dm->device_id != 0);
+}
+
+static inline bool xtensa_dm_tap_was_reset(struct xtensa_debug_module *dm)
+{
+ return !(dm->power_status.prev_stat & PWRSTAT_DEBUGWASRESET) &&
+ dm->power_status.stat & PWRSTAT_DEBUGWASRESET;
+}
+
+static inline bool xtensa_dm_core_was_reset(struct xtensa_debug_module *dm)
+{
+ return !(dm->power_status.prev_stat & PWRSTAT_COREWASRESET) &&
+ dm->power_status.stat & PWRSTAT_COREWASRESET;
+}
+
+static inline bool xtensa_dm_core_is_stalled(struct xtensa_debug_module *dm)
+{
+ return dm->core_status.dsr & OCDDSR_RUNSTALLSAMPLE;
+}
+
+static inline bool xtensa_dm_is_powered(struct xtensa_debug_module *dm)
+{
+ return dm->core_status.dsr & OCDDSR_DBGMODPOWERON;
+}
+
+int xtensa_dm_perfmon_enable(struct xtensa_debug_module *dm, int counter_id,
+ const struct xtensa_perfmon_config *config);
+int xtensa_dm_perfmon_dump(struct xtensa_debug_module *dm, int counter_id,
+ struct xtensa_perfmon_result *out_result);
+
+#endif /* OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H */
diff --git a/src/target/xtensa/xtensa_regs.h b/src/target/xtensa/xtensa_regs.h
new file mode 100644
index 0000000..7602131
--- /dev/null
+++ b/src/target/xtensa/xtensa_regs.h
@@ -0,0 +1,278 @@
+/***************************************************************************
+ * Generic Xtensa target API for OpenOCD *
+ * Copyright (C) 2016-2019 Espressif Systems Ltd. *
+ * Author: Angus Gratton gus@projectgus.com *
+ * Author: Jeroen Domburg <jeroen@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+#ifndef OPENOCD_TARGET_XTENSA_REGS_H
+#define OPENOCD_TARGET_XTENSA_REGS_H
+
+struct reg_arch_type;
+
+enum xtensa_reg_id {
+ XT_REG_IDX_PC = 0,
+ XT_REG_IDX_AR0,
+ XT_REG_IDX_AR1,
+ XT_REG_IDX_AR2,
+ XT_REG_IDX_AR3,
+ XT_REG_IDX_AR4,
+ XT_REG_IDX_AR5,
+ XT_REG_IDX_AR6,
+ XT_REG_IDX_AR7,
+ XT_REG_IDX_AR8,
+ XT_REG_IDX_AR9,
+ XT_REG_IDX_AR10,
+ XT_REG_IDX_AR11,
+ XT_REG_IDX_AR12,
+ XT_REG_IDX_AR13,
+ XT_REG_IDX_AR14,
+ XT_REG_IDX_AR15,
+ XT_REG_IDX_AR16,
+ XT_REG_IDX_AR17,
+ XT_REG_IDX_AR18,
+ XT_REG_IDX_AR19,
+ XT_REG_IDX_AR20,
+ XT_REG_IDX_AR21,
+ XT_REG_IDX_AR22,
+ XT_REG_IDX_AR23,
+ XT_REG_IDX_AR24,
+ XT_REG_IDX_AR25,
+ XT_REG_IDX_AR26,
+ XT_REG_IDX_AR27,
+ XT_REG_IDX_AR28,
+ XT_REG_IDX_AR29,
+ XT_REG_IDX_AR30,
+ XT_REG_IDX_AR31,
+ XT_REG_IDX_AR32,
+ XT_REG_IDX_AR33,
+ XT_REG_IDX_AR34,
+ XT_REG_IDX_AR35,
+ XT_REG_IDX_AR36,
+ XT_REG_IDX_AR37,
+ XT_REG_IDX_AR38,
+ XT_REG_IDX_AR39,
+ XT_REG_IDX_AR40,
+ XT_REG_IDX_AR41,
+ XT_REG_IDX_AR42,
+ XT_REG_IDX_AR43,
+ XT_REG_IDX_AR44,
+ XT_REG_IDX_AR45,
+ XT_REG_IDX_AR46,
+ XT_REG_IDX_AR47,
+ XT_REG_IDX_AR48,
+ XT_REG_IDX_AR49,
+ XT_REG_IDX_AR50,
+ XT_REG_IDX_AR51,
+ XT_REG_IDX_AR52,
+ XT_REG_IDX_AR53,
+ XT_REG_IDX_AR54,
+ XT_REG_IDX_AR55,
+ XT_REG_IDX_AR56,
+ XT_REG_IDX_AR57,
+ XT_REG_IDX_AR58,
+ XT_REG_IDX_AR59,
+ XT_REG_IDX_AR60,
+ XT_REG_IDX_AR61,
+ XT_REG_IDX_AR62,
+ XT_REG_IDX_AR63,
+ XT_REG_IDX_LBEG,
+ XT_REG_IDX_LEND,
+ XT_REG_IDX_LCOUNT,
+ XT_REG_IDX_SAR,
+ XT_REG_IDX_WINDOWBASE,
+ XT_REG_IDX_WINDOWSTART,
+ XT_REG_IDX_CONFIGID0,
+ XT_REG_IDX_CONFIGID1,
+ XT_REG_IDX_PS,
+ XT_REG_IDX_THREADPTR,
+ XT_REG_IDX_BR,
+ XT_REG_IDX_SCOMPARE1,
+ XT_REG_IDX_ACCLO,
+ XT_REG_IDX_ACCHI,
+ XT_REG_IDX_M0,
+ XT_REG_IDX_M1,
+ XT_REG_IDX_M2,
+ XT_REG_IDX_M3,
+ XT_REG_IDX_F0,
+ XT_REG_IDX_F1,
+ XT_REG_IDX_F2,
+ XT_REG_IDX_F3,
+ XT_REG_IDX_F4,
+ XT_REG_IDX_F5,
+ XT_REG_IDX_F6,
+ XT_REG_IDX_F7,
+ XT_REG_IDX_F8,
+ XT_REG_IDX_F9,
+ XT_REG_IDX_F10,
+ XT_REG_IDX_F11,
+ XT_REG_IDX_F12,
+ XT_REG_IDX_F13,
+ XT_REG_IDX_F14,
+ XT_REG_IDX_F15,
+ XT_REG_IDX_FCR,
+ XT_REG_IDX_FSR,
+ XT_REG_IDX_MMID,
+ XT_REG_IDX_IBREAKENABLE,
+ XT_REG_IDX_MEMCTL,
+ XT_REG_IDX_ATOMCTL,
+ XT_REG_IDX_IBREAKA0,
+ XT_REG_IDX_IBREAKA1,
+ XT_REG_IDX_DBREAKA0,
+ XT_REG_IDX_DBREAKA1,
+ XT_REG_IDX_DBREAKC0,
+ XT_REG_IDX_DBREAKC1,
+ XT_REG_IDX_EPC1,
+ XT_REG_IDX_EPC2,
+ XT_REG_IDX_EPC3,
+ XT_REG_IDX_EPC4,
+ XT_REG_IDX_EPC5,
+ XT_REG_IDX_EPC6,
+ XT_REG_IDX_EPC7,
+ XT_REG_IDX_DEPC,
+ XT_REG_IDX_EPS2,
+ XT_REG_IDX_EPS3,
+ XT_REG_IDX_EPS4,
+ XT_REG_IDX_EPS5,
+ XT_REG_IDX_EPS6,
+ XT_REG_IDX_EPS7,
+ XT_REG_IDX_EXCSAVE1,
+ XT_REG_IDX_EXCSAVE2,
+ XT_REG_IDX_EXCSAVE3,
+ XT_REG_IDX_EXCSAVE4,
+ XT_REG_IDX_EXCSAVE5,
+ XT_REG_IDX_EXCSAVE6,
+ XT_REG_IDX_EXCSAVE7,
+ XT_REG_IDX_CPENABLE,
+ XT_REG_IDX_INTERRUPT,
+ XT_REG_IDX_INTSET,
+ XT_REG_IDX_INTCLEAR,
+ XT_REG_IDX_INTENABLE,
+ XT_REG_IDX_VECBASE,
+ XT_REG_IDX_EXCCAUSE,
+ XT_REG_IDX_DEBUGCAUSE,
+ XT_REG_IDX_CCOUNT,
+ XT_REG_IDX_PRID,
+ XT_REG_IDX_ICOUNT,
+ XT_REG_IDX_ICOUNTLEVEL,
+ XT_REG_IDX_EXCVADDR,
+ XT_REG_IDX_CCOMPARE0,
+ XT_REG_IDX_CCOMPARE1,
+ XT_REG_IDX_CCOMPARE2,
+ XT_REG_IDX_MISC0,
+ XT_REG_IDX_MISC1,
+ XT_REG_IDX_MISC2,
+ XT_REG_IDX_MISC3,
+ XT_REG_IDX_LITBASE,
+ XT_REG_IDX_PTEVADDR,
+ XT_REG_IDX_RASID,
+ XT_REG_IDX_ITLBCFG,
+ XT_REG_IDX_DTLBCFG,
+ XT_REG_IDX_MEPC,
+ XT_REG_IDX_MEPS,
+ XT_REG_IDX_MESAVE,
+ XT_REG_IDX_MESR,
+ XT_REG_IDX_MECR,
+ XT_REG_IDX_MEVADDR,
+ XT_REG_IDX_A0,
+ XT_REG_IDX_A1,
+ XT_REG_IDX_A2,
+ XT_REG_IDX_A3,
+ XT_REG_IDX_A4,
+ XT_REG_IDX_A5,
+ XT_REG_IDX_A6,
+ XT_REG_IDX_A7,
+ XT_REG_IDX_A8,
+ XT_REG_IDX_A9,
+ XT_REG_IDX_A10,
+ XT_REG_IDX_A11,
+ XT_REG_IDX_A12,
+ XT_REG_IDX_A13,
+ XT_REG_IDX_A14,
+ XT_REG_IDX_A15,
+ XT_REG_IDX_PWRCTL,
+ XT_REG_IDX_PWRSTAT,
+ XT_REG_IDX_ERISTAT,
+ XT_REG_IDX_CS_ITCTRL,
+ XT_REG_IDX_CS_CLAIMSET,
+ XT_REG_IDX_CS_CLAIMCLR,
+ XT_REG_IDX_CS_LOCKACCESS,
+ XT_REG_IDX_CS_LOCKSTATUS,
+ XT_REG_IDX_CS_AUTHSTATUS,
+ XT_REG_IDX_FAULT_INFO,
+ XT_REG_IDX_TRAX_ID,
+ XT_REG_IDX_TRAX_CTRL,
+ XT_REG_IDX_TRAX_STAT,
+ XT_REG_IDX_TRAX_DATA,
+ XT_REG_IDX_TRAX_ADDR,
+ XT_REG_IDX_TRAX_PCTRIGGER,
+ XT_REG_IDX_TRAX_PCMATCH,
+ XT_REG_IDX_TRAX_DELAY,
+ XT_REG_IDX_TRAX_MEMSTART,
+ XT_REG_IDX_TRAX_MEMEND,
+ XT_REG_IDX_PMG,
+ XT_REG_IDX_PMPC,
+ XT_REG_IDX_PM0,
+ XT_REG_IDX_PM1,
+ XT_REG_IDX_PMCTRL0,
+ XT_REG_IDX_PMCTRL1,
+ XT_REG_IDX_PMSTAT0,
+ XT_REG_IDX_PMSTAT1,
+ XT_REG_IDX_OCD_ID,
+ XT_REG_IDX_OCD_DCRCLR,
+ XT_REG_IDX_OCD_DCRSET,
+ XT_REG_IDX_OCD_DSR,
+ XT_REG_IDX_OCD_DDR,
+ XT_NUM_REGS,
+ /* chip-specific user registers go after ISA-defined ones */
+ XT_USR_REG_START = XT_NUM_REGS
+};
+
+typedef uint32_t xtensa_reg_val_t;
+
+enum xtensa_reg_type {
+ XT_REG_GENERAL = 0, /* General-purpose register; part of the windowed register set */
+ XT_REG_USER = 1, /* User register, needs RUR to read */
+ XT_REG_SPECIAL = 2, /* Special register, needs RSR to read */
+ XT_REG_DEBUG = 3, /* Register used for the debug interface. Don't mess with this. */
+ XT_REG_RELGEN = 4, /* Relative general address. Points to the absolute addresses plus the window
+ *index */
+ XT_REG_FR = 5, /* Floating-point register */
+};
+
+enum xtensa_reg_flags {
+ XT_REGF_NOREAD = 0x01, /* Register is write-only */
+ XT_REGF_COPROC0 = 0x02 /* Can't be read if coproc0 isn't enabled */
+};
+
+struct xtensa_reg_desc {
+ const char *name;
+ unsigned int reg_num; /* ISA register num (meaning depends on register type) */
+ enum xtensa_reg_type type;
+ enum xtensa_reg_flags flags;
+};
+
+struct xtensa_user_reg_desc {
+ const char *name;
+ /* ISA register num (meaning depends on register type) */
+ unsigned int reg_num;
+ enum xtensa_reg_flags flags;
+ uint32_t size;
+ const struct reg_arch_type *type;
+};
+
+extern const struct xtensa_reg_desc xtensa_regs[XT_NUM_REGS];
+
+#endif /* OPENOCD_TARGET_XTENSA_REGS_H */