target: add Espressif ESP32-S2 basic support
authorErhan Kurubas <erhan.kurubas@espressif.com>
Thu, 21 Apr 2022 05:53:54 +0000 (07:53 +0200)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 4 Jun 2022 08:18:44 +0000 (08:18 +0000)
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
17 files changed:
doc/openocd.texi
src/target/Makefile.am
src/target/espressif/Makefile.am [new file with mode: 0644]
src/target/espressif/esp32s2.c [new file with mode: 0644]
src/target/espressif/esp32s2.h [new file with mode: 0644]
src/target/espressif/esp_xtensa.c [new file with mode: 0644]
src/target/espressif/esp_xtensa.h [new file with mode: 0644]
src/target/target.c
src/target/xtensa/Makefile.am [new file with mode: 0644]
src/target/xtensa/xtensa.c [new file with mode: 0644]
src/target/xtensa/xtensa.h [new file with mode: 0644]
src/target/xtensa/xtensa_debug_module.c [new file with mode: 0644]
src/target/xtensa/xtensa_debug_module.h [new file with mode: 0644]
src/target/xtensa/xtensa_regs.h [new file with mode: 0644]
tcl/board/esp32s2-kaluga-1.cfg [new file with mode: 0644]
tcl/interface/ftdi/esp32s2_kaluga_v1.cfg [new file with mode: 0644]
tcl/target/esp32s2.cfg [new file with mode: 0644]

index cc1d10441150dbb6ff6ce2e5aa4ae75d49d5aeda..85be06ea676d47f0d2b86ac5d94fe9c4ea34572a 100644 (file)
@@ -4895,6 +4895,7 @@ compact Thumb2 instruction set. Supports also ARMv6-M and ARMv8-M cores
 @item @code{dsp5680xx} -- implements Freescale's 5680x DSP.
 @item @code{esirisc} -- this is an EnSilica eSi-RISC core.
 The current implementation supports eSi-32xx cores.
+@item @code{esp32s2} -- this is an Espressif SoC with single Xtensa core.
 @item @code{fa526} -- resembles arm920 (w/o Thumb).
 @item @code{feroceon} -- resembles arm926.
 @item @code{hla_target} -- a Cortex-M alternative to work with HL adapters like ST-Link.
@@ -10956,6 +10957,94 @@ STMicroelectronics, based on a proprietary 8-bit core architecture.
 OpenOCD supports debugging STM8 through the STMicroelectronics debug
 protocol SWIM, @pxref{swimtransport,,SWIM}.
 
+@section Xtensa Architecture
+Xtensa processors are based on a modular, highly flexible 32-bit RISC architecture
+that can easily scale from a tiny, cache-less controller or task engine to a high-performance
+SIMD/VLIW DSP provided by Cadence.
+@url{https://www.cadence.com/en_US/home/tools/ip/tensilica-ip/tensilica-xtensa-controllers-and-extensible-processors.html}.
+
+OpenOCD supports generic Xtensa processors implementation which can be customized by
+simply providing vendor-specific core configuration which controls every configurable
+Xtensa architecture option, e.g. number of address registers, exceptions, reduced
+size instructions support, memory banks configuration etc. Also OpenOCD supports SMP
+configurations for Xtensa processors with any number of cores and allows to configure
+their debug signals interconnection (so-called "break/stall networks") which control how
+debug signals are distributed among cores. Xtensa "break networks" are compatible with
+ARM's Cross Trigger Interface (CTI). For debugging code on Xtensa chips OpenOCD
+uses JTAG protocol. Currently OpenOCD implements several Epsressif Xtensa-based chips of
+@uref{https://www.espressif.com/en/products/socs, ESP32 family}.
+
+@subsection General Xtensa Commands
+
+@deffn {Command} {xtensa set_permissive} (0|1)
+By default accessing memory beyond defined regions is forbidden. This commnd controls memory access address check.
+When set to (1), skips access controls and address range check before read/write memory.
+@end deffn
+
+@deffn {Command} {xtensa maskisr} (on|off)
+Selects whether interrupts will be disabled during stepping over single instruction. The default configuration is (off).
+@end deffn
+
+@deffn {Command} {xtensa smpbreak} [none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]
+Configures debug signals connection ("break network") for currently selected core.
+@itemize @bullet
+@item @code{none} - Core's "break/stall network" is disconnected. Core is not affected by any debug
+signal from other cores.
+@item @code{breakinout} - Core's "break network" is fully connected (break inputs and outputs are enabled).
+Core will receive debug break signals from other cores and send such signals to them. For example when another core
+is stopped due to breakpoint hit this core will be stopped too and vice versa.
+@item @code{runstall} - Core's "stall network" is fully connected (stall inputs and outputs are enabled).
+This feature is not well implemented and tested yet.
+@item @code{BreakIn} - Core's "break-in" signal is enabled.
+Core will receive debug break signals from other cores. For example when another core is
+stopped due to breakpoint hit this core will be stopped too.
+@item @code{BreakOut} - Core's "break-out" signal is enabled.
+Core will send debug break signal to other cores. For example when this core is
+stopped due to breakpoint hit other cores with enabled break-in signals will be stopped too.
+@item @code{RunStallIn} - Core's "runstall-in" signal is enabled.
+This feature is not well implemented and tested yet.
+@item @code{DebugModeOut} - Core's "debugmode-out" signal is enabled.
+This feature is not well implemented and tested yet.
+@end itemize
+@end deffn
+
+@deffn {Command} {xtensa perfmon_enable} <counter_id> <select> [mask] [kernelcnt] [tracelevel]
+Enable and start performance counter.
+@itemize @bullet
+@item @code{counter_id} - Counter ID (0-1).
+@item @code{select} - Selects performance metric to be counted by the counter,
+e.g. 0 - CPU cycles, 2 - retired instructions.
+@item @code{mask} - Selects input subsets to be counted (counter will
+increment only once even if more than one condition corresponding to a mask bit occurs).
+@item @code{kernelcnt} - 0 - count events with "CINTLEVEL <= tracelevel",
+1 - count events with "CINTLEVEL > tracelevel".
+@item @code{tracelevel} - Compares this value to "CINTLEVEL" when deciding
+whether to count.
+@end itemize
+@end deffn
+
+@deffn {Command} {xtensa perfmon_dump} (counter_id)
+Dump performance counter value. If no argument specified, dumps all counters.
+@end deffn
+
+@deffn {Command} {xtensa tracestart} [pc <pcval>/[<maskbitcount>]] [after <n> [ins|words]]
+Set up and start a HW trace. Optionally set PC address range to trigger tracing stop when reached during program execution.
+This command also allows to specify the amount of data to capture after stop trigger activation.
+@itemize @bullet
+@item @code{pcval} - PC value which will trigger trace data collection stop.
+@item @code{maskbitcount} - PC value mask.
+@item @code{n} - Maximum number of instructions/words to capture after trace stop trigger.
+@end itemize
+@end deffn
+
+@deffn {Command} {xtensa tracestop}
+Stop current trace as started by the tracestart command.
+@end deffn
+
+@deffn {Command} {xtensa tracedump} <outfile>
+Dump trace memory to a file.
+@end deffn
+
 @anchor{softwaredebugmessagesandtracing}
 @section Software Debug Messages and Tracing
 @cindex Linux-ARM DCC support
index 49e882fe6c6a5e2c062525eb4fb75e0356b5796a..799c3dd07536f9eed1d9b281b0fd19d4f9f3e3f1 100644 (file)
@@ -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 (file)
index 0000000..c681e09
--- /dev/null
@@ -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 (file)
index 0000000..212533f
--- /dev/null
@@ -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 (file)
index 0000000..2c43e3e
--- /dev/null
@@ -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 (file)
index 0000000..89393f4
--- /dev/null
@@ -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 (file)
index 0000000..6badb1b
--- /dev/null
@@ -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 */
index 76327b1c771a6e0748ccc8b38c259546ce29d810..e2004e4a9e139b53fc314bdc602120552a73b30b 100644 (file)
@@ -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 (file)
index 0000000..f6cee99
--- /dev/null
@@ -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 (file)
index 0000000..a955960
--- /dev/null
@@ -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 (file)
index 0000000..d8b15e1
--- /dev/null
@@ -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 (file)
index 0000000..c6959ae
--- /dev/null
@@ -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 (file)
index 0000000..692f0f6
--- /dev/null
@@ -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 (file)
index 0000000..7602131
--- /dev/null
@@ -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 */
diff --git a/tcl/board/esp32s2-kaluga-1.cfg b/tcl/board/esp32s2-kaluga-1.cfg
new file mode 100644 (file)
index 0000000..783ea21
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-S2 Kaluga board.
+#
+# For example, OpenOCD can be started for ESP32-S2 debugging on
+#
+#   openocd -f board/esp32s2-kaluga-1.cfg
+#
+
+source [find interface/ftdi/esp32s2_kaluga_v1.cfg]
+source [find target/esp32s2.cfg]
+
+# The speed of the JTAG interface, in kHz. If you get DSR/DIR errors (and they
+# do not relate to OpenOCD trying to read from a memory range without physical
+# memory being present there), you can try lowering this.
+# On ESP32-S2, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
+# if CPU frequency is 160MHz or 240MHz.
+adapter speed 20000
diff --git a/tcl/interface/ftdi/esp32s2_kaluga_v1.cfg b/tcl/interface/ftdi/esp32s2_kaluga_v1.cfg
new file mode 100644 (file)
index 0000000..1880bcb
--- /dev/null
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Driver for the FT2232H JTAG chip on the Espressif Kaluga-1 ESP32-S2 board
+# (and most other FT2232H and FT232H based boards)
+#
+# JTAG DIP switch (labelled SW5 in the schematic) should be "ON" for lines
+# labelled TCK, TDO, TDI and TWS, to connect the FT2232H to the ESP32-S2.
+#
+
+adapter driver ftdi
+ftdi vid_pid 0x0403 0x6010 0x0403 0x6014
+
+# interface 1 is the uart
+ftdi channel 0
+
+# TCK, TDI, TDO, TMS: ADBUS0-3
+# TRST/SRST: ADBUS5 (unused for now)
+# LEDs: ACBUS3-4 (inverted)
+
+ftdi layout_init 0x0008 0x180b
+ftdi layout_signal LED -ndata 0x0800
+ftdi layout_signal LED2 -ndata 0x1000
+
+# ESP32* series chips do not have a TRST input, and the SRST line is connected
+# to the EN pin.
+# The target code doesn't handle SRST reset properly yet, so this is
+# commented out:
+# ftdi layout_signal nSRST -oe 0x0020
+# reset_config srst_only
diff --git a/tcl/target/esp32s2.cfg b/tcl/target/esp32s2.cfg
new file mode 100644 (file)
index 0000000..ab64c31
--- /dev/null
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# The ESP32-S2 only supports JTAG.
+transport select jtag
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $CHIPNAME
+} else {
+       set _CHIPNAME esp32s2
+}
+
+if { [info exists CPUTAPID] } {
+       set _CPUTAPID $CPUTAPID
+} else {
+       set _CPUTAPID 0x120034e5
+}
+
+set _TARGETNAME $_CHIPNAME
+set _CPUNAME cpu
+set _TAPNAME $_CHIPNAME.$_CPUNAME
+
+jtag newtap $_CHIPNAME $_CPUNAME -irlen 5 -expected-id $_CPUTAPID
+
+target create $_TARGETNAME esp32s2 -endian little -chain-position $_TAPNAME
+
+xtensa maskisr on
+
+$_TARGETNAME configure -event reset-assert-post { soft_reset_halt }
+
+gdb_breakpoint_override hard