aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2023-02-28 10:53:40 -0800
committerTim Newsome <tim@sifive.com>2023-02-28 10:54:48 -0800
commit4f97898889e64579a35cf55be80c71c37ab4dd09 (patch)
treecec668206ffd1cf00f6fdbb02761187dfb49b059
parentb8f4b8887b0ad51e657bbbbc244ff69455984a50 (diff)
parentd1b882f2c014258be5397067e45848fa5465b78b (diff)
downloadriscv-openocd-4f97898889e64579a35cf55be80c71c37ab4dd09.zip
riscv-openocd-4f97898889e64579a35cf55be80c71c37ab4dd09.tar.gz
riscv-openocd-4f97898889e64579a35cf55be80c71c37ab4dd09.tar.bz2
Merge commit 'd1b882f2c014258be5397067e45848fa5465b78b' into from_upstream
Conflicts: doc/openocd.texi src/target/riscv/riscv-013.c src/target/riscv/riscv.c Change-Id: I8cd557a10c3d5beeaed05ecc05d4c325a9ee7e70
-rw-r--r--.github/workflows/snapshot.yml4
-rw-r--r--.gitignore1
-rw-r--r--HACKING36
-rw-r--r--configure.ac16
-rwxr-xr-xcontrib/cross-build.sh2
-rw-r--r--doc/openocd.texi354
-rw-r--r--src/flash/nor/bluenrg-x.c19
-rw-r--r--src/flash/nor/core.c6
-rw-r--r--src/flash/nor/numicro.c7
-rw-r--r--src/flash/nor/stm32l4x.c89
-rw-r--r--src/helper/startup.tcl22
-rw-r--r--src/jtag/drivers/Makefile.am3
-rw-r--r--src/jtag/drivers/am335xgpio.c733
-rw-r--r--src/jtag/drivers/bcm2835gpio.c28
-rw-r--r--src/jtag/drivers/bitbang.c3
-rw-r--r--src/jtag/drivers/stlink_usb.c7
-rw-r--r--src/jtag/interfaces.c6
-rw-r--r--src/openocd.c3
-rw-r--r--src/server/gdb_server.c2
-rw-r--r--src/server/server.c2
-rw-r--r--src/server/telnet_server.c17
-rw-r--r--src/target/Makefile.am6
-rw-r--r--src/target/aarch64.c25
-rw-r--r--src/target/adi_v5_jtag.c4
-rw-r--r--src/target/arm_adi_v5.c728
-rw-r--r--src/target/arm_adi_v5.h6
-rw-r--r--src/target/arm_coresight.h9
-rw-r--r--src/target/armv7a.c2
-rw-r--r--src/target/cortex_a.c11
-rw-r--r--src/target/espressif/Makefile.am6
-rw-r--r--src/target/espressif/esp32s2.c715
-rw-r--r--src/target/espressif/esp32s2.h40
-rw-r--r--src/target/espressif/esp_xtensa.c71
-rw-r--r--src/target/espressif/esp_xtensa.h48
-rw-r--r--src/target/riscv/riscv-013.c11
-rw-r--r--src/target/semihosting_common.c56
-rw-r--r--src/target/semihosting_common.h3
-rw-r--r--src/target/smp.c7
-rw-r--r--src/target/smp.h2
-rw-r--r--src/target/target.c17
-rw-r--r--src/target/target.h2
-rw-r--r--src/target/target_type.h11
-rw-r--r--src/target/xtensa/Makefile.am7
-rw-r--r--src/target/xtensa/xtensa.c2731
-rw-r--r--src/target/xtensa/xtensa.h309
-rw-r--r--src/target/xtensa/xtensa_debug_module.c359
-rw-r--r--src/target/xtensa/xtensa_debug_module.h385
-rw-r--r--src/target/xtensa/xtensa_regs.h278
-rw-r--r--tcl/board/esp32s2-kaluga-1.cfg18
-rw-r--r--tcl/board/kontron_sl28.cfg14
-rw-r--r--tcl/board/steval-idb012v1.cfg5
-rw-r--r--tcl/interface/beaglebone-jtag-native.cfg28
-rw-r--r--tcl/interface/beaglebone-swd-native.cfg29
-rw-r--r--tcl/interface/ftdi/esp32s2_kaluga_v1.cfg29
-rw-r--r--tcl/interface/ftdi/tigard.cfg19
-rw-r--r--tcl/target/bluenrg-x.cfg7
-rw-r--r--tcl/target/esp32s2.cfg30
-rw-r--r--tcl/target/ls1028a.cfg18
-rw-r--r--tcl/target/ls1088a.cfg57
-rw-r--r--tcl/target/lsch3_common.cfg63
-rw-r--r--tcl/target/renesas_rz_five.cfg22
-rw-r--r--tcl/target/renesas_rz_g2.cfg28
62 files changed, 7137 insertions, 439 deletions
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index 972011c..9d356ae 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -22,7 +22,7 @@ jobs:
- run: ./bootstrap
- name: Prepare libusb1
env:
- LIBUSB1_VER: 1.0.24
+ LIBUSB1_VER: 1.0.26
run: |
mkdir -p $DL_DIR && cd $DL_DIR
wget "https://github.com/libusb/libusb/releases/download/v${LIBUSB1_VER}/libusb-${LIBUSB1_VER}.tar.bz2"
@@ -30,7 +30,7 @@ jobs:
echo "LIBUSB1_SRC=$PWD/libusb-${LIBUSB1_VER}" >> $GITHUB_ENV
- name: Prepare hidapi
env:
- HIDAPI_VER: 0.10.1
+ HIDAPI_VER: 0.11.2
run: |
mkdir -p $DL_DIR && cd $DL_DIR
wget "https://github.com/libusb/hidapi/archive/hidapi-${HIDAPI_VER}.tar.gz"
diff --git a/.gitignore b/.gitignore
index 955ca3c..f5aa68a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,6 +68,7 @@ doxygen
doxygen.log
Doxyfile
libtool
+*-libtool
Makefile
!contrib/loaders/**/Makefile
stamp-h1
diff --git a/HACKING b/HACKING
index 1eeb1a2..5d110fb 100644
--- a/HACKING
+++ b/HACKING
@@ -197,11 +197,11 @@ while(!done) {
@endcode
\note use "git add ." before commit to add new files.
- Comment template, notice the short first line w/topic. The topic field
- should identify the main part or subsystem the patch touches. Check
- git log for examples.
-@code
-topic: Short comment
+ Commit message template, notice the short first line.
+ The field '<c>specify touched area</c>'
+ should identify the main part or subsystem the patch touches.
+@code{.unparsed}
+specify touched area: short comment
<blank line>
Longer comments over several lines, explaining (where applicable) the
reason for the patch and the general idea the solution is based on,
@@ -209,6 +209,32 @@ any major design decisions, etc...
<blank line>
Signed-off-by: ...
@endcode
+ Examples:
+@code{.unparsed}
+flash/nor/atsame5: add SAME59 support
+
+Add new device ID
+@endcode
+@code{.unparsed}
+flash/nor: flash driver for XYZ123
+
+Add new flash driver for internal flash of ...
+@endcode
+@code{.unparsed}
+target/cortex_m: fix segmentation fault in cmd 'soft_reset_halt'
+
+soft_reset_halt command failed reproducibly under following conditions: ...
+Test for NULL pointer and return error ...
+
+Reported-by: John Reporter <rep9876@gmail.com>
+Fixes: 123456789abc ("target: the commit where the problem started")
+BugLink: https://sourceforge.net/p/openocd/tickets/999/
+@endcode
+@code{.unparsed}
+doc: fix typos
+@endcode
+ See "git log" for more examples.
+
-# Next you need to make sure that your patches
are on top of the latest stuff on the server and
that there are no conflicts:
diff --git a/configure.ac b/configure.ac
index 081e774..687fffa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -299,10 +299,14 @@ AS_CASE(["${host_cpu}"],
AC_ARG_ENABLE([imx_gpio],
AS_HELP_STRING([--enable-imx_gpio], [Enable building support for bitbanging on NXP IMX processors]),
[build_imx_gpio=$enableval], [build_imx_gpio=no])
+ AC_ARG_ENABLE([am335xgpio],
+ AS_HELP_STRING([--enable-am335xgpio], [Enable building support for bitbanging on AM335x (as found in Beaglebones)]),
+ [build_am335xgpio=$enableval], [build_am335xgpio=no])
],
[
build_bcm2835gpio=no
build_imx_gpio=no
+ build_am335xgpio=no
])
AS_CASE(["${host_cpu}"],
@@ -508,6 +512,13 @@ AS_IF([test "x$build_imx_gpio" = "xyes"], [
AC_DEFINE([BUILD_IMX_GPIO], [0], [0 if you don't want imx_gpio.])
])
+AS_IF([test "x$build_am335xgpio" = "xyes"], [
+ build_bitbang=yes
+ AC_DEFINE([BUILD_AM335XGPIO], [1], [1 if you want am335xgpio.])
+], [
+ AC_DEFINE([BUILD_AM335XGPIO], [0], [0 if you don't want am335xgpio.])
+])
+
AS_IF([test "x$parport_use_ppdev" = "xyes"], [
AC_DEFINE([PARPORT_USE_PPDEV], [1], [1 if you want parport to use ppdev.])
], [
@@ -560,9 +571,9 @@ AS_IF([test "x$enable_buspirate" != "xno"], [
AS_IF([test "x$use_internal_jimtcl" = "xyes"], [
AS_IF([test -f "$srcdir/jimtcl/configure.ac"], [
AS_IF([test "x$use_internal_jimtcl_maintainer" = "xyes"], [
- jimtcl_config_options="--disable-install-jim --maintainer"
+ jimtcl_config_options="--disable-install-jim --with-ext=json --maintainer"
], [
- jimtcl_config_options="--disable-install-jim"
+ jimtcl_config_options="--disable-install-jim --with-ext=json"
])
AX_CONFIG_SUBDIR_OPTION([jimtcl], [$jimtcl_config_options])
], [
@@ -712,6 +723,7 @@ AM_CONDITIONAL([EP93XX], [test "x$build_ep93xx" = "xyes"])
AM_CONDITIONAL([AT91RM9200], [test "x$build_at91rm9200" = "xyes"])
AM_CONDITIONAL([BCM2835GPIO], [test "x$build_bcm2835gpio" = "xyes"])
AM_CONDITIONAL([IMX_GPIO], [test "x$build_imx_gpio" = "xyes"])
+AM_CONDITIONAL([AM335XGPIO], [test "x$build_am335xgpio" = "xyes"])
AM_CONDITIONAL([BITBANG], [test "x$build_bitbang" = "xyes"])
AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes"])
AM_CONDITIONAL([VDEBUG], [test "x$build_vdebug" = "xyes"])
diff --git a/contrib/cross-build.sh b/contrib/cross-build.sh
index 856551d..2275f68 100755
--- a/contrib/cross-build.sh
+++ b/contrib/cross-build.sh
@@ -154,7 +154,7 @@ if [ -d $CAPSTONE_SRC ] ; then
sed -i '1s;^;prefix=/usr \
exec_prefix=${prefix} \
libdir=${exec_prefix}/lib \
-includedir=${prefix}/include\n\n;' $CAPSTONE_PC_FILE
+includedir=${prefix}/include/capstone\n\n;' $CAPSTONE_PC_FILE
fi
diff --git a/doc/openocd.texi b/doc/openocd.texi
index a5d561c..99cae9a 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -584,6 +584,9 @@ produced, PDF schematics are easily found and it is easy to make.
@item @b{imx_gpio}
@* A NXP i.MX-based board (e.g. Wandboard) using the GPIO pins (should work on any i.MX processor).
+@item @b{am335xgpio}
+@* A Texas Instruments AM335x-based board (e.g. BeagleBone Black) using the GPIO pins of the expansion headers.
+
@item @b{jtag_vpi}
@* A JTAG driver acting as a client for the JTAG VPI server interface.
@* Link: @url{http://github.com/fjullien/jtag_vpi}
@@ -2120,6 +2123,15 @@ corresponding subsystems:
@deffnx {Config Command} {pld init}
@deffnx {Command} {tpiu init}
@end deffn
+
+At last, @command{init} executes all the commands that are specified in
+the TCL list @var{post_init_commands}. The commands are executed in the
+same order they occupy in the list. If one of the commands fails, then
+the error is propagated and OpenOCD fails too.
+@example
+lappend post_init_commands @{echo "OpenOCD successfully initialized."@}
+lappend post_init_commands @{echo "Have fun with OpenOCD !"@}
+@end example
@end deffn
@deffn {Config Command} {noinit}
@@ -3342,11 +3354,180 @@ pinout.
@end deffn
+@deffn {Interface Driver} {am335xgpio} The AM335x SoC is present in BeagleBone
+Black and BeagleBone Green single-board computers which expose some of the GPIOs
+on the two expansion headers.
+
+For maximum performance the driver accesses memory-mapped GPIO peripheral
+registers directly. The memory mapping requires read and write permission to
+kernel memory; if /dev/gpiomem exists it will be used, otherwise /dev/mem will
+be used. The driver restores the GPIO state on exit.
+
+All four GPIO ports are available. GPIOs numbered 0 to 31 are mapped to GPIO port
+0, GPIO numbers 32 to 63 are mapped to GPIO port 1 and so on.
+
+See @file{interface/beaglebone-swd-native.cfg} for a sample configuration file.
+
+@deffn {Config Command} {am335xgpio jtag_nums} @var{tck} @var{tms} @var{tdi} @var{tdo}
+Set JTAG transport GPIO numbers for TCK, TMS, TDI, and TDO (in that order).
+Must be specified to enable JTAG transport. These pins can also be specified
+individually.
+@end deffn
+
+@deffn {Config Command} {am335xgpio tck_num} @var{tck}
+Set TCK GPIO number. Must be specified to enable JTAG transport. Can also be
+specified using the configuration command @command{am335xgpio jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio tms_num} @var{tms}
+Set TMS GPIO number. Must be specified to enable JTAG transport. Can also be
+specified using the configuration command @command{am335xgpio jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio tdo_num} @var{tdo}
+Set TDO GPIO number. Must be specified to enable JTAG transport. Can also be
+specified using the configuration command @command{am335xgpio jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio tdi_num} @var{tdi}
+Set TDI GPIO number. Must be specified to enable JTAG transport. Can also be
+specified using the configuration command @command{am335xgpio jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio swd_nums} @var{swclk} @var{swdio}
+Set SWD transport GPIO numbers for SWCLK and SWDIO (in that order). Must be
+specified to enable SWD transport. These pins can also be specified individually.
+@end deffn
+
+@deffn {Config Command} {am335xgpio swclk_num} @var{swclk}
+Set SWCLK GPIO number. Must be specified to enable SWD transport. Can also be
+specified using the configuration command @command{am335xgpio swd_nums}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio swdio_num} @var{swdio}
+Set SWDIO GPIO number. Must be specified to enable SWD transport. Can also be
+specified using the configuration command @command{am335xgpio swd_nums}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio swdio_dir_num} @var{swdio_dir}
+Set SWDIO direction control pin GPIO number. If specified, this pin can be used
+to control the direction of an external buffer on the SWDIO pin. The direction
+control state can be set with the command @command{am335xgpio
+swdio_dir_output_state}. If not specified this feature is disabled.
+@end deffn
+
+@deffn {Config Command} {am335xgpio swdio_dir_output_state} @var{output_state}
+Set the state required for an external SWDIO buffer to be an output. Valid
+values are @option{on} (default) and @option{off}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio srst_num} @var{srst}
+Set SRST GPIO number. Must be specified to enable SRST.
+@end deffn
+
+@deffn {Config Command} {am335xgpio trst_num} @var{trst}
+Set TRST GPIO number. Must be specified to enable TRST.
+@end deffn
+
+@deffn {Config Command} {am335xgpio led_num} @var{led}
+Set activity LED GPIO number. If not specified an activity LED is not enabled.
+@end deffn
+
+@deffn {Config Command} {am335xgpio led_on_state} @var{on_state}
+Set required logic level for the LED to be on. Valid values are @option{on}
+(default) and @option{off}.
+@end deffn
+
+@deffn {Config Command} {am335xgpio speed_coeffs} @var{speed_coeff} @var{speed_offset}
+Set SPEED_COEFF and SPEED_OFFSET for delay calculations. If unspecified
+speed_coeff defaults to 600000 and speed_offset defaults to 575.
+@end deffn
+
+@end deffn
+
+
@deffn {Interface Driver} {linuxgpiod}
Linux provides userspace access to GPIO through libgpiod since Linux kernel version v4.6.
-The driver emulates either JTAG and SWD transport through bitbanging.
+The driver emulates either JTAG or SWD transport through bitbanging.
See @file{interface/dln-2-gpiod.cfg} for a sample config.
+
+@deffn {Config Command} {linuxgpiod gpiochip} @var{chip}
+Set the GPIO chip number for all GPIOs used by linuxgpiod. If GPIOs use
+different GPIO chips then the individual GPIO configuration commands (i.e., not
+@command{linuxgpiod jtag_nums} or @command{linuxgpiod swd_nums}) can be used to
+set chip numbers independently for each GPIO.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod jtag_nums} @var{tck} @var{tms} @var{tdi} @var{tdo}
+Set JTAG transport GPIO numbers for TCK, TMS, TDI, and TDO (in that order). Must
+be specified to enable JTAG transport. These pins can also be specified
+individually.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod tck_num} [@var{chip}] @var{tck}
+Set TCK GPIO number, and optionally TCK chip number. Must be specified to enable
+JTAG transport. Can also be specified using the configuration command
+@command{linuxgpiod jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod tms_num} [@var{chip}] @var{tms}
+Set TMS GPIO number, and optionally TMS chip number. Must be specified to enable
+JTAG transport. Can also be specified using the configuration command
+@command{linuxgpiod jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod tdo_num} [@var{chip}] @var{tdo}
+Set TDO GPIO number, and optionally TDO chip number. Must be specified to enable
+JTAG transport. Can also be specified using the configuration command
+@command{linuxgpiod jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod tdi_num} [@var{chip}] @var{tdi}
+Set TDI GPIO number, and optionally TDI chip number. Must be specified to enable
+JTAG transport. Can also be specified using the configuration command
+@command{linuxgpiod jtag_nums}.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod trst_num} [@var{chip}] @var{trst}
+Set TRST GPIO number, and optionally TRST chip number. Must be specified to
+enable TRST.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod swd_nums} @var{swclk} @var{swdio}
+Set SWD transport GPIO numbers for SWCLK and SWDIO (in that order). Must be
+specified to enable SWD transport. These pins can also be specified
+individually.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod swclk_num} [@var{chip}] @var{swclk}
+Set SWCLK GPIO number, and optionally SWCLK chip number. Must be specified to
+enable SWD transport. Can also be specified using the configuration command
+@command{linuxgpiod swd_nums}.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod swdio_num} [@var{chip}] @var{swdio}
+Set SWDIO GPIO number, and optionally SWDIO chip number. Must be specified to
+enable SWD transport. Can also be specified using the configuration command
+@command{linuxgpiod swd_nums}.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod swdio_dir_num} [@var{chip}] @var{swdio_dir}
+Set SWDIO direction control GPIO number, and optionally SWDIO direction control
+chip number. If specified, this GPIO can be used to control the direction of an
+external buffer connected to the SWDIO GPIO (set=output mode, clear=input mode).
+@end deffn
+
+@deffn {Config Command} {linuxgpiod srst_num} [@var{chip}] @var{srst}
+Set SRST GPIO number, and optionally SRST chip number. Must be specified to
+enable SRST.
+@end deffn
+
+@deffn {Config Command} {linuxgpiod led_num} [@var{chip}] @var{led}
+Set activity LED GPIO number, and optionally activity LED chip number. If not
+specified an activity LED is not enabled.
+@end deffn
+
@end deffn
@@ -4745,6 +4926,7 @@ compact Thumb2 instruction set. Supports also ARMv6-M and ARMv8-M cores
The current implementation supports eSi-32xx cores.
@item @code{esp32c2} -- this is an Espressif SoC with single RISC-V core.
@item @code{esp32c3} -- this is an Espressif SoC with single RISC-V core.
+@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.
@@ -6449,7 +6631,7 @@ The AVR 8-bit microcontrollers from Atmel integrate flash memory.
@end deffn
@deffn {Flash Driver} {bluenrg-x}
-STMicroelectronics BlueNRG-1, BlueNRG-2 and BlueNRG-LP Bluetooth low energy wireless system-on-chip. They include ARM Cortex-M0/M0+ core and internal flash memory.
+STMicroelectronics BlueNRG-1, BlueNRG-2 and BlueNRG-LP/LPS Bluetooth low energy wireless system-on-chip. They include ARM Cortex-M0/M0+ core and internal flash memory.
The driver automatically recognizes these chips using
the chip identification registers, and autoconfigures itself.
@@ -8464,18 +8646,19 @@ Close the OpenOCD server, disconnecting all clients (GDB, telnet,
other). If option @option{error} is used, OpenOCD will return a
non-zero exit code to the parent process.
-Like any TCL commands, also @command{shutdown} can be redefined, e.g.:
+If user types CTRL-C or kills OpenOCD, the command @command{shutdown}
+will be automatically executed to cause OpenOCD to exit.
+
+It is possible to specify, in the TCL list @var{pre_shutdown_commands} , a
+set of commands to be automatically executed before @command{shutdown} , e.g.:
@example
-# redefine shutdown
-rename shutdown original_shutdown
-proc shutdown @{@} @{
- puts "This is my implementation of shutdown"
- # my own stuff before exit OpenOCD
- original_shutdown
-@}
+lappend pre_shutdown_commands @{echo "Goodbye, my friend ..."@}
+lappend pre_shutdown_commands @{echo "see you soon !"@}
@end example
-If user types CTRL-C or kills OpenOCD, either the command @command{shutdown}
-or its replacement will be automatically executed before OpenOCD exits.
+The commands in the list will be executed (in the same order they occupy
+in the list) before OpenOCD exits. If one of the commands in the list
+fails, then the remaining commands are not executed anymore while OpenOCD
+will proceed to quit.
@end deffn
@anchor{debuglevel}
@@ -9473,6 +9656,12 @@ is valid during the run of the event handlers and is accessible with this
command.
@end deffn
+@deffn {Command} {arm semihosting_basedir} [dir]
+@cindex ARM semihosting
+Set the base directory for semihosting I/O, either an absolute path or a path relative to OpenOCD working directory.
+Use "." for the current directory.
+@end deffn
+
@section ARMv4 and ARMv5 Architecture
@cindex ARMv4
@cindex ARMv5
@@ -10956,6 +11145,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
@@ -11756,57 +12033,6 @@ The @command{step} and @command{stepi} commands can be used to step a specific c
while other cores are free-running or remain halted, depending on the
scheduler-locking mode configured in GDB.
-@section Legacy SMP core switching support
-@quotation Note
-This method is deprecated in favor of the @emph{hwthread} pseudo RTOS.
-@end quotation
-
-For SMP support following GDB serial protocol packet have been defined :
-@itemize @bullet
-@item j - smp status request
-@item J - smp set request
-@end itemize
-
-OpenOCD implements :
-@itemize @bullet
-@item @option{jc} packet for reading core id displayed by
-GDB connection. Reply is @option{XXXXXXXX} (8 hex digits giving core id) or
- @option{E01} for target not smp.
-@item @option{JcXXXXXXXX} (8 hex digits) packet for setting core id displayed at next GDB continue
-(core id -1 is reserved for returning to normal resume mode). Reply @option{E01}
-for target not smp or @option{OK} on success.
-@end itemize
-
-Handling of this packet within GDB can be done :
-@itemize @bullet
-@item by the creation of an internal variable (i.e @option{_core}) by mean
-of function allocate_computed_value allowing following GDB command.
-@example
-set $_core 1
-#Jc01 packet is sent
-print $_core
-#jc packet is sent and result is affected in $
-@end example
-
-@item by the usage of GDB maintenance command as described in following example (2 cpus in SMP with
-core id 0 and 1 @pxref{definecputargetsworkinginsmp,,Define CPU targets working in SMP}).
-
-@example
-# toggle0 : force display of coreid 0
-define toggle0
-maint packet Jc0
-continue
-main packet Jc-1
-end
-# toggle1 : force display of coreid 1
-define toggle1
-maint packet Jc1
-continue
-main packet Jc-1
-end
-@end example
-@end itemize
-
@node Tcl Scripting API
@chapter Tcl Scripting API
@cindex Tcl Scripting API
@@ -12374,7 +12600,7 @@ It sort of works like this:
When the command ``proc'' is parsed (which creates a procedure
function) it gets 3 parameters on the command line. @b{1} the name of
the proc (function), @b{2} the list of parameters, and @b{3} the body
-of the function. Not the choice of words: LIST and BODY. The PROC
+of the function. Note the choice of words: LIST and BODY. The PROC
command stores these items in a table somewhere so it can be found by
``LookupCommand()''
diff --git a/src/flash/nor/bluenrg-x.c b/src/flash/nor/bluenrg-x.c
index 60eccef..16075ec 100644
--- a/src/flash/nor/bluenrg-x.c
+++ b/src/flash/nor/bluenrg-x.c
@@ -35,6 +35,8 @@
#define JTAG_IDCODE_REG(bluenrgx_info) (bluenrgx_info->flash_ptr->jtag_idcode_reg)
#define FLASH_PAGE_SIZE(bluenrgx_info) (bluenrgx_info->flash_ptr->flash_page_size)
+#define FLASH_SIZE_REG_MASK (0xFFFF)
+
struct flash_ctrl_priv_data {
uint32_t die_id_reg;
uint32_t jtag_idcode_reg;
@@ -75,6 +77,16 @@ static const struct flash_ctrl_priv_data flash_priv_data_lp = {
.part_name = "BLUENRG-LP",
};
+static const struct flash_ctrl_priv_data flash_priv_data_lps = {
+ .die_id_reg = 0x40000000,
+ .jtag_idcode_reg = 0x40000004,
+ .flash_base = 0x10040000,
+ .flash_regs_base = 0x40001000,
+ .flash_page_size = 2048,
+ .jtag_idcode = 0x02028041,
+ .part_name = "BLUENRG-LPS",
+};
+
struct bluenrgx_flash_bank {
bool probed;
uint32_t die_id;
@@ -84,8 +96,8 @@ struct bluenrgx_flash_bank {
static const struct flash_ctrl_priv_data *flash_ctrl[] = {
&flash_priv_data_1,
&flash_priv_data_2,
- &flash_priv_data_lp
-};
+ &flash_priv_data_lp,
+ &flash_priv_data_lps};
/* flash_bank bluenrg-x 0 0 0 0 <target#> */
FLASH_BANK_COMMAND_HANDLER(bluenrgx_flash_bank_command)
@@ -377,7 +389,7 @@ static int bluenrgx_probe(struct flash_bank *bank)
if (retval != ERROR_OK)
return retval;
- if (idcode != flash_priv_data_lp.jtag_idcode) {
+ if ((idcode != flash_priv_data_lp.jtag_idcode) && (idcode != flash_priv_data_lps.jtag_idcode)) {
retval = target_read_u32(bank->target, BLUENRG2_JTAG_REG, &idcode);
if (retval != ERROR_OK)
return retval;
@@ -395,6 +407,7 @@ static int bluenrgx_probe(struct flash_bank *bank)
}
}
retval = bluenrgx_read_flash_reg(bank, FLASH_SIZE_REG, &size_info);
+ size_info = size_info & FLASH_SIZE_REG_MASK;
if (retval != ERROR_OK)
return retval;
diff --git a/src/flash/nor/core.c b/src/flash/nor/core.c
index 30d387a..f4ff5df 100644
--- a/src/flash/nor/core.c
+++ b/src/flash/nor/core.c
@@ -429,7 +429,11 @@ int default_flash_blank_check(struct flash_bank *bank)
bank->sectors[i].is_erased = block_array[i].result;
retval = ERROR_OK;
} else {
- LOG_USER("Running slow fallback erase check - add working memory");
+ if (retval == ERROR_NOT_IMPLEMENTED)
+ LOG_USER("Running slow fallback erase check");
+ else
+ LOG_USER("Running slow fallback erase check - add working memory");
+
retval = default_flash_mem_blank_check(bank);
}
free(block_array);
diff --git a/src/flash/nor/numicro.c b/src/flash/nor/numicro.c
index cb7c1df..3487508 100644
--- a/src/flash/nor/numicro.c
+++ b/src/flash/nor/numicro.c
@@ -1385,13 +1385,6 @@ static int numicro_writeblock(struct flash_bank *bank, const uint8_t *buffer,
init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT); /* faddr */
init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT); /* number of words to program */
- struct armv7m_common *armv7m = target_to_armv7m(target);
- if (!armv7m) {
- /* something is very wrong if armv7m is NULL */
- LOG_ERROR("unable to get armv7m target");
- return retval;
- }
-
/* write code buffer and use Flash programming code within NuMicro */
/* Set breakpoint to 0 with time-out of 1000 ms */
while (count > 0) {
diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c
index 7b35a06..0a9d59c 100644
--- a/src/flash/nor/stm32l4x.c
+++ b/src/flash/nor/stm32l4x.c
@@ -1078,7 +1078,7 @@ static int stm32l4_get_all_wrpxy(struct flash_bank *bank, enum stm32_bank_id dev
if (dev_bank_id != STM32_BANK1 && stm32l4_info->dual_bank_mode)
wrp2y_sectors_offset = stm32l4_info->bank1_sectors;
- if (wrp2y_sectors_offset > -1) {
+ if (wrp2y_sectors_offset >= 0) {
/* get WRP2AR */
ret = stm32l4_get_one_wrpxy(bank, &wrpxy[(*n_wrp)++], STM32_FLASH_WRP2AR_INDEX, wrp2y_sectors_offset);
if (ret != ERROR_OK)
@@ -1220,49 +1220,11 @@ err_lock:
return retval2;
}
-static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
+static int stm32l4_protect_same_bank(struct flash_bank *bank, enum stm32_bank_id bank_id, int set,
+ unsigned int first, unsigned int last)
{
- struct target *target = bank->target;
- struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
- int ret = ERROR_OK;
unsigned int i;
- if (stm32l4_is_otp(bank)) {
- LOG_ERROR("cannot protect/unprotect OTP memory");
- return ERROR_FLASH_OPER_UNSUPPORTED;
- }
-
- if (target->state != TARGET_HALTED) {
- LOG_ERROR("Target not halted");
- return ERROR_TARGET_NOT_HALTED;
- }
-
- /* the requested sectors could be located into bank1 and/or bank2 */
- bool use_bank2 = false;
- if (last >= stm32l4_info->bank1_sectors) {
- if (first < stm32l4_info->bank1_sectors) {
- /* the requested sectors for (un)protection are shared between
- * bank 1 and 2, then split the operation */
-
- /* 1- deal with bank 1 sectors */
- LOG_DEBUG("The requested sectors for %s are shared between bank 1 and 2",
- set ? "protection" : "unprotection");
- ret = stm32l4_protect(bank, set, first, stm32l4_info->bank1_sectors - 1);
- if (ret != ERROR_OK)
- return ret;
-
- /* 2- then continue with bank 2 sectors */
- first = stm32l4_info->bank1_sectors;
- }
-
- use_bank2 = true;
- }
-
- /* refresh the sectors' protection */
- ret = stm32l4_protect_check(bank);
- if (ret != ERROR_OK)
- return ret;
-
/* check if the desired protection is already configured */
for (i = first; i <= last; i++) {
if (bank->sectors[i].is_protected != set)
@@ -1278,7 +1240,7 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first,
unsigned int n_wrp;
struct stm32l4_wrp wrpxy[4];
- ret = stm32l4_get_all_wrpxy(bank, use_bank2 ? STM32_BANK2 : STM32_BANK1, wrpxy, &n_wrp);
+ int ret = stm32l4_get_all_wrpxy(bank, bank_id, wrpxy, &n_wrp);
if (ret != ERROR_OK)
return ret;
@@ -1349,6 +1311,40 @@ static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first,
return stm32l4_write_all_wrpxy(bank, wrpxy, n_wrp);
}
+static int stm32l4_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
+{
+ struct target *target = bank->target;
+ struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
+
+ if (stm32l4_is_otp(bank)) {
+ LOG_ERROR("cannot protect/unprotect OTP memory");
+ return ERROR_FLASH_OPER_UNSUPPORTED;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ /* refresh the sectors' protection */
+ int ret = stm32l4_protect_check(bank);
+ if (ret != ERROR_OK)
+ return ret;
+
+ /* the requested sectors could be located into bank1 and/or bank2 */
+ if (last < stm32l4_info->bank1_sectors) {
+ return stm32l4_protect_same_bank(bank, STM32_BANK1, set, first, last);
+ } else if (first >= stm32l4_info->bank1_sectors) {
+ return stm32l4_protect_same_bank(bank, STM32_BANK2, set, first, last);
+ } else {
+ ret = stm32l4_protect_same_bank(bank, STM32_BANK1, set, first, stm32l4_info->bank1_sectors - 1);
+ if (ret != ERROR_OK)
+ return ret;
+
+ return stm32l4_protect_same_bank(bank, STM32_BANK2, set, stm32l4_info->bank1_sectors, last);
+ }
+}
+
/* count is the size divided by stm32l4_info->data_width */
static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer,
uint32_t offset, uint32_t count)
@@ -1987,6 +1983,15 @@ static int stm32l4_probe(struct flash_bank *bank)
return ERROR_FAIL;
}
+ /* ensure that at least there is 1 flash sector / page */
+ if (num_pages == 0) {
+ if (stm32l4_info->user_bank_size)
+ LOG_ERROR("The specified flash size is less than page size");
+
+ LOG_ERROR("Flash pages count cannot be zero");
+ return ERROR_FAIL;
+ }
+
LOG_INFO("flash mode : %s-bank", stm32l4_info->dual_bank_mode ? "dual" : "single");
const int gap_size_kb = stm32l4_info->hole_sectors * page_size_kb;
diff --git a/src/helper/startup.tcl b/src/helper/startup.tcl
index 71f489d..6389262 100644
--- a/src/helper/startup.tcl
+++ b/src/helper/startup.tcl
@@ -28,4 +28,26 @@ proc script {filename} {
add_help_text script "filename of OpenOCD script (tcl) to run"
add_usage_text script "<file>"
+# Run a list of post-init commands
+# Each command should be added with 'lappend post_init_commands command'
+lappend _telnet_autocomplete_skip _run_post_init_commands
+proc _run_post_init_commands {} {
+ if {[info exists ::post_init_commands]} {
+ foreach cmd $::post_init_commands {
+ eval $cmd
+ }
+ }
+}
+
+# Run a list of pre-shutdown commands
+# Each command should be added with 'lappend pre_shutdown_commands command'
+lappend _telnet_autocomplete_skip _run_pre_shutdown_commands
+proc _run_pre_shutdown_commands {} {
+ if {[info exists ::pre_shutdown_commands]} {
+ foreach cmd $::pre_shutdown_commands {
+ eval $cmd
+ }
+ }
+}
+
#########
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index 887f99b..d05b7b9 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -185,6 +185,9 @@ endif
if XDS110
DRIVERFILES += %D%/xds110.c
endif
+if AM335XGPIO
+DRIVERFILES += %D%/am335xgpio.c
+endif
DRIVERHEADERS = \
%D%/bitbang.h \
diff --git a/src/jtag/drivers/am335xgpio.c b/src/jtag/drivers/am335xgpio.c
new file mode 100644
index 0000000..e04c44c
--- /dev/null
+++ b/src/jtag/drivers/am335xgpio.c
@@ -0,0 +1,733 @@
+/***************************************************************************
+ * Copyright (C) 2022 by Steve Marple, stevemarple@googlemail.com *
+ * *
+ * Based on bcm2835gpio.c and linuxgpiod.c *
+ * *
+ * 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 <jtag/interface.h>
+#include <transport/transport.h>
+#include "bitbang.h"
+
+#include <sys/mman.h>
+
+/*
+ * GPIO register base addresses. Values taken from "AM335x and AMIC110 Sitara
+ * Processors Technical Reference Manual", Chapter 2 Memory Map.
+ */
+#define AM335XGPIO_NUM_GPIO_PORTS 4
+#define AM335XGPIO_GPIO0_HW_ADDR 0x44E07000
+#define AM335XGPIO_GPIO1_HW_ADDR 0x4804C000
+#define AM335XGPIO_GPIO2_HW_ADDR 0x481AC000
+#define AM335XGPIO_GPIO3_HW_ADDR 0x481AE000
+
+/* 32-bit offsets from GPIO port base address. Values taken from "AM335x and
+ * AMIC110 Sitara Processors Technical Reference Manual", Chapter 25
+ * General-Purpose Input/Output.
+ */
+#define AM335XGPIO_GPIO_OE_OFFSET (0x134 / 4)
+#define AM335XGPIO_GPIO_DATAIN_OFFSET (0x138 / 4)
+#define AM335XGPIO_GPIO_DATAOUT_OFFSET (0x13C / 4) /* DATAOUT register uses 0 for output, 1 for input */
+#define AM335XGPIO_GPIO_CLEARDATAOUT_OFFSET (0x190 / 4)
+#define AM335XGPIO_GPIO_SETDATAOUT_OFFSET (0x194 / 4)
+
+/* GPIOs are integer values; need to map to a port module, and the pin within
+ * that module. GPIOs 0 to 31 map to GPIO0, 32 to 63 to GPIO1 etc. This scheme
+ * matches that used by Linux on the BeagleBone.
+ */
+#define AM335XGPIO_PORT_NUM(gpio_num) ((gpio_num) / 32)
+#define AM335XGPIO_BIT_NUM(gpio_num) ((gpio_num) % 32)
+#define AM335XGPIO_BIT_MASK(gpio_num) BIT(AM335XGPIO_BIT_NUM(gpio_num))
+
+#define AM335XGPIO_READ_REG(gpio_num, offset) \
+ (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)))
+
+#define AM335XGPIO_WRITE_REG(gpio_num, offset, value) \
+ (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)) = (value))
+
+#define AM335XGPIO_SET_REG_BITS(gpio_num, offset, bit_mask) \
+ (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)) |= (bit_mask))
+
+#define AM335XGPIO_CLEAR_REG_BITS(gpio_num, offset, bit_mask) \
+ (*(am335xgpio_gpio_port_mmap_addr[AM335XGPIO_PORT_NUM(gpio_num)] + (offset)) &= ~(bit_mask))
+
+enum amx335gpio_gpio_mode {
+ AM335XGPIO_GPIO_MODE_INPUT,
+ AM335XGPIO_GPIO_MODE_OUTPUT, /* To set output mode but not state */
+ AM335XGPIO_GPIO_MODE_OUTPUT_LOW,
+ AM335XGPIO_GPIO_MODE_OUTPUT_HIGH,
+};
+
+static const uint32_t am335xgpio_gpio_port_hw_addr[AM335XGPIO_NUM_GPIO_PORTS] = {
+ AM335XGPIO_GPIO0_HW_ADDR,
+ AM335XGPIO_GPIO1_HW_ADDR,
+ AM335XGPIO_GPIO2_HW_ADDR,
+ AM335XGPIO_GPIO3_HW_ADDR,
+};
+
+/* Memory-mapped address pointers */
+static volatile uint32_t *am335xgpio_gpio_port_mmap_addr[AM335XGPIO_NUM_GPIO_PORTS];
+
+static int dev_mem_fd;
+
+/* GPIO numbers for each signal. Negative values are invalid */
+static int tck_gpio = -1;
+static enum amx335gpio_gpio_mode tck_gpio_mode;
+static int tms_gpio = -1;
+static enum amx335gpio_gpio_mode tms_gpio_mode;
+static int tdi_gpio = -1;
+static enum amx335gpio_gpio_mode tdi_gpio_mode;
+static int tdo_gpio = -1;
+static enum amx335gpio_gpio_mode tdo_gpio_mode;
+static int trst_gpio = -1;
+static enum amx335gpio_gpio_mode trst_gpio_mode;
+static int srst_gpio = -1;
+static enum amx335gpio_gpio_mode srst_gpio_mode;
+static int swclk_gpio = -1;
+static enum amx335gpio_gpio_mode swclk_gpio_mode;
+static int swdio_gpio = -1;
+static enum amx335gpio_gpio_mode swdio_gpio_mode;
+static int swdio_dir_gpio = -1;
+static enum amx335gpio_gpio_mode swdio_dir_gpio_mode;
+static int led_gpio = -1;
+static enum amx335gpio_gpio_mode led_gpio_mode = -1;
+
+static bool swdio_dir_is_active_high = true; /* Active state means output */
+static bool led_is_active_high = true;
+
+/* Transition delay coefficients */
+static int speed_coeff = 600000;
+static int speed_offset = 575;
+static unsigned int jtag_delay;
+
+static int is_gpio_valid(int gpio_num)
+{
+ return gpio_num >= 0 && gpio_num < (32 * AM335XGPIO_NUM_GPIO_PORTS);
+}
+
+static int get_gpio_value(int gpio_num)
+{
+ unsigned int shift = AM335XGPIO_BIT_NUM(gpio_num);
+ return (AM335XGPIO_READ_REG(gpio_num, AM335XGPIO_GPIO_DATAIN_OFFSET) >> shift) & 1;
+}
+
+static void set_gpio_value(int gpio_num, int value)
+{
+ if (value)
+ AM335XGPIO_WRITE_REG(gpio_num, AM335XGPIO_GPIO_SETDATAOUT_OFFSET, AM335XGPIO_BIT_MASK(gpio_num));
+ else
+ AM335XGPIO_WRITE_REG(gpio_num, AM335XGPIO_GPIO_CLEARDATAOUT_OFFSET, AM335XGPIO_BIT_MASK(gpio_num));
+}
+
+static enum amx335gpio_gpio_mode get_gpio_mode(int gpio_num)
+{
+ if (AM335XGPIO_READ_REG(gpio_num, AM335XGPIO_GPIO_OE_OFFSET) & AM335XGPIO_BIT_MASK(gpio_num)) {
+ return AM335XGPIO_GPIO_MODE_INPUT;
+ } else {
+ /* Return output level too so that pin mode can be fully restored */
+ if (AM335XGPIO_READ_REG(gpio_num, AM335XGPIO_GPIO_DATAOUT_OFFSET) & AM335XGPIO_BIT_MASK(gpio_num))
+ return AM335XGPIO_GPIO_MODE_OUTPUT_HIGH;
+ else
+ return AM335XGPIO_GPIO_MODE_OUTPUT_LOW;
+ }
+}
+
+static void set_gpio_mode(int gpio_num, enum amx335gpio_gpio_mode gpio_mode)
+{
+ if (gpio_mode == AM335XGPIO_GPIO_MODE_INPUT) {
+ AM335XGPIO_SET_REG_BITS(gpio_num, AM335XGPIO_GPIO_OE_OFFSET, AM335XGPIO_BIT_MASK(gpio_num));
+ return;
+ }
+
+ if (gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_LOW)
+ set_gpio_value(gpio_num, 0);
+ if (gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_HIGH)
+ set_gpio_value(gpio_num, 1);
+
+ if (gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT ||
+ gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_LOW ||
+ gpio_mode == AM335XGPIO_GPIO_MODE_OUTPUT_HIGH) {
+ AM335XGPIO_CLEAR_REG_BITS(gpio_num, AM335XGPIO_GPIO_OE_OFFSET, AM335XGPIO_BIT_MASK(gpio_num));
+ }
+}
+
+static const char *get_gpio_mode_name(enum amx335gpio_gpio_mode gpio_mode)
+{
+ switch (gpio_mode) {
+ case AM335XGPIO_GPIO_MODE_INPUT:
+ return "input";
+ case AM335XGPIO_GPIO_MODE_OUTPUT:
+ return "output";
+ case AM335XGPIO_GPIO_MODE_OUTPUT_LOW:
+ return "output (low)";
+ case AM335XGPIO_GPIO_MODE_OUTPUT_HIGH:
+ return "output (high)";
+ default:
+ return "unknown";
+ }
+}
+
+static bb_value_t am335xgpio_read(void)
+{
+ return get_gpio_value(tdo_gpio) ? BB_HIGH : BB_LOW;
+}
+
+static int am335xgpio_write(int tck, int tms, int tdi)
+{
+ set_gpio_value(tdi_gpio, tdi);
+ set_gpio_value(tms_gpio, tms);
+ set_gpio_value(tck_gpio, tck); /* Write clock last */
+
+ for (unsigned int i = 0; i < jtag_delay; ++i)
+ asm volatile ("");
+
+ return ERROR_OK;
+}
+
+static int am335xgpio_swd_write(int swclk, int swdio)
+{
+ set_gpio_value(swdio_gpio, swdio);
+ set_gpio_value(swclk_gpio, swclk); /* Write clock last */
+
+ for (unsigned int i = 0; i < jtag_delay; ++i)
+ asm volatile ("");
+
+ return ERROR_OK;
+}
+
+/* (1) assert or (0) deassert reset lines */
+static int am335xgpio_reset(int trst, int srst)
+{
+ /* assume active low */
+ if (is_gpio_valid(srst_gpio)) {
+ if (jtag_get_reset_config() & RESET_SRST_PUSH_PULL)
+ set_gpio_mode(srst_gpio, srst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_OUTPUT_HIGH);
+ else
+ set_gpio_mode(srst_gpio, srst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_INPUT);
+ }
+
+ /* assume active low */
+ if (is_gpio_valid(trst_gpio)) {
+ if (jtag_get_reset_config() & RESET_TRST_OPEN_DRAIN)
+ set_gpio_mode(trst_gpio, trst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_INPUT);
+ else
+ set_gpio_mode(trst_gpio, trst ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_OUTPUT_HIGH);
+ }
+
+ LOG_DEBUG("am335xgpio_reset(%d, %d), trst_gpio: %d (%s), srst_gpio: %d (%s)",
+ trst, srst,
+ trst_gpio, get_gpio_mode_name(get_gpio_mode(trst_gpio)),
+ srst_gpio, get_gpio_mode_name(get_gpio_mode(srst_gpio)));
+ return ERROR_OK;
+}
+
+static void am335xgpio_swdio_drive(bool is_output)
+{
+ if (is_output) {
+ set_gpio_value(swdio_dir_gpio, swdio_dir_is_active_high ? 1 : 0);
+ set_gpio_mode(swdio_gpio, AM335XGPIO_GPIO_MODE_OUTPUT);
+ } else {
+ set_gpio_mode(swdio_gpio, AM335XGPIO_GPIO_MODE_INPUT);
+ set_gpio_value(swdio_dir_gpio, swdio_dir_is_active_high ? 0 : 1);
+ }
+}
+
+static int am335xgpio_swdio_read(void)
+{
+ return get_gpio_value(swdio_gpio);
+}
+
+static int am335xgpio_blink(int on)
+{
+ if (is_gpio_valid(led_gpio))
+ set_gpio_value(led_gpio, (!on ^ led_is_active_high) ? 1 : 0);
+
+ return ERROR_OK;
+}
+
+static struct bitbang_interface am335xgpio_bitbang = {
+ .read = am335xgpio_read,
+ .write = am335xgpio_write,
+ .swdio_read = am335xgpio_swdio_read,
+ .swdio_drive = am335xgpio_swdio_drive,
+ .swd_write = am335xgpio_swd_write,
+ .blink = am335xgpio_blink
+};
+
+static int am335xgpio_khz(int khz, int *jtag_speed)
+{
+ if (!khz) {
+ LOG_DEBUG("RCLK not supported");
+ return ERROR_FAIL;
+ }
+ *jtag_speed = speed_coeff / khz - speed_offset;
+ if (*jtag_speed < 0)
+ *jtag_speed = 0;
+ return ERROR_OK;
+}
+
+static int am335xgpio_speed_div(int speed, int *khz)
+{
+ *khz = speed_coeff / (speed + speed_offset);
+ return ERROR_OK;
+}
+
+static int am335xgpio_speed(int speed)
+{
+ jtag_delay = speed;
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionums)
+{
+ if (CMD_ARGC == 4) {
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], tms_gpio);
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], tdi_gpio);
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[3], tdo_gpio);
+ } else if (CMD_ARGC != 0) {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ command_print(CMD, "AM335x GPIO config: tck = %d, tms = %d, tdi = %d, tdo = %d",
+ tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tck)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tck_gpio);
+
+ command_print(CMD, "AM335x GPIO config: tck = %d", tck_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tms)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tms_gpio);
+
+ command_print(CMD, "AM335x GPIO config: tms = %d", tms_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tdo)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdo_gpio);
+
+ command_print(CMD, "AM335x GPIO config: tdo = %d", tdo_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_tdi)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], tdi_gpio);
+
+ command_print(CMD, "AM335x GPIO config: tdi = %d", tdi_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_srst)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], srst_gpio);
+
+ command_print(CMD, "AM335x GPIO config: srst = %d", srst_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_jtag_gpionum_trst)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], trst_gpio);
+
+ command_print(CMD, "AM335x GPIO config: trst = %d", trst_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_swd_gpionums)
+{
+ if (CMD_ARGC == 2) {
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], swdio_gpio);
+ } else if (CMD_ARGC != 0) {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ command_print(CMD, "AM335x GPIO config: swclk = %d, swdio = %d", swclk_gpio, swdio_gpio);
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_swd_gpionum_swclk)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swclk_gpio);
+
+ command_print(CMD, "AM335x GPIO config: swclk = %d", swclk_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_swd_gpionum_swdio)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_gpio);
+
+ command_print(CMD, "AM335x GPIO config: swdio = %d", swdio_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_swd_gpionum_swdio_dir)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], swdio_dir_gpio);
+
+ command_print(CMD, "AM335x GPIO config: swdio_dir = %d", swdio_dir_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_swd_dir_output_state)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_BOOL(CMD_ARGV[0], swdio_dir_is_active_high, "high", "low");
+
+ command_print(CMD, "AM335x GPIO config: swdio_dir_output_state = %s", swdio_dir_is_active_high ? "high" : "low");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_gpionum_led)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], led_gpio);
+
+ command_print(CMD, "AM335x GPIO config: led = %d", led_gpio);
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_led_on_state)
+{
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_BOOL(CMD_ARGV[0], led_is_active_high, "high", "low");
+
+ command_print(CMD, "AM335x GPIO config: led_on_state = %s", led_is_active_high ? "high" : "low");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(am335xgpio_handle_speed_coeffs)
+{
+ if (CMD_ARGC == 2) {
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], speed_coeff);
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[1], speed_offset);
+ }
+
+ command_print(CMD, "AM335x GPIO config: speed_coeffs = %d, speed_offset = %d",
+ speed_coeff, speed_offset);
+ return ERROR_OK;
+}
+
+static const struct command_registration am335xgpio_subcommand_handlers[] = {
+ {
+ .name = "jtag_nums",
+ .handler = am335xgpio_handle_jtag_gpionums,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio numbers for tck, tms, tdi, tdo (in that order).",
+ .usage = "[tck tms tdi tdo]",
+ },
+ {
+ .name = "tck_num",
+ .handler = am335xgpio_handle_jtag_gpionum_tck,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for tck.",
+ .usage = "[tck]",
+ },
+ {
+ .name = "tms_num",
+ .handler = am335xgpio_handle_jtag_gpionum_tms,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for tms.",
+ .usage = "[tms]",
+ },
+ {
+ .name = "tdo_num",
+ .handler = am335xgpio_handle_jtag_gpionum_tdo,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for tdo.",
+ .usage = "[tdo]",
+ },
+ {
+ .name = "tdi_num",
+ .handler = am335xgpio_handle_jtag_gpionum_tdi,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for tdi.",
+ .usage = "[tdi]",
+ },
+ {
+ .name = "swd_nums",
+ .handler = am335xgpio_handle_swd_gpionums,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio numbers for swclk, swdio (in that order).",
+ .usage = "[swclk swdio]",
+ },
+ {
+ .name = "swclk_num",
+ .handler = am335xgpio_handle_swd_gpionum_swclk,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for swclk.",
+ .usage = "[swclk]",
+ },
+ {
+ .name = "swdio_num",
+ .handler = am335xgpio_handle_swd_gpionum_swdio,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for swdio.",
+ .usage = "[swdio]",
+ },
+ {
+ .name = "swdio_dir_num",
+ .handler = am335xgpio_handle_swd_gpionum_swdio_dir,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for swdio direction control pin.",
+ .usage = "[swdio_dir]",
+ },
+ {
+ .name = "swdio_dir_output_state",
+ .handler = am335xgpio_handle_swd_dir_output_state,
+ .mode = COMMAND_CONFIG,
+ .help = "required state for swdio_dir pin to select SWDIO buffer to be output.",
+ .usage = "['off'|'on']",
+ },
+ {
+ .name = "srst_num",
+ .handler = am335xgpio_handle_jtag_gpionum_srst,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for srst.",
+ .usage = "[srst]",
+ },
+ {
+ .name = "trst_num",
+ .handler = am335xgpio_handle_jtag_gpionum_trst,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for trst.",
+ .usage = "[trst]",
+ },
+ {
+ .name = "led_num",
+ .handler = am335xgpio_handle_gpionum_led,
+ .mode = COMMAND_CONFIG,
+ .help = "gpio number for led.",
+ .usage = "[led]",
+ },
+ {
+ .name = "led_on_state",
+ .handler = am335xgpio_handle_led_on_state,
+ .mode = COMMAND_CONFIG,
+ .help = "required state for led pin to turn on LED.",
+ .usage = "['off'|'on']",
+ },
+ {
+ .name = "speed_coeffs",
+ .handler = am335xgpio_handle_speed_coeffs,
+ .mode = COMMAND_CONFIG,
+ .help = "SPEED_COEFF and SPEED_OFFSET for delay calculations.",
+ .usage = "[SPEED_COEFF SPEED_OFFSET]",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration am335xgpio_command_handlers[] = {
+ {
+ .name = "am335xgpio",
+ .mode = COMMAND_ANY,
+ .help = "perform am335xgpio management",
+ .chain = am335xgpio_subcommand_handlers,
+ .usage = "",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const char * const am335xgpio_transports[] = { "jtag", "swd", NULL };
+
+static struct jtag_interface am335xgpio_interface = {
+ .supported = DEBUG_CAP_TMS_SEQ,
+ .execute_queue = bitbang_execute_queue,
+};
+
+static bool am335xgpio_jtag_mode_possible(void)
+{
+ if (!is_gpio_valid(tck_gpio))
+ return false;
+ if (!is_gpio_valid(tms_gpio))
+ return false;
+ if (!is_gpio_valid(tdi_gpio))
+ return false;
+ if (!is_gpio_valid(tdo_gpio))
+ return false;
+ return true;
+}
+
+static bool am335xgpio_swd_mode_possible(void)
+{
+ if (!is_gpio_valid(swclk_gpio))
+ return false;
+ if (!is_gpio_valid(swdio_gpio))
+ return false;
+ return true;
+}
+
+static int am335xgpio_init(void)
+{
+ bitbang_interface = &am335xgpio_bitbang;
+
+ LOG_INFO("AM335x GPIO JTAG/SWD bitbang driver");
+
+ if (transport_is_jtag() && !am335xgpio_jtag_mode_possible()) {
+ LOG_ERROR("Require tck, tms, tdi and tdo gpios for JTAG mode");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ if (transport_is_swd() && !am335xgpio_swd_mode_possible()) {
+ LOG_ERROR("Require swclk and swdio gpio for SWD mode");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ dev_mem_fd = open("/dev/gpiomem", O_RDWR | O_SYNC);
+ if (dev_mem_fd < 0) {
+ LOG_DEBUG("Cannot open /dev/gpiomem, fallback to /dev/mem");
+ dev_mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
+ }
+ if (dev_mem_fd < 0) {
+ LOG_ERROR("open: %s", strerror(errno));
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ for (unsigned int i = 0; i < AM335XGPIO_NUM_GPIO_PORTS; ++i) {
+ am335xgpio_gpio_port_mmap_addr[i] = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
+ MAP_SHARED, dev_mem_fd, am335xgpio_gpio_port_hw_addr[i]);
+
+ if (am335xgpio_gpio_port_mmap_addr[i] == MAP_FAILED) {
+ LOG_ERROR("mmap: %s", strerror(errno));
+ close(dev_mem_fd);
+ return ERROR_JTAG_INIT_FAILED;
+ }
+ }
+
+ /*
+ * Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST as outputs.
+ * Drive TDI and TCK low, and TMS high.
+ */
+ if (transport_is_jtag()) {
+ tdo_gpio_mode = get_gpio_mode(tdo_gpio);
+ tdi_gpio_mode = get_gpio_mode(tdi_gpio);
+ tck_gpio_mode = get_gpio_mode(tck_gpio);
+ tms_gpio_mode = get_gpio_mode(tms_gpio);
+ LOG_DEBUG("saved GPIO mode for tdo (GPIO #%d): %s", tdo_gpio, get_gpio_mode_name(tdo_gpio_mode));
+ LOG_DEBUG("saved GPIO mode for tdi (GPIO #%d): %s", tdi_gpio, get_gpio_mode_name(tdi_gpio_mode));
+ LOG_DEBUG("saved GPIO mode for tck (GPIO #%d): %s", tck_gpio, get_gpio_mode_name(tck_gpio_mode));
+ LOG_DEBUG("saved GPIO mode for tms (GPIO #%d): %s", tms_gpio, get_gpio_mode_name(tms_gpio_mode));
+
+ set_gpio_mode(tdo_gpio, AM335XGPIO_GPIO_MODE_INPUT);
+ set_gpio_mode(tdi_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW);
+ set_gpio_mode(tms_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_HIGH);
+ set_gpio_mode(tck_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW);
+
+ if (is_gpio_valid(trst_gpio)) {
+ trst_gpio_mode = get_gpio_mode(trst_gpio);
+ LOG_DEBUG("saved GPIO mode for trst (GPIO #%d): %s", trst_gpio, get_gpio_mode_name(trst_gpio_mode));
+ }
+ }
+
+ if (transport_is_swd()) {
+ swclk_gpio_mode = get_gpio_mode(swclk_gpio);
+ swdio_gpio_mode = get_gpio_mode(swdio_gpio);
+ LOG_DEBUG("saved GPIO mode for swclk (GPIO #%d): %s", swclk_gpio, get_gpio_mode_name(swclk_gpio_mode));
+ LOG_DEBUG("saved GPIO mode for swdio (GPIO #%d): %s", swdio_gpio, get_gpio_mode_name(swdio_gpio_mode));
+ if (is_gpio_valid(swdio_dir_gpio)) {
+ swdio_dir_gpio_mode = get_gpio_mode(swdio_dir_gpio);
+ LOG_DEBUG("saved GPIO mode for swdio_dir (GPIO #%d): %s",
+ swdio_dir_gpio, get_gpio_mode_name(swdio_dir_gpio_mode));
+ set_gpio_mode(swdio_dir_gpio,
+ swdio_dir_is_active_high ? AM335XGPIO_GPIO_MODE_OUTPUT_HIGH : AM335XGPIO_GPIO_MODE_OUTPUT_LOW);
+
+ }
+ set_gpio_mode(swdio_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW);
+ set_gpio_mode(swclk_gpio, AM335XGPIO_GPIO_MODE_OUTPUT_LOW);
+ }
+
+ if (is_gpio_valid(srst_gpio)) {
+ srst_gpio_mode = get_gpio_mode(srst_gpio);
+ LOG_DEBUG("saved GPIO mode for srst (GPIO #%d): %s", srst_gpio, get_gpio_mode_name(srst_gpio_mode));
+ }
+
+ if (is_gpio_valid(led_gpio)) {
+ led_gpio_mode = get_gpio_mode(led_gpio);
+ LOG_DEBUG("saved GPIO mode for led (GPIO #%d): %s", led_gpio, get_gpio_mode_name(led_gpio_mode));
+ set_gpio_mode(led_gpio,
+ led_is_active_high ? AM335XGPIO_GPIO_MODE_OUTPUT_LOW : AM335XGPIO_GPIO_MODE_OUTPUT_HIGH);
+ }
+
+ /* Set GPIO modes for TRST and SRST and make both inactive */
+ am335xgpio_reset(0, 0);
+ return ERROR_OK;
+}
+
+static int am335xgpio_quit(void)
+{
+ if (transport_is_jtag()) {
+ set_gpio_mode(tdo_gpio, tdo_gpio_mode);
+ set_gpio_mode(tdi_gpio, tdi_gpio_mode);
+ set_gpio_mode(tck_gpio, tck_gpio_mode);
+ set_gpio_mode(tms_gpio, tms_gpio_mode);
+ if (is_gpio_valid(trst_gpio))
+ set_gpio_mode(trst_gpio, trst_gpio_mode);
+ }
+
+ if (transport_is_swd()) {
+ set_gpio_mode(swclk_gpio, swclk_gpio_mode);
+ set_gpio_mode(swdio_gpio, swdio_gpio_mode);
+ if (is_gpio_valid(swdio_dir_gpio))
+ set_gpio_mode(swdio_dir_gpio, swdio_dir_gpio_mode);
+ }
+
+ if (is_gpio_valid(srst_gpio))
+ set_gpio_mode(srst_gpio, srst_gpio_mode);
+
+ if (is_gpio_valid(led_gpio))
+ set_gpio_mode(led_gpio, led_gpio_mode);
+
+ return ERROR_OK;
+}
+
+struct adapter_driver am335xgpio_adapter_driver = {
+ .name = "am335xgpio",
+ .transports = am335xgpio_transports,
+ .commands = am335xgpio_command_handlers,
+
+ .init = am335xgpio_init,
+ .quit = am335xgpio_quit,
+ .reset = am335xgpio_reset,
+ .speed = am335xgpio_speed,
+ .khz = am335xgpio_khz,
+ .speed_div = am335xgpio_speed_div,
+
+ .jtag_ops = &am335xgpio_interface,
+ .swd_ops = &bitbang_swd,
+};
diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c
index b7a4d99..22d237f 100644
--- a/src/jtag/drivers/bcm2835gpio.c
+++ b/src/jtag/drivers/bcm2835gpio.c
@@ -94,6 +94,11 @@ static int speed_coeff = 113714;
static int speed_offset = 28;
static unsigned int jtag_delay;
+static int is_gpio_valid(int gpio)
+{
+ return gpio >= 0 && gpio <= 31;
+}
+
static bb_value_t bcm2835gpio_read(void)
{
return (GPIO_LEV & 1<<tdo_gpio) ? BB_HIGH : BB_LOW;
@@ -133,12 +138,12 @@ static int bcm2835gpio_reset(int trst, int srst)
uint32_t set = 0;
uint32_t clear = 0;
- if (trst_gpio > 0) {
+ if (is_gpio_valid(trst_gpio)) {
set |= !trst<<trst_gpio;
clear |= trst<<trst_gpio;
}
- if (srst_gpio > 0) {
+ if (is_gpio_valid(srst_gpio)) {
set |= !srst<<srst_gpio;
clear |= srst<<srst_gpio;
}
@@ -151,7 +156,7 @@ static int bcm2835gpio_reset(int trst, int srst)
static void bcm2835_swdio_drive(bool is_output)
{
- if (swdio_dir_gpio > 0) {
+ if (is_gpio_valid(swdio_dir_gpio)) {
if (is_output) {
GPIO_SET = 1 << swdio_dir_gpio;
OUT_GPIO(swdio_gpio);
@@ -196,11 +201,6 @@ static int bcm2835gpio_speed(int speed)
return ERROR_OK;
}
-static int is_gpio_valid(int gpio)
-{
- return gpio >= 0 && gpio <= 31;
-}
-
COMMAND_HANDLER(bcm2835gpio_handle_jtag_gpionums)
{
if (CMD_ARGC == 4) {
@@ -557,7 +557,7 @@ static int bcm2835gpio_init(void)
OUT_GPIO(tck_gpio);
OUT_GPIO(tms_gpio);
- if (trst_gpio != -1) {
+ if (is_gpio_valid(trst_gpio)) {
trst_gpio_mode = MODE_GPIO(trst_gpio);
GPIO_SET = 1 << trst_gpio;
OUT_GPIO(trst_gpio);
@@ -566,7 +566,7 @@ static int bcm2835gpio_init(void)
if (transport_is_swd()) {
/* Make buffer an output before the GPIO connected to it */
- if (swdio_dir_gpio != -1) {
+ if (is_gpio_valid(swdio_dir_gpio)) {
swdio_dir_gpio_mode = MODE_GPIO(swdio_dir_gpio);
GPIO_SET = 1 << swdio_dir_gpio;
OUT_GPIO(swdio_dir_gpio);
@@ -581,7 +581,7 @@ static int bcm2835gpio_init(void)
OUT_GPIO(swdio_gpio);
}
- if (srst_gpio != -1) {
+ if (is_gpio_valid(srst_gpio)) {
srst_gpio_mode = MODE_GPIO(srst_gpio);
GPIO_SET = 1 << srst_gpio;
OUT_GPIO(srst_gpio);
@@ -601,7 +601,7 @@ static int bcm2835gpio_quit(void)
SET_MODE_GPIO(tdi_gpio, tdi_gpio_mode);
SET_MODE_GPIO(tck_gpio, tck_gpio_mode);
SET_MODE_GPIO(tms_gpio, tms_gpio_mode);
- if (trst_gpio != -1)
+ if (is_gpio_valid(trst_gpio))
SET_MODE_GPIO(trst_gpio, trst_gpio_mode);
}
@@ -610,10 +610,10 @@ static int bcm2835gpio_quit(void)
SET_MODE_GPIO(swdio_gpio, swdio_gpio_mode);
}
- if (srst_gpio != -1)
+ if (is_gpio_valid(srst_gpio))
SET_MODE_GPIO(srst_gpio, srst_gpio_mode);
- if (swdio_dir_gpio != -1)
+ if (is_gpio_valid(swdio_dir_gpio))
SET_MODE_GPIO(swdio_dir_gpio, swdio_dir_gpio_mode);
return ERROR_OK;
diff --git a/src/jtag/drivers/bitbang.c b/src/jtag/drivers/bitbang.c
index 898d6d3..78dcb29 100644
--- a/src/jtag/drivers/bitbang.c
+++ b/src/jtag/drivers/bitbang.c
@@ -532,8 +532,9 @@ static void bitbang_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay
/* Devices do not reply to DP_TARGETSEL write cmd, ignore received ack */
bool check_ack = swd_cmd_returns_ack(cmd);
+ /* init the array to silence scan-build */
+ uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)] = {0};
for (;;) {
- uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
buf_set_u32(trn_ack_data_parity_trn, 1 + 3 + 1, 32, value);
buf_set_u32(trn_ack_data_parity_trn, 1 + 3 + 1 + 32, 1, parity_u32(value));
diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c
index 2785d9b..bb2c817 100644
--- a/src/jtag/drivers/stlink_usb.c
+++ b/src/jtag/drivers/stlink_usb.c
@@ -504,6 +504,8 @@ static inline int stlink_usb_xfer_noerrcheck(void *handle, const uint8_t *buf, i
#define STLINK_TCP_SS_CMD_NOT_AVAILABLE 0x00001053
#define STLINK_TCP_SS_TCP_ERROR 0x00002001
#define STLINK_TCP_SS_TCP_CANT_CONNECT 0x00002002
+#define STLINK_TCP_SS_TCP_CLOSE_ERROR 0x00002003
+#define STLINK_TCP_SS_TCP_BUSY 0x00002004
#define STLINK_TCP_SS_WIN32_ERROR 0x00010000
/*
@@ -971,6 +973,11 @@ static int stlink_tcp_send_cmd(void *handle, int send_size, int recv_size, bool
if (check_tcp_status) {
uint32_t tcp_ss = le_to_h_u32(h->tcp_backend_priv.recv_buf);
if (tcp_ss != STLINK_TCP_SS_OK) {
+ if (tcp_ss == STLINK_TCP_SS_TCP_BUSY) {
+ LOG_DEBUG("TCP busy");
+ return ERROR_WAIT;
+ }
+
LOG_ERROR("TCP error status 0x%X", tcp_ss);
return ERROR_FAIL;
}
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index ddf70cc..9afd69d 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -150,6 +150,9 @@ extern struct adapter_driver stlink_dap_adapter_driver;
#if BUILD_RSHIM == 1
extern struct adapter_driver rshim_dap_adapter_driver;
#endif
+#if BUILD_AM335XGPIO == 1
+extern struct adapter_driver am335xgpio_adapter_driver;
+#endif
/**
* The list of built-in JTAG interfaces, containing entries for those
@@ -264,5 +267,8 @@ struct adapter_driver *adapter_drivers[] = {
#if BUILD_RSHIM == 1
&rshim_dap_adapter_driver,
#endif
+#if BUILD_AM335XGPIO == 1
+ &am335xgpio_adapter_driver,
+#endif
NULL,
};
diff --git a/src/openocd.c b/src/openocd.c
index 3c96d32..fdc4a87 100644
--- a/src/openocd.c
+++ b/src/openocd.c
@@ -182,6 +182,9 @@ COMMAND_HANDLER(handle_init_command)
target_register_event_callback(log_target_callback_event_handler, CMD_CTX);
+ if (command_run_line(CMD_CTX, "_run_post_init_commands") != ERROR_OK)
+ return ERROR_FAIL;
+
return ERROR_OK;
}
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index edea5f5..e5bf74f 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -3716,12 +3716,14 @@ static int gdb_input_inner(struct connection *connection)
break;
case 'j':
+ /* DEPRECATED */
/* packet supported only by smp target i.e cortex_a.c*/
/* handle smp packet replying coreid played to gbd */
gdb_read_smp_packet(connection, packet, packet_size);
break;
case 'J':
+ /* DEPRECATED */
/* packet supported only by smp target i.e cortex_a.c */
/* handle smp packet setting coreid to be played at next
* resume to gdb */
diff --git a/src/server/server.c b/src/server/server.c
index 7b0004d..082febb 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -763,6 +763,8 @@ COMMAND_HANDLER(handle_shutdown_command)
shutdown_openocd = SHUTDOWN_REQUESTED;
+ command_run_line(CMD_CTX, "_run_pre_shutdown_commands");
+
if (CMD_ARGC == 1) {
if (!strcmp(CMD_ARGV[0], "error")) {
shutdown_openocd = SHUTDOWN_WITH_ERROR_CODE;
diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c
index 791a1a5..8e79626 100644
--- a/src/server/telnet_server.c
+++ b/src/server/telnet_server.c
@@ -222,9 +222,8 @@ static int telnet_new_connection(struct connection *connection)
{
struct telnet_connection *telnet_connection;
struct telnet_service *telnet_service = connection->service->priv;
- int i;
- telnet_connection = malloc(sizeof(struct telnet_connection));
+ telnet_connection = calloc(1, sizeof(struct telnet_connection));
if (!telnet_connection) {
LOG_ERROR("Failed to allocate telnet connection.");
@@ -234,9 +233,6 @@ static int telnet_new_connection(struct connection *connection)
connection->priv = telnet_connection;
/* initialize telnet connection information */
- telnet_connection->closed = false;
- telnet_connection->line_size = 0;
- telnet_connection->line_cursor = 0;
telnet_connection->prompt = strdup("> ");
telnet_connection->prompt_visible = true;
telnet_connection->state = TELNET_STATE_DATA;
@@ -257,11 +253,6 @@ static int telnet_new_connection(struct connection *connection)
telnet_write(connection, "\r", 1);
telnet_prompt(connection);
- /* initialize history */
- for (i = 0; i < TELNET_LINE_HISTORY_SIZE; i++)
- telnet_connection->history[i] = NULL;
- telnet_connection->next_history = 0;
- telnet_connection->current_history = 0;
telnet_load_history(telnet_connection);
log_add_callback(telnet_log_callback, connection);
@@ -624,7 +615,11 @@ static void telnet_auto_complete(struct connection *connection)
while ((usr_cmd_pos < t_con->line_cursor) && isspace(t_con->line[usr_cmd_pos]))
usr_cmd_pos++;
- /* user command length */
+ /* check user command length */
+ if (t_con->line_cursor < usr_cmd_pos) {
+ telnet_bell(connection);
+ return;
+ }
size_t usr_cmd_len = t_con->line_cursor - usr_cmd_pos;
/* optimize multiple spaces in the user command,
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 49e882f..799c3dd 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -1,5 +1,7 @@
%C%_libtarget_la_LIBADD = %D%/openrisc/libopenrisc.la \
- %D%/riscv/libriscv.la
+ %D%/riscv/libriscv.la \
+ %D%/xtensa/libxtensa.la \
+ %D%/espressif/libespressif.la
%C%_libtarget_la_CPPFLAGS = $(AM_CPPFLAGS)
@@ -260,3 +262,5 @@ ARC_SRC = \
include %D%/openrisc/Makefile.am
include %D%/riscv/Makefile.am
+include %D%/xtensa/Makefile.am
+include %D%/espressif/Makefile.am \ No newline at end of file
diff --git a/src/target/aarch64.c b/src/target/aarch64.c
index 8838da9..ecd9324 100644
--- a/src/target/aarch64.c
+++ b/src/target/aarch64.c
@@ -2026,9 +2026,13 @@ static int aarch64_deassert_reset(struct target *target)
if (target->state != TARGET_HALTED) {
LOG_WARNING("%s: ran after reset and before halt ...",
target_name(target));
- retval = target_halt(target);
- if (retval != ERROR_OK)
- return retval;
+ if (target_was_examined(target)) {
+ retval = aarch64_halt_one(target, HALT_LAZY);
+ if (retval != ERROR_OK)
+ return retval;
+ } else {
+ target->state = TARGET_UNKNOWN;
+ }
}
}
@@ -2574,20 +2578,13 @@ static int aarch64_examine_first(struct target *target)
armv8->debug_ap->memaccess_tck = 10;
if (!target->dbgbase_set) {
- target_addr_t dbgbase;
- /* Get ROM Table base */
- uint32_t apid;
- int32_t coreidx = target->coreid;
- retval = dap_get_debugbase(armv8->debug_ap, &dbgbase, &apid);
- if (retval != ERROR_OK)
- return retval;
/* Lookup Processor DAP */
- retval = dap_lookup_cs_component(armv8->debug_ap, dbgbase, ARM_CS_C9_DEVTYPE_CORE_DEBUG,
- &armv8->debug_base, &coreidx);
+ retval = dap_lookup_cs_component(armv8->debug_ap, ARM_CS_C9_DEVTYPE_CORE_DEBUG,
+ &armv8->debug_base, target->coreid);
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("Detected core %" PRId32 " dbgbase: " TARGET_ADDR_FMT
- " apid: %08" PRIx32, coreidx, armv8->debug_base, apid);
+ LOG_DEBUG("Detected core %" PRId32 " dbgbase: " TARGET_ADDR_FMT,
+ target->coreid, armv8->debug_base);
} else
armv8->debug_base = target->dbgbase;
diff --git a/src/target/adi_v5_jtag.c b/src/target/adi_v5_jtag.c
index be62580..94ee8cf 100644
--- a/src/target/adi_v5_jtag.c
+++ b/src/target/adi_v5_jtag.c
@@ -532,7 +532,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap)
/* check for overrun condition in the last batch of transactions */
if (found_wait) {
- LOG_INFO("DAP transaction stalled (WAIT) - slowing down");
+ LOG_INFO("DAP transaction stalled (WAIT) - slowing down and resending");
/* clear the sticky overrun condition */
retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC,
DP_CTRL_STAT, DPAP_WRITE,
@@ -574,7 +574,7 @@ static int jtagdp_overrun_check(struct adiv5_dap *dap)
retval = ERROR_JTAG_DEVICE_ERROR;
break;
}
- LOG_INFO("DAP transaction stalled during replay (WAIT) - resending");
+ LOG_DEBUG("DAP transaction stalled during replay (WAIT) - resending");
/* clear the sticky overrun condition */
retval = adi_jtag_scan_inout_check_u32(dap, JTAG_DP_DPACC,
DP_CTRL_STAT, DPAP_WRITE,
diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c
index 8d6d661..4d5f02b 100644
--- a/src/target/arm_adi_v5.c
+++ b/src/target/arm_adi_v5.c
@@ -893,6 +893,55 @@ static const char *class_description[16] = {
[0xF] = "CoreLink, PrimeCell or System component",
};
+#define ARCH_ID(architect, archid) ( \
+ (((architect) << ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT) & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) | \
+ (((archid) << ARM_CS_C9_DEVARCH_ARCHID_SHIFT) & ARM_CS_C9_DEVARCH_ARCHID_MASK) \
+)
+
+static const struct {
+ uint32_t arch_id;
+ const char *description;
+} class0x9_devarch[] = {
+ /* keep same unsorted order as in ARM IHI0029E */
+ { ARCH_ID(ARM_ID, 0x0A00), "RAS architecture" },
+ { ARCH_ID(ARM_ID, 0x1A01), "Instrumentation Trace Macrocell (ITM) architecture" },
+ { ARCH_ID(ARM_ID, 0x1A02), "DWT architecture" },
+ { ARCH_ID(ARM_ID, 0x1A03), "Flash Patch and Breakpoint unit (FPB) architecture" },
+ { ARCH_ID(ARM_ID, 0x2A04), "Processor debug architecture (ARMv8-M)" },
+ { ARCH_ID(ARM_ID, 0x6A05), "Processor debug architecture (ARMv8-R)" },
+ { ARCH_ID(ARM_ID, 0x0A10), "PC sample-based profiling" },
+ { ARCH_ID(ARM_ID, 0x4A13), "Embedded Trace Macrocell (ETM) architecture" },
+ { ARCH_ID(ARM_ID, 0x1A14), "Cross Trigger Interface (CTI) architecture" },
+ { ARCH_ID(ARM_ID, 0x6A15), "Processor debug architecture (v8.0-A)" },
+ { ARCH_ID(ARM_ID, 0x7A15), "Processor debug architecture (v8.1-A)" },
+ { ARCH_ID(ARM_ID, 0x8A15), "Processor debug architecture (v8.2-A)" },
+ { ARCH_ID(ARM_ID, 0x2A16), "Processor Performance Monitor (PMU) architecture" },
+ { ARCH_ID(ARM_ID, 0x0A17), "Memory Access Port v2 architecture" },
+ { ARCH_ID(ARM_ID, 0x0A27), "JTAG Access Port v2 architecture" },
+ { ARCH_ID(ARM_ID, 0x0A31), "Basic trace router" },
+ { ARCH_ID(ARM_ID, 0x0A37), "Power requestor" },
+ { ARCH_ID(ARM_ID, 0x0A47), "Unknown Access Port v2 architecture" },
+ { ARCH_ID(ARM_ID, 0x0A50), "HSSTP architecture" },
+ { ARCH_ID(ARM_ID, 0x0A63), "System Trace Macrocell (STM) architecture" },
+ { ARCH_ID(ARM_ID, 0x0A75), "CoreSight ELA architecture" },
+ { ARCH_ID(ARM_ID, 0x0AF7), "CoreSight ROM architecture" },
+};
+
+#define DEVARCH_ID_MASK (ARM_CS_C9_DEVARCH_ARCHITECT_MASK | ARM_CS_C9_DEVARCH_ARCHID_MASK)
+#define DEVARCH_ROM_C_0X9 ARCH_ID(ARM_ID, 0x0AF7)
+
+static const char *class0x9_devarch_description(uint32_t devarch)
+{
+ if (!(devarch & ARM_CS_C9_DEVARCH_PRESENT))
+ return "not present";
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(class0x9_devarch); i++)
+ if ((devarch & DEVARCH_ID_MASK) == class0x9_devarch[i].arch_id)
+ return class0x9_devarch[i].description;
+
+ return "unknown";
+}
+
static const struct {
enum ap_type type;
const char *description;
@@ -953,7 +1002,7 @@ int dap_find_ap(struct adiv5_dap *dap, enum ap_type type_to_find, struct adiv5_a
return ERROR_FAIL;
}
-int dap_get_debugbase(struct adiv5_ap *ap,
+static int dap_get_debugbase(struct adiv5_ap *ap,
target_addr_t *dbgbase, uint32_t *apid)
{
struct adiv5_dap *dap = ap->dap;
@@ -989,109 +1038,89 @@ int dap_get_debugbase(struct adiv5_ap *ap,
return ERROR_OK;
}
-int dap_lookup_cs_component(struct adiv5_ap *ap,
- target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx)
-{
- uint32_t romentry, entry_offset = 0, devtype;
+/** Holds registers and coordinates of a CoreSight component */
+struct cs_component_vals {
+ struct adiv5_ap *ap;
target_addr_t component_base;
- int retval;
+ uint64_t pid;
+ uint32_t cid;
+ uint32_t devarch;
+ uint32_t devid;
+ uint32_t devtype_memtype;
+};
- dbgbase &= 0xFFFFFFFFFFFFF000ull;
- *addr = 0;
+/**
+ * Read the CoreSight registers needed during ROM Table Parsing (RTP).
+ *
+ * @param ap Pointer to AP containing the component.
+ * @param component_base On MEM-AP access method, base address of the component.
+ * @param v Pointer to the struct holding the value of registers.
+ *
+ * @return ERROR_OK on success, else a fault code.
+ */
+static int rtp_read_cs_regs(struct adiv5_ap *ap, target_addr_t component_base,
+ struct cs_component_vals *v)
+{
+ assert(IS_ALIGNED(component_base, ARM_CS_ALIGN));
+ assert(ap && v);
- do {
- retval = mem_ap_read_atomic_u32(ap, dbgbase |
- entry_offset, &romentry);
- if (retval != ERROR_OK)
- return retval;
+ uint32_t cid0, cid1, cid2, cid3;
+ uint32_t pid0, pid1, pid2, pid3, pid4;
+ int retval = ERROR_OK;
- component_base = dbgbase + (target_addr_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK);
+ v->ap = ap;
+ v->component_base = component_base;
- if (romentry & ARM_CS_ROMENTRY_PRESENT) {
- uint32_t c_cid1;
- retval = mem_ap_read_atomic_u32(ap, component_base + ARM_CS_CIDR1, &c_cid1);
- if (retval != ERROR_OK) {
- LOG_ERROR("Can't read component with base address " TARGET_ADDR_FMT
- ", the corresponding core might be turned off", component_base);
- return retval;
- }
- unsigned int class = (c_cid1 & ARM_CS_CIDR1_CLASS_MASK) >> ARM_CS_CIDR1_CLASS_SHIFT;
- if (class == ARM_CS_CLASS_0X1_ROM_TABLE) {
- retval = dap_lookup_cs_component(ap, component_base,
- type, addr, idx);
- if (retval == ERROR_OK)
- break;
- if (retval != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
- return retval;
- }
+ /* sort by offset to gain speed */
- retval = mem_ap_read_atomic_u32(ap, component_base + ARM_CS_C9_DEVTYPE, &devtype);
- if (retval != ERROR_OK)
- return retval;
- if ((devtype & ARM_CS_C9_DEVTYPE_MASK) == type) {
- if (!*idx) {
- *addr = component_base;
- break;
- } else
- (*idx)--;
- }
- }
- entry_offset += 4;
- } while ((romentry > 0) && (entry_offset < 0xf00));
+ /*
+ * Registers DEVARCH, DEVID and DEVTYPE are valid on Class 0x9 devices
+ * only, but are at offset above 0xf00, so can be read on any device
+ * without triggering error. Read them for eventual use on Class 0x9.
+ */
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVARCH, &v->devarch);
- if (!*addr)
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVID, &v->devid);
- return ERROR_OK;
-}
+ /* Same address as ARM_CS_C1_MEMTYPE */
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_C9_DEVTYPE, &v->devtype_memtype);
-static int dap_read_part_id(struct adiv5_ap *ap, target_addr_t component_base, uint32_t *cid, uint64_t *pid)
-{
- assert(IS_ALIGNED(component_base, ARM_CS_ALIGN));
- assert(ap && cid && pid);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR4, &pid4);
- uint32_t cid0, cid1, cid2, cid3;
- uint32_t pid0, pid1, pid2, pid3, pid4;
- int retval;
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR0, &pid0);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR1, &pid1);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR2, &pid2);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR3, &pid3);
- /* IDs are in last 4K section */
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR0, &pid0);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR1, &pid1);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR2, &pid2);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR3, &pid3);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_PIDR4, &pid4);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR0, &cid0);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR1, &cid1);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR2, &cid2);
- if (retval != ERROR_OK)
- return retval;
- retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR3, &cid3);
- if (retval != ERROR_OK)
- return retval;
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR0, &cid0);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR1, &cid1);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR2, &cid2);
+ if (retval == ERROR_OK)
+ retval = mem_ap_read_u32(ap, component_base + ARM_CS_CIDR3, &cid3);
- retval = dap_run(ap->dap);
- if (retval != ERROR_OK)
+ if (retval == ERROR_OK)
+ retval = dap_run(ap->dap);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Failed read CoreSight registers");
return retval;
+ }
- *cid = (cid3 & 0xff) << 24
+ v->cid = (cid3 & 0xff) << 24
| (cid2 & 0xff) << 16
| (cid1 & 0xff) << 8
| (cid0 & 0xff);
- *pid = (uint64_t)(pid4 & 0xff) << 32
+ v->pid = (uint64_t)(pid4 & 0xff) << 32
| (pid3 & 0xff) << 24
| (pid2 & 0xff) << 16
| (pid1 & 0xff) << 8
@@ -1397,122 +1426,269 @@ static int dap_devtype_display(struct command_invocation *cmd, uint32_t devtype)
return ERROR_OK;
}
-static int dap_rom_display(struct command_invocation *cmd,
- struct adiv5_ap *ap, target_addr_t dbgbase, int depth)
-{
- int retval;
- uint64_t pid;
- uint32_t cid;
- char tabs[16] = "";
+/**
+ * Actions/operations to be executed while parsing ROM tables.
+ */
+struct rtp_ops {
+ /**
+ * Executed at the start of a new MEM-AP, typically to print the MEM-AP header.
+ * @param retval Error encountered while reading AP.
+ * @param ap Pointer to AP.
+ * @param dbgbase Value of MEM-AP Debug Base Address register.
+ * @param apid Value of MEM-AP IDR Identification Register.
+ * @param priv Pointer to private data.
+ * @return ERROR_OK on success, else a fault code.
+ */
+ int (*mem_ap_header)(int retval, struct adiv5_ap *ap, uint64_t dbgbase,
+ uint32_t apid, void *priv);
+ /**
+ * Executed when a CoreSight component is parsed, typically to print
+ * information on the component.
+ * @param retval Error encountered while reading component's registers.
+ * @param v Pointer to a container of the component's registers.
+ * @param depth The current depth level of ROM table.
+ * @param priv Pointer to private data.
+ * @return ERROR_OK on success, else a fault code.
+ */
+ int (*cs_component)(int retval, struct cs_component_vals *v, int depth, void *priv);
+ /**
+ * Executed for each entry of a ROM table, typically to print the entry
+ * and information about validity or end-of-table mark.
+ * @param retval Error encountered while reading the ROM table entry.
+ * @param depth The current depth level of ROM table.
+ * @param offset The offset of the entry in the ROM table.
+ * @param romentry The value of the ROM table entry.
+ * @param priv Pointer to private data.
+ * @return ERROR_OK on success, else a fault code.
+ */
+ int (*rom_table_entry)(int retval, int depth, unsigned int offset, uint64_t romentry,
+ void *priv);
+ /**
+ * Private data
+ */
+ void *priv;
+};
- if (depth > 16) {
- command_print(cmd, "\tTables too deep");
- return ERROR_FAIL;
- }
+/**
+ * Wrapper around struct rtp_ops::mem_ap_header.
+ * Input parameter @a retval is propagated.
+ */
+static int rtp_ops_mem_ap_header(const struct rtp_ops *ops,
+ int retval, struct adiv5_ap *ap, uint64_t dbgbase, uint32_t apid)
+{
+ if (!ops->mem_ap_header)
+ return retval;
- if (depth)
- snprintf(tabs, sizeof(tabs), "[L%02d] ", depth);
+ int retval1 = ops->mem_ap_header(retval, ap, dbgbase, apid, ops->priv);
+ if (retval != ERROR_OK)
+ return retval;
+ return retval1;
+}
- target_addr_t base_addr = dbgbase & 0xFFFFFFFFFFFFF000ull;
- command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, base_addr);
+/**
+ * Wrapper around struct rtp_ops::cs_component.
+ * Input parameter @a retval is propagated.
+ */
+static int rtp_ops_cs_component(const struct rtp_ops *ops,
+ int retval, struct cs_component_vals *v, int depth)
+{
+ if (!ops->cs_component)
+ return retval;
- retval = dap_read_part_id(ap, base_addr, &cid, &pid);
- if (retval != ERROR_OK) {
- command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off");
- return ERROR_OK; /* Don't abort recursion */
- }
+ int retval1 = ops->cs_component(retval, v, depth, ops->priv);
+ if (retval != ERROR_OK)
+ return retval;
+ return retval1;
+}
- if (!is_valid_arm_cs_cidr(cid)) {
- command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, cid);
- return ERROR_OK; /* Don't abort recursion */
- }
+/**
+ * Wrapper around struct rtp_ops::rom_table_entry.
+ * Input parameter @a retval is propagated.
+ */
+static int rtp_ops_rom_table_entry(const struct rtp_ops *ops,
+ int retval, int depth, unsigned int offset, uint64_t romentry)
+{
+ if (!ops->rom_table_entry)
+ return retval;
- /* component may take multiple 4K pages */
- uint32_t size = ARM_CS_PIDR_SIZE(pid);
- if (size > 0)
- command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, base_addr - 0x1000 * size);
+ int retval1 = ops->rom_table_entry(retval, depth, offset, romentry, ops->priv);
+ if (retval != ERROR_OK)
+ return retval;
+ return retval1;
+}
- command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, pid);
+/* Broken ROM tables can have circular references. Stop after a while */
+#define ROM_TABLE_MAX_DEPTH (16)
- const unsigned int class = (cid & ARM_CS_CIDR_CLASS_MASK) >> ARM_CS_CIDR_CLASS_SHIFT;
- const unsigned int part_num = ARM_CS_PIDR_PART(pid);
- unsigned int designer_id = ARM_CS_PIDR_DESIGNER(pid);
+/**
+ * Value used only during lookup of a CoreSight component in ROM table.
+ * Return CORESIGHT_COMPONENT_FOUND when component is found.
+ * Return ERROR_OK when component is not found yet.
+ * Return any other ERROR_* in case of error.
+ */
+#define CORESIGHT_COMPONENT_FOUND (1)
- if (pid & ARM_CS_PIDR_JEDEC) {
- /* JEP106 code */
- command_print(cmd, "\t\tDesigner is 0x%03x, %s",
- designer_id, jep106_manufacturer(designer_id));
- } else {
- /* Legacy ASCII ID, clear invalid bits */
- designer_id &= 0x7f;
- command_print(cmd, "\t\tDesigner ASCII code 0x%02x, %s",
- designer_id, designer_id == 0x41 ? "ARM" : "<unknown>");
- }
+static int rtp_cs_component(const struct rtp_ops *ops,
+ struct adiv5_ap *ap, target_addr_t dbgbase, int depth);
- const struct dap_part_nums *partnum = pidr_to_part_num(designer_id, part_num);
- command_print(cmd, "\t\tPart is 0x%03x, %s %s", part_num, partnum->type, partnum->full);
- command_print(cmd, "\t\tComponent class is 0x%x, %s", class, class_description[class]);
+static int rtp_rom_loop(const struct rtp_ops *ops,
+ struct adiv5_ap *ap, target_addr_t base_address, int depth,
+ unsigned int width, unsigned int max_entries)
+{
+ assert(IS_ALIGNED(base_address, ARM_CS_ALIGN));
+
+ unsigned int offset = 0;
+ while (max_entries--) {
+ uint64_t romentry;
+ uint32_t romentry_low, romentry_high;
+ target_addr_t component_base;
+ unsigned int saved_offset = offset;
+
+ int retval = mem_ap_read_u32(ap, base_address + offset, &romentry_low);
+ offset += 4;
+ if (retval == ERROR_OK && width == 64) {
+ retval = mem_ap_read_u32(ap, base_address + offset, &romentry_high);
+ offset += 4;
+ }
+ if (retval == ERROR_OK)
+ retval = dap_run(ap->dap);
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("Failed read ROM table entry");
+ return retval;
+ }
- if (class == ARM_CS_CLASS_0X1_ROM_TABLE) {
- uint32_t memtype;
- retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C1_MEMTYPE, &memtype);
+ if (width == 64) {
+ romentry = (((uint64_t)romentry_high) << 32) | romentry_low;
+ component_base = base_address +
+ ((((uint64_t)romentry_high) << 32) | (romentry_low & ARM_CS_ROMENTRY_OFFSET_MASK));
+ } else {
+ romentry = romentry_low;
+ /* "romentry" is signed */
+ component_base = base_address + (int32_t)(romentry_low & ARM_CS_ROMENTRY_OFFSET_MASK);
+ if (!is_64bit_ap(ap))
+ component_base = (uint32_t)component_base;
+ }
+ retval = rtp_ops_rom_table_entry(ops, retval, depth, saved_offset, romentry);
if (retval != ERROR_OK)
return retval;
- if (memtype & ARM_CS_C1_MEMTYPE_SYSMEM_MASK)
- command_print(cmd, "\t\tMEMTYPE system memory present on bus");
- else
- command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus");
+ if (romentry == 0) {
+ /* End of ROM table */
+ break;
+ }
- /* Read ROM table entries from base address until we get 0x00000000 or reach the reserved area */
- for (uint16_t entry_offset = 0; entry_offset < 0xF00; entry_offset += 4) {
- uint32_t romentry;
- retval = mem_ap_read_atomic_u32(ap, base_addr | entry_offset, &romentry);
- if (retval != ERROR_OK)
- return retval;
- command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%" PRIx32 "",
- tabs, entry_offset, romentry);
- if (romentry & ARM_CS_ROMENTRY_PRESENT) {
- /* Recurse. "romentry" is signed */
- retval = dap_rom_display(cmd, ap, base_addr + (int32_t)(romentry & ARM_CS_ROMENTRY_OFFSET_MASK),
- depth + 1);
- if (retval != ERROR_OK)
- return retval;
- } else if (romentry != 0) {
- command_print(cmd, "\t\tComponent not present");
- } else {
- command_print(cmd, "\t%s\tEnd of ROM table", tabs);
- break;
- }
+ if (!(romentry & ARM_CS_ROMENTRY_PRESENT))
+ continue;
+
+ /* Recurse */
+ retval = rtp_cs_component(ops, ap, component_base, depth + 1);
+ if (retval == CORESIGHT_COMPONENT_FOUND)
+ return CORESIGHT_COMPONENT_FOUND;
+ if (retval != ERROR_OK) {
+ /* TODO: do we need to send an ABORT before continuing? */
+ LOG_DEBUG("Ignore error parsing CoreSight component");
+ continue;
}
- } else if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) {
- uint32_t devtype;
- retval = mem_ap_read_atomic_u32(ap, base_addr + ARM_CS_C9_DEVTYPE, &devtype);
- if (retval != ERROR_OK)
- return retval;
+ }
- retval = dap_devtype_display(cmd, devtype);
- if (retval != ERROR_OK)
- return retval;
+ return ERROR_OK;
+}
- /* REVISIT also show ARM_CS_C9_DEVID */
+static int rtp_cs_component(const struct rtp_ops *ops,
+ struct adiv5_ap *ap, target_addr_t base_address, int depth)
+{
+ struct cs_component_vals v;
+ int retval;
+
+ assert(IS_ALIGNED(base_address, ARM_CS_ALIGN));
+
+ if (depth > ROM_TABLE_MAX_DEPTH)
+ retval = ERROR_FAIL;
+ else
+ retval = rtp_read_cs_regs(ap, base_address, &v);
+
+ retval = rtp_ops_cs_component(ops, retval, &v, depth);
+ if (retval == CORESIGHT_COMPONENT_FOUND)
+ return CORESIGHT_COMPONENT_FOUND;
+ if (retval != ERROR_OK)
+ return ERROR_OK; /* Don't abort recursion */
+
+ if (!is_valid_arm_cs_cidr(v.cid))
+ return ERROR_OK; /* Don't abort recursion */
+
+ const unsigned int class = ARM_CS_CIDR_CLASS(v.cid);
+
+ if (class == ARM_CS_CLASS_0X1_ROM_TABLE)
+ return rtp_rom_loop(ops, ap, base_address, depth, 32, 960);
+
+ if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) {
+ if ((v.devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0)
+ return ERROR_OK;
+
+ /* quit if not ROM table */
+ if ((v.devarch & DEVARCH_ID_MASK) != DEVARCH_ROM_C_0X9)
+ return ERROR_OK;
+
+ if ((v.devid & ARM_CS_C9_DEVID_FORMAT_MASK) == ARM_CS_C9_DEVID_FORMAT_64BIT)
+ return rtp_rom_loop(ops, ap, base_address, depth, 64, 256);
+ else
+ return rtp_rom_loop(ops, ap, base_address, depth, 32, 512);
}
+ /* Class other than 0x1 and 0x9 */
return ERROR_OK;
}
-int dap_info_command(struct command_invocation *cmd,
- struct adiv5_ap *ap)
+static int rtp_ap(const struct rtp_ops *ops, struct adiv5_ap *ap)
{
int retval;
uint32_t apid;
- target_addr_t dbgbase;
- target_addr_t dbgaddr;
+ target_addr_t dbgbase, invalid_entry;
/* Now we read ROM table ID registers, ref. ARM IHI 0029B sec */
retval = dap_get_debugbase(ap, &dbgbase, &apid);
if (retval != ERROR_OK)
return retval;
+ retval = rtp_ops_mem_ap_header(ops, retval, ap, dbgbase, apid);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (apid == 0)
+ return ERROR_FAIL;
+
+ /* NOTE: a MEM-AP may have a single CoreSight component that's
+ * not a ROM table ... or have no such components at all.
+ */
+ const unsigned int class = (apid & AP_REG_IDR_CLASS_MASK) >> AP_REG_IDR_CLASS_SHIFT;
+
+ if (class == AP_REG_IDR_CLASS_MEM_AP) {
+ if (is_64bit_ap(ap))
+ invalid_entry = 0xFFFFFFFFFFFFFFFFull;
+ else
+ invalid_entry = 0xFFFFFFFFul;
+
+ if (dbgbase != invalid_entry && (dbgbase & 0x3) != 0x2) {
+ retval = rtp_cs_component(ops, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0);
+ if (retval == CORESIGHT_COMPONENT_FOUND)
+ return CORESIGHT_COMPONENT_FOUND;
+ }
+ }
+
+ return ERROR_OK;
+}
+
+/* Actions for command "dap info" */
+
+static int dap_info_mem_ap_header(int retval, struct adiv5_ap *ap,
+ target_addr_t dbgbase, uint32_t apid, void *priv)
+{
+ struct command_invocation *cmd = priv;
+ target_addr_t invalid_entry;
+
+ if (retval != ERROR_OK) {
+ command_print(cmd, "\t\tCan't read MEM-AP, the corresponding core might be turned off");
+ return retval;
+ }
command_print(cmd, "AP ID register 0x%8.8" PRIx32, apid);
if (apid == 0) {
@@ -1529,27 +1705,221 @@ int dap_info_command(struct command_invocation *cmd,
if (class == AP_REG_IDR_CLASS_MEM_AP) {
if (is_64bit_ap(ap))
- dbgaddr = 0xFFFFFFFFFFFFFFFFull;
+ invalid_entry = 0xFFFFFFFFFFFFFFFFull;
else
- dbgaddr = 0xFFFFFFFFul;
+ invalid_entry = 0xFFFFFFFFul;
command_print(cmd, "MEM-AP BASE " TARGET_ADDR_FMT, dbgbase);
- if (dbgbase == dbgaddr || (dbgbase & 0x3) == 0x2) {
+ if (dbgbase == invalid_entry || (dbgbase & 0x3) == 0x2) {
command_print(cmd, "\tNo ROM table present");
} else {
if (dbgbase & 0x01)
command_print(cmd, "\tValid ROM table present");
else
command_print(cmd, "\tROM table in legacy format");
+ }
+ }
+
+ return ERROR_OK;
+}
+
+static int dap_info_cs_component(int retval, struct cs_component_vals *v, int depth, void *priv)
+{
+ struct command_invocation *cmd = priv;
+
+ if (depth > ROM_TABLE_MAX_DEPTH) {
+ command_print(cmd, "\tTables too deep");
+ return ERROR_FAIL;
+ }
+
+ command_print(cmd, "\t\tComponent base address " TARGET_ADDR_FMT, v->component_base);
+
+ if (retval != ERROR_OK) {
+ command_print(cmd, "\t\tCan't read component, the corresponding core might be turned off");
+ return retval;
+ }
+
+ if (!is_valid_arm_cs_cidr(v->cid)) {
+ command_print(cmd, "\t\tInvalid CID 0x%08" PRIx32, v->cid);
+ return ERROR_OK; /* Don't abort recursion */
+ }
+
+ /* component may take multiple 4K pages */
+ uint32_t size = ARM_CS_PIDR_SIZE(v->pid);
+ if (size > 0)
+ command_print(cmd, "\t\tStart address " TARGET_ADDR_FMT, v->component_base - 0x1000 * size);
+
+ command_print(cmd, "\t\tPeripheral ID 0x%010" PRIx64, v->pid);
+
+ const unsigned int part_num = ARM_CS_PIDR_PART(v->pid);
+ unsigned int designer_id = ARM_CS_PIDR_DESIGNER(v->pid);
+
+ if (v->pid & ARM_CS_PIDR_JEDEC) {
+ /* JEP106 code */
+ command_print(cmd, "\t\tDesigner is 0x%03x, %s",
+ designer_id, jep106_manufacturer(designer_id));
+ } else {
+ /* Legacy ASCII ID, clear invalid bits */
+ designer_id &= 0x7f;
+ command_print(cmd, "\t\tDesigner ASCII code 0x%02x, %s",
+ designer_id, designer_id == 0x41 ? "ARM" : "<unknown>");
+ }
- dap_rom_display(cmd, ap, dbgbase & 0xFFFFFFFFFFFFF000ull, 0);
+ const struct dap_part_nums *partnum = pidr_to_part_num(designer_id, part_num);
+ command_print(cmd, "\t\tPart is 0x%03x, %s %s", part_num, partnum->type, partnum->full);
+
+ const unsigned int class = ARM_CS_CIDR_CLASS(v->cid);
+ command_print(cmd, "\t\tComponent class is 0x%x, %s", class, class_description[class]);
+
+ if (class == ARM_CS_CLASS_0X1_ROM_TABLE) {
+ if (v->devtype_memtype & ARM_CS_C1_MEMTYPE_SYSMEM_MASK)
+ command_print(cmd, "\t\tMEMTYPE system memory present on bus");
+ else
+ command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus");
+ return ERROR_OK;
+ }
+
+ if (class == ARM_CS_CLASS_0X9_CS_COMPONENT) {
+ dap_devtype_display(cmd, v->devtype_memtype);
+
+ /* REVISIT also show ARM_CS_C9_DEVID */
+
+ if ((v->devarch & ARM_CS_C9_DEVARCH_PRESENT) == 0)
+ return ERROR_OK;
+
+ unsigned int architect_id = ARM_CS_C9_DEVARCH_ARCHITECT(v->devarch);
+ unsigned int revision = ARM_CS_C9_DEVARCH_REVISION(v->devarch);
+ command_print(cmd, "\t\tDev Arch is 0x%08" PRIx32 ", %s \"%s\" rev.%u", v->devarch,
+ jep106_manufacturer(architect_id), class0x9_devarch_description(v->devarch),
+ revision);
+
+ if ((v->devarch & DEVARCH_ID_MASK) == DEVARCH_ROM_C_0X9) {
+ command_print(cmd, "\t\tType is ROM table");
+
+ if (v->devid & ARM_CS_C9_DEVID_SYSMEM_MASK)
+ command_print(cmd, "\t\tMEMTYPE system memory present on bus");
+ else
+ command_print(cmd, "\t\tMEMTYPE system memory not present: dedicated debug bus");
}
+ return ERROR_OK;
}
+ /* Class other than 0x1 and 0x9 */
return ERROR_OK;
}
+static int dap_info_rom_table_entry(int retval, int depth,
+ unsigned int offset, uint64_t romentry, void *priv)
+{
+ struct command_invocation *cmd = priv;
+ char tabs[16] = "";
+
+ if (depth)
+ snprintf(tabs, sizeof(tabs), "[L%02d] ", depth);
+
+ if (retval != ERROR_OK) {
+ command_print(cmd, "\t%sROMTABLE[0x%x] Read error", tabs, offset);
+ command_print(cmd, "\t\tUnable to continue");
+ command_print(cmd, "\t%s\tStop parsing of ROM table", tabs);
+ return retval;
+ }
+
+ command_print(cmd, "\t%sROMTABLE[0x%x] = 0x%08" PRIx64,
+ tabs, offset, romentry);
+
+ if (romentry == 0) {
+ command_print(cmd, "\t%s\tEnd of ROM table", tabs);
+ return ERROR_OK;
+ }
+
+ if (!(romentry & ARM_CS_ROMENTRY_PRESENT)) {
+ command_print(cmd, "\t\tComponent not present");
+ return ERROR_OK;
+ }
+
+ return ERROR_OK;
+}
+
+int dap_info_command(struct command_invocation *cmd, struct adiv5_ap *ap)
+{
+ struct rtp_ops dap_info_ops = {
+ .mem_ap_header = dap_info_mem_ap_header,
+ .cs_component = dap_info_cs_component,
+ .rom_table_entry = dap_info_rom_table_entry,
+ .priv = cmd,
+ };
+
+ return rtp_ap(&dap_info_ops, ap);
+}
+
+/* Actions for dap_lookup_cs_component() */
+
+struct dap_lookup_data {
+ /* input */
+ unsigned int idx;
+ unsigned int type;
+ /* output */
+ uint64_t component_base;
+};
+
+static int dap_lookup_cs_component_cs_component(int retval,
+ struct cs_component_vals *v, int depth, void *priv)
+{
+ struct dap_lookup_data *lookup = priv;
+
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (!is_valid_arm_cs_cidr(v->cid))
+ return ERROR_OK;
+
+ const unsigned int class = ARM_CS_CIDR_CLASS(v->cid);
+ if (class != ARM_CS_CLASS_0X9_CS_COMPONENT)
+ return ERROR_OK;
+
+ if ((v->devtype_memtype & ARM_CS_C9_DEVTYPE_MASK) != lookup->type)
+ return ERROR_OK;
+
+ if (lookup->idx) {
+ /* search for next one */
+ --lookup->idx;
+ return ERROR_OK;
+ }
+
+ /* Found! */
+ lookup->component_base = v->component_base;
+ return CORESIGHT_COMPONENT_FOUND;
+}
+
+int dap_lookup_cs_component(struct adiv5_ap *ap, uint8_t type,
+ target_addr_t *addr, int32_t core_id)
+{
+ struct dap_lookup_data lookup = {
+ .type = type,
+ .idx = core_id,
+ };
+ struct rtp_ops dap_lookup_cs_component_ops = {
+ .mem_ap_header = NULL,
+ .cs_component = dap_lookup_cs_component_cs_component,
+ .rom_table_entry = NULL,
+ .priv = &lookup,
+ };
+
+ int retval = rtp_ap(&dap_lookup_cs_component_ops, ap);
+ if (retval == CORESIGHT_COMPONENT_FOUND) {
+ LOG_DEBUG("CS lookup found at 0x%" PRIx64, lookup.component_base);
+ *addr = lookup.component_base;
+ return ERROR_OK;
+ }
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("CS lookup error %d", retval);
+ return retval;
+ }
+ LOG_DEBUG("CS lookup not found");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+}
+
enum adiv5_cfg_param {
CFG_DAP,
CFG_AP_NUM,
@@ -1568,6 +1938,8 @@ static const struct jim_nvp nvp_config_opts[] = {
static int adiv5_jim_spot_configure(struct jim_getopt_info *goi,
struct adiv5_dap **dap_p, int *ap_num_p, uint32_t *base_p)
{
+ assert(dap_p && ap_num_p);
+
if (!goi->argc)
return JIM_OK;
@@ -1672,6 +2044,10 @@ int adiv5_jim_configure(struct target *target, struct jim_getopt_info *goi)
pc = (struct adiv5_private_config *)target->private_config;
if (!pc) {
pc = calloc(1, sizeof(struct adiv5_private_config));
+ if (!pc) {
+ LOG_ERROR("Out of memory");
+ return JIM_ERR;
+ }
pc->ap_num = DP_APSEL_INVALID;
target->private_config = pc;
}
diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h
index 5c598f1..8c9a60f 100644
--- a/src/target/arm_adi_v5.h
+++ b/src/target/arm_adi_v5.h
@@ -619,10 +619,6 @@ int mem_ap_init(struct adiv5_ap *ap);
/* Invalidate cached DP select and cached TAR and CSW of all APs */
void dap_invalidate_cache(struct adiv5_dap *dap);
-/* Probe the AP for ROM Table location */
-int dap_get_debugbase(struct adiv5_ap *ap,
- target_addr_t *dbgbase, uint32_t *apid);
-
/* Probe Access Ports to find a particular type */
int dap_find_ap(struct adiv5_dap *dap,
enum ap_type type_to_find,
@@ -641,7 +637,7 @@ static inline bool dap_is_multidrop(struct adiv5_dap *dap)
/* Lookup CoreSight component */
int dap_lookup_cs_component(struct adiv5_ap *ap,
- target_addr_t dbgbase, uint8_t type, target_addr_t *addr, int32_t *idx);
+ uint8_t type, target_addr_t *addr, int32_t idx);
struct target;
diff --git a/src/target/arm_coresight.h b/src/target/arm_coresight.h
index a08f4fb..58139dc 100644
--- a/src/target/arm_coresight.h
+++ b/src/target/arm_coresight.h
@@ -44,13 +44,10 @@
#define ARM_CS_CIDR3 (0xFFC)
#define ARM_CS_CIDR_CLASS_MASK (0x0000F000)
-#define ARM_CS_CIDR_CLASS_SHIFT (12)
+#define ARM_CS_CIDR_CLASS(cidr) (((cidr) >> 12) & 0x000F)
#define ARM_CS_CLASS_0X1_ROM_TABLE (0x1)
#define ARM_CS_CLASS_0X9_CS_COMPONENT (0x9)
-#define ARM_CS_CIDR1_CLASS_MASK (0x000000F0)
-#define ARM_CS_CIDR1_CLASS_SHIFT (4)
-
static inline bool is_valid_arm_cs_cidr(uint32_t cidr)
{
return (cidr & ~ARM_CS_CIDR_CLASS_MASK) == 0xB105000D;
@@ -66,6 +63,10 @@ static inline bool is_valid_arm_cs_cidr(uint32_t cidr)
#define ARM_CS_C9_DEVARCH_PRESENT BIT(20)
#define ARM_CS_C9_DEVARCH_ARCHITECT_MASK (0xFFE00000)
#define ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT (21)
+#define ARM_CS_C9_DEVARCH_REVISION(devarch) \
+ (((devarch) & ARM_CS_C9_DEVARCH_REVISION_MASK) >> ARM_CS_C9_DEVARCH_REVISION_SHIFT)
+#define ARM_CS_C9_DEVARCH_ARCHITECT(devarch) \
+ (((devarch) & ARM_CS_C9_DEVARCH_ARCHITECT_MASK) >> ARM_CS_C9_DEVARCH_ARCHITECT_SHIFT)
#define ARM_CS_C9_DEVID (0xFC8)
diff --git a/src/target/armv7a.c b/src/target/armv7a.c
index d564f19..1e425aa 100644
--- a/src/target/armv7a.c
+++ b/src/target/armv7a.c
@@ -112,7 +112,7 @@ static int armv7a_read_midr(struct target *target)
armv7a->arch = (midr >> 16) & 0xf;
armv7a->variant = (midr >> 20) & 0xf;
armv7a->implementor = (midr >> 24) & 0xff;
- LOG_INFO("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32
+ LOG_DEBUG("%s rev %" PRIx32 ", partnum %" PRIx32 ", arch %" PRIx32
", variant %" PRIx32 ", implementor %" PRIx32,
target->cmd_name,
armv7a->rev,
diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c
index 2dc1091..20b2e51 100644
--- a/src/target/cortex_a.c
+++ b/src/target/cortex_a.c
@@ -2905,18 +2905,11 @@ static int cortex_a_examine_first(struct target *target)
armv7a->debug_ap->memaccess_tck = 80;
if (!target->dbgbase_set) {
- target_addr_t dbgbase;
- /* Get ROM Table base */
- uint32_t apid;
- int32_t coreidx = target->coreid;
LOG_DEBUG("%s's dbgbase is not set, trying to detect using the ROM table",
target->cmd_name);
- retval = dap_get_debugbase(armv7a->debug_ap, &dbgbase, &apid);
- if (retval != ERROR_OK)
- return retval;
/* Lookup Processor DAP */
- retval = dap_lookup_cs_component(armv7a->debug_ap, dbgbase, ARM_CS_C9_DEVTYPE_CORE_DEBUG,
- &armv7a->debug_base, &coreidx);
+ retval = dap_lookup_cs_component(armv7a->debug_ap, ARM_CS_C9_DEVTYPE_CORE_DEBUG,
+ &armv7a->debug_base, target->coreid);
if (retval != ERROR_OK) {
LOG_ERROR("Can't detect %s's dbgbase from the ROM table; you need to specify it explicitly.",
target->cmd_name);
diff --git a/src/target/espressif/Makefile.am b/src/target/espressif/Makefile.am
new file mode 100644
index 0000000..c681e09
--- /dev/null
+++ b/src/target/espressif/Makefile.am
@@ -0,0 +1,6 @@
+noinst_LTLIBRARIES += %D%/libespressif.la
+%C%_libespressif_la_SOURCES = \
+ %D%/esp_xtensa.c \
+ %D%/esp_xtensa.h \
+ %D%/esp32s2.c \
+ %D%/esp32s2.h
diff --git a/src/target/espressif/esp32s2.c b/src/target/espressif/esp32s2.c
new file mode 100644
index 0000000..212533f
--- /dev/null
+++ b/src/target/espressif/esp32s2.c
@@ -0,0 +1,715 @@
+/***************************************************************************
+ * ESP32-S2 target for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "assert.h"
+#include <target/target.h>
+#include <target/target_type.h>
+#include "esp_xtensa.h"
+#include "esp32s2.h"
+
+/* Overall memory map
+ * TODO: read memory configuration from target registers */
+#define ESP32_S2_IROM_MASK_LOW 0x40000000
+#define ESP32_S2_IROM_MASK_HIGH 0x40020000
+#define ESP32_S2_IRAM_LOW 0x40020000
+#define ESP32_S2_IRAM_HIGH 0x40070000
+#define ESP32_S2_DRAM_LOW 0x3ffb0000
+#define ESP32_S2_DRAM_HIGH 0x40000000
+#define ESP32_S2_RTC_IRAM_LOW 0x40070000
+#define ESP32_S2_RTC_IRAM_HIGH 0x40072000
+#define ESP32_S2_RTC_DRAM_LOW 0x3ff9e000
+#define ESP32_S2_RTC_DRAM_HIGH 0x3ffa0000
+#define ESP32_S2_RTC_DATA_LOW 0x50000000
+#define ESP32_S2_RTC_DATA_HIGH 0x50002000
+#define ESP32_S2_EXTRAM_DATA_LOW 0x3f500000
+#define ESP32_S2_EXTRAM_DATA_HIGH 0x3ff80000
+#define ESP32_S2_DR_REG_LOW 0x3f400000
+#define ESP32_S2_DR_REG_HIGH 0x3f4d3FFC
+#define ESP32_S2_SYS_RAM_LOW 0x60000000UL
+#define ESP32_S2_SYS_RAM_HIGH (ESP32_S2_SYS_RAM_LOW + 0x20000000UL)
+/* ESP32-S2 DROM mapping is not contiguous. */
+/* IDF declares this as 0x3F000000..0x3FF80000, but there are peripheral registers mapped to
+ * 0x3f400000..0x3f4d3FFC. */
+#define ESP32_S2_DROM0_LOW ESP32_S2_DROM_LOW
+#define ESP32_S2_DROM0_HIGH ESP32_S2_DR_REG_LOW
+#define ESP32_S2_DROM1_LOW ESP32_S2_DR_REG_HIGH
+#define ESP32_S2_DROM1_HIGH ESP32_S2_DROM_HIGH
+
+/* ESP32 WDT */
+#define ESP32_S2_WDT_WKEY_VALUE 0x50d83aa1
+#define ESP32_S2_TIMG0_BASE 0x3f41F000
+#define ESP32_S2_TIMG1_BASE 0x3f420000
+#define ESP32_S2_TIMGWDT_CFG0_OFF 0x48
+#define ESP32_S2_TIMGWDT_PROTECT_OFF 0x64
+#define ESP32_S2_TIMG0WDT_CFG0 (ESP32_S2_TIMG0_BASE + ESP32_S2_TIMGWDT_CFG0_OFF)
+#define ESP32_S2_TIMG1WDT_CFG0 (ESP32_S2_TIMG1_BASE + ESP32_S2_TIMGWDT_CFG0_OFF)
+#define ESP32_S2_TIMG0WDT_PROTECT (ESP32_S2_TIMG0_BASE + ESP32_S2_TIMGWDT_PROTECT_OFF)
+#define ESP32_S2_TIMG1WDT_PROTECT (ESP32_S2_TIMG1_BASE + ESP32_S2_TIMGWDT_PROTECT_OFF)
+#define ESP32_S2_RTCCNTL_BASE 0x3f408000
+#define ESP32_S2_RTCWDT_CFG_OFF 0x94
+#define ESP32_S2_RTCWDT_PROTECT_OFF 0xAC
+#define ESP32_S2_SWD_CONF_OFF 0xB0
+#define ESP32_S2_SWD_WPROTECT_OFF 0xB4
+#define ESP32_S2_RTC_CNTL_DIG_PWC_REG_OFF 0x8C
+#define ESP32_S2_RTC_CNTL_DIG_PWC_REG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_RTC_CNTL_DIG_PWC_REG_OFF)
+#define ESP32_S2_RTCWDT_CFG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_RTCWDT_CFG_OFF)
+#define ESP32_S2_RTCWDT_PROTECT (ESP32_S2_RTCCNTL_BASE + ESP32_S2_RTCWDT_PROTECT_OFF)
+#define ESP32_S2_SWD_CONF_REG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_SWD_CONF_OFF)
+#define ESP32_S2_SWD_WPROTECT_REG (ESP32_S2_RTCCNTL_BASE + ESP32_S2_SWD_WPROTECT_OFF)
+#define ESP32_S2_SWD_AUTO_FEED_EN_M BIT(31)
+#define ESP32_S2_SWD_WKEY_VALUE 0x8F1D312AU
+#define ESP32_S2_OPTIONS0 (ESP32_S2_RTCCNTL_BASE + 0x0000)
+#define ESP32_S2_SW_SYS_RST_M 0x80000000
+#define ESP32_S2_SW_SYS_RST_V 0x1
+#define ESP32_S2_SW_SYS_RST_S 31
+#define ESP32_S2_SW_STALL_PROCPU_C0_M ((ESP32_S2_SW_STALL_PROCPU_C0_V) << (ESP32_S2_SW_STALL_PROCPU_C0_S))
+#define ESP32_S2_SW_STALL_PROCPU_C0_V 0x3
+#define ESP32_S2_SW_STALL_PROCPU_C0_S 2
+#define ESP32_S2_SW_CPU_STALL (ESP32_S2_RTCCNTL_BASE + 0x00B8)
+#define ESP32_S2_SW_STALL_PROCPU_C1_M ((ESP32_S2_SW_STALL_PROCPU_C1_V) << (ESP32_S2_SW_STALL_PROCPU_C1_S))
+#define ESP32_S2_SW_STALL_PROCPU_C1_V 0x3FU
+#define ESP32_S2_SW_STALL_PROCPU_C1_S 26
+#define ESP32_S2_CLK_CONF (ESP32_S2_RTCCNTL_BASE + 0x0074)
+#define ESP32_S2_CLK_CONF_DEF 0x1583218
+#define ESP32_S2_STORE4 (ESP32_S2_RTCCNTL_BASE + 0x00BC)
+#define ESP32_S2_STORE5 (ESP32_S2_RTCCNTL_BASE + 0x00C0)
+#define ESP32_S2_DPORT_PMS_OCCUPY_3 0x3F4C10E0
+
+#define ESP32_S2_TRACEMEM_BLOCK_SZ 0x4000
+
+#define ESP32_S2_DR_REG_UART_BASE 0x3f400000
+#define ESP32_S2_REG_UART_BASE(i) (ESP32_S2_DR_REG_UART_BASE + (i) * 0x10000)
+#define ESP32_S2_UART_DATE_REG(i) (ESP32_S2_REG_UART_BASE(i) + 0x74)
+
+/* this should map local reg IDs to GDB reg mapping as defined in xtensa-config.c 'rmap' in
+ * xtensa-overlay */
+static const unsigned int esp32s2_gdb_regs_mapping[ESP32_S2_NUM_REGS] = {
+ XT_REG_IDX_PC,
+ XT_REG_IDX_AR0, XT_REG_IDX_AR1, XT_REG_IDX_AR2, XT_REG_IDX_AR3,
+ XT_REG_IDX_AR4, XT_REG_IDX_AR5, XT_REG_IDX_AR6, XT_REG_IDX_AR7,
+ XT_REG_IDX_AR8, XT_REG_IDX_AR9, XT_REG_IDX_AR10, XT_REG_IDX_AR11,
+ XT_REG_IDX_AR12, XT_REG_IDX_AR13, XT_REG_IDX_AR14, XT_REG_IDX_AR15,
+ XT_REG_IDX_AR16, XT_REG_IDX_AR17, XT_REG_IDX_AR18, XT_REG_IDX_AR19,
+ XT_REG_IDX_AR20, XT_REG_IDX_AR21, XT_REG_IDX_AR22, XT_REG_IDX_AR23,
+ XT_REG_IDX_AR24, XT_REG_IDX_AR25, XT_REG_IDX_AR26, XT_REG_IDX_AR27,
+ XT_REG_IDX_AR28, XT_REG_IDX_AR29, XT_REG_IDX_AR30, XT_REG_IDX_AR31,
+ XT_REG_IDX_AR32, XT_REG_IDX_AR33, XT_REG_IDX_AR34, XT_REG_IDX_AR35,
+ XT_REG_IDX_AR36, XT_REG_IDX_AR37, XT_REG_IDX_AR38, XT_REG_IDX_AR39,
+ XT_REG_IDX_AR40, XT_REG_IDX_AR41, XT_REG_IDX_AR42, XT_REG_IDX_AR43,
+ XT_REG_IDX_AR44, XT_REG_IDX_AR45, XT_REG_IDX_AR46, XT_REG_IDX_AR47,
+ XT_REG_IDX_AR48, XT_REG_IDX_AR49, XT_REG_IDX_AR50, XT_REG_IDX_AR51,
+ XT_REG_IDX_AR52, XT_REG_IDX_AR53, XT_REG_IDX_AR54, XT_REG_IDX_AR55,
+ XT_REG_IDX_AR56, XT_REG_IDX_AR57, XT_REG_IDX_AR58, XT_REG_IDX_AR59,
+ XT_REG_IDX_AR60, XT_REG_IDX_AR61, XT_REG_IDX_AR62, XT_REG_IDX_AR63,
+ XT_REG_IDX_SAR,
+ XT_REG_IDX_WINDOWBASE, XT_REG_IDX_WINDOWSTART, XT_REG_IDX_CONFIGID0, XT_REG_IDX_CONFIGID1,
+ XT_REG_IDX_PS, XT_REG_IDX_THREADPTR,
+ ESP32_S2_REG_IDX_GPIOOUT,
+ XT_REG_IDX_MMID, XT_REG_IDX_IBREAKENABLE, XT_REG_IDX_OCD_DDR,
+ XT_REG_IDX_IBREAKA0, XT_REG_IDX_IBREAKA1, XT_REG_IDX_DBREAKA0, XT_REG_IDX_DBREAKA1,
+ XT_REG_IDX_DBREAKC0, XT_REG_IDX_DBREAKC1,
+ XT_REG_IDX_EPC1, XT_REG_IDX_EPC2, XT_REG_IDX_EPC3, XT_REG_IDX_EPC4,
+ XT_REG_IDX_EPC5, XT_REG_IDX_EPC6, XT_REG_IDX_EPC7, XT_REG_IDX_DEPC,
+ XT_REG_IDX_EPS2, XT_REG_IDX_EPS3, XT_REG_IDX_EPS4, XT_REG_IDX_EPS5,
+ XT_REG_IDX_EPS6, XT_REG_IDX_EPS7,
+ XT_REG_IDX_EXCSAVE1, XT_REG_IDX_EXCSAVE2, XT_REG_IDX_EXCSAVE3, XT_REG_IDX_EXCSAVE4,
+ XT_REG_IDX_EXCSAVE5, XT_REG_IDX_EXCSAVE6, XT_REG_IDX_EXCSAVE7, XT_REG_IDX_CPENABLE,
+ XT_REG_IDX_INTERRUPT, XT_REG_IDX_INTSET, XT_REG_IDX_INTCLEAR, XT_REG_IDX_INTENABLE,
+ XT_REG_IDX_VECBASE, XT_REG_IDX_EXCCAUSE, XT_REG_IDX_DEBUGCAUSE, XT_REG_IDX_CCOUNT,
+ XT_REG_IDX_PRID, XT_REG_IDX_ICOUNT, XT_REG_IDX_ICOUNTLEVEL, XT_REG_IDX_EXCVADDR,
+ XT_REG_IDX_CCOMPARE0, XT_REG_IDX_CCOMPARE1, XT_REG_IDX_CCOMPARE2,
+ XT_REG_IDX_MISC0, XT_REG_IDX_MISC1, XT_REG_IDX_MISC2, XT_REG_IDX_MISC3,
+ XT_REG_IDX_A0, XT_REG_IDX_A1, XT_REG_IDX_A2, XT_REG_IDX_A3,
+ XT_REG_IDX_A4, XT_REG_IDX_A5, XT_REG_IDX_A6, XT_REG_IDX_A7,
+ XT_REG_IDX_A8, XT_REG_IDX_A9, XT_REG_IDX_A10, XT_REG_IDX_A11,
+ XT_REG_IDX_A12, XT_REG_IDX_A13, XT_REG_IDX_A14, XT_REG_IDX_A15,
+ XT_REG_IDX_PWRCTL, XT_REG_IDX_PWRSTAT, XT_REG_IDX_ERISTAT,
+ XT_REG_IDX_CS_ITCTRL, XT_REG_IDX_CS_CLAIMSET, XT_REG_IDX_CS_CLAIMCLR,
+ XT_REG_IDX_CS_LOCKACCESS, XT_REG_IDX_CS_LOCKSTATUS, XT_REG_IDX_CS_AUTHSTATUS,
+ XT_REG_IDX_FAULT_INFO,
+ XT_REG_IDX_TRAX_ID, XT_REG_IDX_TRAX_CTRL, XT_REG_IDX_TRAX_STAT,
+ XT_REG_IDX_TRAX_DATA, XT_REG_IDX_TRAX_ADDR, XT_REG_IDX_TRAX_PCTRIGGER,
+ XT_REG_IDX_TRAX_PCMATCH, XT_REG_IDX_TRAX_DELAY, XT_REG_IDX_TRAX_MEMSTART,
+ XT_REG_IDX_TRAX_MEMEND,
+ XT_REG_IDX_PMG, XT_REG_IDX_PMPC, XT_REG_IDX_PM0, XT_REG_IDX_PM1,
+ XT_REG_IDX_PMCTRL0, XT_REG_IDX_PMCTRL1, XT_REG_IDX_PMSTAT0, XT_REG_IDX_PMSTAT1,
+ XT_REG_IDX_OCD_ID, XT_REG_IDX_OCD_DCRCLR, XT_REG_IDX_OCD_DCRSET, XT_REG_IDX_OCD_DSR,
+};
+
+static const struct xtensa_user_reg_desc esp32s2_user_regs[ESP32_S2_NUM_REGS - XT_NUM_REGS] = {
+ { "gpio_out", 0x00, 0, 32, &xtensa_user_reg_u32_type },
+};
+
+static const struct xtensa_config esp32s2_xtensa_cfg = {
+ .density = true,
+ .aregs_num = XT_AREGS_NUM_MAX,
+ .windowed = true,
+ .coproc = true,
+ .miscregs_num = 4,
+ .reloc_vec = true,
+ .proc_id = true,
+ .threadptr = true,
+ .user_regs_num = ARRAY_SIZE(esp32s2_user_regs),
+ .user_regs = esp32s2_user_regs,
+ .fetch_user_regs = xtensa_fetch_user_regs_u32,
+ .queue_write_dirty_user_regs = xtensa_queue_write_dirty_user_regs_u32,
+ .gdb_general_regs_num = ESP32_S2_NUM_REGS_G_COMMAND,
+ .gdb_regs_mapping = esp32s2_gdb_regs_mapping,
+ .irom = {
+ .count = 2,
+ .regions = {
+ {
+ .base = ESP32_S2_IROM_LOW,
+ .size = ESP32_S2_IROM_HIGH - ESP32_S2_IROM_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ {
+ .base = ESP32_S2_IROM_MASK_LOW,
+ .size = ESP32_S2_IROM_MASK_HIGH - ESP32_S2_IROM_MASK_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ }
+ },
+ .iram = {
+ .count = 2,
+ .regions = {
+ {
+ .base = ESP32_S2_IRAM_LOW,
+ .size = ESP32_S2_IRAM_HIGH - ESP32_S2_IRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_RTC_IRAM_LOW,
+ .size = ESP32_S2_RTC_IRAM_HIGH - ESP32_S2_RTC_IRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ }
+ },
+ .drom = {
+ .count = 2,
+ .regions = {
+ {
+ .base = ESP32_S2_DROM0_LOW,
+ .size = ESP32_S2_DROM0_HIGH - ESP32_S2_DROM0_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ {
+ .base = ESP32_S2_DROM1_LOW,
+ .size = ESP32_S2_DROM1_HIGH - ESP32_S2_DROM1_LOW,
+ .access = XT_MEM_ACCESS_READ,
+ },
+ }
+ },
+ .dram = {
+ .count = 6,
+ .regions = {
+ {
+ .base = ESP32_S2_DRAM_LOW,
+ .size = ESP32_S2_DRAM_HIGH - ESP32_S2_DRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_RTC_DRAM_LOW,
+ .size = ESP32_S2_RTC_DRAM_HIGH - ESP32_S2_RTC_DRAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_RTC_DATA_LOW,
+ .size = ESP32_S2_RTC_DATA_HIGH - ESP32_S2_RTC_DATA_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_EXTRAM_DATA_LOW,
+ .size = ESP32_S2_EXTRAM_DATA_HIGH - ESP32_S2_EXTRAM_DATA_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_DR_REG_LOW,
+ .size = ESP32_S2_DR_REG_HIGH - ESP32_S2_DR_REG_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ {
+ .base = ESP32_S2_SYS_RAM_LOW,
+ .size = ESP32_S2_SYS_RAM_HIGH - ESP32_S2_SYS_RAM_LOW,
+ .access = XT_MEM_ACCESS_READ | XT_MEM_ACCESS_WRITE,
+ },
+ }
+ },
+ .exc = {
+ .enabled = true,
+ },
+ .irq = {
+ .enabled = true,
+ .irq_num = 32,
+ },
+ .high_irq = {
+ .enabled = true,
+ .excm_level = 3,
+ .nmi_num = 1,
+ },
+ .tim_irq = {
+ .enabled = true,
+ .comp_num = 3,
+ },
+ .debug = {
+ .enabled = true,
+ .irq_level = 6,
+ .ibreaks_num = 2,
+ .dbreaks_num = 2,
+ .icount_sz = 32,
+ },
+ .trace = {
+ .enabled = true,
+ .mem_sz = ESP32_S2_TRACEMEM_BLOCK_SZ,
+ },
+};
+
+struct esp32s2_common {
+ struct esp_xtensa_common esp_xtensa;
+};
+
+static int esp32s2_soc_reset(struct target *target);
+static int esp32s2_disable_wdts(struct target *target);
+
+static int esp32s2_assert_reset(struct target *target)
+{
+ return ERROR_OK;
+}
+
+static int esp32s2_deassert_reset(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "begin");
+
+ int res = xtensa_deassert_reset(target);
+ if (res != ERROR_OK)
+ return res;
+
+ /* restore configured value
+ esp32s2_soc_reset() modified it, but can not restore just after SW reset for some reason (???) */
+ res = xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to restore smpbreak (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+int esp32s2_soft_reset_halt(struct target *target)
+{
+ LOG_TARGET_DEBUG(target, "begin");
+
+ /* Reset the SoC first */
+ int res = esp32s2_soc_reset(target);
+ if (res != ERROR_OK)
+ return res;
+ return xtensa_assert_reset(target);
+}
+
+static int esp32s2_set_peri_reg_mask(struct target *target,
+ target_addr_t addr,
+ uint32_t mask,
+ uint32_t val)
+{
+ uint32_t reg_val;
+ int res = target_read_u32(target, addr, &reg_val);
+ if (res != ERROR_OK)
+ return res;
+ reg_val = (reg_val & (~mask)) | val;
+ res = target_write_u32(target, addr, reg_val);
+ if (res != ERROR_OK)
+ return res;
+
+ return ERROR_OK;
+}
+
+static int esp32s2_stall_set(struct target *target, bool stall)
+{
+ LOG_TARGET_DEBUG(target, "begin");
+
+ int res = esp32s2_set_peri_reg_mask(target,
+ ESP32_S2_SW_CPU_STALL,
+ ESP32_S2_SW_STALL_PROCPU_C1_M,
+ stall ? 0x21U << ESP32_S2_SW_STALL_PROCPU_C1_S : 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_SW_CPU_STALL (%d)!", res);
+ return res;
+ }
+ res = esp32s2_set_peri_reg_mask(target,
+ ESP32_S2_OPTIONS0,
+ ESP32_S2_SW_STALL_PROCPU_C0_M,
+ stall ? 0x2 << ESP32_S2_SW_STALL_PROCPU_C0_S : 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_OPTIONS0 (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+static inline int esp32s2_stall(struct target *target)
+{
+ return esp32s2_stall_set(target, true);
+}
+
+static inline int esp32s2_unstall(struct target *target)
+{
+ return esp32s2_stall_set(target, false);
+}
+
+/* Reset ESP32-S2's peripherals.
+Postconditions: all peripherals except RTC_CNTL are reset, CPU's PC is undefined, PRO CPU is halted, APP CPU is in reset
+How this works:
+0. make sure target is halted; if not, try to halt it; if that fails, try to reset it (via OCD) and then halt
+1. Resets clock related registers
+2. Stalls CPU
+3. trigger SoC reset using RTC_CNTL_SW_SYS_RST bit
+4. CPU is reset and stalled at the first reset vector instruction
+5. wait for the OCD to be reset
+6. halt the target
+7. Unstalls CPU
+8. Disables WDTs and trace memory mapping
+*/
+static int esp32s2_soc_reset(struct target *target)
+{
+ int res;
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_DEBUG("start");
+
+ /* In order to write to peripheral registers, target must be halted first */
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target, "Target not halted before SoC reset, trying to halt it first");
+ xtensa_halt(target);
+ res = target_wait_state(target, TARGET_HALTED, 1000);
+ if (res != ERROR_OK) {
+ LOG_TARGET_DEBUG(target, "Couldn't halt target before SoC reset, trying to do reset-halt");
+ res = xtensa_assert_reset(target);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(
+ target,
+ "Couldn't halt target before SoC reset! (xtensa_assert_reset returned %d)",
+ res);
+ return res;
+ }
+ alive_sleep(10);
+ xtensa_poll(target);
+ int reset_halt_save = target->reset_halt;
+ target->reset_halt = 1;
+ res = xtensa_deassert_reset(target);
+ target->reset_halt = reset_halt_save;
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(
+ target,
+ "Couldn't halt target before SoC reset! (xtensa_deassert_reset returned %d)",
+ res);
+ return res;
+ }
+ alive_sleep(10);
+ xtensa_poll(target);
+ xtensa_halt(target);
+ res = target_wait_state(target, TARGET_HALTED, 1000);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Couldn't halt target before SoC reset");
+ return res;
+ }
+ }
+ }
+
+ assert(target->state == TARGET_HALTED);
+
+ /* Set some clock-related RTC registers to the default values */
+ res = target_write_u32(target, ESP32_S2_STORE4, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_STORE4 (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_STORE5, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_STORE5 (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_RTC_CNTL_DIG_PWC_REG, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_RTC_CNTL_DIG_PWC_REG (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_CLK_CONF, ESP32_S2_CLK_CONF_DEF);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_CLK_CONF (%d)!", res);
+ return res;
+ }
+ /* Stall CPU */
+ res = esp32s2_stall(target);
+ if (res != ERROR_OK)
+ return res;
+ /* enable stall */
+ res = xtensa_smpbreak_write(xtensa, OCDDCR_RUNSTALLINEN);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to set smpbreak (%d)!", res);
+ return res;
+ }
+ /* Reset CPU */
+ xtensa->suppress_dsr_errors = true;
+ res = esp32s2_set_peri_reg_mask(target,
+ ESP32_S2_OPTIONS0,
+ ESP32_S2_SW_SYS_RST_M,
+ 1U << ESP32_S2_SW_SYS_RST_S);
+ xtensa->suppress_dsr_errors = false;
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_OPTIONS0 (%d)!", res);
+ return res;
+ }
+ /* Wait for SoC to reset */
+ alive_sleep(100);
+ int timeout = 100;
+ while (target->state != TARGET_RESET && target->state != TARGET_RUNNING && --timeout > 0) {
+ alive_sleep(10);
+ xtensa_poll(target);
+ }
+ if (timeout == 0) {
+ LOG_ERROR("Timed out waiting for CPU to be reset, target->state=%d", target->state);
+ return ERROR_TARGET_TIMEOUT;
+ }
+ xtensa_halt(target);
+ res = target_wait_state(target, TARGET_HALTED, 1000);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Couldn't halt target before SoC reset");
+ return res;
+ }
+ /* Unstall CPU */
+ res = esp32s2_unstall(target);
+ if (res != ERROR_OK)
+ return res;
+ /* Disable WDTs */
+ res = esp32s2_disable_wdts(target);
+ if (res != ERROR_OK)
+ return res;
+ /* Disable trace memory mapping */
+ res = target_write_u32(target, ESP32_S2_DPORT_PMS_OCCUPY_3, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_DPORT_PMS_OCCUPY_3 (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+static int esp32s2_disable_wdts(struct target *target)
+{
+ /* TIMG1 WDT */
+ int res = target_write_u32(target, ESP32_S2_TIMG0WDT_PROTECT, ESP32_S2_WDT_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG0WDT_PROTECT (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_TIMG0WDT_CFG0, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG0WDT_CFG0 (%d)!", res);
+ return res;
+ }
+ /* TIMG2 WDT */
+ res = target_write_u32(target, ESP32_S2_TIMG1WDT_PROTECT, ESP32_S2_WDT_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG1WDT_PROTECT (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_TIMG1WDT_CFG0, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_TIMG1WDT_CFG0 (%d)!", res);
+ return res;
+ }
+ /* RTC WDT */
+ res = target_write_u32(target, ESP32_S2_RTCWDT_PROTECT, ESP32_S2_WDT_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_RTCWDT_PROTECT (%d)!", res);
+ return res;
+ }
+ res = target_write_u32(target, ESP32_S2_RTCWDT_CFG, 0);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_RTCWDT_CFG (%d)!", res);
+ return res;
+ }
+ /* Enable SWD auto-feed */
+ res = target_write_u32(target, ESP32_S2_SWD_WPROTECT_REG, ESP32_S2_SWD_WKEY_VALUE);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_SWD_WPROTECT_REG (%d)!", res);
+ return res;
+ }
+ uint32_t swd_conf_reg = 0;
+ res = target_read_u32(target, ESP32_S2_SWD_CONF_REG, &swd_conf_reg);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to read ESP32_S2_SWD_CONF_REG (%d)!", res);
+ return res;
+ }
+ swd_conf_reg |= ESP32_S2_SWD_AUTO_FEED_EN_M;
+ res = target_write_u32(target, ESP32_S2_SWD_CONF_REG, swd_conf_reg);
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to write ESP32_S2_SWD_CONF_REG (%d)!", res);
+ return res;
+ }
+ return ERROR_OK;
+}
+
+static int esp32s2_arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+static int esp32s2_on_halt(struct target *target)
+{
+ return esp32s2_disable_wdts(target);
+}
+
+static int esp32s2_step(struct target *target, int current, target_addr_t address, int handle_breakpoints)
+{
+ int ret = xtensa_step(target, current, address, handle_breakpoints);
+ if (ret == ERROR_OK) {
+ esp32s2_on_halt(target);
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ return ret;
+}
+
+static int esp32s2_poll(struct target *target)
+{
+ enum target_state old_state = target->state;
+ int ret = esp_xtensa_poll(target);
+
+ if (old_state != TARGET_HALTED && target->state == TARGET_HALTED) {
+ /* Call any event callbacks that are applicable */
+ if (old_state == TARGET_DEBUG_RUNNING) {
+ target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
+ } else {
+ esp32s2_on_halt(target);
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+ }
+
+ return ret;
+}
+
+static int esp32s2_virt2phys(struct target *target,
+ target_addr_t virtual, target_addr_t *physical)
+{
+ *physical = virtual;
+ return ERROR_OK;
+}
+
+static int esp32s2_target_init(struct command_context *cmd_ctx, struct target *target)
+{
+ return esp_xtensa_target_init(cmd_ctx, target);
+}
+
+static const struct xtensa_debug_ops esp32s2_dbg_ops = {
+ .queue_enable = xtensa_dm_queue_enable,
+ .queue_reg_read = xtensa_dm_queue_reg_read,
+ .queue_reg_write = xtensa_dm_queue_reg_write
+};
+
+static const struct xtensa_power_ops esp32s2_pwr_ops = {
+ .queue_reg_read = xtensa_dm_queue_pwr_reg_read,
+ .queue_reg_write = xtensa_dm_queue_pwr_reg_write
+};
+
+static int esp32s2_target_create(struct target *target, Jim_Interp *interp)
+{
+ struct xtensa_debug_module_config esp32s2_dm_cfg = {
+ .dbg_ops = &esp32s2_dbg_ops,
+ .pwr_ops = &esp32s2_pwr_ops,
+ .tap = target->tap,
+ .queue_tdi_idle = NULL,
+ .queue_tdi_idle_arg = NULL
+ };
+
+ /* creates xtensa object */
+ struct esp32s2_common *esp32 = calloc(1, sizeof(*esp32));
+ if (!esp32) {
+ LOG_ERROR("Failed to alloc memory for arch info!");
+ return ERROR_FAIL;
+ }
+
+ int ret = esp_xtensa_init_arch_info(target, &esp32->esp_xtensa, &esp32s2_xtensa_cfg, &esp32s2_dm_cfg);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to init arch info!");
+ free(esp32);
+ return ret;
+ }
+
+ /* Assume running target. If different, the first poll will fix this */
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ return ERROR_OK;
+}
+
+static const struct command_registration esp32s2_command_handlers[] = {
+ {
+ .name = "xtensa",
+ .mode = COMMAND_ANY,
+ .help = "Xtensa commands group",
+ .usage = "",
+ .chain = xtensa_command_handlers,
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+/* Holds methods for Xtensa targets. */
+struct target_type esp32s2_target = {
+ .name = "esp32s2",
+
+ .poll = esp32s2_poll,
+ .arch_state = esp32s2_arch_state,
+
+ .halt = xtensa_halt,
+ .resume = xtensa_resume,
+ .step = esp32s2_step,
+
+ .assert_reset = esp32s2_assert_reset,
+ .deassert_reset = esp32s2_deassert_reset,
+ .soft_reset_halt = esp32s2_soft_reset_halt,
+
+ .virt2phys = esp32s2_virt2phys,
+ .mmu = xtensa_mmu_is_enabled,
+ .read_memory = xtensa_read_memory,
+ .write_memory = xtensa_write_memory,
+
+ .read_buffer = xtensa_read_buffer,
+ .write_buffer = xtensa_write_buffer,
+
+ .checksum_memory = xtensa_checksum_memory,
+
+ .get_gdb_arch = xtensa_get_gdb_arch,
+ .get_gdb_reg_list = xtensa_get_gdb_reg_list,
+
+ .add_breakpoint = esp_xtensa_breakpoint_add,
+ .remove_breakpoint = esp_xtensa_breakpoint_remove,
+
+ .add_watchpoint = xtensa_watchpoint_add,
+ .remove_watchpoint = xtensa_watchpoint_remove,
+
+ .target_create = esp32s2_target_create,
+ .init_target = esp32s2_target_init,
+ .examine = xtensa_examine,
+ .deinit_target = esp_xtensa_target_deinit,
+
+ .commands = esp32s2_command_handlers,
+};
diff --git a/src/target/espressif/esp32s2.h b/src/target/espressif/esp32s2.h
new file mode 100644
index 0000000..2c43e3e
--- /dev/null
+++ b/src/target/espressif/esp32s2.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * ESP32-S2 target for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESP32S2_H
+#define OPENOCD_TARGET_ESP32S2_H
+
+#include <target/xtensa/xtensa_regs.h>
+
+#define ESP32_S2_DROM_LOW 0x3f000000
+#define ESP32_S2_DROM_HIGH 0x3ff80000
+#define ESP32_S2_IROM_LOW 0x40080000
+#define ESP32_S2_IROM_HIGH 0x40800000
+
+/* Number of registers returned directly by the G command
+ * Corresponds to the amount of regs listed in regformats/reg-xtensa.dat in the gdb source */
+#define ESP32_S2_NUM_REGS_G_COMMAND 72
+
+enum esp32s2_reg_id {
+ /* chip specific registers that extend ISA go after ISA-defined ones */
+ ESP32_S2_REG_IDX_GPIOOUT = XT_USR_REG_START,
+ ESP32_S2_NUM_REGS,
+};
+
+#endif /* OPENOCD_TARGET_ESP32S2_H */
diff --git a/src/target/espressif/esp_xtensa.c b/src/target/espressif/esp_xtensa.c
new file mode 100644
index 0000000..89393f4
--- /dev/null
+++ b/src/target/espressif/esp_xtensa.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Espressif Xtensa target API for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <target/smp.h>
+#include "esp_xtensa.h"
+#include <target/register.h>
+
+int esp_xtensa_init_arch_info(struct target *target,
+ struct esp_xtensa_common *esp_xtensa,
+ const struct xtensa_config *xtensa_cfg,
+ struct xtensa_debug_module_config *dm_cfg)
+{
+ return xtensa_init_arch_info(target, &esp_xtensa->xtensa, xtensa_cfg, dm_cfg);
+}
+
+int esp_xtensa_target_init(struct command_context *cmd_ctx, struct target *target)
+{
+ return xtensa_target_init(cmd_ctx, target);
+}
+
+void esp_xtensa_target_deinit(struct target *target)
+{
+ LOG_DEBUG("start");
+
+ xtensa_target_deinit(target);
+ free(target_to_esp_xtensa(target)); /* same as free(xtensa) */
+}
+
+int esp_xtensa_arch_state(struct target *target)
+{
+ return ERROR_OK;
+}
+
+int esp_xtensa_poll(struct target *target)
+{
+ return xtensa_poll(target);
+}
+
+int esp_xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint)
+{
+ return xtensa_breakpoint_add(target, breakpoint);
+ /* flash breakpoints will be handled in another patch */
+}
+
+int esp_xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint)
+{
+ return xtensa_breakpoint_remove(target, breakpoint);
+ /* flash breakpoints will be handled in another patch */
+}
diff --git a/src/target/espressif/esp_xtensa.h b/src/target/espressif/esp_xtensa.h
new file mode 100644
index 0000000..6badb1b
--- /dev/null
+++ b/src/target/espressif/esp_xtensa.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Generic ESP xtensa target implementation for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESP_XTENSA_H
+#define OPENOCD_TARGET_ESP_XTENSA_H
+
+#include <helper/command.h>
+#include <target/target.h>
+#include <target/xtensa/xtensa.h>
+
+struct esp_xtensa_common {
+ struct xtensa xtensa; /* must be the first element */
+};
+
+static inline struct esp_xtensa_common *target_to_esp_xtensa(struct target *target)
+{
+ return container_of(target->arch_info, struct esp_xtensa_common, xtensa);
+}
+
+int esp_xtensa_init_arch_info(struct target *target,
+ struct esp_xtensa_common *esp_xtensa,
+ const struct xtensa_config *xtensa_cfg,
+ struct xtensa_debug_module_config *dm_cfg);
+int esp_xtensa_target_init(struct command_context *cmd_ctx, struct target *target);
+void esp_xtensa_target_deinit(struct target *target);
+int esp_xtensa_arch_state(struct target *target);
+void esp_xtensa_queue_tdi_idle(struct target *target);
+int esp_xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint);
+int esp_xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint);
+int esp_xtensa_poll(struct target *target);
+
+#endif /* OPENOCD_TARGET_ESP_XTENSA_H */
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 28789bd..ee3ced6 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -2468,10 +2468,11 @@ static int deassert_reset(struct target *target)
select_dmi(target);
/* Clear the reset, but make sure haltreq is still set */
- uint32_t control = 0;
- control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
+ uint32_t control = 0, control_haltreq;
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
- dmi_write(target, DM_DMCONTROL, set_dmcontrol_hartsel(control, info->index));
+ control_haltreq = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
+ dmi_write(target, DM_DMCONTROL,
+ set_dmcontrol_hartsel(control_haltreq, info->index));
uint32_t dmstatus;
int dmi_busy_delay = info->dmi_busy_delay;
@@ -2483,7 +2484,7 @@ static int deassert_reset(struct target *target)
if (index != info->index)
continue;
dmi_write(target, DM_DMCONTROL,
- set_dmcontrol_hartsel(control, index));
+ set_dmcontrol_hartsel(control_haltreq, index));
} else {
index = info->index;
}
@@ -2525,7 +2526,7 @@ static int deassert_reset(struct target *target)
}
if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) {
- /* Ack reset. */
+ /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */
dmi_write(target, DM_DMCONTROL,
set_dmcontrol_hartsel(control, index) |
DM_DMCONTROL_ACKHAVERESET);
diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c
index bc1f417..2df6e38 100644
--- a/src/target/semihosting_common.c
+++ b/src/target/semihosting_common.c
@@ -159,6 +159,7 @@ int semihosting_common_init(struct target *target, void *setup,
semihosting->result = -1;
semihosting->sys_errno = -1;
semihosting->cmdline = NULL;
+ semihosting->basedir = NULL;
/* If possible, update it in setup(). */
semihosting->setup_time = clock();
@@ -870,17 +871,21 @@ int semihosting_common(struct target *target)
semihosting->sys_errno = EINVAL;
break;
}
- uint8_t *fn = malloc(len+1);
+ size_t basedir_len = semihosting->basedir ? strlen(semihosting->basedir) : 0;
+ uint8_t *fn = malloc(basedir_len + len + 2);
if (!fn) {
semihosting->result = -1;
semihosting->sys_errno = ENOMEM;
} else {
- retval = target_read_memory(target, addr, 1, len, fn);
+ strncpy((char *)fn, semihosting->basedir, basedir_len);
+ if (fn[basedir_len - 1] != '/')
+ fn[basedir_len++] = '/';
+ retval = target_read_memory(target, addr, 1, len, fn + basedir_len);
if (retval != ERROR_OK) {
free(fn);
return retval;
}
- fn[len] = 0;
+ fn[basedir_len + len] = 0;
/* TODO: implement the :semihosting-features special file.
* */
if (semihosting->is_fileio) {
@@ -2025,6 +2030,44 @@ COMMAND_HANDLER(handle_common_semihosting_read_user_param_command)
return ERROR_OK;
}
+COMMAND_HANDLER(handle_common_semihosting_basedir_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (!target) {
+ LOG_ERROR("No target selected");
+ return ERROR_FAIL;
+ }
+
+ struct semihosting *semihosting = target->semihosting;
+ if (!semihosting) {
+ command_print(CMD, "semihosting not supported for current target");
+ return ERROR_FAIL;
+ }
+
+ if (!semihosting->is_active) {
+ command_print(CMD, "semihosting not yet enabled for current target");
+ return ERROR_FAIL;
+ }
+
+ if (CMD_ARGC > 0) {
+ free(semihosting->basedir);
+ semihosting->basedir = strdup(CMD_ARGV[0]);
+ if (!semihosting->basedir) {
+ command_print(CMD, "semihosting failed to allocate memory for basedir!");
+ return ERROR_FAIL;
+ }
+ }
+
+ command_print(CMD, "semihosting base dir: %s",
+ semihosting->basedir ? semihosting->basedir : "");
+
+ return ERROR_OK;
+}
+
const struct command_registration semihosting_common_handlers[] = {
{
.name = "semihosting",
@@ -2068,5 +2111,12 @@ const struct command_registration semihosting_common_handlers[] = {
.usage = "",
.help = "read parameters in semihosting-user-cmd-0x10X callbacks",
},
+ {
+ .name = "semihosting_basedir",
+ .handler = handle_common_semihosting_basedir_command,
+ .mode = COMMAND_EXEC,
+ .usage = "[dir]",
+ .help = "set the base directory for semihosting I/O operations",
+ },
COMMAND_REGISTRATION_DONE
};
diff --git a/src/target/semihosting_common.h b/src/target/semihosting_common.h
index 459faf6..404080f 100644
--- a/src/target/semihosting_common.h
+++ b/src/target/semihosting_common.h
@@ -176,6 +176,9 @@ struct semihosting {
/** The current time when 'execution starts' */
clock_t setup_time;
+ /** Base directory for semihosting I/O operations. */
+ char *basedir;
+
int (*setup)(struct target *target, int enable);
int (*post_result)(struct target *target);
};
diff --git a/src/target/smp.c b/src/target/smp.c
index 3e1ded8..569abd7 100644
--- a/src/target/smp.c
+++ b/src/target/smp.c
@@ -28,6 +28,7 @@
#include "smp.h"
#include "helper/binarybuffer.h"
+/* DEPRECATED: gdb_read_smp_packet/gdb_write_smp_packet to be removed */
/* implementation of new packet in gdb interface for smp feature */
/* */
/* j : smp status request */
@@ -53,11 +54,15 @@
/* maint packet jc */
/* packet j :smp status request */
+#define DEPRECATED_MSG "DEPRECATED: This method is deprecated in favor of the hwthread pseudo RTOS"
int gdb_read_smp_packet(struct connection *connection,
char const *packet, int packet_size)
{
struct target *target = get_target_from_connection(connection);
int retval = ERROR_OK;
+
+ LOG_WARNING(DEPRECATED_MSG);
+
if (target->smp) {
if (strncmp(packet, "jc", 2) == 0) {
const uint32_t len = sizeof(target->gdb_service->core[0]);
@@ -83,6 +88,8 @@ int gdb_write_smp_packet(struct connection *connection,
int coreid = 0;
int retval = ERROR_OK;
+ LOG_WARNING(DEPRECATED_MSG);
+
/* skip command character */
if (target->smp) {
if (strncmp(packet, "Jc", 2) == 0) {
diff --git a/src/target/smp.h b/src/target/smp.h
index 490a493..d373c90 100644
--- a/src/target/smp.h
+++ b/src/target/smp.h
@@ -30,8 +30,10 @@
extern const struct command_registration smp_command_handlers[];
+/* DEPRECATED */
int gdb_read_smp_packet(struct connection *connection,
char const *packet, int packet_size);
+/* DEPRECATED */
int gdb_write_smp_packet(struct connection *connection,
char const *packet, int packet_size);
diff --git a/src/target/target.c b/src/target/target.c
index 30f2cf0..a373848 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -57,6 +57,7 @@
#include "transport/transport.h"
#include "arm_cti.h"
#include "smp.h"
+#include "semihosting_common.h"
/* default halt wait timeout (ms) */
#define DEFAULT_HALT_TIMEOUT 5000
@@ -104,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;
@@ -140,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,
@@ -2268,6 +2271,8 @@ static void target_destroy(struct target *target)
if (target->type->deinit_target)
target->type->deinit_target(target);
+ if (target->semihosting)
+ free(target->semihosting->basedir);
free(target->semihosting);
jtag_unregister_event_callback(jtag_enable_callback, target);
@@ -2597,7 +2602,7 @@ int target_blank_check_memory(struct target *target,
}
if (!target->type->blank_check_memory)
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ return ERROR_NOT_IMPLEMENTED;
return target->type->blank_check_memory(target, blocks, num_blocks, erased_value);
}
@@ -5224,10 +5229,18 @@ static int target_jim_set_reg(Jim_Interp *interp, int argc,
}
int tmp;
+#if JIM_VERSION >= 80
Jim_Obj **dict = Jim_DictPairs(interp, argv[1], &tmp);
if (!dict)
return JIM_ERR;
+#else
+ Jim_Obj **dict;
+ int ret = Jim_DictPairs(interp, argv[1], &dict, &tmp);
+
+ if (ret != JIM_OK)
+ return ret;
+#endif
const unsigned int length = tmp;
struct command_context *cmd_ctx = current_command_context(interp);
@@ -6473,7 +6486,7 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
smp_group++;
if (target && target->rtos)
- retval = rtos_smp_init(head->target);
+ retval = rtos_smp_init(target);
return retval;
}
diff --git a/src/target/target.h b/src/target/target.h
index 2166f7e..e67016c 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -206,7 +206,7 @@ struct target {
* poll too quickly because we'll just overwhelm the user with error
* messages. */
struct backoff_timer backoff;
- int smp; /* add some target attributes for smp support */
+ int smp; /* Unique non-zero number for each SMP group */
struct list_head *smp_targets; /* list all targets in this smp group/cluster
* The head of the list is shared between the
* cluster, thus here there is a pointer */
diff --git a/src/target/target_type.h b/src/target/target_type.h
index d6b6086..a26c2e7 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -242,6 +242,17 @@ struct target_type {
/**
* Free all the resources allocated by the target.
*
+ * WARNING: deinit_target is called unconditionally regardless the target has
+ * ever been examined/initialised or not.
+ * If a problem has prevented establishing JTAG/SWD/... communication
+ * or
+ * if the target was created with -defer-examine flag and has never been
+ * examined
+ * then it is not possible to communicate with the target.
+ *
+ * If you need to talk to the target during deinit, first check if
+ * target_was_examined()!
+ *
* @param target The target to deinit
*/
void (*deinit_target)(struct target *target);
diff --git a/src/target/xtensa/Makefile.am b/src/target/xtensa/Makefile.am
new file mode 100644
index 0000000..f6cee99
--- /dev/null
+++ b/src/target/xtensa/Makefile.am
@@ -0,0 +1,7 @@
+noinst_LTLIBRARIES += %D%/libxtensa.la
+%C%_libxtensa_la_SOURCES = \
+ %D%/xtensa.c \
+ %D%/xtensa.h \
+ %D%/xtensa_debug_module.c \
+ %D%/xtensa_debug_module.h \
+ %D%/xtensa_regs.h
diff --git a/src/target/xtensa/xtensa.c b/src/target/xtensa/xtensa.c
new file mode 100644
index 0000000..a955960
--- /dev/null
+++ b/src/target/xtensa/xtensa.c
@@ -0,0 +1,2731 @@
+/***************************************************************************
+ * Generic Xtensa target API for OpenOCD *
+ * Copyright (C) 2016-2019 Espressif Systems Ltd. *
+ * Derived from esp108.c *
+ * Author: Angus Gratton gus@projectgus.com *
+ * Author: Jeroen Domburg <jeroen@espressif.com> *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * Author: Andrey Gramakov <andrei.gramakov@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <helper/time_support.h>
+#include <helper/align.h>
+#include <target/register.h>
+
+#include "xtensa.h"
+
+
+#define _XT_INS_FORMAT_RSR(OPCODE, SR, T) ((OPCODE) \
+ | (((SR) & 0xFF) << 8) \
+ | (((T) & 0x0F) << 4))
+
+#define _XT_INS_FORMAT_RRR(OPCODE, ST, R) ((OPCODE) \
+ | (((ST) & 0xFF) << 4) \
+ | (((R) & 0x0F) << 12))
+
+#define _XT_INS_FORMAT_RRRN(OPCODE, S, T, IMM4) ((OPCODE) \
+ | (((T) & 0x0F) << 4) \
+ | (((S) & 0x0F) << 8) \
+ | (((IMM4) & 0x0F) << 12))
+
+#define _XT_INS_FORMAT_RRI8(OPCODE, R, S, T, IMM8) ((OPCODE) \
+ | (((IMM8) & 0xFF) << 16) \
+ | (((R) & 0x0F) << 12) \
+ | (((S) & 0x0F) << 8) \
+ | (((T) & 0x0F) << 4))
+
+#define _XT_INS_FORMAT_RRI4(OPCODE, IMM4, R, S, T) ((OPCODE) \
+ | (((IMM4) & 0x0F) << 20) \
+ | (((R) & 0x0F) << 12) \
+ | (((S) & 0x0F) << 8) \
+ | (((T) & 0x0F) << 4))
+
+/* Xtensa processor instruction opcodes
+ * "Return From Debug Operation" to Normal */
+#define XT_INS_RFDO 0xf1e000
+/* "Return From Debug and Dispatch" - allow sw debugging stuff to take over */
+#define XT_INS_RFDD 0xf1e010
+
+/* Load to DDR register, increase addr register */
+#define XT_INS_LDDR32P(S) (0x0070E0 | ((S) << 8))
+/* Store from DDR register, increase addr register */
+#define XT_INS_SDDR32P(S) (0x0070F0 | ((S) << 8))
+
+/* Load 32-bit Indirect from A(S) + 4 * IMM8 to A(T) */
+#define XT_INS_L32I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x002002, 0, S, T, IMM8)
+/* Load 16-bit Unsigned from A(S) + 2 * IMM8 to A(T) */
+#define XT_INS_L16UI(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x001002, 0, S, T, IMM8)
+/* Load 8-bit Unsigned from A(S) + IMM8 to A(T) */
+#define XT_INS_L8UI(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x000002, 0, S, T, IMM8)
+
+/* Store 32-bit Indirect to A(S) + 4 * IMM8 from A(T) */
+#define XT_INS_S32I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x006002, 0, S, T, IMM8)
+/* Store 16-bit to A(S) + 2 * IMM8 from A(T) */
+#define XT_INS_S16I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x005002, 0, S, T, IMM8)
+/* Store 8-bit to A(S) + IMM8 from A(T) */
+#define XT_INS_S8I(S, T, IMM8) _XT_INS_FORMAT_RRI8(0x004002, 0, S, T, IMM8)
+
+/* Read Special Register */
+#define XT_INS_RSR(SR, T) _XT_INS_FORMAT_RSR(0x030000, SR, T)
+/* Write Special Register */
+#define XT_INS_WSR(SR, T) _XT_INS_FORMAT_RSR(0x130000, SR, T)
+/* Swap Special Register */
+#define XT_INS_XSR(SR, T) _XT_INS_FORMAT_RSR(0x610000, SR, T)
+
+/* Rotate Window by (-8..7) */
+#define XT_INS_ROTW(N) ((0x408000) | (((N) & 15) << 4))
+
+/* Read User Register */
+#define XT_INS_RUR(UR, T) _XT_INS_FORMAT_RRR(0xE30000, UR, T)
+/* Write User Register */
+#define XT_INS_WUR(UR, T) _XT_INS_FORMAT_RSR(0xF30000, UR, T)
+
+/* Read Floating-Point Register */
+#define XT_INS_RFR(FR, T) _XT_INS_FORMAT_RRR(0xFA0000, (((FR) << 4) | 0x4), T)
+/* Write Floating-Point Register */
+#define XT_INS_WFR(FR, T) _XT_INS_FORMAT_RRR(0xFA0000, (((FR) << 4) | 0x5), T)
+
+/* 32-bit break */
+#define XT_INS_BREAK(IMM1, IMM2) _XT_INS_FORMAT_RRR(0x000000, \
+ (((IMM1) & 0x0F) << 4) | ((IMM2) & 0x0F), 0x4)
+/* 16-bit break */
+#define XT_INS_BREAKN(IMM4) _XT_INS_FORMAT_RRRN(0x00000D, IMM4, 0x2, 0xF)
+
+#define XT_INS_L32E(R, S, T) _XT_INS_FORMAT_RRI4(0x90000, 0, R, S, T)
+#define XT_INS_S32E(R, S, T) _XT_INS_FORMAT_RRI4(0x490000, 0, R, S, T)
+#define XT_INS_L32E_S32E_MASK 0xFF000F
+
+#define XT_INS_RFWO 0x3400
+#define XT_INS_RFWU 0x3500
+#define XT_INS_RFWO_RFWU_MASK 0xFFFFFF
+
+#define XT_WATCHPOINTS_NUM_MAX 2
+
+/* Special register number macro for DDR register.
+* this gets used a lot so making a shortcut to it is
+* useful.
+*/
+#define XT_SR_DDR (xtensa_regs[XT_REG_IDX_OCD_DDR].reg_num)
+
+/*Same thing for A3/A4 */
+#define XT_REG_A3 (xtensa_regs[XT_REG_IDX_AR3].reg_num)
+#define XT_REG_A4 (xtensa_regs[XT_REG_IDX_AR4].reg_num)
+
+#define XT_PC_REG_NUM_BASE (176)
+#define XT_SW_BREAKPOINTS_MAX_NUM 32
+
+const struct xtensa_reg_desc xtensa_regs[XT_NUM_REGS] = {
+ { "pc", XT_PC_REG_NUM_BASE /*+XT_DEBUGLEVEL*/, XT_REG_SPECIAL, 0 }, /* actually epc[debuglevel] */
+ { "ar0", 0x00, XT_REG_GENERAL, 0 },
+ { "ar1", 0x01, XT_REG_GENERAL, 0 },
+ { "ar2", 0x02, XT_REG_GENERAL, 0 },
+ { "ar3", 0x03, XT_REG_GENERAL, 0 },
+ { "ar4", 0x04, XT_REG_GENERAL, 0 },
+ { "ar5", 0x05, XT_REG_GENERAL, 0 },
+ { "ar6", 0x06, XT_REG_GENERAL, 0 },
+ { "ar7", 0x07, XT_REG_GENERAL, 0 },
+ { "ar8", 0x08, XT_REG_GENERAL, 0 },
+ { "ar9", 0x09, XT_REG_GENERAL, 0 },
+ { "ar10", 0x0A, XT_REG_GENERAL, 0 },
+ { "ar11", 0x0B, XT_REG_GENERAL, 0 },
+ { "ar12", 0x0C, XT_REG_GENERAL, 0 },
+ { "ar13", 0x0D, XT_REG_GENERAL, 0 },
+ { "ar14", 0x0E, XT_REG_GENERAL, 0 },
+ { "ar15", 0x0F, XT_REG_GENERAL, 0 },
+ { "ar16", 0x10, XT_REG_GENERAL, 0 },
+ { "ar17", 0x11, XT_REG_GENERAL, 0 },
+ { "ar18", 0x12, XT_REG_GENERAL, 0 },
+ { "ar19", 0x13, XT_REG_GENERAL, 0 },
+ { "ar20", 0x14, XT_REG_GENERAL, 0 },
+ { "ar21", 0x15, XT_REG_GENERAL, 0 },
+ { "ar22", 0x16, XT_REG_GENERAL, 0 },
+ { "ar23", 0x17, XT_REG_GENERAL, 0 },
+ { "ar24", 0x18, XT_REG_GENERAL, 0 },
+ { "ar25", 0x19, XT_REG_GENERAL, 0 },
+ { "ar26", 0x1A, XT_REG_GENERAL, 0 },
+ { "ar27", 0x1B, XT_REG_GENERAL, 0 },
+ { "ar28", 0x1C, XT_REG_GENERAL, 0 },
+ { "ar29", 0x1D, XT_REG_GENERAL, 0 },
+ { "ar30", 0x1E, XT_REG_GENERAL, 0 },
+ { "ar31", 0x1F, XT_REG_GENERAL, 0 },
+ { "ar32", 0x20, XT_REG_GENERAL, 0 },
+ { "ar33", 0x21, XT_REG_GENERAL, 0 },
+ { "ar34", 0x22, XT_REG_GENERAL, 0 },
+ { "ar35", 0x23, XT_REG_GENERAL, 0 },
+ { "ar36", 0x24, XT_REG_GENERAL, 0 },
+ { "ar37", 0x25, XT_REG_GENERAL, 0 },
+ { "ar38", 0x26, XT_REG_GENERAL, 0 },
+ { "ar39", 0x27, XT_REG_GENERAL, 0 },
+ { "ar40", 0x28, XT_REG_GENERAL, 0 },
+ { "ar41", 0x29, XT_REG_GENERAL, 0 },
+ { "ar42", 0x2A, XT_REG_GENERAL, 0 },
+ { "ar43", 0x2B, XT_REG_GENERAL, 0 },
+ { "ar44", 0x2C, XT_REG_GENERAL, 0 },
+ { "ar45", 0x2D, XT_REG_GENERAL, 0 },
+ { "ar46", 0x2E, XT_REG_GENERAL, 0 },
+ { "ar47", 0x2F, XT_REG_GENERAL, 0 },
+ { "ar48", 0x30, XT_REG_GENERAL, 0 },
+ { "ar49", 0x31, XT_REG_GENERAL, 0 },
+ { "ar50", 0x32, XT_REG_GENERAL, 0 },
+ { "ar51", 0x33, XT_REG_GENERAL, 0 },
+ { "ar52", 0x34, XT_REG_GENERAL, 0 },
+ { "ar53", 0x35, XT_REG_GENERAL, 0 },
+ { "ar54", 0x36, XT_REG_GENERAL, 0 },
+ { "ar55", 0x37, XT_REG_GENERAL, 0 },
+ { "ar56", 0x38, XT_REG_GENERAL, 0 },
+ { "ar57", 0x39, XT_REG_GENERAL, 0 },
+ { "ar58", 0x3A, XT_REG_GENERAL, 0 },
+ { "ar59", 0x3B, XT_REG_GENERAL, 0 },
+ { "ar60", 0x3C, XT_REG_GENERAL, 0 },
+ { "ar61", 0x3D, XT_REG_GENERAL, 0 },
+ { "ar62", 0x3E, XT_REG_GENERAL, 0 },
+ { "ar63", 0x3F, XT_REG_GENERAL, 0 },
+ { "lbeg", 0x00, XT_REG_SPECIAL, 0 },
+ { "lend", 0x01, XT_REG_SPECIAL, 0 },
+ { "lcount", 0x02, XT_REG_SPECIAL, 0 },
+ { "sar", 0x03, XT_REG_SPECIAL, 0 },
+ { "windowbase", 0x48, XT_REG_SPECIAL, 0 },
+ { "windowstart", 0x49, XT_REG_SPECIAL, 0 },
+ { "configid0", 0xB0, XT_REG_SPECIAL, 0 },
+ { "configid1", 0xD0, XT_REG_SPECIAL, 0 },
+ { "ps", 0xC6, XT_REG_SPECIAL, 0 }, /* actually EPS[debuglevel] */
+ { "threadptr", 0xE7, XT_REG_USER, 0 },
+ { "br", 0x04, XT_REG_SPECIAL, 0 },
+ { "scompare1", 0x0C, XT_REG_SPECIAL, 0 },
+ { "acclo", 0x10, XT_REG_SPECIAL, 0 },
+ { "acchi", 0x11, XT_REG_SPECIAL, 0 },
+ { "m0", 0x20, XT_REG_SPECIAL, 0 },
+ { "m1", 0x21, XT_REG_SPECIAL, 0 },
+ { "m2", 0x22, XT_REG_SPECIAL, 0 },
+ { "m3", 0x23, XT_REG_SPECIAL, 0 },
+ { "f0", 0x00, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f1", 0x01, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f2", 0x02, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f3", 0x03, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f4", 0x04, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f5", 0x05, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f6", 0x06, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f7", 0x07, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f8", 0x08, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f9", 0x09, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f10", 0x0A, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f11", 0x0B, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f12", 0x0C, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f13", 0x0D, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f14", 0x0E, XT_REG_FR, XT_REGF_COPROC0 },
+ { "f15", 0x0F, XT_REG_FR, XT_REGF_COPROC0 },
+ { "fcr", 0xE8, XT_REG_USER, XT_REGF_COPROC0 },
+ { "fsr", 0xE9, XT_REG_USER, XT_REGF_COPROC0 },
+ { "mmid", 0x59, XT_REG_SPECIAL, XT_REGF_NOREAD },
+ { "ibreakenable", 0x60, XT_REG_SPECIAL, 0 },
+ { "memctl", 0x61, XT_REG_SPECIAL, 0 },
+ { "atomctl", 0x63, XT_REG_SPECIAL, 0 },
+ { "ibreaka0", 0x80, XT_REG_SPECIAL, 0 },
+ { "ibreaka1", 0x81, XT_REG_SPECIAL, 0 },
+ { "dbreaka0", 0x90, XT_REG_SPECIAL, 0 },
+ { "dbreaka1", 0x91, XT_REG_SPECIAL, 0 },
+ { "dbreakc0", 0xA0, XT_REG_SPECIAL, 0 },
+ { "dbreakc1", 0xA1, XT_REG_SPECIAL, 0 },
+ { "epc1", 0xB1, XT_REG_SPECIAL, 0 },
+ { "epc2", 0xB2, XT_REG_SPECIAL, 0 },
+ { "epc3", 0xB3, XT_REG_SPECIAL, 0 },
+ { "epc4", 0xB4, XT_REG_SPECIAL, 0 },
+ { "epc5", 0xB5, XT_REG_SPECIAL, 0 },
+ { "epc6", 0xB6, XT_REG_SPECIAL, 0 },
+ { "epc7", 0xB7, XT_REG_SPECIAL, 0 },
+ { "depc", 0xC0, XT_REG_SPECIAL, 0 },
+ { "eps2", 0xC2, XT_REG_SPECIAL, 0 },
+ { "eps3", 0xC3, XT_REG_SPECIAL, 0 },
+ { "eps4", 0xC4, XT_REG_SPECIAL, 0 },
+ { "eps5", 0xC5, XT_REG_SPECIAL, 0 },
+ { "eps6", 0xC6, XT_REG_SPECIAL, 0 },
+ { "eps7", 0xC7, XT_REG_SPECIAL, 0 },
+ { "excsave1", 0xD1, XT_REG_SPECIAL, 0 },
+ { "excsave2", 0xD2, XT_REG_SPECIAL, 0 },
+ { "excsave3", 0xD3, XT_REG_SPECIAL, 0 },
+ { "excsave4", 0xD4, XT_REG_SPECIAL, 0 },
+ { "excsave5", 0xD5, XT_REG_SPECIAL, 0 },
+ { "excsave6", 0xD6, XT_REG_SPECIAL, 0 },
+ { "excsave7", 0xD7, XT_REG_SPECIAL, 0 },
+ { "cpenable", 0xE0, XT_REG_SPECIAL, 0 },
+ { "interrupt", 0xE2, XT_REG_SPECIAL, 0 },
+ { "intset", 0xE2, XT_REG_SPECIAL, XT_REGF_NOREAD },
+ { "intclear", 0xE3, XT_REG_SPECIAL, XT_REGF_NOREAD },
+ { "intenable", 0xE4, XT_REG_SPECIAL, 0 },
+ { "vecbase", 0xE7, XT_REG_SPECIAL, 0 },
+ { "exccause", 0xE8, XT_REG_SPECIAL, 0 },
+ { "debugcause", 0xE9, XT_REG_SPECIAL, 0 },
+ { "ccount", 0xEA, XT_REG_SPECIAL, 0 },
+ { "prid", 0xEB, XT_REG_SPECIAL, 0 },
+ { "icount", 0xEC, XT_REG_SPECIAL, 0 },
+ { "icountlevel", 0xED, XT_REG_SPECIAL, 0 },
+ { "excvaddr", 0xEE, XT_REG_SPECIAL, 0 },
+ { "ccompare0", 0xF0, XT_REG_SPECIAL, 0 },
+ { "ccompare1", 0xF1, XT_REG_SPECIAL, 0 },
+ { "ccompare2", 0xF2, XT_REG_SPECIAL, 0 },
+ { "misc0", 0xF4, XT_REG_SPECIAL, 0 },
+ { "misc1", 0xF5, XT_REG_SPECIAL, 0 },
+ { "misc2", 0xF6, XT_REG_SPECIAL, 0 },
+ { "misc3", 0xF7, XT_REG_SPECIAL, 0 },
+ { "litbase", 0x05, XT_REG_SPECIAL, 0 },
+ { "ptevaddr", 0x53, XT_REG_SPECIAL, 0 },
+ { "rasid", 0x5A, XT_REG_SPECIAL, 0 },
+ { "itlbcfg", 0x5B, XT_REG_SPECIAL, 0 },
+ { "dtlbcfg", 0x5C, XT_REG_SPECIAL, 0 },
+ { "mepc", 0x6A, XT_REG_SPECIAL, 0 },
+ { "meps", 0x6B, XT_REG_SPECIAL, 0 },
+ { "mesave", 0x6C, XT_REG_SPECIAL, 0 },
+ { "mesr", 0x6D, XT_REG_SPECIAL, 0 },
+ { "mecr", 0x6E, XT_REG_SPECIAL, 0 },
+ { "mevaddr", 0x6F, XT_REG_SPECIAL, 0 },
+ { "a0", XT_REG_IDX_AR0, XT_REG_RELGEN, 0 }, /* WARNING: For these registers, regnum points to the */
+ { "a1", XT_REG_IDX_AR1, XT_REG_RELGEN, 0 }, /* index of the corresponding ARxregisters, NOT to */
+ { "a2", XT_REG_IDX_AR2, XT_REG_RELGEN, 0 }, /* the processor register number! */
+ { "a3", XT_REG_IDX_AR3, XT_REG_RELGEN, 0 },
+ { "a4", XT_REG_IDX_AR4, XT_REG_RELGEN, 0 },
+ { "a5", XT_REG_IDX_AR5, XT_REG_RELGEN, 0 },
+ { "a6", XT_REG_IDX_AR6, XT_REG_RELGEN, 0 },
+ { "a7", XT_REG_IDX_AR7, XT_REG_RELGEN, 0 },
+ { "a8", XT_REG_IDX_AR8, XT_REG_RELGEN, 0 },
+ { "a9", XT_REG_IDX_AR9, XT_REG_RELGEN, 0 },
+ { "a10", XT_REG_IDX_AR10, XT_REG_RELGEN, 0 },
+ { "a11", XT_REG_IDX_AR11, XT_REG_RELGEN, 0 },
+ { "a12", XT_REG_IDX_AR12, XT_REG_RELGEN, 0 },
+ { "a13", XT_REG_IDX_AR13, XT_REG_RELGEN, 0 },
+ { "a14", XT_REG_IDX_AR14, XT_REG_RELGEN, 0 },
+ { "a15", XT_REG_IDX_AR15, XT_REG_RELGEN, 0 },
+
+ { "pwrctl", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pwrstat", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "eristat", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_itctrl", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_claimset", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_claimclr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_lockaccess", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_lockstatus", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "cs_authstatus", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "fault_info", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_id", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_ctrl", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_stat", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_data", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_addr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_pctrigger", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_pcmatch", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_delay", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_memstart", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "trax_memend", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmg", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmoc", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pm0", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pm1", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmctrl0", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmctrl1", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmstat0", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "pmstat1", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_id", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_dcrclr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_dcrset", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ocd_dsr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+ { "ddr", 0x68, XT_REG_DEBUG, XT_REGF_NOREAD },
+};
+
+
+/**
+ * Types of memory used at xtensa target
+ */
+enum xtensa_mem_region_type {
+ XTENSA_MEM_REG_IROM = 0x0,
+ XTENSA_MEM_REG_IRAM,
+ XTENSA_MEM_REG_DROM,
+ XTENSA_MEM_REG_DRAM,
+ XTENSA_MEM_REG_URAM,
+ XTENSA_MEM_REG_XLMI,
+ XTENSA_MEM_REGS_NUM
+};
+
+/**
+ * Gets a config for the specific mem type
+ */
+static inline const struct xtensa_local_mem_config *xtensa_get_mem_config(
+ struct xtensa *xtensa,
+ enum xtensa_mem_region_type type)
+{
+ switch (type) {
+ case XTENSA_MEM_REG_IROM:
+ return &xtensa->core_config->irom;
+ case XTENSA_MEM_REG_IRAM:
+ return &xtensa->core_config->iram;
+ case XTENSA_MEM_REG_DROM:
+ return &xtensa->core_config->drom;
+ case XTENSA_MEM_REG_DRAM:
+ return &xtensa->core_config->dram;
+ case XTENSA_MEM_REG_URAM:
+ return &xtensa->core_config->uram;
+ case XTENSA_MEM_REG_XLMI:
+ return &xtensa->core_config->xlmi;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * Extracts an exact xtensa_local_mem_region_config from xtensa_local_mem_config
+ * for a given address
+ * Returns NULL if nothing found
+ */
+static inline const struct xtensa_local_mem_region_config *xtensa_memory_region_find(
+ const struct xtensa_local_mem_config *mem,
+ target_addr_t address)
+{
+ for (unsigned int i = 0; i < mem->count; i++) {
+ const struct xtensa_local_mem_region_config *region = &mem->regions[i];
+ if (address >= region->base && address < (region->base + region->size))
+ return region;
+ }
+ return NULL;
+}
+
+/**
+ * Returns a corresponding xtensa_local_mem_region_config from the xtensa target
+ * for a given address
+ * Returns NULL if nothing found
+ */
+static inline const struct xtensa_local_mem_region_config *xtensa_target_memory_region_find(
+ struct xtensa *xtensa,
+ target_addr_t address)
+{
+ const struct xtensa_local_mem_region_config *result;
+ const struct xtensa_local_mem_config *mcgf;
+ for (unsigned int mtype = 0; mtype < XTENSA_MEM_REGS_NUM; mtype++) {
+ mcgf = xtensa_get_mem_config(xtensa, mtype);
+ result = xtensa_memory_region_find(mcgf, address);
+ if (result)
+ return result;
+ }
+ return NULL;
+}
+
+static int xtensa_core_reg_get(struct reg *reg)
+{
+ /*We don't need this because we read all registers on halt anyway. */
+ struct xtensa *xtensa = (struct xtensa *)reg->arch_info;
+ struct target *target = xtensa->target;
+
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+ return ERROR_OK;
+}
+
+static int xtensa_core_reg_set(struct reg *reg, uint8_t *buf)
+{
+ struct xtensa *xtensa = (struct xtensa *)reg->arch_info;
+ struct target *target = xtensa->target;
+
+ assert(reg->size <= 64 && "up to 64-bit regs are supported only!");
+ if (target->state != TARGET_HALTED)
+ return ERROR_TARGET_NOT_HALTED;
+
+ buf_cpy(buf, reg->value, reg->size);
+ reg->dirty = true;
+ reg->valid = true;
+
+ return ERROR_OK;
+}
+
+static const struct reg_arch_type xtensa_reg_type = {
+ .get = xtensa_core_reg_get,
+ .set = xtensa_core_reg_set,
+};
+
+const struct reg_arch_type xtensa_user_reg_u32_type = {
+ .get = xtensa_core_reg_get,
+ .set = xtensa_core_reg_set,
+};
+
+const struct reg_arch_type xtensa_user_reg_u128_type = {
+ .get = xtensa_core_reg_get,
+ .set = xtensa_core_reg_set,
+};
+
+static inline size_t xtensa_insn_size_get(uint32_t insn)
+{
+ return insn & BIT(3) ? 2 : XT_ISNS_SZ_MAX;
+}
+
+/* Convert a register index that's indexed relative to windowbase, to the real address. */
+static enum xtensa_reg_id xtensa_windowbase_offset_to_canonical(enum xtensa_reg_id reg_idx, int windowbase)
+{
+ unsigned int idx;
+ if (reg_idx >= XT_REG_IDX_AR0 && reg_idx <= XT_REG_IDX_AR63) {
+ idx = reg_idx - XT_REG_IDX_AR0;
+ } else if (reg_idx >= XT_REG_IDX_A0 && reg_idx <= XT_REG_IDX_A15) {
+ idx = reg_idx - XT_REG_IDX_A0;
+ } else {
+ LOG_ERROR("Error: can't convert register %d to non-windowbased register!", reg_idx);
+ return -1;
+ }
+ return ((idx + windowbase * 4) & 63) + XT_REG_IDX_AR0;
+}
+
+static enum xtensa_reg_id xtensa_canonical_to_windowbase_offset(enum xtensa_reg_id reg_idx, int windowbase)
+{
+ return xtensa_windowbase_offset_to_canonical(reg_idx, -windowbase);
+}
+
+static void xtensa_mark_register_dirty(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ reg_list[reg_idx].dirty = true;
+}
+
+static int xtensa_queue_dbg_reg_read(struct xtensa *xtensa, unsigned int reg, uint8_t *data)
+{
+ struct xtensa_debug_module *dm = &xtensa->dbg_mod;
+
+ if (!xtensa->core_config->trace.enabled &&
+ (reg <= NARADR_MEMADDREND || (reg >= NARADR_PMG && reg <= NARADR_PMSTAT7))) {
+ LOG_ERROR("Can not access %u reg when Trace Port option disabled!", reg);
+ return ERROR_FAIL;
+ }
+ return dm->dbg_ops->queue_reg_read(dm, reg, data);
+}
+
+static int xtensa_queue_dbg_reg_write(struct xtensa *xtensa, unsigned int reg, uint32_t data)
+{
+ struct xtensa_debug_module *dm = &xtensa->dbg_mod;
+
+ if (!xtensa->core_config->trace.enabled &&
+ (reg <= NARADR_MEMADDREND || (reg >= NARADR_PMG && reg <= NARADR_PMSTAT7))) {
+ LOG_ERROR("Can not access %u reg when Trace Port option disabled!", reg);
+ return ERROR_FAIL;
+ }
+ return dm->dbg_ops->queue_reg_write(dm, reg, data);
+}
+
+static void xtensa_queue_exec_ins(struct xtensa *xtensa, uint32_t ins)
+{
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DIR0EXEC, ins);
+}
+
+static bool xtensa_reg_is_readable(enum xtensa_reg_flags flags, xtensa_reg_val_t cpenable)
+{
+ if (flags & XT_REGF_NOREAD)
+ return false;
+ if ((flags & XT_REGF_COPROC0) && (cpenable & BIT(0)) == 0)
+ return false;
+ return true;
+}
+
+static int xtensa_queue_pwr_reg_write(struct xtensa *xtensa, unsigned int reg, uint32_t data)
+{
+ struct xtensa_debug_module *dm = &xtensa->dbg_mod;
+ return dm->pwr_ops->queue_reg_write(dm, reg, data);
+}
+
+static bool xtensa_special_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ /* TODO: array of size XT_NUM_REGS can be used here to map special register ID to
+ * corresponding config option 'enabled' flag */
+ if (reg_idx >= XT_REG_IDX_LBEG && reg_idx <= XT_REG_IDX_LCOUNT)
+ return xtensa->core_config->loop;
+ else if (reg_idx == XT_REG_IDX_BR)
+ return xtensa->core_config->boolean;
+ else if (reg_idx == XT_REG_IDX_LITBASE)
+ return xtensa->core_config->ext_l32r;
+ else if (reg_idx == XT_REG_IDX_SCOMPARE1 || reg_idx == XT_REG_IDX_ATOMCTL)
+ return xtensa->core_config->cond_store;
+ else if (reg_idx >= XT_REG_IDX_ACCLO && reg_idx <= XT_REG_IDX_M3)
+ return xtensa->core_config->mac16;
+ else if (reg_idx == XT_REG_IDX_WINDOWBASE || reg_idx == XT_REG_IDX_WINDOWSTART)
+ return xtensa->core_config->windowed;
+ else if (reg_idx >= XT_REG_IDX_PTEVADDR && reg_idx <= XT_REG_IDX_DTLBCFG)
+ return xtensa->core_config->mmu.enabled;
+ else if (reg_idx == XT_REG_IDX_MMID)
+ return xtensa->core_config->trace.enabled;
+ else if (reg_idx >= XT_REG_IDX_MEPC && reg_idx <= XT_REG_IDX_MEVADDR)
+ return xtensa->core_config->mem_err_check;
+ else if (reg_idx == XT_REG_IDX_CPENABLE)
+ return xtensa->core_config->coproc;
+ else if (reg_idx == XT_REG_IDX_VECBASE)
+ return xtensa->core_config->reloc_vec;
+ else if (reg_idx == XT_REG_IDX_CCOUNT)
+ return xtensa->core_config->tim_irq.enabled;
+ else if (reg_idx >= XT_REG_IDX_CCOMPARE0 && reg_idx <= XT_REG_IDX_CCOMPARE2)
+ return xtensa->core_config->tim_irq.enabled &&
+ (reg_idx - XT_REG_IDX_CCOMPARE0 < xtensa->core_config->tim_irq.comp_num);
+ else if (reg_idx == XT_REG_IDX_PRID)
+ return xtensa->core_config->proc_id;
+ else if (reg_idx >= XT_REG_IDX_MISC0 && reg_idx <= XT_REG_IDX_MISC3)
+ return reg_idx - XT_REG_IDX_MISC0 < xtensa->core_config->miscregs_num;
+ return true;
+}
+
+static bool xtensa_user_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ if (reg_idx == XT_REG_IDX_THREADPTR)
+ return xtensa->core_config->threadptr;
+ if (reg_idx == XT_REG_IDX_FCR || reg_idx == XT_REG_IDX_FSR)
+ return xtensa->core_config->fp_coproc;
+ return false;
+}
+
+static inline bool xtensa_fp_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ return xtensa->core_config->fp_coproc;
+}
+
+static inline bool xtensa_regular_reg_exists(struct xtensa *xtensa, enum xtensa_reg_id reg_idx)
+{
+ if (reg_idx >= XT_REG_IDX_AR0 && reg_idx <= XT_REG_IDX_AR63)
+ return reg_idx - XT_REG_IDX_AR0 < xtensa->core_config->aregs_num;
+ return true;
+}
+
+static int xtensa_write_dirty_registers(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res;
+ xtensa_reg_val_t regval, windowbase = 0;
+ bool scratch_reg_dirty = false;
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ /*We need to write the dirty registers in the cache list back to the processor.
+ *Start by writing the SFR/user registers. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (reg_list[i].dirty) {
+ if (xtensa_regs[i].type == XT_REG_SPECIAL ||
+ xtensa_regs[i].type == XT_REG_USER ||
+ xtensa_regs[i].type == XT_REG_FR) {
+ scratch_reg_dirty = true;
+ regval = xtensa_reg_get(target, i);
+ LOG_TARGET_DEBUG(target, "Writing back reg %s val %08" PRIX32,
+ xtensa_regs[i].name,
+ regval);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, regval);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ if (xtensa_regs[i].type == XT_REG_USER) {
+ if (reg_list[i].exist)
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WUR(xtensa_regs[i].reg_num,
+ XT_REG_A3));
+ } else if (xtensa_regs[i].type == XT_REG_FR) {
+ if (reg_list[i].exist)
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WFR(xtensa_regs[i].reg_num,
+ XT_REG_A3));
+ } else {/*SFR */
+ if (reg_list[i].exist) {
+ unsigned int reg_num = xtensa_regs[i].reg_num;
+ if (reg_num == XT_PC_REG_NUM_BASE)
+ /* reg number of PC for debug interrupt
+ * depends on NDEBUGLEVEL */
+ reg_num += xtensa->core_config->debug.irq_level;
+
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WSR(reg_num, XT_REG_A3));
+ }
+ }
+ reg_list[i].dirty = false;
+ }
+ }
+ }
+ if (scratch_reg_dirty)
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+
+ if (xtensa->core_config->user_regs_num > 0 &&
+ xtensa->core_config->queue_write_dirty_user_regs)
+ xtensa->core_config->queue_write_dirty_user_regs(target);
+
+ if (xtensa->core_config->windowed) {
+ /*Grab the windowbase, we need it. */
+ windowbase = xtensa_reg_get(target, XT_REG_IDX_WINDOWBASE);
+ /*Check if there are problems with both the ARx as well as the corresponding Rx
+ * registers set and dirty. */
+ /*Warn the user if this happens, not much else we can do... */
+ for (unsigned int i = XT_REG_IDX_A0; i <= XT_REG_IDX_A15; i++) {
+ unsigned int j = xtensa_windowbase_offset_to_canonical(i, windowbase);
+ if (reg_list[i].dirty && reg_list[j].dirty) {
+ if (memcmp(reg_list[i].value, reg_list[j].value,
+ sizeof(xtensa_reg_val_t)) != 0)
+ LOG_WARNING(
+ "Warning: Both A%d as well as the physical register it points to (AR%d) are dirty and differs in value. Results are undefined!",
+ i - XT_REG_IDX_A0,
+ j - XT_REG_IDX_AR0);
+ }
+ }
+ }
+
+ /*Write A0-A16 */
+ for (unsigned int i = 0; i < 16; i++) {
+ if (reg_list[XT_REG_IDX_A0 + i].dirty) {
+ regval = xtensa_reg_get(target, XT_REG_IDX_A0 + i);
+ LOG_TARGET_DEBUG(target, "Writing back reg %s value %08" PRIX32 ", num =%i",
+ xtensa_regs[XT_REG_IDX_A0 + i].name,
+ regval,
+ xtensa_regs[XT_REG_IDX_A0 + i].reg_num);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, regval);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, i));
+ reg_list[XT_REG_IDX_A0 + i].dirty = false;
+ }
+ }
+
+ if (xtensa->core_config->windowed) {
+ /*Now write AR0-AR63. */
+ for (unsigned int j = 0; j < 64; j += 16) {
+ /*Write the 16 registers we can see */
+ for (unsigned int i = 0; i < 16; i++) {
+ if (i + j < xtensa->core_config->aregs_num) {
+ enum xtensa_reg_id realadr =
+ xtensa_windowbase_offset_to_canonical(XT_REG_IDX_AR0 + i + j,
+ windowbase);
+ /*Write back any dirty un-windowed registers */
+ if (reg_list[realadr].dirty) {
+ regval = xtensa_reg_get(target, realadr);
+ LOG_TARGET_DEBUG(
+ target,
+ "Writing back reg %s value %08" PRIX32 ", num =%i",
+ xtensa_regs[realadr].name,
+ regval,
+ xtensa_regs[realadr].reg_num);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, regval);
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_RSR(XT_SR_DDR, xtensa_regs[XT_REG_IDX_AR0 + i].reg_num));
+ reg_list[realadr].dirty = false;
+ }
+ }
+ }
+ /*Now rotate the window so we'll see the next 16 registers. The final rotate
+ * will wraparound, */
+ /*leaving us in the state we were. */
+ xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(4));
+ }
+ }
+ res = jtag_execute_queue();
+ xtensa_core_status_check(target);
+
+ return res;
+}
+
+int xtensa_queue_write_dirty_user_regs_u32(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ xtensa_reg_val_t reg_val;
+ bool scratch_reg_dirty = false;
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ /* We need to write the dirty registers in the cache list back to the processor.
+ * Start by writing the SFR/user registers. */
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (!reg_list[XT_USR_REG_START + i].dirty)
+ continue;
+ scratch_reg_dirty = true;
+ reg_val = xtensa_reg_get(target, XT_USR_REG_START + i);
+ LOG_TARGET_DEBUG(target, "Writing back reg %s val %08" PRIX32,
+ xtensa->core_config->user_regs[i].name,
+ reg_val);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, reg_val);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WUR(xtensa->core_config->user_regs[i].reg_num,
+ XT_REG_A3));
+ reg_list[XT_USR_REG_START + i].dirty = false;
+ }
+ if (scratch_reg_dirty)
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+
+ return ERROR_OK;
+}
+
+static inline bool xtensa_is_stopped(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ return xtensa->dbg_mod.core_status.dsr & OCDDSR_STOPPED;
+}
+
+int xtensa_examine(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int cmd = PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP;
+
+ LOG_DEBUG("coreid = %d", target->coreid);
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd);
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE);
+ xtensa_dm_queue_enable(&xtensa->dbg_mod);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ if (!xtensa_dm_is_online(&xtensa->dbg_mod)) {
+ LOG_ERROR("Unexpected OCD_ID = %08" PRIx32, xtensa->dbg_mod.device_id);
+ return ERROR_TARGET_FAILURE;
+ }
+ LOG_DEBUG("OCD_ID = %08" PRIx32, xtensa->dbg_mod.device_id);
+ if (!target_was_examined(target))
+ target_set_examined(target);
+ xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ return ERROR_OK;
+}
+
+int xtensa_wakeup(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int cmd = PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP;
+
+ if (xtensa->reset_asserted)
+ cmd |= PWRCTL_CORERESET;
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd);
+ /* TODO: can we join this with the write above? */
+ xtensa_queue_pwr_reg_write(xtensa, DMREG_PWRCTL, cmd | PWRCTL_JTAGDEBUGUSE);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ return jtag_execute_queue();
+}
+
+int xtensa_smpbreak_write(struct xtensa *xtensa, uint32_t set)
+{
+ uint32_t dsr_data = 0x00110000;
+ uint32_t clear = (set | OCDDCR_ENABLEOCD) ^
+ (OCDDCR_BREAKINEN | OCDDCR_BREAKOUTEN | OCDDCR_RUNSTALLINEN |
+ OCDDCR_DEBUGMODEOUTEN | OCDDCR_ENABLEOCD);
+
+ LOG_TARGET_DEBUG(xtensa->target, "write smpbreak set=0x%" PRIx32 " clear=0x%" PRIx32, set, clear);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRSET, set | OCDDCR_ENABLEOCD);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRCLR, clear);
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DSR, dsr_data);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ return jtag_execute_queue();
+}
+
+int xtensa_smpbreak_set(struct target *target, uint32_t set)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res = ERROR_OK;
+
+ xtensa->smp_break = set;
+ if (target_was_examined(target))
+ res = xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ LOG_TARGET_DEBUG(target, "set smpbreak=%" PRIx32 ", state=%i", set, target->state);
+ return res;
+}
+
+int xtensa_smpbreak_read(struct xtensa *xtensa, uint32_t *val)
+{
+ uint8_t dcr_buf[sizeof(uint32_t)];
+
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DCRSET, dcr_buf);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ *val = buf_get_u32(dcr_buf, 0, 32);
+
+ return res;
+}
+
+int xtensa_smpbreak_get(struct target *target, uint32_t *val)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ *val = xtensa->smp_break;
+ return ERROR_OK;
+}
+
+static inline xtensa_reg_val_t xtensa_reg_get_value(struct reg *reg)
+{
+ return buf_get_u32(reg->value, 0, 32);
+}
+
+static inline void xtensa_reg_set_value(struct reg *reg, xtensa_reg_val_t value)
+{
+ buf_set_u32(reg->value, 0, 32, value);
+ reg->dirty = true;
+}
+
+int xtensa_core_status_check(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res, needclear = 0;
+
+ xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ xtensa_dsr_t dsr = xtensa_dm_core_status_get(&xtensa->dbg_mod);
+ LOG_TARGET_DEBUG(target, "DSR (%08" PRIX32 ")", dsr);
+ if (dsr & OCDDSR_EXECBUSY) {
+ if (!xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target, "DSR (%08" PRIX32 ") indicates target still busy!", dsr);
+ needclear = 1;
+ }
+ if (dsr & OCDDSR_EXECEXCEPTION) {
+ if (!xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target,
+ "DSR (%08" PRIX32 ") indicates DIR instruction generated an exception!",
+ dsr);
+ needclear = 1;
+ }
+ if (dsr & OCDDSR_EXECOVERRUN) {
+ if (!xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target,
+ "DSR (%08" PRIX32 ") indicates DIR instruction generated an overrun!",
+ dsr);
+ needclear = 1;
+ }
+ if (needclear) {
+ res = xtensa_dm_core_status_clear(&xtensa->dbg_mod,
+ OCDDSR_EXECEXCEPTION | OCDDSR_EXECOVERRUN);
+ if (res != ERROR_OK && !xtensa->suppress_dsr_errors)
+ LOG_TARGET_ERROR(target, "clearing DSR failed!");
+ return xtensa->suppress_dsr_errors ? ERROR_OK : ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+xtensa_reg_val_t xtensa_reg_get(struct target *target, enum xtensa_reg_id reg_id)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg = &xtensa->core_cache->reg_list[reg_id];
+ assert(reg_id < xtensa->core_cache->num_regs && "Attempt to access non-existing reg!");
+ return xtensa_reg_get_value(reg);
+}
+
+void xtensa_reg_set(struct target *target, enum xtensa_reg_id reg_id, xtensa_reg_val_t value)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg = &xtensa->core_cache->reg_list[reg_id];
+ assert(reg_id < xtensa->core_cache->num_regs && "Attempt to access non-existing reg!");
+ if (xtensa_reg_get_value(reg) == value)
+ return;
+ xtensa_reg_set_value(reg, value);
+}
+
+int xtensa_assert_reset(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "target_number=%i, begin", target->target_number);
+ target->state = TARGET_RESET;
+ xtensa_queue_pwr_reg_write(xtensa,
+ DMREG_PWRCTL,
+ PWRCTL_JTAGDEBUGUSE | PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP |
+ PWRCTL_CORERESET);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ xtensa->reset_asserted = true;
+ return res;
+}
+
+int xtensa_deassert_reset(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "halt=%d", target->reset_halt);
+ if (target->reset_halt)
+ xtensa_queue_dbg_reg_write(xtensa,
+ NARADR_DCRSET,
+ OCDDCR_ENABLEOCD | OCDDCR_DEBUGINTERRUPT);
+ xtensa_queue_pwr_reg_write(xtensa,
+ DMREG_PWRCTL,
+ PWRCTL_JTAGDEBUGUSE | PWRCTL_DEBUGWAKEUP | PWRCTL_MEMWAKEUP | PWRCTL_COREWAKEUP);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ target->state = TARGET_RUNNING;
+ xtensa->reset_asserted = false;
+ return res;
+}
+
+int xtensa_fetch_all_regs(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ xtensa_reg_val_t cpenable = 0, windowbase = 0;
+ uint8_t regvals[XT_NUM_REGS][sizeof(xtensa_reg_val_t)];
+ uint8_t dsrs[XT_NUM_REGS][sizeof(xtensa_dsr_t)];
+ bool debug_dsrs = !xtensa->regs_fetched || LOG_LEVEL_IS(LOG_LVL_DEBUG);
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ /* Assume the CPU has just halted. We now want to fill the register cache with all the
+ * register contents GDB needs. For speed, we pipeline all the read operations, execute them
+ * in one go, then sort everything out from the regvals variable. */
+
+ /* Start out with AREGS; we can reach those immediately. Grab them per 16 registers. */
+ for (unsigned int j = 0; j < XT_AREGS_NUM_MAX; j += 16) {
+ /*Grab the 16 registers we can see */
+ for (unsigned int i = 0; i < 16; i++) {
+ if (i + j < xtensa->core_config->aregs_num) {
+ xtensa_queue_exec_ins(xtensa,
+ XT_INS_WSR(XT_SR_DDR, xtensa_regs[XT_REG_IDX_AR0 + i].reg_num));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[XT_REG_IDX_AR0 + i + j]);
+ if (debug_dsrs)
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DSR, dsrs[XT_REG_IDX_AR0 + i + j]);
+ }
+ }
+ if (xtensa->core_config->windowed) {
+ /* Now rotate the window so we'll see the next 16 registers. The final rotate
+ * will wraparound, */
+ /* leaving us in the state we were. */
+ xtensa_queue_exec_ins(xtensa, XT_INS_ROTW(4));
+ }
+ }
+ if (xtensa->core_config->coproc) {
+ /* As the very first thing after AREGS, go grab the CPENABLE registers. It indicates
+ * if we can also grab the FP */
+ /* (and theoretically other coprocessor) registers, or if this is a bad thing to do.*/
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(xtensa_regs[XT_REG_IDX_CPENABLE].reg_num, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[XT_REG_IDX_CPENABLE]);
+ }
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to read ARs (%d)!", res);
+ return res;
+ }
+ xtensa_core_status_check(target);
+
+ if (xtensa->core_config->coproc)
+ cpenable = buf_get_u32(regvals[XT_REG_IDX_CPENABLE], 0, 32);
+ /* We're now free to use any of A0-A15 as scratch registers
+ * Grab the SFRs and user registers first. We use A3 as a scratch register. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (xtensa_reg_is_readable(xtensa_regs[i].flags, cpenable) && reg_list[i].exist &&
+ (xtensa_regs[i].type == XT_REG_SPECIAL ||
+ xtensa_regs[i].type == XT_REG_USER || xtensa_regs[i].type == XT_REG_FR)) {
+ if (xtensa_regs[i].type == XT_REG_USER) {
+ xtensa_queue_exec_ins(xtensa, XT_INS_RUR(xtensa_regs[i].reg_num, XT_REG_A3));
+ } else if (xtensa_regs[i].type == XT_REG_FR) {
+ xtensa_queue_exec_ins(xtensa, XT_INS_RFR(xtensa_regs[i].reg_num, XT_REG_A3));
+ } else { /*SFR */
+ unsigned int reg_num = xtensa_regs[i].reg_num;
+ if (reg_num == XT_PC_REG_NUM_BASE) {
+ /* reg number of PC for debug interrupt depends on NDEBUGLEVEL */
+ reg_num += xtensa->core_config->debug.irq_level;
+ }
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(reg_num, XT_REG_A3));
+ }
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[i]);
+ if (debug_dsrs)
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DSR, dsrs[i]);
+ }
+ }
+ /* Ok, send the whole mess to the CPU. */
+ res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to fetch AR regs!");
+ return res;
+ }
+ xtensa_core_status_check(target);
+
+ if (debug_dsrs) {
+ /* DSR checking: follows order in which registers are requested. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (xtensa_reg_is_readable(xtensa_regs[i].flags, cpenable) && reg_list[i].exist &&
+ (xtensa_regs[i].type == XT_REG_SPECIAL || xtensa_regs[i].type == XT_REG_USER ||
+ xtensa_regs[i].type == XT_REG_FR)) {
+ if (buf_get_u32(dsrs[i], 0, 32) & OCDDSR_EXECEXCEPTION) {
+ LOG_ERROR("Exception reading %s!", xtensa_regs[i].name);
+ return ERROR_FAIL;
+ }
+ }
+ }
+ }
+
+ if (xtensa->core_config->user_regs_num > 0 && xtensa->core_config->fetch_user_regs) {
+ res = xtensa->core_config->fetch_user_regs(target);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ if (xtensa->core_config->windowed) {
+ /* We need the windowbase to decode the general addresses. */
+ windowbase = buf_get_u32(regvals[XT_REG_IDX_WINDOWBASE], 0, 32);
+ }
+ /* Decode the result and update the cache. */
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ if (xtensa_reg_is_readable(xtensa_regs[i].flags, cpenable) && reg_list[i].exist) {
+ if (xtensa_regs[i].type == XT_REG_GENERAL) {
+ /* TODO: add support for non-windowed configs */
+ assert(
+ xtensa->core_config->windowed &&
+ "Regs fetch is not supported for non-windowed configs!");
+ /* The 64-value general register set is read from (windowbase) on down.
+ * We need to get the real register address by subtracting windowbase and
+ * wrapping around. */
+ int realadr = xtensa_canonical_to_windowbase_offset(i, windowbase);
+ buf_cpy(regvals[realadr], reg_list[i].value, reg_list[i].size);
+ } else if (xtensa_regs[i].type == XT_REG_RELGEN) {
+ buf_cpy(regvals[xtensa_regs[i].reg_num], reg_list[i].value, reg_list[i].size);
+ } else {
+ buf_cpy(regvals[i], reg_list[i].value, reg_list[i].size);
+ }
+ reg_list[i].valid = true;
+ } else {
+ reg_list[i].valid = false;
+ }
+ }
+ /* We have used A3 as a scratch register and we will need to write that back. */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+ xtensa->regs_fetched = true;
+
+ return ERROR_OK;
+}
+
+int xtensa_fetch_user_regs_u32(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg *reg_list = xtensa->core_cache->reg_list;
+ xtensa_reg_val_t cpenable = 0;
+ uint8_t regvals[XT_USER_REGS_NUM_MAX][sizeof(xtensa_reg_val_t)];
+ uint8_t dsrs[XT_USER_REGS_NUM_MAX][sizeof(xtensa_dsr_t)];
+ bool debug_dsrs = !xtensa->regs_fetched || LOG_LEVEL_IS(LOG_LVL_DEBUG);
+
+ assert(xtensa->core_config->user_regs_num < XT_USER_REGS_NUM_MAX && "Too many user regs configured!");
+ if (xtensa->core_config->coproc)
+ cpenable = xtensa_reg_get(target, XT_REG_IDX_CPENABLE);
+
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (!xtensa_reg_is_readable(xtensa->core_config->user_regs[i].flags, cpenable))
+ continue;
+ xtensa_queue_exec_ins(xtensa, XT_INS_RUR(xtensa->core_config->user_regs[i].reg_num, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_WSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, regvals[i]);
+ if (debug_dsrs)
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DSR, dsrs[i]);
+ }
+ /* Ok, send the whole mess to the CPU. */
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Failed to fetch AR regs!");
+ return res;
+ }
+ xtensa_core_status_check(target);
+
+ if (debug_dsrs) {
+ /* DSR checking: follows order in which registers are requested. */
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (!xtensa_reg_is_readable(xtensa->core_config->user_regs[i].flags, cpenable))
+ continue;
+ if (buf_get_u32(dsrs[i], 0, 32) & OCDDSR_EXECEXCEPTION) {
+ LOG_ERROR("Exception reading %s!", xtensa->core_config->user_regs[i].name);
+ return ERROR_FAIL;
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ if (xtensa_reg_is_readable(xtensa->core_config->user_regs[i].flags, cpenable)) {
+ buf_cpy(regvals[i], reg_list[XT_USR_REG_START + i].value, reg_list[XT_USR_REG_START + i].size);
+ reg_list[XT_USR_REG_START + i].valid = true;
+ } else {
+ reg_list[XT_USR_REG_START + i].valid = false;
+ }
+ }
+
+ /* We have used A3 as a scratch register and we will need to write that back. */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+ return ERROR_OK;
+}
+
+int xtensa_get_gdb_reg_list(struct target *target,
+ struct reg **reg_list[],
+ int *reg_list_size,
+ enum target_register_class reg_class)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int num_regs = xtensa->core_config->gdb_general_regs_num;
+
+ if (reg_class == REG_CLASS_ALL)
+ num_regs = xtensa->regs_num;
+
+ LOG_DEBUG("reg_class=%i, num_regs=%d", reg_class, num_regs);
+
+ *reg_list = malloc(num_regs * sizeof(struct reg *));
+ if (!*reg_list)
+ return ERROR_FAIL;
+
+ for (unsigned int k = 0; k < num_regs; k++) {
+ unsigned int reg_id = xtensa->core_config->gdb_regs_mapping[k];
+ (*reg_list)[k] = &xtensa->core_cache->reg_list[reg_id];
+ }
+
+ *reg_list_size = num_regs;
+
+ return ERROR_OK;
+}
+
+int xtensa_mmu_is_enabled(struct target *target, int *enabled)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ *enabled = xtensa->core_config->mmu.itlb_entries_count > 0 ||
+ xtensa->core_config->mmu.dtlb_entries_count > 0;
+ return ERROR_OK;
+}
+
+int xtensa_halt(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "start");
+ if (target->state == TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target, "target was already halted");
+ return ERROR_OK;
+ }
+ /* First we have to read dsr and check if the target stopped */
+ int res = xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read core status!");
+ return res;
+ }
+ LOG_TARGET_DEBUG(target, "Core status 0x%" PRIx32, xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ if (!xtensa_is_stopped(target)) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRSET, OCDDCR_ENABLEOCD | OCDDCR_DEBUGINTERRUPT);
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set OCDDCR_DEBUGINTERRUPT. Can't halt.");
+ }
+
+ return res;
+}
+
+int xtensa_prepare_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ uint32_t bpena = 0;
+
+ LOG_TARGET_DEBUG(target,
+ "current=%d address=" TARGET_ADDR_FMT ", handle_breakpoints=%i, debug_execution=%i)",
+ current,
+ address,
+ handle_breakpoints,
+ debug_execution);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (address && !current) {
+ xtensa_reg_set(target, XT_REG_IDX_PC, address);
+ } else {
+ xtensa_reg_val_t cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
+ if (cause & DEBUGCAUSE_DB) {
+ /* We stopped due to a watchpoint. We can't just resume executing the
+ * instruction again because */
+ /* that would trigger the watchpoint again. To fix this, we single-step,
+ * which ignores watchpoints. */
+ xtensa_do_step(target, current, address, handle_breakpoints);
+ }
+ if (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN)) {
+ /* We stopped due to a break instruction. We can't just resume executing the
+ * instruction again because */
+ /* that would trigger the break again. To fix this, we single-step, which
+ * ignores break. */
+ xtensa_do_step(target, current, address, handle_breakpoints);
+ }
+ }
+
+ /* Write back hw breakpoints. Current FreeRTOS SMP code can set a hw breakpoint on an
+ * exception; we need to clear that and return to the breakpoints gdb has set on resume. */
+ for (unsigned int slot = 0; slot < xtensa->core_config->debug.ibreaks_num; slot++) {
+ if (xtensa->hw_brps[slot]) {
+ /* Write IBREAKA[slot] and set bit #slot in IBREAKENABLE */
+ xtensa_reg_set(target, XT_REG_IDX_IBREAKA0 + slot, xtensa->hw_brps[slot]->address);
+ bpena |= BIT(slot);
+ }
+ }
+ xtensa_reg_set(target, XT_REG_IDX_IBREAKENABLE, bpena);
+
+ /* Here we write all registers to the targets */
+ int res = xtensa_write_dirty_registers(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to write back register cache.");
+ return res;
+}
+
+int xtensa_do_resume(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_TARGET_DEBUG(target, "start");
+
+ xtensa_queue_exec_ins(xtensa, XT_INS_RFDO);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to exec RFDO %d!", res);
+ return res;
+ }
+ xtensa_core_status_check(target);
+ return ERROR_OK;
+}
+
+int xtensa_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution)
+{
+ LOG_TARGET_DEBUG(target, "start");
+ int res = xtensa_prepare_resume(target, current, address, handle_breakpoints, debug_execution);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to prepare for resume!");
+ return res;
+ }
+ res = xtensa_do_resume(target);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to resume!");
+ return res;
+ }
+
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ if (!debug_execution)
+ target->state = TARGET_RUNNING;
+ else
+ target->state = TARGET_DEBUG_RUNNING;
+
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+
+ return ERROR_OK;
+}
+
+static bool xtensa_pc_in_winexc(struct target *target, target_addr_t pc)
+{
+ uint8_t insn_buf[XT_ISNS_SZ_MAX];
+ int err = xtensa_read_buffer(target, pc, sizeof(insn_buf), insn_buf);
+ if (err != ERROR_OK)
+ return false;
+
+ xtensa_insn_t insn = buf_get_u32(insn_buf, 0, 24);
+ xtensa_insn_t masked = insn & XT_INS_L32E_S32E_MASK;
+ if (masked == XT_INS_L32E(0, 0, 0) || masked == XT_INS_S32E(0, 0, 0))
+ return true;
+
+ masked = insn & XT_INS_RFWO_RFWU_MASK;
+ if (masked == XT_INS_RFWO || masked == XT_INS_RFWU)
+ return true;
+
+ return false;
+}
+
+int xtensa_do_step(struct target *target, int current, target_addr_t address, int handle_breakpoints)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ int res;
+ const uint32_t icount_val = -2; /* ICOUNT value to load for 1 step */
+ xtensa_reg_val_t dbreakc[XT_WATCHPOINTS_NUM_MAX];
+ xtensa_reg_val_t icountlvl, cause;
+ xtensa_reg_val_t oldps, newps, oldpc, cur_pc;
+
+ LOG_TARGET_DEBUG(target, "current=%d, address=" TARGET_ADDR_FMT ", handle_breakpoints=%i",
+ current, address, handle_breakpoints);
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (xtensa->core_config->debug.icount_sz != 32) {
+ LOG_TARGET_WARNING(target, "stepping for ICOUNT less then 32 bits is not implemented!");
+ return ERROR_FAIL;
+ }
+
+ /* Save old ps/pc */
+ oldps = xtensa_reg_get(target, XT_REG_IDX_PS);
+ oldpc = xtensa_reg_get(target, XT_REG_IDX_PC);
+
+ cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
+ LOG_TARGET_DEBUG(target, "oldps=%" PRIx32 ", oldpc=%" PRIx32 " dbg_cause=%" PRIx32 " exc_cause=%" PRIx32,
+ oldps,
+ oldpc,
+ cause,
+ xtensa_reg_get(target, XT_REG_IDX_EXCCAUSE));
+ if (handle_breakpoints && (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN))) {
+ /* handle hard-coded SW breakpoints (e.g. syscalls) */
+ LOG_TARGET_DEBUG(target, "Increment PC to pass break instruction...");
+ xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0); /* so we don't recurse into the same routine */
+ xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
+ /* pretend that we have stepped */
+ if (cause & DEBUGCAUSE_BI)
+ xtensa_reg_set(target, XT_REG_IDX_PC, oldpc + 3); /* PC = PC+3 */
+ else
+ xtensa_reg_set(target, XT_REG_IDX_PC, oldpc + 2); /* PC = PC+2 */
+ return ERROR_OK;
+ }
+
+ /* Xtensa has an ICOUNTLEVEL register which sets the maximum interrupt level at which the
+ * instructions are to be counted while stepping.
+ * For example, if we need to step by 2 instructions, and an interrupt occurs inbetween,
+ * the processor will execute the interrupt, return, and halt after the 2nd instruction.
+ * However, sometimes we don't want the interrupt handlers to be executed at all, while
+ * stepping through the code. In this case (XT_STEPPING_ISR_OFF), PS.INTLEVEL can be raised
+ * to only allow Debug and NMI interrupts.
+ */
+ if (xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF) {
+ if (!xtensa->core_config->high_irq.enabled) {
+ LOG_TARGET_WARNING(
+ target,
+ "disabling IRQs while stepping is not implemented w/o high prio IRQs option!");
+ return ERROR_FAIL;
+ }
+ /* Mask all interrupts below Debug, i.e. PS.INTLEVEL = DEBUGLEVEL - 1 */
+ xtensa_reg_val_t temp_ps = (oldps & ~0xF) | (xtensa->core_config->debug.irq_level - 1);
+ xtensa_reg_set(target, XT_REG_IDX_PS, temp_ps);
+ }
+ /* Regardless of ISRs masking mode we need to count instructions at any CINTLEVEL during step.
+ So set `icountlvl` to DEBUGLEVEL.
+ If ISRs are masked they are disabled in PS (see above), so having `icountlvl` set to DEBUGLEVEL
+ will allow to step through any type of the code, e.g. 'high int level' ISR.
+ If ISRs are not masked With `icountlvl` set to DEBUGLEVEL, we can step into any ISR
+ which can happen (enabled in PS).
+ */
+ icountlvl = xtensa->core_config->debug.irq_level;
+
+ if (cause & DEBUGCAUSE_DB) {
+ /* We stopped due to a watchpoint. We can't just resume executing the instruction again because
+ * that would trigger the watchpoint again. To fix this, we remove watchpoints,single-step and
+ * re-enable the watchpoint. */
+ LOG_TARGET_DEBUG(
+ target,
+ "Single-stepping to get past instruction that triggered the watchpoint...");
+ xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0); /*so we don't recurse into
+ * the same routine */
+ xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
+ /*Save all DBREAKCx registers and set to 0 to disable watchpoints */
+ for (unsigned int slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++) {
+ dbreakc[slot] = xtensa_reg_get(target, XT_REG_IDX_DBREAKC0 + slot);
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, 0);
+ }
+ }
+
+ if (!handle_breakpoints && (cause & (DEBUGCAUSE_BI | DEBUGCAUSE_BN))) {
+ /* handle normal SW breakpoint */
+ xtensa_reg_set(target, XT_REG_IDX_DEBUGCAUSE, 0); /*so we don't recurse into
+ * the same routine */
+ xtensa->core_cache->reg_list[XT_REG_IDX_DEBUGCAUSE].dirty = false;
+ }
+ do {
+ xtensa_reg_set(target, XT_REG_IDX_ICOUNTLEVEL, icountlvl);
+ xtensa_reg_set(target, XT_REG_IDX_ICOUNT, icount_val);
+
+ /* Now ICOUNT is set, we can resume as if we were going to run */
+ res = xtensa_prepare_resume(target, current, address, 0, 0);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to prepare resume for single step");
+ return res;
+ }
+ res = xtensa_do_resume(target);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to resume after setting up single step");
+ return res;
+ }
+
+ /* Wait for stepping to complete */
+ long long start = timeval_ms();
+ while (timeval_ms() < start + 500) {
+ /* Do not use target_poll here, it also triggers other things... just manually read the DSR
+ *until stepping is complete. */
+ usleep(1000);
+ res = xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ if (res != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read core status!");
+ return res;
+ }
+ if (xtensa_is_stopped(target))
+ break;
+ usleep(1000);
+ }
+ LOG_TARGET_DEBUG(target, "Finish stepping. dsr=0x%08" PRIx32,
+ xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ if (!xtensa_is_stopped(target)) {
+ LOG_TARGET_WARNING(
+ target,
+ "Timed out waiting for target to finish stepping. dsr=0x%08" PRIx32,
+ xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ target->state = TARGET_RUNNING;
+ return ERROR_FAIL;
+ }
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ target->state = TARGET_HALTED;
+
+ xtensa_fetch_all_regs(target);
+
+ cur_pc = xtensa_reg_get(target, XT_REG_IDX_PC);
+
+ LOG_TARGET_DEBUG(target,
+ "cur_ps=%" PRIx32 ", cur_pc=%" PRIx32 " dbg_cause=%" PRIx32 " exc_cause=%" PRIx32,
+ xtensa_reg_get(target, XT_REG_IDX_PS),
+ cur_pc,
+ xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE),
+ xtensa_reg_get(target, XT_REG_IDX_EXCCAUSE));
+
+ /* Do not step into WindowOverflow if ISRs are masked.
+ If we stop in WindowOverflow at breakpoint with masked ISRs and
+ try to do a step it will get us out of that handler */
+ if (xtensa->core_config->windowed &&
+ xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF &&
+ xtensa_pc_in_winexc(target, cur_pc)) {
+ /* isrmask = on, need to step out of the window exception handler */
+ LOG_DEBUG("Stepping out of window exception, PC=%" PRIX32, cur_pc);
+ oldpc = cur_pc;
+ address = oldpc + 3;
+ continue;
+ }
+
+ if (oldpc == cur_pc)
+ LOG_TARGET_WARNING(target, "Stepping doesn't seem to change PC! dsr=0x%08" PRIx32,
+ xtensa_dm_core_status_get(&xtensa->dbg_mod));
+ else
+ LOG_DEBUG("Stepped from %" PRIX32 " to %" PRIX32, oldpc, cur_pc);
+ break;
+ } while (true);
+ LOG_DEBUG("Done stepping, PC=%" PRIX32, cur_pc);
+
+ if (cause & DEBUGCAUSE_DB) {
+ LOG_TARGET_DEBUG(target, "...Done, re-installing watchpoints.");
+ /* Restore the DBREAKCx registers */
+ for (unsigned int slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++)
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, dbreakc[slot]);
+ }
+
+ /* Restore int level */
+ /* TODO: Theoretically, this can mess up stepping over an instruction that modifies
+ * ps.intlevel by itself. TODO: Look into this. */
+ if (xtensa->stepping_isr_mode == XT_STEPPING_ISR_OFF) {
+ newps = xtensa_reg_get(target, XT_REG_IDX_PS);
+ newps = (newps & ~0xF) | (oldps & 0xf);
+ xtensa_reg_set(target, XT_REG_IDX_PS, newps);
+ }
+
+ /* write ICOUNTLEVEL back to zero */
+ xtensa_reg_set(target, XT_REG_IDX_ICOUNTLEVEL, 0);
+ /* TODO: can we skip writing dirty registers and re-fetching them? */
+ res = xtensa_write_dirty_registers(target);
+ xtensa_fetch_all_regs(target);
+ return res;
+}
+
+int xtensa_step(struct target *target, int current, target_addr_t address, int handle_breakpoints)
+{
+ return xtensa_do_step(target, current, address, handle_breakpoints);
+}
+
+/**
+ * Returns true if two ranges are overlapping
+ */
+static inline bool xtensa_memory_regions_overlap(target_addr_t r1_start,
+ target_addr_t r1_end,
+ target_addr_t r2_start,
+ target_addr_t r2_end)
+{
+ if ((r2_start >= r1_start) && (r2_start < r1_end))
+ return true; /* r2_start is in r1 region */
+ if ((r2_end > r1_start) && (r2_end <= r1_end))
+ return true; /* r2_end is in r1 region */
+ return false;
+}
+
+/**
+ * Returns a size of overlapped region of two ranges.
+ */
+static inline target_addr_t xtensa_get_overlap_size(target_addr_t r1_start,
+ target_addr_t r1_end,
+ target_addr_t r2_start,
+ target_addr_t r2_end)
+{
+ if (xtensa_memory_regions_overlap(r1_start, r1_end, r2_start, r2_end)) {
+ target_addr_t ov_start = r1_start < r2_start ? r2_start : r1_start;
+ target_addr_t ov_end = r1_end > r2_end ? r2_end : r1_end;
+ return ov_end - ov_start;
+ }
+ return 0;
+}
+
+/**
+ * Check if the address gets to memory regions, and it's access mode
+ */
+static bool xtensa_memory_op_validate_range(struct xtensa *xtensa, target_addr_t address, size_t size, int access)
+{
+ target_addr_t adr_pos = address; /* address cursor set to the beginning start */
+ target_addr_t adr_end = address + size; /* region end */
+ target_addr_t overlap_size;
+ const struct xtensa_local_mem_region_config *cm; /* current mem region */
+
+ while (adr_pos < adr_end) {
+ cm = xtensa_target_memory_region_find(xtensa, adr_pos);
+ if (!cm) /* address is not belong to anything */
+ return false;
+ if ((cm->access & access) != access) /* access check */
+ return false;
+ overlap_size = xtensa_get_overlap_size(cm->base, (cm->base + cm->size), adr_pos, adr_end);
+ assert(overlap_size != 0);
+ adr_pos += overlap_size;
+ }
+ return true;
+}
+
+int xtensa_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ /* We are going to read memory in 32-bit increments. This may not be what the calling
+ * function expects, so we may need to allocate a temp buffer and read into that first. */
+ target_addr_t addrstart_al = ALIGN_DOWN(address, 4);
+ target_addr_t addrend_al = ALIGN_UP(address + size * count, 4);
+ target_addr_t adr = addrstart_al;
+ uint8_t *albuff;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!xtensa->permissive_mode) {
+ if (!xtensa_memory_op_validate_range(xtensa, address, (size * count),
+ XT_MEM_ACCESS_READ)) {
+ LOG_DEBUG("address " TARGET_ADDR_FMT " not readable", address);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (addrstart_al == address && addrend_al == address + (size * count)) {
+ albuff = buffer;
+ } else {
+ albuff = malloc(addrend_al - addrstart_al);
+ if (!albuff) {
+ LOG_TARGET_ERROR(target, "Out of memory allocating %" TARGET_PRIdADDR " bytes!",
+ addrend_al - addrstart_al);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+
+ /* We're going to use A3 here */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+ /* Write start address to A3 */
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrstart_al);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ /* Now we can safely read data from addrstart_al up to addrend_al into albuff */
+ for (unsigned int i = 0; adr != addrend_al; i += sizeof(uint32_t), adr += sizeof(uint32_t)) {
+ xtensa_queue_exec_ins(xtensa, XT_INS_LDDR32P(XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, &albuff[i]);
+ }
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK)
+ res = xtensa_core_status_check(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_WARNING(target, "Failed reading %d bytes at address " TARGET_ADDR_FMT,
+ count * size, address);
+
+ if (albuff != buffer) {
+ memcpy(buffer, albuff + (address & 3), (size * count));
+ free(albuff);
+ }
+
+ return res;
+}
+
+int xtensa_read_buffer(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer)
+{
+ /* xtensa_read_memory can also read unaligned stuff. Just pass through to that routine. */
+ return xtensa_read_memory(target, address, 1, count, buffer);
+}
+
+int xtensa_write_memory(struct target *target,
+ target_addr_t address,
+ uint32_t size,
+ uint32_t count,
+ const uint8_t *buffer)
+{
+ /* This memory write function can get thrown nigh everything into it, from
+ * aligned uint32 writes to unaligned uint8ths. The Xtensa memory doesn't always
+ * accept anything but aligned uint32 writes, though. That is why we convert
+ * everything into that. */
+ struct xtensa *xtensa = target_to_xtensa(target);
+ target_addr_t addrstart_al = ALIGN_DOWN(address, 4);
+ target_addr_t addrend_al = ALIGN_UP(address + size * count, 4);
+ target_addr_t adr = addrstart_al;
+ int res;
+ uint8_t *albuff;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (!xtensa->permissive_mode) {
+ if (!xtensa_memory_op_validate_range(xtensa, address, (size * count), XT_MEM_ACCESS_WRITE)) {
+ LOG_WARNING("address " TARGET_ADDR_FMT " not writable", address);
+ return ERROR_FAIL;
+ }
+ }
+
+ if (size == 0 || count == 0 || !buffer)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ /* Allocate a temporary buffer to put the aligned bytes in, if needed. */
+ if (addrstart_al == address && addrend_al == address + (size * count)) {
+ /* We discard the const here because albuff can also be non-const */
+ albuff = (uint8_t *)buffer;
+ } else {
+ albuff = malloc(addrend_al - addrstart_al);
+ if (!albuff) {
+ LOG_TARGET_ERROR(target, "Out of memory allocating %" TARGET_PRIdADDR " bytes!",
+ addrend_al - addrstart_al);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+
+ /* We're going to use A3 here */
+ xtensa_mark_register_dirty(xtensa, XT_REG_IDX_A3);
+
+ /* If we're using a temp aligned buffer, we need to fill the head and/or tail bit of it. */
+ if (albuff != buffer) {
+ /* See if we need to read the first and/or last word. */
+ if (address & 3) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrstart_al);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_LDDR32P(XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR, &albuff[0]);
+ }
+ if ((address + (size * count)) & 3) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrend_al - 4);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ xtensa_queue_exec_ins(xtensa, XT_INS_LDDR32P(XT_REG_A3));
+ xtensa_queue_dbg_reg_read(xtensa, NARADR_DDR,
+ &albuff[addrend_al - addrstart_al - 4]);
+ }
+ /* Grab bytes */
+ res = jtag_execute_queue();
+ if (res != ERROR_OK) {
+ LOG_ERROR("Error issuing unaligned memory write context instruction(s): %d", res);
+ if (albuff != buffer)
+ free(albuff);
+ return res;
+ }
+ xtensa_core_status_check(target);
+ /* Copy data to be written into the aligned buffer */
+ memcpy(&albuff[address & 3], buffer, size * count);
+ /* Now we can write albuff in aligned uint32s. */
+ }
+
+ /* Write start address to A3 */
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, addrstart_al);
+ xtensa_queue_exec_ins(xtensa, XT_INS_RSR(XT_SR_DDR, XT_REG_A3));
+ /* Write the aligned buffer */
+ for (unsigned int i = 0; adr != addrend_al; i += sizeof(uint32_t), adr += sizeof(uint32_t)) {
+ xtensa_queue_dbg_reg_write(xtensa, NARADR_DDR, buf_get_u32(&albuff[i], 0, 32));
+ xtensa_queue_exec_ins(xtensa, XT_INS_SDDR32P(XT_REG_A3));
+ }
+ res = jtag_execute_queue();
+ if (res == ERROR_OK)
+ res = xtensa_core_status_check(target);
+ if (res != ERROR_OK)
+ LOG_TARGET_WARNING(target, "Failed writing %d bytes at address " TARGET_ADDR_FMT, count * size, address);
+ if (albuff != buffer)
+ free(albuff);
+
+ return res;
+}
+
+int xtensa_write_buffer(struct target *target, target_addr_t address, uint32_t count, const uint8_t *buffer)
+{
+ /* xtensa_write_memory can handle everything. Just pass on to that. */
+ return xtensa_write_memory(target, address, 1, count, buffer);
+}
+
+int xtensa_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum)
+{
+ LOG_WARNING("not implemented yet");
+ return ERROR_FAIL;
+}
+
+int xtensa_poll(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ int res = xtensa_dm_power_status_read(&xtensa->dbg_mod, PWRSTAT_DEBUGWASRESET | PWRSTAT_COREWASRESET);
+ if (res != ERROR_OK)
+ return res;
+
+ if (xtensa_dm_tap_was_reset(&xtensa->dbg_mod)) {
+ LOG_TARGET_INFO(target, "Debug controller was reset.");
+ res = xtensa_smpbreak_write(xtensa, xtensa->smp_break);
+ if (res != ERROR_OK)
+ return res;
+ }
+ if (xtensa_dm_core_was_reset(&xtensa->dbg_mod))
+ LOG_TARGET_INFO(target, "Core was reset.");
+ xtensa_dm_power_status_cache(&xtensa->dbg_mod);
+ /* Enable JTAG, set reset if needed */
+ res = xtensa_wakeup(target);
+ if (res != ERROR_OK)
+ return res;
+
+ res = xtensa_dm_core_status_read(&xtensa->dbg_mod);
+ if (res != ERROR_OK)
+ return res;
+ if (xtensa->dbg_mod.power_status.stath & PWRSTAT_COREWASRESET) {
+ /* if RESET state is persitent */
+ target->state = TARGET_RESET;
+ } else if (!xtensa_dm_is_powered(&xtensa->dbg_mod)) {
+ LOG_TARGET_DEBUG(target, "not powered 0x%" PRIX32 "%ld",
+ xtensa->dbg_mod.core_status.dsr,
+ xtensa->dbg_mod.core_status.dsr & OCDDSR_STOPPED);
+ target->state = TARGET_UNKNOWN;
+ if (xtensa->come_online_probes_num == 0)
+ target->examined = false;
+ else
+ xtensa->come_online_probes_num--;
+ } else if (xtensa_is_stopped(target)) {
+ if (target->state != TARGET_HALTED) {
+ enum target_state oldstate = target->state;
+ target->state = TARGET_HALTED;
+ /* Examine why the target has been halted */
+ target->debug_reason = DBG_REASON_DBGRQ;
+ xtensa_fetch_all_regs(target);
+ /* When setting debug reason DEBUGCAUSE events have the following
+ * priorities: watchpoint == breakpoint > single step > debug interrupt. */
+ /* Watchpoint and breakpoint events at the same time results in special
+ * debug reason: DBG_REASON_WPTANDBKPT. */
+ xtensa_reg_val_t halt_cause = xtensa_reg_get(target, XT_REG_IDX_DEBUGCAUSE);
+ /* TODO: Add handling of DBG_REASON_EXC_CATCH */
+ if (halt_cause & DEBUGCAUSE_IC)
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ if (halt_cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BN | DEBUGCAUSE_BI)) {
+ if (halt_cause & DEBUGCAUSE_DB)
+ target->debug_reason = DBG_REASON_WPTANDBKPT;
+ else
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ } else if (halt_cause & DEBUGCAUSE_DB) {
+ target->debug_reason = DBG_REASON_WATCHPOINT;
+ }
+ LOG_TARGET_DEBUG(target, "Target halted, pc=0x%08" PRIX32 ", debug_reason=%08x, oldstate=%08x",
+ xtensa_reg_get(target, XT_REG_IDX_PC),
+ target->debug_reason,
+ oldstate);
+ LOG_TARGET_DEBUG(target, "Halt reason=0x%08" PRIX32 ", exc_cause=%" PRId32 ", dsr=0x%08" PRIx32,
+ halt_cause,
+ xtensa_reg_get(target, XT_REG_IDX_EXCCAUSE),
+ xtensa->dbg_mod.core_status.dsr);
+ LOG_TARGET_INFO(target, "Target halted, PC=0x%08" PRIX32 ", debug_reason=%08x",
+ xtensa_reg_get(target, XT_REG_IDX_PC), target->debug_reason);
+ xtensa_dm_core_status_clear(
+ &xtensa->dbg_mod,
+ OCDDSR_DEBUGPENDBREAK | OCDDSR_DEBUGINTBREAK | OCDDSR_DEBUGPENDTRAX |
+ OCDDSR_DEBUGINTTRAX |
+ OCDDSR_DEBUGPENDHOST | OCDDSR_DEBUGINTHOST);
+ }
+ } else {
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ if (target->state != TARGET_RUNNING && target->state != TARGET_DEBUG_RUNNING) {
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ }
+ }
+ if (xtensa->trace_active) {
+ /* Detect if tracing was active but has stopped. */
+ struct xtensa_trace_status trace_status;
+ res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res == ERROR_OK) {
+ if (!(trace_status.stat & TRAXSTAT_TRACT)) {
+ LOG_INFO("Detected end of trace.");
+ if (trace_status.stat & TRAXSTAT_PCMTG)
+ LOG_TARGET_INFO(target, "Trace stop triggered by PC match");
+ if (trace_status.stat & TRAXSTAT_PTITG)
+ LOG_TARGET_INFO(target, "Trace stop triggered by Processor Trigger Input");
+ if (trace_status.stat & TRAXSTAT_CTITG)
+ LOG_TARGET_INFO(target, "Trace stop triggered by Cross-trigger Input");
+ xtensa->trace_active = false;
+ }
+ }
+ }
+ return ERROR_OK;
+}
+
+static int xtensa_sw_breakpoint_add(struct target *target,
+ struct breakpoint *breakpoint,
+ struct xtensa_sw_breakpoint *sw_bp)
+{
+ int ret = target_read_buffer(target, breakpoint->address, XT_ISNS_SZ_MAX, sw_bp->insn);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read original instruction (%d)!", ret);
+ return ret;
+ }
+
+ sw_bp->insn_sz = xtensa_insn_size_get(buf_get_u32(sw_bp->insn, 0, 24));
+ sw_bp->oocd_bp = breakpoint;
+
+ uint32_t break_insn = sw_bp->insn_sz == XT_ISNS_SZ_MAX ? XT_INS_BREAK(0, 0) : XT_INS_BREAKN(0);
+ /* convert to target endianness */
+ uint8_t break_insn_buff[4];
+ target_buffer_set_u32(target, break_insn_buff, break_insn);
+
+ ret = target_write_buffer(target, breakpoint->address, sw_bp->insn_sz, break_insn_buff);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to write breakpoint instruction (%d)!", ret);
+ return ret;
+ }
+
+ return ERROR_OK;
+}
+
+static int xtensa_sw_breakpoint_remove(struct target *target, struct xtensa_sw_breakpoint *sw_bp)
+{
+ int ret = target_write_buffer(target, sw_bp->oocd_bp->address, sw_bp->insn_sz, sw_bp->insn);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read insn (%d)!", ret);
+ return ret;
+ }
+ sw_bp->oocd_bp = NULL;
+ return ERROR_OK;
+}
+
+int xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+
+ if (breakpoint->type == BKPT_SOFT) {
+ for (slot = 0; slot < XT_SW_BREAKPOINTS_MAX_NUM; slot++) {
+ if (!xtensa->sw_brps[slot].oocd_bp ||
+ xtensa->sw_brps[slot].oocd_bp == breakpoint)
+ break;
+ }
+ if (slot == XT_SW_BREAKPOINTS_MAX_NUM) {
+ LOG_TARGET_WARNING(target, "No free slots to add SW breakpoint!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ int ret = xtensa_sw_breakpoint_add(target, breakpoint, &xtensa->sw_brps[slot]);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to add SW breakpoint!");
+ return ret;
+ }
+ LOG_TARGET_DEBUG(target, "placed SW breakpoint %u @ " TARGET_ADDR_FMT,
+ slot,
+ breakpoint->address);
+ return ERROR_OK;
+ }
+
+ for (slot = 0; slot < xtensa->core_config->debug.ibreaks_num; slot++) {
+ if (!xtensa->hw_brps[slot] || xtensa->hw_brps[slot] == breakpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.ibreaks_num) {
+ LOG_TARGET_ERROR(target, "No free slots to add HW breakpoint!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ xtensa->hw_brps[slot] = breakpoint;
+ /* We will actually write the breakpoints when we resume the target. */
+ LOG_TARGET_DEBUG(target, "placed HW breakpoint @ " TARGET_ADDR_FMT,
+ breakpoint->address);
+
+ return ERROR_OK;
+}
+
+int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+
+ if (breakpoint->type == BKPT_SOFT) {
+ for (slot = 0; slot < XT_SW_BREAKPOINTS_MAX_NUM; slot++) {
+ if (xtensa->sw_brps[slot].oocd_bp && xtensa->sw_brps[slot].oocd_bp == breakpoint)
+ break;
+ }
+ if (slot == XT_SW_BREAKPOINTS_MAX_NUM) {
+ LOG_TARGET_WARNING(target, "Max SW breakpoints slot reached, slot=%u!", slot);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ int ret = xtensa_sw_breakpoint_remove(target, &xtensa->sw_brps[slot]);
+ if (ret != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to remove SW breakpoint (%d)!", ret);
+ return ret;
+ }
+ LOG_TARGET_DEBUG(target, "cleared SW breakpoint %u @ " TARGET_ADDR_FMT, slot, breakpoint->address);
+ return ERROR_OK;
+ }
+
+ for (slot = 0; slot < xtensa->core_config->debug.ibreaks_num; slot++) {
+ if (xtensa->hw_brps[slot] == breakpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.ibreaks_num) {
+ LOG_TARGET_ERROR(target, "HW breakpoint not found!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ xtensa->hw_brps[slot] = NULL;
+ LOG_TARGET_DEBUG(target, "cleared HW breakpoint %u @ " TARGET_ADDR_FMT, slot, breakpoint->address);
+ return ERROR_OK;
+}
+
+int xtensa_watchpoint_add(struct target *target, struct watchpoint *watchpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+ xtensa_reg_val_t dbreakcval;
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_WARNING(target, "target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (watchpoint->mask != ~(uint32_t)0) {
+ LOG_TARGET_ERROR(target, "watchpoint value masks not supported");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ for (slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++) {
+ if (!xtensa->hw_wps[slot] || xtensa->hw_wps[slot] == watchpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.dbreaks_num) {
+ LOG_TARGET_WARNING(target, "No free slots to add HW watchpoint!");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ /* Figure out value for dbreakc5..0
+ * It's basically 0x3F with an incremental bit removed from the LSB for each extra length power of 2. */
+ if (watchpoint->length < 1 || watchpoint->length > 64 ||
+ !IS_PWR_OF_2(watchpoint->length) ||
+ !IS_ALIGNED(watchpoint->address, watchpoint->length)) {
+ LOG_TARGET_WARNING(
+ target,
+ "Watchpoint with length %d on address " TARGET_ADDR_FMT
+ " not supported by hardware.",
+ watchpoint->length,
+ watchpoint->address);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ dbreakcval = ALIGN_DOWN(0x3F, watchpoint->length);
+
+ if (watchpoint->rw == WPT_READ)
+ dbreakcval |= BIT(30);
+ if (watchpoint->rw == WPT_WRITE)
+ dbreakcval |= BIT(31);
+ if (watchpoint->rw == WPT_ACCESS)
+ dbreakcval |= BIT(30) | BIT(31);
+
+ /* Write DBREAKA[slot] and DBCREAKC[slot] */
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKA0 + slot, watchpoint->address);
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, dbreakcval);
+ xtensa->hw_wps[slot] = watchpoint;
+ LOG_TARGET_DEBUG(target, "placed HW watchpoint @ " TARGET_ADDR_FMT,
+ watchpoint->address);
+ return ERROR_OK;
+}
+
+int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoint)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ unsigned int slot;
+
+ for (slot = 0; slot < xtensa->core_config->debug.dbreaks_num; slot++) {
+ if (xtensa->hw_wps[slot] == watchpoint)
+ break;
+ }
+ if (slot == xtensa->core_config->debug.dbreaks_num) {
+ LOG_TARGET_WARNING(target, "HW watchpoint " TARGET_ADDR_FMT " not found!", watchpoint->address);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ xtensa_reg_set(target, XT_REG_IDX_DBREAKC0 + slot, 0);
+ xtensa->hw_wps[slot] = NULL;
+ LOG_TARGET_DEBUG(target, "cleared HW watchpoint @ " TARGET_ADDR_FMT,
+ watchpoint->address);
+ return ERROR_OK;
+}
+
+static int xtensa_build_reg_cache(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
+ struct reg_cache *reg_cache = calloc(1, sizeof(struct reg_cache));
+
+ if (!reg_cache) {
+ LOG_ERROR("Failed to alloc reg cache!");
+ return ERROR_FAIL;
+ }
+ reg_cache->name = "Xtensa registers";
+ reg_cache->next = NULL;
+ reg_cache->num_regs = XT_NUM_REGS + xtensa->core_config->user_regs_num;
+ /* Init reglist */
+ struct reg *reg_list = calloc(reg_cache->num_regs, sizeof(struct reg));
+ if (!reg_list) {
+ LOG_ERROR("Failed to alloc reg list!");
+ goto fail;
+ }
+ xtensa->regs_num = 0;
+
+ for (unsigned int i = 0; i < XT_NUM_REGS; i++) {
+ reg_list[i].exist = false;
+ if (xtensa_regs[i].type == XT_REG_USER) {
+ if (xtensa_user_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("User reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ } else if (xtensa_regs[i].type == XT_REG_FR) {
+ if (xtensa_fp_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("FP reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ } else if (xtensa_regs[i].type == XT_REG_SPECIAL) {
+ if (xtensa_special_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("Special reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ } else {
+ if (xtensa_regular_reg_exists(xtensa, i))
+ reg_list[i].exist = true;
+ else
+ LOG_DEBUG("Regular reg '%s' (%d) does not exist", xtensa_regs[i].name, i);
+ }
+ reg_list[i].name = xtensa_regs[i].name;
+ reg_list[i].size = 32;
+ reg_list[i].value = calloc(1, 4 /*XT_REG_LEN*/);/* make Clang Static Analyzer happy */
+ if (!reg_list[i].value) {
+ LOG_ERROR("Failed to alloc reg list value!");
+ goto fail;
+ }
+ reg_list[i].dirty = false;
+ reg_list[i].valid = false;
+ reg_list[i].type = &xtensa_reg_type;
+ reg_list[i].arch_info = xtensa;
+ if (reg_list[i].exist)
+ xtensa->regs_num++;
+ }
+ for (unsigned int i = 0; i < xtensa->core_config->user_regs_num; i++) {
+ reg_list[XT_USR_REG_START + i].exist = true;
+ reg_list[XT_USR_REG_START + i].name = xtensa->core_config->user_regs[i].name;
+ reg_list[XT_USR_REG_START + i].size = xtensa->core_config->user_regs[i].size;
+ reg_list[XT_USR_REG_START + i].value = calloc(1, reg_list[XT_USR_REG_START + i].size / 8);
+ if (!reg_list[XT_USR_REG_START + i].value) {
+ LOG_ERROR("Failed to alloc user reg list value!");
+ goto fail;
+ }
+ reg_list[XT_USR_REG_START + i].dirty = false;
+ reg_list[XT_USR_REG_START + i].valid = false;
+ reg_list[XT_USR_REG_START + i].type = xtensa->core_config->user_regs[i].type;
+ reg_list[XT_USR_REG_START + i].arch_info = xtensa;
+ xtensa->regs_num++;
+ }
+ if (xtensa->core_config->gdb_general_regs_num >= xtensa->regs_num) {
+ LOG_ERROR("Regs number less then GDB general regs number!");
+ goto fail;
+ }
+
+ /* assign GDB reg numbers to registers */
+ for (unsigned int gdb_reg_id = 0; gdb_reg_id < xtensa->regs_num; gdb_reg_id++) {
+ unsigned int reg_id = xtensa->core_config->gdb_regs_mapping[gdb_reg_id];
+ if (reg_id >= reg_cache->num_regs) {
+ LOG_ERROR("Invalid GDB map!");
+ goto fail;
+ }
+ if (!reg_list[reg_id].exist) {
+ LOG_ERROR("Non-existing reg in GDB map!");
+ goto fail;
+ }
+ reg_list[reg_id].number = gdb_reg_id;
+ }
+ reg_cache->reg_list = reg_list;
+
+ xtensa->algo_context_backup = calloc(reg_cache->num_regs, sizeof(void *));
+ if (!xtensa->algo_context_backup) {
+ LOG_ERROR("Failed to alloc mem for algorithm context backup!");
+ goto fail;
+ }
+ for (unsigned int i = 0; i < reg_cache->num_regs; i++) {
+ struct reg *reg = &reg_cache->reg_list[i];
+ xtensa->algo_context_backup[i] = calloc(1, reg->size / 8);
+ if (!xtensa->algo_context_backup[i]) {
+ LOG_ERROR("Failed to alloc mem for algorithm context!");
+ goto fail;
+ }
+ }
+
+ xtensa->core_cache = reg_cache;
+ if (cache_p)
+ *cache_p = reg_cache;
+ return ERROR_OK;
+
+fail:
+ if (reg_list) {
+ for (unsigned int i = 0; i < reg_cache->num_regs; i++)
+ free(reg_list[i].value);
+ free(reg_list);
+ }
+ if (xtensa->algo_context_backup) {
+ for (unsigned int i = 0; i < reg_cache->num_regs; i++)
+ free(xtensa->algo_context_backup[i]);
+ free(xtensa->algo_context_backup);
+ }
+ free(reg_cache);
+
+ return ERROR_FAIL;
+}
+
+int xtensa_init_arch_info(struct target *target, struct xtensa *xtensa,
+ const struct xtensa_config *xtensa_config,
+ const struct xtensa_debug_module_config *dm_cfg)
+{
+ target->arch_info = xtensa;
+ xtensa->common_magic = XTENSA_COMMON_MAGIC;
+ xtensa->target = target;
+ xtensa->core_config = xtensa_config;
+ xtensa->stepping_isr_mode = XT_STEPPING_ISR_ON;
+
+ if (!xtensa->core_config->exc.enabled || !xtensa->core_config->irq.enabled ||
+ !xtensa->core_config->high_irq.enabled || !xtensa->core_config->debug.enabled) {
+ LOG_ERROR("Xtensa configuration does not support debugging!");
+ return ERROR_FAIL;
+ }
+ return xtensa_dm_init(&xtensa->dbg_mod, dm_cfg);
+}
+
+void xtensa_set_permissive_mode(struct target *target, bool state)
+{
+ target_to_xtensa(target)->permissive_mode = state;
+}
+
+int xtensa_target_init(struct command_context *cmd_ctx, struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ xtensa->come_online_probes_num = 3;
+ xtensa->hw_brps = calloc(xtensa->core_config->debug.ibreaks_num, sizeof(struct breakpoint *));
+ if (!xtensa->hw_brps) {
+ LOG_ERROR("Failed to alloc memory for HW breakpoints!");
+ return ERROR_FAIL;
+ }
+ xtensa->hw_wps = calloc(xtensa->core_config->debug.dbreaks_num, sizeof(struct watchpoint *));
+ if (!xtensa->hw_wps) {
+ free(xtensa->hw_brps);
+ LOG_ERROR("Failed to alloc memory for HW watchpoints!");
+ return ERROR_FAIL;
+ }
+ xtensa->sw_brps = calloc(XT_SW_BREAKPOINTS_MAX_NUM, sizeof(struct xtensa_sw_breakpoint));
+ if (!xtensa->sw_brps) {
+ free(xtensa->hw_brps);
+ free(xtensa->hw_wps);
+ LOG_ERROR("Failed to alloc memory for SW breakpoints!");
+ return ERROR_FAIL;
+ }
+
+ return xtensa_build_reg_cache(target);
+}
+
+static void xtensa_free_reg_cache(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+ struct reg_cache *cache = xtensa->core_cache;
+
+ if (cache) {
+ register_unlink_cache(&target->reg_cache, cache);
+ for (unsigned int i = 0; i < cache->num_regs; i++) {
+ free(xtensa->algo_context_backup[i]);
+ free(cache->reg_list[i].value);
+ }
+ free(xtensa->algo_context_backup);
+ free(cache->reg_list);
+ free(cache);
+ }
+ xtensa->core_cache = NULL;
+ xtensa->algo_context_backup = NULL;
+}
+
+void xtensa_target_deinit(struct target *target)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ LOG_DEBUG("start");
+
+ if (target_was_examined(target)) {
+ int ret = xtensa_queue_dbg_reg_write(xtensa, NARADR_DCRCLR, OCDDCR_ENABLEOCD);
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to queue OCDDCR_ENABLEOCD clear operation!");
+ return;
+ }
+ xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+ ret = jtag_execute_queue();
+ if (ret != ERROR_OK) {
+ LOG_ERROR("Failed to clear OCDDCR_ENABLEOCD!");
+ return;
+ }
+ }
+ xtensa_free_reg_cache(target);
+ free(xtensa->hw_brps);
+ free(xtensa->hw_wps);
+ free(xtensa->sw_brps);
+}
+
+const char *xtensa_get_gdb_arch(struct target *target)
+{
+ return "xtensa";
+}
+
+COMMAND_HELPER(xtensa_cmd_permissive_mode_do, struct xtensa *xtensa)
+{
+ return CALL_COMMAND_HANDLER(handle_command_parse_bool,
+ &xtensa->permissive_mode, "xtensa permissive mode");
+}
+
+COMMAND_HANDLER(xtensa_cmd_permissive_mode)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_permissive_mode_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+/* perfmon_enable <counter_id> <select> [mask] [kernelcnt] [tracelevel] */
+COMMAND_HELPER(xtensa_cmd_perfmon_enable_do, struct xtensa *xtensa)
+{
+ struct xtensa_perfmon_config config = {
+ .mask = 0xffff,
+ .kernelcnt = 0,
+ .tracelevel = -1 /* use DEBUGLEVEL by default */
+ };
+
+ if (CMD_ARGC < 2 || CMD_ARGC > 6)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ unsigned int counter_id = strtoul(CMD_ARGV[0], NULL, 0);
+ if (counter_id >= XTENSA_MAX_PERF_COUNTERS) {
+ command_print(CMD, "counter_id should be < %d", XTENSA_MAX_PERF_COUNTERS);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ config.select = strtoul(CMD_ARGV[1], NULL, 0);
+ if (config.select > XTENSA_MAX_PERF_SELECT) {
+ command_print(CMD, "select should be < %d", XTENSA_MAX_PERF_SELECT);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ if (CMD_ARGC >= 3) {
+ config.mask = strtoul(CMD_ARGV[2], NULL, 0);
+ if (config.mask > XTENSA_MAX_PERF_MASK) {
+ command_print(CMD, "mask should be < %d", XTENSA_MAX_PERF_MASK);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ if (CMD_ARGC >= 4) {
+ config.kernelcnt = strtoul(CMD_ARGV[3], NULL, 0);
+ if (config.kernelcnt > 1) {
+ command_print(CMD, "kernelcnt should be 0 or 1");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ if (CMD_ARGC >= 5) {
+ config.tracelevel = strtoul(CMD_ARGV[4], NULL, 0);
+ if (config.tracelevel > 7) {
+ command_print(CMD, "tracelevel should be <=7");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ if (config.tracelevel == -1)
+ config.tracelevel = xtensa->core_config->debug.irq_level;
+
+ return xtensa_dm_perfmon_enable(&xtensa->dbg_mod, counter_id, &config);
+}
+
+COMMAND_HANDLER(xtensa_cmd_perfmon_enable)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_enable_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+/* perfmon_dump [counter_id] */
+COMMAND_HELPER(xtensa_cmd_perfmon_dump_do, struct xtensa *xtensa)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ int counter_id = -1;
+ if (CMD_ARGC == 1) {
+ counter_id = strtol(CMD_ARGV[0], NULL, 0);
+ if (counter_id > XTENSA_MAX_PERF_COUNTERS) {
+ command_print(CMD, "counter_id should be < %d", XTENSA_MAX_PERF_COUNTERS);
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ }
+
+ unsigned int counter_start = (counter_id < 0) ? 0 : counter_id;
+ unsigned int counter_end = (counter_id < 0) ? XTENSA_MAX_PERF_COUNTERS : counter_id + 1;
+ for (unsigned int counter = counter_start; counter < counter_end; ++counter) {
+ char result_buf[128] = { 0 };
+ size_t result_pos = snprintf(result_buf, sizeof(result_buf), "Counter %d: ", counter);
+ struct xtensa_perfmon_result result;
+ int res = xtensa_dm_perfmon_dump(&xtensa->dbg_mod, counter, &result);
+ if (res != ERROR_OK)
+ return res;
+ snprintf(result_buf + result_pos, sizeof(result_buf) - result_pos,
+ "%-12" PRIu64 "%s",
+ result.value,
+ result.overflow ? " (overflow)" : "");
+ LOG_INFO("%s", result_buf);
+ }
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_perfmon_dump)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_mask_interrupts_do, struct xtensa *xtensa)
+{
+ int state = -1;
+
+ if (CMD_ARGC < 1) {
+ const char *st;
+ state = xtensa->stepping_isr_mode;
+ if (state == XT_STEPPING_ISR_ON)
+ st = "OFF";
+ else if (state == XT_STEPPING_ISR_OFF)
+ st = "ON";
+ else
+ st = "UNKNOWN";
+ command_print(CMD, "Current ISR step mode: %s", st);
+ return ERROR_OK;
+ }
+ /* Masking is ON -> interrupts during stepping are OFF, and vice versa */
+ if (!strcasecmp(CMD_ARGV[0], "off"))
+ state = XT_STEPPING_ISR_ON;
+ else if (!strcasecmp(CMD_ARGV[0], "on"))
+ state = XT_STEPPING_ISR_OFF;
+
+ if (state == -1) {
+ command_print(CMD, "Argument unknown. Please pick one of ON, OFF");
+ return ERROR_FAIL;
+ }
+ xtensa->stepping_isr_mode = state;
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_mask_interrupts)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_mask_interrupts_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_smpbreak_do, struct target *target)
+{
+ int res = ERROR_OK;
+ uint32_t val = 0;
+
+ if (CMD_ARGC >= 1) {
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ if (!strcasecmp(CMD_ARGV[0], "none")) {
+ val = 0;
+ } else if (!strcasecmp(CMD_ARGV[i], "BreakIn")) {
+ val |= OCDDCR_BREAKINEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "BreakOut")) {
+ val |= OCDDCR_BREAKOUTEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "RunStallIn")) {
+ val |= OCDDCR_RUNSTALLINEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "DebugModeOut")) {
+ val |= OCDDCR_DEBUGMODEOUTEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "BreakInOut")) {
+ val |= OCDDCR_BREAKINEN | OCDDCR_BREAKOUTEN;
+ } else if (!strcasecmp(CMD_ARGV[i], "RunStall")) {
+ val |= OCDDCR_RUNSTALLINEN | OCDDCR_DEBUGMODEOUTEN;
+ } else {
+ command_print(CMD, "Unknown arg %s", CMD_ARGV[i]);
+ command_print(
+ CMD,
+ "use either BreakInOut, None or RunStall as arguments, or any combination of BreakIn, BreakOut, RunStallIn and DebugModeOut.");
+ return ERROR_OK;
+ }
+ }
+ res = xtensa_smpbreak_set(target, val);
+ if (res != ERROR_OK)
+ command_print(CMD, "Failed to set smpbreak config %d", res);
+ } else {
+ struct xtensa *xtensa = target_to_xtensa(target);
+ res = xtensa_smpbreak_read(xtensa, &val);
+ if (res == ERROR_OK) {
+ command_print(CMD, "Current bits set:%s%s%s%s",
+ (val & OCDDCR_BREAKINEN) ? " BreakIn" : "",
+ (val & OCDDCR_BREAKOUTEN) ? " BreakOut" : "",
+ (val & OCDDCR_RUNSTALLINEN) ? " RunStallIn" : "",
+ (val & OCDDCR_DEBUGMODEOUTEN) ? " DebugModeOut" : ""
+ );
+ } else {
+ command_print(CMD, "Failed to get smpbreak config %d", res);
+ }
+ }
+ return res;
+}
+
+COMMAND_HANDLER(xtensa_cmd_smpbreak)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_smpbreak_do,
+ get_current_target(CMD_CTX));
+}
+
+COMMAND_HELPER(xtensa_cmd_tracestart_do, struct xtensa *xtensa)
+{
+ struct xtensa_trace_status trace_status;
+ struct xtensa_trace_start_config cfg = {
+ .stoppc = 0,
+ .stopmask = XTENSA_STOPMASK_DISABLED,
+ .after = 0,
+ .after_is_words = false
+ };
+
+ /* Parse arguments */
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ if ((!strcasecmp(CMD_ARGV[i], "pc")) && CMD_ARGC > i) {
+ char *e;
+ i++;
+ cfg.stoppc = strtol(CMD_ARGV[i], &e, 0);
+ cfg.stopmask = 0;
+ if (*e == '/')
+ cfg.stopmask = strtol(e, NULL, 0);
+ } else if ((!strcasecmp(CMD_ARGV[i], "after")) && CMD_ARGC > i) {
+ i++;
+ cfg.after = strtol(CMD_ARGV[i], NULL, 0);
+ } else if (!strcasecmp(CMD_ARGV[i], "ins")) {
+ cfg.after_is_words = 0;
+ } else if (!strcasecmp(CMD_ARGV[i], "words")) {
+ cfg.after_is_words = 1;
+ } else {
+ command_print(CMD, "Did not understand %s", CMD_ARGV[i]);
+ return ERROR_FAIL;
+ }
+ }
+
+ int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+ if (trace_status.stat & TRAXSTAT_TRACT) {
+ LOG_WARNING("Silently stop active tracing!");
+ res = xtensa_dm_trace_stop(&xtensa->dbg_mod, false);
+ if (res != ERROR_OK)
+ return res;
+ }
+
+ res = xtensa_dm_trace_start(&xtensa->dbg_mod, &cfg);
+ if (res != ERROR_OK)
+ return res;
+
+ xtensa->trace_active = true;
+ command_print(CMD, "Trace started.");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_tracestart)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_tracestart_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_tracestop_do, struct xtensa *xtensa)
+{
+ struct xtensa_trace_status trace_status;
+
+ int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+
+ if (!(trace_status.stat & TRAXSTAT_TRACT)) {
+ command_print(CMD, "No trace is currently active.");
+ return ERROR_FAIL;
+ }
+
+ res = xtensa_dm_trace_stop(&xtensa->dbg_mod, true);
+ if (res != ERROR_OK)
+ return res;
+
+ xtensa->trace_active = false;
+ command_print(CMD, "Trace stop triggered.");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_tracestop)
+{
+ return CALL_COMMAND_HANDLER(xtensa_cmd_tracestop_do,
+ target_to_xtensa(get_current_target(CMD_CTX)));
+}
+
+COMMAND_HELPER(xtensa_cmd_tracedump_do, struct xtensa *xtensa, const char *fname)
+{
+ struct xtensa_trace_config trace_config;
+ struct xtensa_trace_status trace_status;
+ uint32_t memsz, wmem;
+
+ int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+
+ if (trace_status.stat & TRAXSTAT_TRACT) {
+ command_print(CMD, "Tracing is still active. Please stop it first.");
+ return ERROR_FAIL;
+ }
+
+ res = xtensa_dm_trace_config_read(&xtensa->dbg_mod, &trace_config);
+ if (res != ERROR_OK)
+ return res;
+
+ if (!(trace_config.ctrl & TRAXCTRL_TREN)) {
+ command_print(CMD, "No active trace found; nothing to dump.");
+ return ERROR_FAIL;
+ }
+
+ memsz = trace_config.memaddr_end - trace_config.memaddr_start + 1;
+ LOG_INFO("Total trace memory: %d words", memsz);
+ if ((trace_config.addr &
+ ((TRAXADDR_TWRAP_MASK << TRAXADDR_TWRAP_SHIFT) | TRAXADDR_TWSAT)) == 0) {
+ /*Memory hasn't overwritten itself yet. */
+ wmem = trace_config.addr & TRAXADDR_TADDR_MASK;
+ LOG_INFO("...but trace is only %d words", wmem);
+ if (wmem < memsz)
+ memsz = wmem;
+ } else {
+ if (trace_config.addr & TRAXADDR_TWSAT) {
+ LOG_INFO("Real trace is many times longer than that (overflow)");
+ } else {
+ uint32_t trc_sz = (trace_config.addr >> TRAXADDR_TWRAP_SHIFT) & TRAXADDR_TWRAP_MASK;
+ trc_sz = (trc_sz * memsz) + (trace_config.addr & TRAXADDR_TADDR_MASK);
+ LOG_INFO("Real trace is %d words, but the start has been truncated.", trc_sz);
+ }
+ }
+
+ uint8_t *tracemem = malloc(memsz * 4);
+ if (!tracemem) {
+ command_print(CMD, "Failed to alloc memory for trace data!");
+ return ERROR_FAIL;
+ }
+ res = xtensa_dm_trace_data_read(&xtensa->dbg_mod, tracemem, memsz * 4);
+ if (res != ERROR_OK) {
+ free(tracemem);
+ return res;
+ }
+
+ int f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (f <= 0) {
+ free(tracemem);
+ command_print(CMD, "Unable to open file %s", fname);
+ return ERROR_FAIL;
+ }
+ if (write(f, tracemem, memsz * 4) != (int)memsz * 4)
+ command_print(CMD, "Unable to write to file %s", fname);
+ else
+ command_print(CMD, "Written %d bytes of trace data to %s", memsz * 4, fname);
+ close(f);
+
+ bool is_all_zeroes = true;
+ for (unsigned int i = 0; i < memsz * 4; i++) {
+ if (tracemem[i] != 0) {
+ is_all_zeroes = false;
+ break;
+ }
+ }
+ free(tracemem);
+ if (is_all_zeroes)
+ command_print(
+ CMD,
+ "WARNING: File written is all zeroes. Are you sure you enabled trace memory?");
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(xtensa_cmd_tracedump)
+{
+ if (CMD_ARGC != 1) {
+ command_print(CMD, "Command takes exactly 1 parameter.Need filename to dump to as output!");
+ return ERROR_FAIL;
+ }
+
+ return CALL_COMMAND_HANDLER(xtensa_cmd_tracedump_do,
+ target_to_xtensa(get_current_target(CMD_CTX)), CMD_ARGV[0]);
+}
+
+const struct command_registration xtensa_command_handlers[] = {
+ {
+ .name = "set_permissive",
+ .handler = xtensa_cmd_permissive_mode,
+ .mode = COMMAND_ANY,
+ .help = "When set to 1, enable Xtensa permissive mode (less client-side checks)",
+ .usage = "[0|1]",
+ },
+ {
+ .name = "maskisr",
+ .handler = xtensa_cmd_mask_interrupts,
+ .mode = COMMAND_ANY,
+ .help = "mask Xtensa interrupts at step",
+ .usage = "['on'|'off']",
+ },
+ {
+ .name = "smpbreak",
+ .handler = xtensa_cmd_smpbreak,
+ .mode = COMMAND_ANY,
+ .help = "Set the way the CPU chains OCD breaks",
+ .usage =
+ "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]",
+ },
+ {
+ .name = "perfmon_enable",
+ .handler = xtensa_cmd_perfmon_enable,
+ .mode = COMMAND_EXEC,
+ .help = "Enable and start performance counter",
+ .usage = "<counter_id> <select> [mask] [kernelcnt] [tracelevel]",
+ },
+ {
+ .name = "perfmon_dump",
+ .handler = xtensa_cmd_perfmon_dump,
+ .mode = COMMAND_EXEC,
+ .help =
+ "Dump performance counter value. If no argument specified, dumps all counters.",
+ .usage = "[counter_id]",
+ },
+ {
+ .name = "tracestart",
+ .handler = xtensa_cmd_tracestart,
+ .mode = COMMAND_EXEC,
+ .help =
+ "Tracing: Set up and start a trace. Optionally set stop trigger address and amount of data captured after.",
+ .usage = "[pc <pcval>/[maskbitcount]] [after <n> [ins|words]]",
+ },
+ {
+ .name = "tracestop",
+ .handler = xtensa_cmd_tracestop,
+ .mode = COMMAND_EXEC,
+ .help = "Tracing: Stop current trace as started by the tracestart command",
+ .usage = "",
+ },
+ {
+ .name = "tracedump",
+ .handler = xtensa_cmd_tracedump,
+ .mode = COMMAND_EXEC,
+ .help = "Tracing: Dump trace memory to a files. One file per core.",
+ .usage = "<outfile>",
+ },
+ COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/xtensa/xtensa.h b/src/target/xtensa/xtensa.h
new file mode 100644
index 0000000..d8b15e1
--- /dev/null
+++ b/src/target/xtensa/xtensa.h
@@ -0,0 +1,309 @@
+/***************************************************************************
+ * Generic Xtensa target *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * Author: Alexey Gerenkov <alexey@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_XTENSA_H
+#define OPENOCD_TARGET_XTENSA_H
+
+#include "assert.h"
+#include <target/target.h>
+#include <target/breakpoints.h>
+#include "xtensa_regs.h"
+#include "xtensa_debug_module.h"
+
+/**
+ * @file
+ * Holds the interface to Xtensa cores.
+ */
+
+#define XT_ISNS_SZ_MAX 3
+
+#define XT_PS_RING(_v_) ((uint32_t)((_v_) & 0x3) << 6)
+#define XT_PS_RING_MSK (0x3 << 6)
+#define XT_PS_RING_GET(_v_) (((_v_) >> 6) & 0x3)
+#define XT_PS_CALLINC_MSK (0x3 << 16)
+#define XT_PS_OWB_MSK (0xF << 8)
+
+#define XT_LOCAL_MEM_REGIONS_NUM_MAX 8
+
+#define XT_AREGS_NUM_MAX 64
+#define XT_USER_REGS_NUM_MAX 256
+
+#define XT_MEM_ACCESS_NONE 0x0
+#define XT_MEM_ACCESS_READ 0x1
+#define XT_MEM_ACCESS_WRITE 0x2
+
+enum xtensa_mem_err_detect {
+ XT_MEM_ERR_DETECT_NONE,
+ XT_MEM_ERR_DETECT_PARITY,
+ XT_MEM_ERR_DETECT_ECC,
+};
+
+struct xtensa_cache_config {
+ uint8_t way_count;
+ uint8_t line_size;
+ uint16_t size;
+ bool writeback;
+ enum xtensa_mem_err_detect mem_err_check;
+};
+
+struct xtensa_local_mem_region_config {
+ target_addr_t base;
+ uint32_t size;
+ enum xtensa_mem_err_detect mem_err_check;
+ int access;
+};
+
+struct xtensa_local_mem_config {
+ uint16_t count;
+ struct xtensa_local_mem_region_config regions[XT_LOCAL_MEM_REGIONS_NUM_MAX];
+};
+
+struct xtensa_mmu_config {
+ bool enabled;
+ uint8_t itlb_entries_count;
+ uint8_t dtlb_entries_count;
+ bool ivarway56;
+ bool dvarway56;
+};
+
+struct xtensa_exception_config {
+ bool enabled;
+ uint8_t depc_num;
+};
+
+struct xtensa_irq_config {
+ bool enabled;
+ uint8_t irq_num;
+};
+
+struct xtensa_high_prio_irq_config {
+ bool enabled;
+ uint8_t excm_level;
+ uint8_t nmi_num;
+};
+
+struct xtensa_debug_config {
+ bool enabled;
+ uint8_t irq_level;
+ uint8_t ibreaks_num;
+ uint8_t dbreaks_num;
+ uint8_t icount_sz;
+};
+
+struct xtensa_tracing_config {
+ bool enabled;
+ uint32_t mem_sz;
+ bool reversed_mem_access;
+};
+
+struct xtensa_timer_irq_config {
+ bool enabled;
+ uint8_t comp_num;
+};
+
+struct xtensa_config {
+ bool density;
+ uint8_t aregs_num;
+ bool windowed;
+ bool coproc;
+ bool fp_coproc;
+ bool loop;
+ uint8_t miscregs_num;
+ bool threadptr;
+ bool boolean;
+ bool cond_store;
+ bool ext_l32r;
+ bool mac16;
+ bool reloc_vec;
+ bool proc_id;
+ bool mem_err_check;
+ uint16_t user_regs_num;
+ const struct xtensa_user_reg_desc *user_regs;
+ int (*fetch_user_regs)(struct target *target);
+ int (*queue_write_dirty_user_regs)(struct target *target);
+ struct xtensa_cache_config icache;
+ struct xtensa_cache_config dcache;
+ struct xtensa_local_mem_config irom;
+ struct xtensa_local_mem_config iram;
+ struct xtensa_local_mem_config drom;
+ struct xtensa_local_mem_config dram;
+ struct xtensa_local_mem_config uram;
+ struct xtensa_local_mem_config xlmi;
+ struct xtensa_mmu_config mmu;
+ struct xtensa_exception_config exc;
+ struct xtensa_irq_config irq;
+ struct xtensa_high_prio_irq_config high_irq;
+ struct xtensa_timer_irq_config tim_irq;
+ struct xtensa_debug_config debug;
+ struct xtensa_tracing_config trace;
+ unsigned int gdb_general_regs_num;
+ const unsigned int *gdb_regs_mapping;
+};
+
+typedef uint32_t xtensa_insn_t;
+
+enum xtensa_stepping_isr_mode {
+ XT_STEPPING_ISR_OFF, /* interrupts are disabled during stepping */
+ XT_STEPPING_ISR_ON, /* interrupts are enabled during stepping */
+};
+
+/* Only supported in cores with in-CPU MMU. None of Espressif chips as of now. */
+enum xtensa_mode {
+ XT_MODE_RING0,
+ XT_MODE_RING1,
+ XT_MODE_RING2,
+ XT_MODE_RING3,
+ XT_MODE_ANY /* special value to run algorithm in current core mode */
+};
+
+struct xtensa_sw_breakpoint {
+ struct breakpoint *oocd_bp;
+ /* original insn */
+ uint8_t insn[XT_ISNS_SZ_MAX];
+ /* original insn size */
+ uint8_t insn_sz; /* 2 or 3 bytes */
+};
+
+#define XTENSA_COMMON_MAGIC 0x54E4E555U
+
+/**
+ * Represents a generic Xtensa core.
+ */
+struct xtensa {
+ unsigned int common_magic;
+ const struct xtensa_config *core_config;
+ struct xtensa_debug_module dbg_mod;
+ struct reg_cache *core_cache;
+ unsigned int regs_num;
+ /* An array of pointers to buffers to backup registers' values while algo is run on target.
+ * Size is 'regs_num'. */
+ void **algo_context_backup;
+ struct target *target;
+ bool reset_asserted;
+ enum xtensa_stepping_isr_mode stepping_isr_mode;
+ struct breakpoint **hw_brps;
+ struct watchpoint **hw_wps;
+ struct xtensa_sw_breakpoint *sw_brps;
+ bool trace_active;
+ bool permissive_mode; /* bypass memory checks */
+ bool suppress_dsr_errors;
+ uint32_t smp_break;
+ /* Sometimes debug module's 'powered' bit is cleared after reset, but get set after some
+ * time.This is the number of polling periods after which core is considered to be powered
+ * off (marked as unexamined) if the bit retains to be cleared (e.g. if core is disabled by
+ * SW running on target).*/
+ uint8_t come_online_probes_num;
+ bool regs_fetched; /* true after first register fetch completed successfully */
+};
+
+static inline struct xtensa *target_to_xtensa(struct target *target)
+{
+ assert(target);
+ struct xtensa *xtensa = target->arch_info;
+ assert(xtensa->common_magic == XTENSA_COMMON_MAGIC);
+ return xtensa;
+}
+
+int xtensa_init_arch_info(struct target *target,
+ struct xtensa *xtensa,
+ const struct xtensa_config *cfg,
+ const struct xtensa_debug_module_config *dm_cfg);
+int xtensa_target_init(struct command_context *cmd_ctx, struct target *target);
+void xtensa_target_deinit(struct target *target);
+
+static inline bool xtensa_addr_in_mem(const struct xtensa_local_mem_config *mem, uint32_t addr)
+{
+ for (unsigned int i = 0; i < mem->count; i++) {
+ if (addr >= mem->regions[i].base &&
+ addr < mem->regions[i].base + mem->regions[i].size)
+ return true;
+ }
+ return false;
+}
+
+static inline bool xtensa_data_addr_valid(struct target *target, uint32_t addr)
+{
+ struct xtensa *xtensa = target_to_xtensa(target);
+
+ if (xtensa_addr_in_mem(&xtensa->core_config->drom, addr))
+ return true;
+ if (xtensa_addr_in_mem(&xtensa->core_config->dram, addr))
+ return true;
+ if (xtensa_addr_in_mem(&xtensa->core_config->uram, addr))
+ return true;
+ return false;
+}
+
+int xtensa_core_status_check(struct target *target);
+
+int xtensa_examine(struct target *target);
+int xtensa_wakeup(struct target *target);
+int xtensa_smpbreak_set(struct target *target, uint32_t set);
+int xtensa_smpbreak_get(struct target *target, uint32_t *val);
+int xtensa_smpbreak_write(struct xtensa *xtensa, uint32_t set);
+int xtensa_smpbreak_read(struct xtensa *xtensa, uint32_t *val);
+xtensa_reg_val_t xtensa_reg_get(struct target *target, enum xtensa_reg_id reg_id);
+void xtensa_reg_set(struct target *target, enum xtensa_reg_id reg_id, xtensa_reg_val_t value);
+int xtensa_fetch_all_regs(struct target *target);
+int xtensa_get_gdb_reg_list(struct target *target,
+ struct reg **reg_list[],
+ int *reg_list_size,
+ enum target_register_class reg_class);
+int xtensa_poll(struct target *target);
+void xtensa_on_poll(struct target *target);
+int xtensa_halt(struct target *target);
+int xtensa_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution);
+int xtensa_prepare_resume(struct target *target,
+ int current,
+ target_addr_t address,
+ int handle_breakpoints,
+ int debug_execution);
+int xtensa_do_resume(struct target *target);
+int xtensa_step(struct target *target, int current, target_addr_t address, int handle_breakpoints);
+int xtensa_do_step(struct target *target, int current, target_addr_t address, int handle_breakpoints);
+int xtensa_mmu_is_enabled(struct target *target, int *enabled);
+int xtensa_read_memory(struct target *target, target_addr_t address, uint32_t size, uint32_t count, uint8_t *buffer);
+int xtensa_read_buffer(struct target *target, target_addr_t address, uint32_t count, uint8_t *buffer);
+int xtensa_write_memory(struct target *target,
+ target_addr_t address,
+ uint32_t size,
+ uint32_t count,
+ const uint8_t *buffer);
+int xtensa_write_buffer(struct target *target, target_addr_t address, uint32_t count, const uint8_t *buffer);
+int xtensa_checksum_memory(struct target *target, target_addr_t address, uint32_t count, uint32_t *checksum);
+int xtensa_assert_reset(struct target *target);
+int xtensa_deassert_reset(struct target *target);
+int xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint);
+int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint);
+int xtensa_watchpoint_add(struct target *target, struct watchpoint *watchpoint);
+int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoint);
+void xtensa_set_permissive_mode(struct target *target, bool state);
+int xtensa_fetch_user_regs_u32(struct target *target);
+int xtensa_queue_write_dirty_user_regs_u32(struct target *target);
+const char *xtensa_get_gdb_arch(struct target *target);
+
+extern const struct reg_arch_type xtensa_user_reg_u32_type;
+extern const struct reg_arch_type xtensa_user_reg_u128_type;
+extern const struct command_registration xtensa_command_handlers[];
+
+#endif /* OPENOCD_TARGET_XTENSA_H */
diff --git a/src/target/xtensa/xtensa_debug_module.c b/src/target/xtensa/xtensa_debug_module.c
new file mode 100644
index 0000000..c6959ae
--- /dev/null
+++ b/src/target/xtensa/xtensa_debug_module.c
@@ -0,0 +1,359 @@
+/***************************************************************************
+ * Generic Xtensa debug module API for OpenOCD *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xtensa_debug_module.h"
+
+#define TAPINS_PWRCTL 0x08
+#define TAPINS_PWRSTAT 0x09
+#define TAPINS_NARSEL 0x1C
+#define TAPINS_IDCODE 0x1E
+#define TAPINS_BYPASS 0x1F
+
+#define TAPINS_PWRCTL_LEN 8
+#define TAPINS_PWRSTAT_LEN 8
+#define TAPINS_NARSEL_ADRLEN 8
+#define TAPINS_NARSEL_DATALEN 32
+#define TAPINS_IDCODE_LEN 32
+#define TAPINS_BYPASS_LEN 1
+
+
+static void xtensa_dm_add_set_ir(struct xtensa_debug_module *dm, uint8_t value)
+{
+ struct scan_field field;
+ uint8_t t[4] = { 0 };
+
+ memset(&field, 0, sizeof(field));
+ field.num_bits = dm->tap->ir_length;
+ field.out_value = t;
+ buf_set_u32(t, 0, field.num_bits, value);
+ jtag_add_ir_scan(dm->tap, &field, TAP_IDLE);
+}
+
+static void xtensa_dm_add_dr_scan(struct xtensa_debug_module *dm,
+ int len,
+ const uint8_t *src,
+ uint8_t *dest,
+ tap_state_t endstate)
+{
+ struct scan_field field;
+
+ memset(&field, 0, sizeof(field));
+ field.num_bits = len;
+ field.out_value = src;
+ field.in_value = dest;
+ jtag_add_dr_scan(dm->tap, 1, &field, endstate);
+}
+
+int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_module_config *cfg)
+{
+ if (!dm || !cfg)
+ return ERROR_FAIL;
+
+ dm->pwr_ops = cfg->pwr_ops;
+ dm->dbg_ops = cfg->dbg_ops;
+ dm->tap = cfg->tap;
+ dm->queue_tdi_idle = cfg->queue_tdi_idle;
+ dm->queue_tdi_idle_arg = cfg->queue_tdi_idle_arg;
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_enable(struct xtensa_debug_module *dm)
+{
+ return dm->dbg_ops->queue_reg_write(dm, NARADR_DCRSET, OCDDCR_ENABLEOCD);
+}
+
+int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *value)
+{
+ uint8_t regdata = (reg << 1) | 0;
+ uint8_t dummy[4] = { 0, 0, 0, 0 };
+
+ if (reg > NARADR_MAX) {
+ LOG_ERROR("Invalid DBG reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, &regdata, NULL, TAP_IDLE);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, dummy, value, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint32_t value)
+{
+ uint8_t regdata = (reg << 1) | 1;
+ uint8_t valdata[] = { value, value >> 8, value >> 16, value >> 24 };
+
+ if (reg > NARADR_MAX) {
+ LOG_ERROR("Invalid DBG reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, TAPINS_NARSEL);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_ADRLEN, &regdata, NULL, TAP_IDLE);
+ xtensa_dm_add_dr_scan(dm, TAPINS_NARSEL_DATALEN, valdata, NULL, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data, uint8_t clear)
+{
+ uint8_t value_clr = clear;
+ uint8_t tap_insn;
+ int tap_insn_sz;
+
+ if (reg == DMREG_PWRCTL) {
+ tap_insn = TAPINS_PWRCTL;
+ tap_insn_sz = TAPINS_PWRCTL_LEN;
+ } else if (reg == DMREG_PWRSTAT) {
+ tap_insn = TAPINS_PWRSTAT;
+ tap_insn_sz = TAPINS_PWRSTAT_LEN;
+ } else {
+ LOG_ERROR("Invalid PWR reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, tap_insn);
+ xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value_clr, data, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data)
+{
+ uint8_t value = data;
+ uint8_t tap_insn;
+ int tap_insn_sz;
+
+ if (reg == DMREG_PWRCTL) {
+ tap_insn = TAPINS_PWRCTL;
+ tap_insn_sz = TAPINS_PWRCTL_LEN;
+ } else if (reg == DMREG_PWRSTAT) {
+ tap_insn = TAPINS_PWRSTAT;
+ tap_insn_sz = TAPINS_PWRSTAT_LEN;
+ } else {
+ LOG_ERROR("Invalid PWR reg ID %d!", reg);
+ return ERROR_FAIL;
+ }
+ xtensa_dm_add_set_ir(dm, tap_insn);
+ xtensa_dm_add_dr_scan(dm, tap_insn_sz, &value, NULL, TAP_IDLE);
+ return ERROR_OK;
+}
+
+int xtensa_dm_device_id_read(struct xtensa_debug_module *dm)
+{
+ uint8_t id_buf[sizeof(uint32_t)];
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_OCDID, id_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ dm->device_id = buf_get_u32(id_buf, 0, 32);
+ return ERROR_OK;
+}
+
+int xtensa_dm_power_status_read(struct xtensa_debug_module *dm, uint32_t clear)
+{
+ /* uint8_t id_buf[sizeof(uint32_t)]; */
+
+ /* TODO: JTAG does not work when PWRCTL_JTAGDEBUGUSE is not set.
+ * It is set in xtensa_examine(), need to move reading of NARADR_OCDID out of this function */
+ /* dm->dbg_ops->queue_reg_read(dm, NARADR_OCDID, id_buf);
+ *Read reset state */
+ dm->pwr_ops->queue_reg_read(dm, DMREG_PWRSTAT, &dm->power_status.stat, clear);
+ dm->pwr_ops->queue_reg_read(dm, DMREG_PWRSTAT, &dm->power_status.stath, clear);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_core_status_read(struct xtensa_debug_module *dm)
+{
+ uint8_t dsr_buf[sizeof(uint32_t)];
+
+ xtensa_dm_queue_enable(dm);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_DSR, dsr_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ dm->core_status.dsr = buf_get_u32(dsr_buf, 0, 32);
+ return res;
+}
+
+int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bits)
+{
+ dm->dbg_ops->queue_reg_write(dm, NARADR_DSR, bits);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg)
+{
+ /*Turn off trace unit so we can start a new trace. */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXCTRL, 0);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+
+ /*Set up parameters */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXADDR, 0);
+ if (cfg->stopmask != XTENSA_STOPMASK_DISABLED) {
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PCMATCHCTRL,
+ (cfg->stopmask << PCMATCHCTRL_PCML_SHIFT));
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRIGGERPC, cfg->stoppc);
+ }
+ dm->dbg_ops->queue_reg_write(dm, NARADR_DELAYCNT, cfg->after);
+ /*Options are mostly hardcoded for now. ToDo: make this more configurable. */
+ dm->dbg_ops->queue_reg_write(
+ dm,
+ NARADR_TRAXCTRL,
+ TRAXCTRL_TREN |
+ ((cfg->stopmask != XTENSA_STOPMASK_DISABLED) ? TRAXCTRL_PCMEN : 0) | TRAXCTRL_TMEN |
+ (cfg->after_is_words ? 0 : TRAXCTRL_CNTU) | (0 << TRAXCTRL_SMPER_SHIFT) | TRAXCTRL_PTOWS);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_trace_stop(struct xtensa_debug_module *dm, bool pto_enable)
+{
+ uint8_t traxctl_buf[sizeof(uint32_t)];
+ uint32_t traxctl;
+ struct xtensa_trace_status trace_status;
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXCTRL, traxctl_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+ traxctl = buf_get_u32(traxctl_buf, 0, 32);
+
+ if (!pto_enable)
+ traxctl &= ~(TRAXCTRL_PTOWS | TRAXCTRL_PTOWT);
+
+ dm->dbg_ops->queue_reg_write(dm, NARADR_TRAXCTRL, traxctl | TRAXCTRL_TRSTP);
+ xtensa_dm_queue_tdi_idle(dm);
+ res = jtag_execute_queue();
+ if (res != ERROR_OK)
+ return res;
+
+ /*Check current status of trace hardware */
+ res = xtensa_dm_trace_status_read(dm, &trace_status);
+ if (res != ERROR_OK)
+ return res;
+
+ if (trace_status.stat & TRAXSTAT_TRACT) {
+ LOG_ERROR("Failed to stop tracing (0x%x)!", trace_status.stat);
+ return ERROR_FAIL;
+ }
+ return ERROR_OK;
+}
+
+int xtensa_dm_trace_status_read(struct xtensa_debug_module *dm, struct xtensa_trace_status *status)
+{
+ uint8_t traxstat_buf[sizeof(uint32_t)];
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXSTAT, traxstat_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK && status)
+ status->stat = buf_get_u32(traxstat_buf, 0, 32);
+ return res;
+}
+
+int xtensa_dm_trace_config_read(struct xtensa_debug_module *dm, struct xtensa_trace_config *config)
+{
+ uint8_t traxctl_buf[sizeof(uint32_t)];
+ uint8_t memadrstart_buf[sizeof(uint32_t)];
+ uint8_t memadrend_buf[sizeof(uint32_t)];
+ uint8_t adr_buf[sizeof(uint32_t)];
+
+ if (!config)
+ return ERROR_FAIL;
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXCTRL, traxctl_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_MEMADDRSTART, memadrstart_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_MEMADDREND, memadrend_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXADDR, adr_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK) {
+ config->ctrl = buf_get_u32(traxctl_buf, 0, 32);
+ config->memaddr_start = buf_get_u32(memadrstart_buf, 0, 32);
+ config->memaddr_end = buf_get_u32(memadrend_buf, 0, 32);
+ config->addr = buf_get_u32(adr_buf, 0, 32);
+ }
+ return res;
+}
+
+int xtensa_dm_trace_data_read(struct xtensa_debug_module *dm, uint8_t *dest, uint32_t size)
+{
+ if (!dest)
+ return ERROR_FAIL;
+
+ for (unsigned int i = 0; i < size / 4; i++)
+ dm->dbg_ops->queue_reg_read(dm, NARADR_TRAXDATA, &dest[i * 4]);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_perfmon_enable(struct xtensa_debug_module *dm, int counter_id,
+ const struct xtensa_perfmon_config *config)
+{
+ if (!config)
+ return ERROR_FAIL;
+
+ uint8_t pmstat_buf[4];
+ uint32_t pmctrl = ((config->tracelevel) << 4) +
+ (config->select << 8) +
+ (config->mask << 16) +
+ (config->kernelcnt << 3);
+
+ /* enable performance monitor */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PMG, 0x1);
+ /* reset counter */
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PM0 + counter_id, 0);
+ dm->dbg_ops->queue_reg_write(dm, NARADR_PMCTRL0 + counter_id, pmctrl);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_PMSTAT0 + counter_id, pmstat_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ return jtag_execute_queue();
+}
+
+int xtensa_dm_perfmon_dump(struct xtensa_debug_module *dm, int counter_id,
+ struct xtensa_perfmon_result *out_result)
+{
+ uint8_t pmstat_buf[4];
+ uint8_t pmcount_buf[4];
+
+ dm->dbg_ops->queue_reg_read(dm, NARADR_PMSTAT0 + counter_id, pmstat_buf);
+ dm->dbg_ops->queue_reg_read(dm, NARADR_PM0 + counter_id, pmcount_buf);
+ xtensa_dm_queue_tdi_idle(dm);
+ int res = jtag_execute_queue();
+ if (res == ERROR_OK) {
+ uint32_t stat = buf_get_u32(pmstat_buf, 0, 32);
+ uint64_t result = buf_get_u32(pmcount_buf, 0, 32);
+
+ /* TODO: if counter # counter_id+1 has 'select' set to 1, use its value as the
+ * high 32 bits of the counter. */
+ if (out_result) {
+ out_result->overflow = ((stat & 1) != 0);
+ out_result->value = result;
+ }
+ }
+
+ return res;
+}
diff --git a/src/target/xtensa/xtensa_debug_module.h b/src/target/xtensa/xtensa_debug_module.h
new file mode 100644
index 0000000..692f0f6
--- /dev/null
+++ b/src/target/xtensa/xtensa_debug_module.h
@@ -0,0 +1,385 @@
+/***************************************************************************
+ * Xtensa debug module API *
+ * Copyright (C) 2019 Espressif Systems Ltd. *
+ * <alexey@espressif.com> *
+ * *
+ * Derived from original ESP8266 target. *
+ * Copyright (C) 2015 by Angus Gratton *
+ * gus@projectgus.com *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H
+#define OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H
+
+#include <jtag/jtag.h>
+#include <helper/bits.h>
+#include <target/target.h>
+
+/* Virtual IDs for using with xtensa_power_ops API */
+#define DMREG_PWRCTL 0x00
+#define DMREG_PWRSTAT 0x01
+
+/*
+ From the manual:
+ To properly use Debug registers through JTAG, software must ensure that:
+ - Tap is out of reset
+ - Xtensa Debug Module is out of reset
+ - Other bits of PWRCTL are set to their desired values, and finally
+ - JtagDebugUse transitions from 0 to 1
+ The bit must continue to be 1 in order for JTAG accesses to the Debug
+ Module to happen correctly. When it is set, any write to this bit clears it.
+ Either don't access it, or re-write it to 1 so JTAG accesses continue.
+*/
+#define PWRCTL_JTAGDEBUGUSE BIT(7)
+#define PWRCTL_DEBUGRESET BIT(6)
+#define PWRCTL_CORERESET BIT(4)
+#define PWRCTL_DEBUGWAKEUP BIT(2)
+#define PWRCTL_MEMWAKEUP BIT(1)
+#define PWRCTL_COREWAKEUP BIT(0)
+
+#define PWRSTAT_DEBUGWASRESET BIT(6)
+#define PWRSTAT_COREWASRESET BIT(4)
+#define PWRSTAT_CORESTILLNEEDED BIT(3)
+#define PWRSTAT_DEBUGDOMAINON BIT(2)
+#define PWRSTAT_MEMDOMAINON BIT(1)
+#define PWRSTAT_COREDOMAINON BIT(0)
+
+/* *** NAR addresses (also used as IDs for debug registers in xtensa_debug_ops API) ***
+ *TRAX registers */
+#define NARADR_TRAXID 0x00
+#define NARADR_TRAXCTRL 0x01
+#define NARADR_TRAXSTAT 0x02
+#define NARADR_TRAXDATA 0x03
+#define NARADR_TRAXADDR 0x04
+#define NARADR_TRIGGERPC 0x05
+#define NARADR_PCMATCHCTRL 0x06
+#define NARADR_DELAYCNT 0x07
+#define NARADR_MEMADDRSTART 0x08
+#define NARADR_MEMADDREND 0x09
+/*Performance monitor registers */
+#define NARADR_PMG 0x20
+#define NARADR_INTPC 0x24
+#define NARADR_PM0 0x28
+/*... */
+#define NARADR_PM7 0x2F
+#define NARADR_PMCTRL0 0x30
+/*... */
+#define NARADR_PMCTRL7 0x37
+#define NARADR_PMSTAT0 0x38
+/*... */
+#define NARADR_PMSTAT7 0x3F
+/*OCD registers */
+#define NARADR_OCDID 0x40
+#define NARADR_DCRCLR 0x42
+#define NARADR_DCRSET 0x43
+#define NARADR_DSR 0x44
+#define NARADR_DDR 0x45
+#define NARADR_DDREXEC 0x46
+#define NARADR_DIR0EXEC 0x47
+#define NARADR_DIR0 0x48
+#define NARADR_DIR1 0x49
+/*... */
+#define NARADR_DIR7 0x4F
+/*Misc registers */
+#define NARADR_PWRCTL 0x58
+#define NARADR_PWRSTAT 0x59
+#define NARADR_ERISTAT 0x5A
+/*CoreSight registers */
+#define NARADR_ITCTRL 0x60
+#define NARADR_CLAIMSET 0x68
+#define NARADR_CLAIMCLR 0x69
+#define NARADR_LOCKACCESS 0x6c
+#define NARADR_LOCKSTATUS 0x6d
+#define NARADR_AUTHSTATUS 0x6e
+#define NARADR_DEVID 0x72
+#define NARADR_DEVTYPE 0x73
+#define NARADR_PERID4 0x74
+/*... */
+#define NARADR_PERID7 0x77
+#define NARADR_PERID0 0x78
+/*... */
+#define NARADR_PERID3 0x7b
+#define NARADR_COMPID0 0x7c
+/*... */
+#define NARADR_COMPID3 0x7f
+#define NARADR_MAX NARADR_COMPID3
+
+/*OCD registers, bit definitions */
+#define OCDDCR_ENABLEOCD BIT(0)
+#define OCDDCR_DEBUGINTERRUPT BIT(1)
+#define OCDDCR_INTERRUPTALLCONDS BIT(2)
+#define OCDDCR_BREAKINEN BIT(16)
+#define OCDDCR_BREAKOUTEN BIT(17)
+#define OCDDCR_DEBUGSWACTIVE BIT(20)
+#define OCDDCR_RUNSTALLINEN BIT(21)
+#define OCDDCR_DEBUGMODEOUTEN BIT(22)
+#define OCDDCR_BREAKOUTITO BIT(24)
+#define OCDDCR_BREAKACKITO BIT(25)
+
+#define OCDDSR_EXECDONE BIT(0)
+#define OCDDSR_EXECEXCEPTION BIT(1)
+#define OCDDSR_EXECBUSY BIT(2)
+#define OCDDSR_EXECOVERRUN BIT(3)
+#define OCDDSR_STOPPED BIT(4)
+#define OCDDSR_COREWROTEDDR BIT(10)
+#define OCDDSR_COREREADDDR BIT(11)
+#define OCDDSR_HOSTWROTEDDR BIT(14)
+#define OCDDSR_HOSTREADDDR BIT(15)
+#define OCDDSR_DEBUGPENDBREAK BIT(16)
+#define OCDDSR_DEBUGPENDHOST BIT(17)
+#define OCDDSR_DEBUGPENDTRAX BIT(18)
+#define OCDDSR_DEBUGINTBREAK BIT(20)
+#define OCDDSR_DEBUGINTHOST BIT(21)
+#define OCDDSR_DEBUGINTTRAX BIT(22)
+#define OCDDSR_RUNSTALLTOGGLE BIT(23)
+#define OCDDSR_RUNSTALLSAMPLE BIT(24)
+#define OCDDSR_BREACKOUTACKITI BIT(25)
+#define OCDDSR_BREAKINITI BIT(26)
+#define OCDDSR_DBGMODPOWERON BIT(31)
+
+#define DEBUGCAUSE_IC BIT(0) /* ICOUNT exception */
+#define DEBUGCAUSE_IB BIT(1) /* IBREAK exception */
+#define DEBUGCAUSE_DB BIT(2) /* DBREAK exception */
+#define DEBUGCAUSE_BI BIT(3) /* BREAK instruction encountered */
+#define DEBUGCAUSE_BN BIT(4) /* BREAK.N instruction encountered */
+#define DEBUGCAUSE_DI BIT(5) /* Debug Interrupt */
+
+#define TRAXCTRL_TREN BIT(0) /* Trace enable. Tracing starts on 0->1 */
+#define TRAXCTRL_TRSTP BIT(1) /* Trace Stop. Make 1 to stop trace. */
+#define TRAXCTRL_PCMEN BIT(2) /* PC match enable */
+#define TRAXCTRL_PTIEN BIT(4) /* Processor-trigger enable */
+#define TRAXCTRL_CTIEN BIT(5) /* Cross-trigger enable */
+#define TRAXCTRL_TMEN BIT(7) /* Tracemem Enable. Always set. */
+#define TRAXCTRL_CNTU BIT(9) /* Post-stop-trigger countdown units; selects when DelayCount-- happens.
+ *0 - every 32-bit word written to tracemem, 1 - every cpu instruction */
+#define TRAXCTRL_TSEN BIT(11) /* Undocumented/deprecated? */
+#define TRAXCTRL_SMPER_SHIFT 12 /* Send sync every 2^(9-smper) messages. 7=reserved, 0=no sync msg */
+#define TRAXCTRL_SMPER_MASK 0x07 /* Synchronization message period */
+#define TRAXCTRL_PTOWT BIT(16) /* Processor Trigger Out (OCD halt) enabled when stop triggered */
+#define TRAXCTRL_PTOWS BIT(17) /* Processor Trigger Out (OCD halt) enabled when trace stop completes */
+#define TRAXCTRL_CTOWT BIT(20) /* Cross-trigger Out enabled when stop triggered */
+#define TRAXCTRL_CTOWS BIT(21) /* Cross-trigger Out enabled when trace stop completes */
+#define TRAXCTRL_ITCTO BIT(22) /* Integration mode: cross-trigger output */
+#define TRAXCTRL_ITCTIA BIT(23) /* Integration mode: cross-trigger ack */
+#define TRAXCTRL_ITATV BIT(24) /* replaces ATID when in integration mode: ATVALID output */
+#define TRAXCTRL_ATID_MASK 0x7F /* ARB source ID */
+#define TRAXCTRL_ATID_SHIFT 24
+#define TRAXCTRL_ATEN BIT(31) /* ATB interface enable */
+
+#define TRAXSTAT_TRACT BIT(0) /* Trace active flag. */
+#define TRAXSTAT_TRIG BIT(1) /* Trace stop trigger. Clears on TREN 1->0 */
+#define TRAXSTAT_PCMTG BIT(2) /* Stop trigger caused by PC match. Clears on TREN 1->0 */
+#define TRAXSTAT_PJTR BIT(3) /* JTAG transaction result. 1=err in preceding jtag transaction. */
+#define TRAXSTAT_PTITG BIT(4) /* Stop trigger caused by Processor Trigger Input.Clears on TREN 1->0 */
+#define TRAXSTAT_CTITG BIT(5) /* Stop trigger caused by Cross-Trigger Input. Clears on TREN 1->0 */
+#define TRAXSTAT_MEMSZ_SHIFT 8 /* Traceram size inducator. Usable trace ram is 2^MEMSZ bytes. */
+#define TRAXSTAT_MEMSZ_MASK 0x1F
+#define TRAXSTAT_PTO BIT(16) /* Processor Trigger Output: current value */
+#define TRAXSTAT_CTO BIT(17) /* Cross-Trigger Output: current value */
+#define TRAXSTAT_ITCTOA BIT(22) /* Cross-Trigger Out Ack: current value */
+#define TRAXSTAT_ITCTI BIT(23) /* Cross-Trigger Input: current value */
+#define TRAXSTAT_ITATR BIT(24) /* ATREADY Input: current value */
+
+#define TRAXADDR_TADDR_SHIFT 0 /* Trax memory address, in 32-bit words. */
+#define TRAXADDR_TADDR_MASK 0x1FFFFF /* Actually is only as big as the trace buffer size max addr. */
+#define TRAXADDR_TWRAP_SHIFT 21 /* Amount of times TADDR has overflown */
+#define TRAXADDR_TWRAP_MASK 0x3FF
+#define TRAXADDR_TWSAT BIT(31) /* 1 if TWRAP has overflown, clear by disabling tren.*/
+
+#define PCMATCHCTRL_PCML_SHIFT 0 /* Amount of lower bits to ignore in pc trigger register */
+#define PCMATCHCTRL_PCML_MASK 0x1F
+#define PCMATCHCTRL_PCMS BIT(31) /* PC Match Sense, 0-match when procs PC is in-range, 1-match when
+ *out-of-range */
+
+#define XTENSA_MAX_PERF_COUNTERS 2
+#define XTENSA_MAX_PERF_SELECT 32
+#define XTENSA_MAX_PERF_MASK 0xffff
+
+#define XTENSA_STOPMASK_DISABLED UINT32_MAX
+
+struct xtensa_debug_module;
+
+struct xtensa_debug_ops {
+ /** enable operation */
+ int (*queue_enable)(struct xtensa_debug_module *dm);
+ /** register read. */
+ int (*queue_reg_read)(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data);
+ /** register write. */
+ int (*queue_reg_write)(struct xtensa_debug_module *dm, unsigned int reg, uint32_t data);
+};
+
+struct xtensa_power_ops {
+ /** register read. */
+ int (*queue_reg_read)(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data,
+ uint8_t clear);
+ /** register write. */
+ int (*queue_reg_write)(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data);
+};
+
+typedef uint8_t xtensa_pwrstat_t;
+typedef uint32_t xtensa_ocdid_t;
+typedef uint32_t xtensa_dsr_t;
+typedef uint32_t xtensa_traxstat_t;
+
+struct xtensa_power_status {
+ xtensa_pwrstat_t stat;
+ xtensa_pwrstat_t stath;
+ /* TODO: do not need to keep previous status to detect that core or debug module has been
+ * reset, */
+ /* we can clear PWRSTAT_DEBUGWASRESET and PWRSTAT_COREWASRESET after reading will do
+ * the job; */
+ /* upon next reet those bits will be set again. So we can get rid of
+ * xtensa_dm_power_status_cache_reset() and xtensa_dm_power_status_cache(). */
+ xtensa_pwrstat_t prev_stat;
+};
+
+struct xtensa_core_status {
+ xtensa_dsr_t dsr;
+};
+
+struct xtensa_trace_config {
+ uint32_t ctrl;
+ uint32_t memaddr_start;
+ uint32_t memaddr_end;
+ uint32_t addr;
+};
+
+struct xtensa_trace_status {
+ xtensa_traxstat_t stat;
+};
+
+struct xtensa_trace_start_config {
+ uint32_t stoppc;
+ bool after_is_words;
+ uint32_t after;
+ uint32_t stopmask; /* UINT32_MAX: disable PC match option */
+};
+
+struct xtensa_perfmon_config {
+ int select;
+ uint32_t mask;
+ int kernelcnt;
+ int tracelevel;
+};
+
+struct xtensa_perfmon_result {
+ uint64_t value;
+ bool overflow;
+};
+
+struct xtensa_debug_module_config {
+ const struct xtensa_power_ops *pwr_ops;
+ const struct xtensa_debug_ops *dbg_ops;
+ struct jtag_tap *tap;
+ void (*queue_tdi_idle)(struct target *target);
+ void *queue_tdi_idle_arg;
+};
+
+struct xtensa_debug_module {
+ const struct xtensa_power_ops *pwr_ops;
+ const struct xtensa_debug_ops *dbg_ops;
+ struct jtag_tap *tap;
+ void (*queue_tdi_idle)(struct target *target);
+ void *queue_tdi_idle_arg;
+
+ struct xtensa_power_status power_status;
+ struct xtensa_core_status core_status;
+ xtensa_ocdid_t device_id;
+};
+
+int xtensa_dm_init(struct xtensa_debug_module *dm, const struct xtensa_debug_module_config *cfg);
+int xtensa_dm_queue_enable(struct xtensa_debug_module *dm);
+int xtensa_dm_queue_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *value);
+int xtensa_dm_queue_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint32_t value);
+int xtensa_dm_queue_pwr_reg_read(struct xtensa_debug_module *dm, unsigned int reg, uint8_t *data, uint8_t clear);
+int xtensa_dm_queue_pwr_reg_write(struct xtensa_debug_module *dm, unsigned int reg, uint8_t data);
+
+static inline void xtensa_dm_queue_tdi_idle(struct xtensa_debug_module *dm)
+{
+ if (dm->queue_tdi_idle)
+ dm->queue_tdi_idle(dm->queue_tdi_idle_arg);
+}
+
+int xtensa_dm_power_status_read(struct xtensa_debug_module *dm, uint32_t clear);
+static inline void xtensa_dm_power_status_cache_reset(struct xtensa_debug_module *dm)
+{
+ dm->power_status.prev_stat = 0;
+}
+static inline void xtensa_dm_power_status_cache(struct xtensa_debug_module *dm)
+{
+ dm->power_status.prev_stat = dm->power_status.stath;
+}
+static inline xtensa_pwrstat_t xtensa_dm_power_status_get(struct xtensa_debug_module *dm)
+{
+ return dm->power_status.stat;
+}
+
+int xtensa_dm_core_status_read(struct xtensa_debug_module *dm);
+int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bits);
+int xtensa_dm_core_status_check(struct xtensa_debug_module *dm);
+static inline xtensa_dsr_t xtensa_dm_core_status_get(struct xtensa_debug_module *dm)
+{
+ return dm->core_status.dsr;
+}
+
+int xtensa_dm_device_id_read(struct xtensa_debug_module *dm);
+static inline xtensa_ocdid_t xtensa_dm_device_id_get(struct xtensa_debug_module *dm)
+{
+ return dm->device_id;
+}
+
+int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg);
+int xtensa_dm_trace_stop(struct xtensa_debug_module *dm, bool pto_enable);
+int xtensa_dm_trace_config_read(struct xtensa_debug_module *dm, struct xtensa_trace_config *config);
+int xtensa_dm_trace_status_read(struct xtensa_debug_module *dm, struct xtensa_trace_status *status);
+int xtensa_dm_trace_data_read(struct xtensa_debug_module *dm, uint8_t *dest, uint32_t size);
+
+static inline bool xtensa_dm_is_online(struct xtensa_debug_module *dm)
+{
+ int res = xtensa_dm_device_id_read(dm);
+ if (res != ERROR_OK)
+ return false;
+ return (dm->device_id != 0xffffffff && dm->device_id != 0);
+}
+
+static inline bool xtensa_dm_tap_was_reset(struct xtensa_debug_module *dm)
+{
+ return !(dm->power_status.prev_stat & PWRSTAT_DEBUGWASRESET) &&
+ dm->power_status.stat & PWRSTAT_DEBUGWASRESET;
+}
+
+static inline bool xtensa_dm_core_was_reset(struct xtensa_debug_module *dm)
+{
+ return !(dm->power_status.prev_stat & PWRSTAT_COREWASRESET) &&
+ dm->power_status.stat & PWRSTAT_COREWASRESET;
+}
+
+static inline bool xtensa_dm_core_is_stalled(struct xtensa_debug_module *dm)
+{
+ return dm->core_status.dsr & OCDDSR_RUNSTALLSAMPLE;
+}
+
+static inline bool xtensa_dm_is_powered(struct xtensa_debug_module *dm)
+{
+ return dm->core_status.dsr & OCDDSR_DBGMODPOWERON;
+}
+
+int xtensa_dm_perfmon_enable(struct xtensa_debug_module *dm, int counter_id,
+ const struct xtensa_perfmon_config *config);
+int xtensa_dm_perfmon_dump(struct xtensa_debug_module *dm, int counter_id,
+ struct xtensa_perfmon_result *out_result);
+
+#endif /* OPENOCD_TARGET_XTENSA_DEBUG_MODULE_H */
diff --git a/src/target/xtensa/xtensa_regs.h b/src/target/xtensa/xtensa_regs.h
new file mode 100644
index 0000000..7602131
--- /dev/null
+++ b/src/target/xtensa/xtensa_regs.h
@@ -0,0 +1,278 @@
+/***************************************************************************
+ * Generic Xtensa target API for OpenOCD *
+ * Copyright (C) 2016-2019 Espressif Systems Ltd. *
+ * Author: Angus Gratton gus@projectgus.com *
+ * Author: Jeroen Domburg <jeroen@espressif.com> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+#ifndef OPENOCD_TARGET_XTENSA_REGS_H
+#define OPENOCD_TARGET_XTENSA_REGS_H
+
+struct reg_arch_type;
+
+enum xtensa_reg_id {
+ XT_REG_IDX_PC = 0,
+ XT_REG_IDX_AR0,
+ XT_REG_IDX_AR1,
+ XT_REG_IDX_AR2,
+ XT_REG_IDX_AR3,
+ XT_REG_IDX_AR4,
+ XT_REG_IDX_AR5,
+ XT_REG_IDX_AR6,
+ XT_REG_IDX_AR7,
+ XT_REG_IDX_AR8,
+ XT_REG_IDX_AR9,
+ XT_REG_IDX_AR10,
+ XT_REG_IDX_AR11,
+ XT_REG_IDX_AR12,
+ XT_REG_IDX_AR13,
+ XT_REG_IDX_AR14,
+ XT_REG_IDX_AR15,
+ XT_REG_IDX_AR16,
+ XT_REG_IDX_AR17,
+ XT_REG_IDX_AR18,
+ XT_REG_IDX_AR19,
+ XT_REG_IDX_AR20,
+ XT_REG_IDX_AR21,
+ XT_REG_IDX_AR22,
+ XT_REG_IDX_AR23,
+ XT_REG_IDX_AR24,
+ XT_REG_IDX_AR25,
+ XT_REG_IDX_AR26,
+ XT_REG_IDX_AR27,
+ XT_REG_IDX_AR28,
+ XT_REG_IDX_AR29,
+ XT_REG_IDX_AR30,
+ XT_REG_IDX_AR31,
+ XT_REG_IDX_AR32,
+ XT_REG_IDX_AR33,
+ XT_REG_IDX_AR34,
+ XT_REG_IDX_AR35,
+ XT_REG_IDX_AR36,
+ XT_REG_IDX_AR37,
+ XT_REG_IDX_AR38,
+ XT_REG_IDX_AR39,
+ XT_REG_IDX_AR40,
+ XT_REG_IDX_AR41,
+ XT_REG_IDX_AR42,
+ XT_REG_IDX_AR43,
+ XT_REG_IDX_AR44,
+ XT_REG_IDX_AR45,
+ XT_REG_IDX_AR46,
+ XT_REG_IDX_AR47,
+ XT_REG_IDX_AR48,
+ XT_REG_IDX_AR49,
+ XT_REG_IDX_AR50,
+ XT_REG_IDX_AR51,
+ XT_REG_IDX_AR52,
+ XT_REG_IDX_AR53,
+ XT_REG_IDX_AR54,
+ XT_REG_IDX_AR55,
+ XT_REG_IDX_AR56,
+ XT_REG_IDX_AR57,
+ XT_REG_IDX_AR58,
+ XT_REG_IDX_AR59,
+ XT_REG_IDX_AR60,
+ XT_REG_IDX_AR61,
+ XT_REG_IDX_AR62,
+ XT_REG_IDX_AR63,
+ XT_REG_IDX_LBEG,
+ XT_REG_IDX_LEND,
+ XT_REG_IDX_LCOUNT,
+ XT_REG_IDX_SAR,
+ XT_REG_IDX_WINDOWBASE,
+ XT_REG_IDX_WINDOWSTART,
+ XT_REG_IDX_CONFIGID0,
+ XT_REG_IDX_CONFIGID1,
+ XT_REG_IDX_PS,
+ XT_REG_IDX_THREADPTR,
+ XT_REG_IDX_BR,
+ XT_REG_IDX_SCOMPARE1,
+ XT_REG_IDX_ACCLO,
+ XT_REG_IDX_ACCHI,
+ XT_REG_IDX_M0,
+ XT_REG_IDX_M1,
+ XT_REG_IDX_M2,
+ XT_REG_IDX_M3,
+ XT_REG_IDX_F0,
+ XT_REG_IDX_F1,
+ XT_REG_IDX_F2,
+ XT_REG_IDX_F3,
+ XT_REG_IDX_F4,
+ XT_REG_IDX_F5,
+ XT_REG_IDX_F6,
+ XT_REG_IDX_F7,
+ XT_REG_IDX_F8,
+ XT_REG_IDX_F9,
+ XT_REG_IDX_F10,
+ XT_REG_IDX_F11,
+ XT_REG_IDX_F12,
+ XT_REG_IDX_F13,
+ XT_REG_IDX_F14,
+ XT_REG_IDX_F15,
+ XT_REG_IDX_FCR,
+ XT_REG_IDX_FSR,
+ XT_REG_IDX_MMID,
+ XT_REG_IDX_IBREAKENABLE,
+ XT_REG_IDX_MEMCTL,
+ XT_REG_IDX_ATOMCTL,
+ XT_REG_IDX_IBREAKA0,
+ XT_REG_IDX_IBREAKA1,
+ XT_REG_IDX_DBREAKA0,
+ XT_REG_IDX_DBREAKA1,
+ XT_REG_IDX_DBREAKC0,
+ XT_REG_IDX_DBREAKC1,
+ XT_REG_IDX_EPC1,
+ XT_REG_IDX_EPC2,
+ XT_REG_IDX_EPC3,
+ XT_REG_IDX_EPC4,
+ XT_REG_IDX_EPC5,
+ XT_REG_IDX_EPC6,
+ XT_REG_IDX_EPC7,
+ XT_REG_IDX_DEPC,
+ XT_REG_IDX_EPS2,
+ XT_REG_IDX_EPS3,
+ XT_REG_IDX_EPS4,
+ XT_REG_IDX_EPS5,
+ XT_REG_IDX_EPS6,
+ XT_REG_IDX_EPS7,
+ XT_REG_IDX_EXCSAVE1,
+ XT_REG_IDX_EXCSAVE2,
+ XT_REG_IDX_EXCSAVE3,
+ XT_REG_IDX_EXCSAVE4,
+ XT_REG_IDX_EXCSAVE5,
+ XT_REG_IDX_EXCSAVE6,
+ XT_REG_IDX_EXCSAVE7,
+ XT_REG_IDX_CPENABLE,
+ XT_REG_IDX_INTERRUPT,
+ XT_REG_IDX_INTSET,
+ XT_REG_IDX_INTCLEAR,
+ XT_REG_IDX_INTENABLE,
+ XT_REG_IDX_VECBASE,
+ XT_REG_IDX_EXCCAUSE,
+ XT_REG_IDX_DEBUGCAUSE,
+ XT_REG_IDX_CCOUNT,
+ XT_REG_IDX_PRID,
+ XT_REG_IDX_ICOUNT,
+ XT_REG_IDX_ICOUNTLEVEL,
+ XT_REG_IDX_EXCVADDR,
+ XT_REG_IDX_CCOMPARE0,
+ XT_REG_IDX_CCOMPARE1,
+ XT_REG_IDX_CCOMPARE2,
+ XT_REG_IDX_MISC0,
+ XT_REG_IDX_MISC1,
+ XT_REG_IDX_MISC2,
+ XT_REG_IDX_MISC3,
+ XT_REG_IDX_LITBASE,
+ XT_REG_IDX_PTEVADDR,
+ XT_REG_IDX_RASID,
+ XT_REG_IDX_ITLBCFG,
+ XT_REG_IDX_DTLBCFG,
+ XT_REG_IDX_MEPC,
+ XT_REG_IDX_MEPS,
+ XT_REG_IDX_MESAVE,
+ XT_REG_IDX_MESR,
+ XT_REG_IDX_MECR,
+ XT_REG_IDX_MEVADDR,
+ XT_REG_IDX_A0,
+ XT_REG_IDX_A1,
+ XT_REG_IDX_A2,
+ XT_REG_IDX_A3,
+ XT_REG_IDX_A4,
+ XT_REG_IDX_A5,
+ XT_REG_IDX_A6,
+ XT_REG_IDX_A7,
+ XT_REG_IDX_A8,
+ XT_REG_IDX_A9,
+ XT_REG_IDX_A10,
+ XT_REG_IDX_A11,
+ XT_REG_IDX_A12,
+ XT_REG_IDX_A13,
+ XT_REG_IDX_A14,
+ XT_REG_IDX_A15,
+ XT_REG_IDX_PWRCTL,
+ XT_REG_IDX_PWRSTAT,
+ XT_REG_IDX_ERISTAT,
+ XT_REG_IDX_CS_ITCTRL,
+ XT_REG_IDX_CS_CLAIMSET,
+ XT_REG_IDX_CS_CLAIMCLR,
+ XT_REG_IDX_CS_LOCKACCESS,
+ XT_REG_IDX_CS_LOCKSTATUS,
+ XT_REG_IDX_CS_AUTHSTATUS,
+ XT_REG_IDX_FAULT_INFO,
+ XT_REG_IDX_TRAX_ID,
+ XT_REG_IDX_TRAX_CTRL,
+ XT_REG_IDX_TRAX_STAT,
+ XT_REG_IDX_TRAX_DATA,
+ XT_REG_IDX_TRAX_ADDR,
+ XT_REG_IDX_TRAX_PCTRIGGER,
+ XT_REG_IDX_TRAX_PCMATCH,
+ XT_REG_IDX_TRAX_DELAY,
+ XT_REG_IDX_TRAX_MEMSTART,
+ XT_REG_IDX_TRAX_MEMEND,
+ XT_REG_IDX_PMG,
+ XT_REG_IDX_PMPC,
+ XT_REG_IDX_PM0,
+ XT_REG_IDX_PM1,
+ XT_REG_IDX_PMCTRL0,
+ XT_REG_IDX_PMCTRL1,
+ XT_REG_IDX_PMSTAT0,
+ XT_REG_IDX_PMSTAT1,
+ XT_REG_IDX_OCD_ID,
+ XT_REG_IDX_OCD_DCRCLR,
+ XT_REG_IDX_OCD_DCRSET,
+ XT_REG_IDX_OCD_DSR,
+ XT_REG_IDX_OCD_DDR,
+ XT_NUM_REGS,
+ /* chip-specific user registers go after ISA-defined ones */
+ XT_USR_REG_START = XT_NUM_REGS
+};
+
+typedef uint32_t xtensa_reg_val_t;
+
+enum xtensa_reg_type {
+ XT_REG_GENERAL = 0, /* General-purpose register; part of the windowed register set */
+ XT_REG_USER = 1, /* User register, needs RUR to read */
+ XT_REG_SPECIAL = 2, /* Special register, needs RSR to read */
+ XT_REG_DEBUG = 3, /* Register used for the debug interface. Don't mess with this. */
+ XT_REG_RELGEN = 4, /* Relative general address. Points to the absolute addresses plus the window
+ *index */
+ XT_REG_FR = 5, /* Floating-point register */
+};
+
+enum xtensa_reg_flags {
+ XT_REGF_NOREAD = 0x01, /* Register is write-only */
+ XT_REGF_COPROC0 = 0x02 /* Can't be read if coproc0 isn't enabled */
+};
+
+struct xtensa_reg_desc {
+ const char *name;
+ unsigned int reg_num; /* ISA register num (meaning depends on register type) */
+ enum xtensa_reg_type type;
+ enum xtensa_reg_flags flags;
+};
+
+struct xtensa_user_reg_desc {
+ const char *name;
+ /* ISA register num (meaning depends on register type) */
+ unsigned int reg_num;
+ enum xtensa_reg_flags flags;
+ uint32_t size;
+ const struct reg_arch_type *type;
+};
+
+extern const struct xtensa_reg_desc xtensa_regs[XT_NUM_REGS];
+
+#endif /* OPENOCD_TARGET_XTENSA_REGS_H */
diff --git a/tcl/board/esp32s2-kaluga-1.cfg b/tcl/board/esp32s2-kaluga-1.cfg
new file mode 100644
index 0000000..783ea21
--- /dev/null
+++ b/tcl/board/esp32s2-kaluga-1.cfg
@@ -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/board/kontron_sl28.cfg b/tcl/board/kontron_sl28.cfg
new file mode 100644
index 0000000..9816f38
--- /dev/null
+++ b/tcl/board/kontron_sl28.cfg
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Kontron SMARC-sAL28
+
+transport select jtag
+reset_config srst_only srst_nogate
+
+jtag newtap unknown0 tap -irlen 12
+
+set _CPUS 2
+source [find target/ls1028a.cfg]
+
+source [find tcl/cpld/altera-epm240.cfg]
+
+adapter speed 2000
diff --git a/tcl/board/steval-idb012v1.cfg b/tcl/board/steval-idb012v1.cfg
new file mode 100644
index 0000000..25efc58
--- /dev/null
+++ b/tcl/board/steval-idb012v1.cfg
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later.
+# This is an evaluation board with a single BlueNRG-LPS chip.
+set CHIPNAME bluenrg-lps
+source [find interface/cmsis-dap.cfg]
+source [find target/bluenrg-x.cfg] \ No newline at end of file
diff --git a/tcl/interface/beaglebone-jtag-native.cfg b/tcl/interface/beaglebone-jtag-native.cfg
new file mode 100644
index 0000000..cd32ca4
--- /dev/null
+++ b/tcl/interface/beaglebone-jtag-native.cfg
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# BeagleBone native GPIO interface for JTAG
+#
+# This is best used with a fast buffer but it is also suitable for a direct
+# connection if the target voltage matches the host's IO voltage (typically
+# 3.3V) and the cable is short.
+#
+# DO NOT APPLY VOLTAGE TO THE GPIO PINS UNTIL SYS_RESETN IS HIGH.
+#
+# Do not forget the GND connection.
+
+adapter driver am335xgpio
+
+# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET
+# These depend on the system clock, calibrated for stock 1 GHz BeagleBoneBlack
+# am335xgpio speed SPEED_COEFF SPEED_OFFSET
+am335xgpio speed_coeffs 600000 575
+
+am335xgpio tdo_num 20
+am335xgpio tdi_num 60
+am335xgpio tms_num 4
+am335xgpio tck_num 2
+
+am335xgpio led_num 51
+am335xgpio led_on_state on
+
+am335xgpio srst_num 65
+reset_config srst_only srst_push_pull
diff --git a/tcl/interface/beaglebone-swd-native.cfg b/tcl/interface/beaglebone-swd-native.cfg
new file mode 100644
index 0000000..f7bff6e
--- /dev/null
+++ b/tcl/interface/beaglebone-swd-native.cfg
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# BeagleBone native GPIO interface for SWD
+#
+# This is best used with a fast buffer but it is also suitable for a direct
+# connection if the target voltage matches the host's IO voltage (typically
+# 3.3V) and the cable is short.
+#
+# DO NOT APPLY VOLTAGE TO THE GPIO PINS UNTIL SYS_RESETN IS HIGH.
+#
+# Do not forget the GND connection.
+
+adapter driver am335xgpio
+
+# Transition delay calculation: SPEED_COEFF/khz - SPEED_OFFSET
+# These depend on the system clock, calibrated for stock 1 GHz BeagleBoneBlack
+# am335xgpio speed SPEED_COEFF SPEED_OFFSET
+am335xgpio speed_coeffs 600000 575
+
+am335xgpio swclk_num 2
+am335xgpio swdio_num 4
+am335xgpio swdio_dir_num 60
+am335xgpio swdio_dir_output_state on
+
+# USR0 LED
+am335xgpio led_num 53
+am335xgpio led_on_state on
+
+am335xgpio srst_num 65
+reset_config srst_only srst_push_pull
diff --git a/tcl/interface/ftdi/esp32s2_kaluga_v1.cfg b/tcl/interface/ftdi/esp32s2_kaluga_v1.cfg
new file mode 100644
index 0000000..1880bcb
--- /dev/null
+++ b/tcl/interface/ftdi/esp32s2_kaluga_v1.cfg
@@ -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/interface/ftdi/tigard.cfg b/tcl/interface/ftdi/tigard.cfg
new file mode 100644
index 0000000..43ce0ad
--- /dev/null
+++ b/tcl/interface/ftdi/tigard.cfg
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Tigard: An FTDI FT2232H-based multi-protocol tool for hardware hacking.
+# https://github.com/tigard-tools/tigard
+
+adapter driver ftdi
+
+ftdi device_desc "Tigard V1.1"
+ftdi vid_pid 0x0403 0x6010
+
+ftdi channel 1
+
+ftdi layout_init 0x0038 0x003b
+ftdi layout_signal nTRST -data 0x0010
+ftdi layout_signal nSRST -data 0x0020
+
+# This board doesn't support open-drain reset modes since its output buffer is
+# always enabled.
+reset_config srst_push_pull trst_push_pull
diff --git a/tcl/target/bluenrg-x.cfg b/tcl/target/bluenrg-x.cfg
index ea94be9..1eba376 100644
--- a/tcl/target/bluenrg-x.cfg
+++ b/tcl/target/bluenrg-x.cfg
@@ -47,11 +47,14 @@ if {![using_hla]} {
cortex_m reset_config sysresetreq
}
+set JTAG_IDCODE_B2 0x0200A041
+set JTAG_IDCODE_B1 0x0
+
$_TARGETNAME configure -event halted {
global WDOG_VALUE
global WDOG_VALUE_SET
set _JTAG_IDCODE [mrw 0x40000004]
- if {$_JTAG_IDCODE != 0x0201E041} {
+ if {$_JTAG_IDCODE == $JTAG_IDCODE_B2 || $_JTAG_IDCODE == $JTAG_IDCODE_B1} {
# Stop watchdog during halt, if enabled. Only Bluenrg-1/2
set WDOG_VALUE [mrw 0x40700008]
if [expr {$WDOG_VALUE & (1 << 1)}] {
@@ -64,7 +67,7 @@ $_TARGETNAME configure -event resumed {
global WDOG_VALUE
global WDOG_VALUE_SET
set _JTAG_IDCODE [mrw 0x40000004]
- if {$_JTAG_IDCODE != 0x0201E041} {
+ if {$_JTAG_IDCODE == $JTAG_IDCODE_B2 || $_JTAG_IDCODE == $JTAG_IDCODE_B1} {
if {$WDOG_VALUE_SET} {
# Restore watchdog enable value after resume. Only Bluenrg-1/2
mww 0x40700008 $WDOG_VALUE
diff --git a/tcl/target/esp32s2.cfg b/tcl/target/esp32s2.cfg
new file mode 100644
index 0000000..ab64c31
--- /dev/null
+++ b/tcl/target/esp32s2.cfg
@@ -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
diff --git a/tcl/target/ls1028a.cfg b/tcl/target/ls1028a.cfg
new file mode 100644
index 0000000..463ec7d
--- /dev/null
+++ b/tcl/target/ls1028a.cfg
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# NXP LS1028A
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME ls1028a
+}
+
+if { [info exists DAP_TAPID] } {
+ set _DAP_TAPID $DAP_TAPID
+} else {
+ set _DAP_TAPID 0x6ba00477
+}
+
+set _CPUS 2
+
+source [find target/lsch3_common.cfg]
diff --git a/tcl/target/ls1088a.cfg b/tcl/target/ls1088a.cfg
index f9ae9a1..193d6dd 100644
--- a/tcl/target/ls1088a.cfg
+++ b/tcl/target/ls1088a.cfg
@@ -13,62 +13,9 @@ if { [info exists DAP_TAPID] } {
set _DAP_TAPID 0x5ba00477
}
-jtag newtap $_CHIPNAME dap -irlen 4 -expected-id $_DAP_TAPID
-dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dap
+set _CPUS 8
-target create $_CHIPNAME.axi mem_ap -dap $_CHIPNAME.dap -ap-num 1
-
-set _CPU_BASE 0x81000000
-set _CPU_STRIDE 0x100000
-set _CPU_DBGOFF 0x10000
-set _CPU_CTIOFF 0x20000
-
-set _TARGETS {}
-for {set i 0} {$i < 8} {incr i} {
- set _BASE [expr {$_CPU_BASE + $_CPU_STRIDE * $i}]
- cti create $_CHIPNAME.cti$i -dap $_CHIPNAME.dap -ap-num 0 \
- -baseaddr [expr {$_BASE + $_CPU_CTIOFF}]
- target create $_CHIPNAME.cpu$i aarch64 -dap $_CHIPNAME.dap \
- -cti $_CHIPNAME.cti$i -dbgbase [expr {$_BASE + $_CPU_DBGOFF}] \
- {*}[expr {$i ? "-coreid $i" : "-rtos hwthread" }]
- lappend _TARGETS $_CHIPNAME.cpu$i
-}
-
-target smp {*}$_TARGETS
-
-# Service processor
-target create $_CHIPNAME.sp cortex_a -dap $_CHIPNAME.dap -ap-num 0 -dbgbase 0x80138000
-
-# Normally you will not need to call this, but if you are using the hard-coded
-# Reset Configuration Word (RCW) you will need to call this manually. The CPU's
-# reset vector is 0, and the boot ROM at that location contains ARMv7-A 32-bit
-# instructions. This will cause the CPU to almost immediately execute an
-# illegal instruction.
-#
-# This code is idempotent; releasing a released CPU has no effect, although it
-# will halt/resume the service processor.
-add_help_text release_cpu "Release a cpu which is held off"
-proc release_cpu {cpu} {
- set RST_BRRL 0x1e60060
-
- set old [target current]
- targets $::_CHIPNAME.sp
- set not_halted [string compare halted [$::_CHIPNAME.sp curstate]]
- if {$not_halted} {
- halt
- }
-
- # Release the cpu; it will start executing something bogus
- mem2array regs 32 $RST_BRRL 1
- mww $RST_BRRL [expr {$regs(0) | 1 << $cpu}]
-
- if {$not_halted} {
- resume
- }
- targets $old
-}
-
-targets $_CHIPNAME.cpu0
+source [find target/lsch3_common.cfg]
# Seems to work OK in testing
adapter speed 10000
diff --git a/tcl/target/lsch3_common.cfg b/tcl/target/lsch3_common.cfg
new file mode 100644
index 0000000..f48d59b
--- /dev/null
+++ b/tcl/target/lsch3_common.cfg
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# This contains common configuration for NXP Layerscape chassis generation 3
+
+if { ![info exists _CPUS] } {
+ error "_CPUS must be set to the number of cores"
+}
+
+jtag newtap $_CHIPNAME dap -irlen 4 -expected-id $_DAP_TAPID
+dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dap
+
+target create $_CHIPNAME.axi mem_ap -dap $_CHIPNAME.dap -ap-num 1
+
+set _CPU_BASE 0x81000000
+set _CPU_STRIDE 0x100000
+set _CPU_DBGOFF 0x10000
+set _CPU_CTIOFF 0x20000
+
+set _TARGETS {}
+for {set i 0} {$i < $_CPUS} {incr i} {
+ set _BASE [expr {$_CPU_BASE + $_CPU_STRIDE * $i}]
+ cti create $_CHIPNAME.cti$i -dap $_CHIPNAME.dap -ap-num 0 \
+ -baseaddr [expr {$_BASE + $_CPU_CTIOFF}]
+ target create $_CHIPNAME.cpu$i aarch64 -dap $_CHIPNAME.dap \
+ -cti $_CHIPNAME.cti$i -dbgbase [expr {$_BASE + $_CPU_DBGOFF}] \
+ {*}[expr {$i ? "-coreid $i" : "-rtos hwthread" }]
+ lappend _TARGETS $_CHIPNAME.cpu$i
+}
+
+target smp {*}$_TARGETS
+
+# Service processor
+target create $_CHIPNAME.sp cortex_a -dap $_CHIPNAME.dap -ap-num 0 -dbgbase 0x80138000
+
+# Normally you will not need to call this, but if you are using the hard-coded
+# Reset Configuration Word (RCW) you will need to call this manually. The CPU's
+# reset vector is 0, and the boot ROM at that location contains ARMv7-A 32-bit
+# instructions. This will cause the CPU to almost immediately execute an
+# illegal instruction.
+#
+# This code is idempotent; releasing a released CPU has no effect, although it
+# will halt/resume the service processor.
+add_help_text release_cpu "Release a cpu which is held off"
+proc release_cpu {cpu} {
+ set RST_BRRL 0x1e60060
+
+ set old [target current]
+ targets $::_CHIPNAME.sp
+ set not_halted [string compare halted [$::_CHIPNAME.sp curstate]]
+ if {$not_halted} {
+ halt
+ }
+
+ # Release the cpu; it will start executing something bogus
+ mem2array regs 32 $RST_BRRL 1
+ mww $RST_BRRL [expr {$regs(0) | 1 << $cpu}]
+
+ if {$not_halted} {
+ resume
+ }
+ targets $old
+}
+
+targets $_CHIPNAME.cpu0
diff --git a/tcl/target/renesas_rz_five.cfg b/tcl/target/renesas_rz_five.cfg
new file mode 100644
index 0000000..5ab94ab
--- /dev/null
+++ b/tcl/target/renesas_rz_five.cfg
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# Renesas RZ/Five SoC
+#
+# General-purpose Microprocessors with RISC-V CPU Core (Andes AX45MP Single) (1.0 GHz)
+
+transport select jtag
+
+reset_config trst_and_srst srst_gates_jtag
+adapter speed 4000
+adapter srst delay 500
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME r9A07g043u
+}
+
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME
diff --git a/tcl/target/renesas_rz_g2.cfg b/tcl/target/renesas_rz_g2.cfg
index 3615aa0..a3d5f48 100644
--- a/tcl/target/renesas_rz_g2.cfg
+++ b/tcl/target/renesas_rz_g2.cfg
@@ -6,11 +6,13 @@
# - Each SOC can boot through the Cortex-A5x cores
# Supported RZ/G2 SOCs and their cores:
-# RZ/G2H: Cortex-A57 x4, Cortex-A53 x4, Cortex-R7
-# RZ/G2M: Cortex-A57 x2, Cortex-A53 x4, Cortex-R7
-# RZ/G2N: Cortex-A57 x2, Cortex-R7
-# RZ/G2E: Cortex-A53 x2, Cortex-R7
-# RZ/G2L: Cortex-A55 x2, Cortex-M33
+# RZ/G2H: Cortex-A57 x4, Cortex-A53 x4, Cortex-R7
+# RZ/G2M: Cortex-A57 x2, Cortex-A53 x4, Cortex-R7
+# RZ/G2N: Cortex-A57 x2, Cortex-R7
+# RZ/G2E: Cortex-A53 x2, Cortex-R7
+# RZ/G2L: Cortex-A55 x2, Cortex-M33
+# RZ/G2LC: Cortex-A55 x2, Cortex-M33
+# RZ/G2UL: Cortex-A55 x1, Cortex-M33
# Usage:
# There are 2 configuration options:
@@ -75,6 +77,20 @@ switch $_soc {
set _boot_core CA55
set _ap_num 0
}
+ G2LC {
+ set _CHIPNAME r9a07g044c
+ set _num_ca55 2
+ set _num_cm33 1
+ set _boot_core CA55
+ set _ap_num 0
+ }
+ G2UL {
+ set _CHIPNAME r9a07g043u
+ set _num_ca55 1
+ set _num_cm33 1
+ set _boot_core CA55
+ set _ap_num 0
+ }
default {
error "'$_soc' is invalid!"
}
@@ -169,7 +185,7 @@ if { $_boot_core == "CA57" } {
echo "SMP targets:$smp_targets"
eval "target smp $smp_targets"
-if { $_soc == "G2L"} {
+if { $_soc == "G2L" || $_soc == "G2LC" || $_soc == "G2UL" } {
target create $_CHIPNAME.axi_ap mem_ap -dap $_DAPNAME -ap-num 1
}