target: add Espressif ESP32-S2 basic support 40/6940/45
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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)