meta-st-stm32mp/recipes-devtools/openocd/openocd-stm32mp/0001-Add-support-of-STLINK-...

5379 lines
167 KiB
Diff

From 0998211996ffbc68670a07c49d3966b34c5b0a37 Mon Sep 17 00:00:00 2001
From: Romuald JEANNE <romuald.jeanne@st.com>
Date: Mon, 15 Oct 2018 17:40:36 +0200
Subject: [PATCH] Add support of STLINK for stm32mp1
Signed-off-by: Romuald JEANNE <romuald.jeanne@st.com>
---
contrib/60-openocd.rules | 7 +
src/helper/bits.h | 88 ++
src/helper/log.h | 1 +
src/jtag/adapter.c | 132 +++
src/jtag/core.c | 66 +-
src/jtag/drivers/stlink_usb.c | 1413 ++++++++++++++++++++++++++----
src/jtag/interfaces.c | 6 +
src/jtag/startup.tcl | 12 +-
src/jtag/tcl.c | 6 +-
src/server/gdb_server.c | 49 +-
src/target/Makefile.am | 4 +
src/target/adi_v5_stlink.c | 311 +++++++
src/target/arm_adi_v5.c | 28 +
src/target/arm_adi_v5.h | 10 +
src/target/arm_dap.c | 9 +
src/target/arm_dpm.h | 29 +-
src/target/armv7a.c | 19 +-
src/target/armv7a.h | 2 +
src/target/armv7m.c | 13 +
src/target/armv7m.h | 2 +-
src/target/cortex_a.c | 313 +++++--
src/target/cortex_m.c | 138 ++-
src/target/cortex_m.h | 1 +
src/target/startup.tcl | 250 +++++-
src/target/target.c | 189 +++-
src/target/target.h | 22 +-
src/target/target_type.h | 16 +
src/transport/transport.h | 2 +
tcl/board/stm32mp15x_dk2.cfg | 9 +
tcl/board/stm32mp15x_ev1_jlink_jtag.cfg | 9 +
tcl/board/stm32mp15x_ev1_jlink_swd.cfg | 9 +
tcl/board/stm32mp15x_ev1_stlink_jtag.cfg | 9 +
tcl/board/stm32mp15x_ev1_stlink_swd.cfg | 9 +
tcl/board/stm32mp15x_ev1_ulink2_jtag.cfg | 9 +
tcl/board/stm32mp15x_ev1_ulink2_swd.cfg | 9 +
tcl/board/stm32mp15x_ev2_jlink_jtag.cfg | 9 +
tcl/board/stm32mp15x_ev2_jlink_swd.cfg | 9 +
tcl/board/stm32mp15x_ev2_stlink_jtag.cfg | 9 +
tcl/board/stm32mp15x_ev2_stlink_swd.cfg | 9 +
tcl/board/stm32mp15x_ev2_ulink2_jtag.cfg | 9 +
tcl/board/stm32mp15x_ev2_ulink2_swd.cfg | 9 +
tcl/interface/stlink-dap.cfg | 18 +
tcl/target/atsamv.cfg | 2 +-
tcl/target/imx6.cfg | 50 +-
tcl/target/kx.cfg | 7 +-
tcl/target/stm32f7x.cfg | 2 +-
tcl/target/stm32mp15x.cfg | 165 ++++
tcl/target/stm32mp15x_stpmu1.cfg | 113 +++
tcl/target/swj-dp.tcl | 2 +
49 files changed, 3181 insertions(+), 433 deletions(-)
create mode 100644 src/helper/bits.h
create mode 100644 src/target/adi_v5_stlink.c
create mode 100644 tcl/board/stm32mp15x_dk2.cfg
create mode 100644 tcl/board/stm32mp15x_ev1_jlink_jtag.cfg
create mode 100644 tcl/board/stm32mp15x_ev1_jlink_swd.cfg
create mode 100644 tcl/board/stm32mp15x_ev1_stlink_jtag.cfg
create mode 100644 tcl/board/stm32mp15x_ev1_stlink_swd.cfg
create mode 100644 tcl/board/stm32mp15x_ev1_ulink2_jtag.cfg
create mode 100644 tcl/board/stm32mp15x_ev1_ulink2_swd.cfg
create mode 100644 tcl/board/stm32mp15x_ev2_jlink_jtag.cfg
create mode 100644 tcl/board/stm32mp15x_ev2_jlink_swd.cfg
create mode 100644 tcl/board/stm32mp15x_ev2_stlink_jtag.cfg
create mode 100644 tcl/board/stm32mp15x_ev2_stlink_swd.cfg
create mode 100644 tcl/board/stm32mp15x_ev2_ulink2_jtag.cfg
create mode 100644 tcl/board/stm32mp15x_ev2_ulink2_swd.cfg
create mode 100644 tcl/interface/stlink-dap.cfg
create mode 100644 tcl/target/stm32mp15x.cfg
create mode 100644 tcl/target/stm32mp15x_stpmu1.cfg
diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules
index 692e1b1..de47541 100644
--- a/contrib/60-openocd.rules
+++ b/contrib/60-openocd.rules
@@ -63,6 +63,13 @@ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="660", GROUP="plugdev",
# STLink v2-1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3752", MODE="660", GROUP="plugdev", TAG+="uaccess"
+
+# STLink v3
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374d", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374e", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374f", MODE="660", GROUP="plugdev", TAG+="uaccess"
+ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3753", MODE="660", GROUP="plugdev", TAG+="uaccess"
# Cypress KitProg in KitProg mode
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="f139", MODE="660", GROUP="plugdev", TAG+="uaccess"
diff --git a/src/helper/bits.h b/src/helper/bits.h
new file mode 100644
index 0000000..988dde0
--- /dev/null
+++ b/src/helper/bits.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author(s): Antonio Borneo <borneo.antonio@gmail.com> for STMicroelectronics
+ *
+ * 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/>.
+ */
+
+/*
+ * The content of this file is mainly copied/inspired from Linux kernel
+ * code in include/linux/types.h include/linux/bitmap.h include/linux/bitops.h
+ */
+
+#ifndef OPENOCD_HELPER_BITS_H
+#define OPENOCD_HELPER_BITS_H
+
+#include <helper/types.h>
+
+#define BIT(nr) (1UL << (nr))
+#define BITS_PER_BYTE 8
+#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
+#define DECLARE_BITMAP(name, bits) unsigned long name[BITS_TO_LONGS(bits)]
+
+/**
+ * bitmap_zero - Clears all the bits in memory
+ * @dst: the address of the bitmap
+ * @nbits: the number of bits to clear
+ */
+static inline void bitmap_zero(unsigned long *dst, unsigned int nbits)
+{
+ if (nbits <= BITS_PER_LONG)
+ *dst = 0UL;
+ else {
+ unsigned int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+ memset(dst, 0, len);
+ }
+}
+
+/**
+ * clear_bit - Clear a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ */
+static inline void clear_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p &= ~mask;
+}
+
+/**
+ * set_bit - Set a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ */
+static inline void set_bit(int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
+
+ *p |= mask;
+}
+
+/**
+ * test_bit - Determine whether a bit is set
+ * @nr: bit number to test
+ * @addr: Address to start counting from
+ */
+static inline int test_bit(int nr, const volatile unsigned long *addr)
+{
+ return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
+}
+
+#endif /* OPENOCD_HELPER_BITS_H */
diff --git a/src/helper/log.h b/src/helper/log.h
index d60587f..751ea17 100644
--- a/src/helper/log.h
+++ b/src/helper/log.h
@@ -151,6 +151,7 @@ extern int debug_level;
#define ERROR_WAIT (-5)
/* ERROR_TIMEOUT is already taken by winerror.h. */
#define ERROR_TIMEOUT_REACHED (-6)
+#define ERROR_OP_NOT_SUPPORTED (-7)
#endif /* OPENOCD_HELPER_LOG_H */
diff --git a/src/jtag/adapter.c b/src/jtag/adapter.c
index 2035788..d62c9a1 100644
--- a/src/jtag/adapter.c
+++ b/src/jtag/adapter.c
@@ -31,6 +31,7 @@
#endif
#include "jtag.h"
+#include "swd.h"
#include "minidriver.h"
#include "interface.h"
#include "interfaces.h"
@@ -47,6 +48,26 @@
extern struct jtag_interface *jtag_interface;
const char * const jtag_only[] = { "jtag", NULL };
+static const Jim_Nvp nvp_assert[] = {
+ { .name = "assert", NVP_ASSERT },
+ { .name = "deassert", NVP_DEASSERT },
+ { .name = "T", NVP_ASSERT },
+ { .name = "F", NVP_DEASSERT },
+ { .name = "t", NVP_ASSERT },
+ { .name = "f", NVP_DEASSERT },
+ { .name = NULL, .value = -1 }
+};
+
+#define RESET_TEST_INDEPENDENT_TRST -1
+static const Jim_Nvp nvp_reset_config_includes[] = {
+ { .name = "srst", RESET_HAS_SRST },
+ { .name = "trst", RESET_HAS_TRST },
+ { .name = "srst_pulls_trst_or_combined", RESET_SRST_PULLS_TRST },
+ { .name = "trst_pulls_srst_or_combined", RESET_TRST_PULLS_SRST },
+ { .name = "srst_nogate", RESET_SRST_NO_GATING },
+ { .name = "connect_assert_srst", RESET_CNCT_UNDER_SRST },
+ { .name = "independent_trst", RESET_TEST_INDEPENDENT_TRST },
+};
static int jim_adapter_name(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
{
@@ -400,6 +421,42 @@ next:
return ERROR_OK;
}
+static int jim_reset_config_includes(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
+{
+ Jim_GetOptInfo goi;
+ Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+
+ if (goi.argc < 1) {
+ Jim_WrongNumArgs(interp, 1, argv, "[srst_any][trst_any]...");
+ return JIM_ERR;
+ }
+
+ int cfg = jtag_get_reset_config();
+ bool result = true;
+
+ while (goi.argc) {
+ Jim_Nvp *n;
+ int e = Jim_GetOpt_Nvp(&goi, nvp_reset_config_includes, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(&goi, nvp_reset_config_includes, 1);
+ return e;
+ }
+
+ if (n->value == RESET_TEST_INDEPENDENT_TRST) {
+ bool indep_trst = cfg & RESET_HAS_TRST;
+ if (cfg & RESET_HAS_SRST && cfg & RESET_SRST_PULLS_TRST)
+ indep_trst = false;
+
+ result = result && indep_trst;
+ } else {
+ result = result && cfg & n->value;
+ }
+ }
+
+ Jim_SetResultString(interp, result ? "1" : "0", -1);
+ return JIM_OK;
+}
+
COMMAND_HANDLER(handle_adapter_nsrst_delay_command)
{
if (CMD_ARGC > 1)
@@ -456,6 +513,57 @@ COMMAND_HANDLER(handle_adapter_khz_command)
return retval;
}
+static int jim_arp_init_reset(Jim_Interp *interp, int argc, Jim_Obj*const *argv)
+{
+ int e = ERROR_OK;
+ Jim_GetOptInfo goi;
+ Jim_GetOpt_Setup(&goi, interp, argc-1, argv + 1);
+ if (goi.argc != 0) {
+ Jim_WrongNumArgs(goi.interp, 1, goi.argv-1, "(no params)");
+ return JIM_ERR;
+ }
+ struct command_context *context = current_command_context(interp);
+ if (transport_is_jtag() || transport_is_stlink_jtag())
+ e = jtag_init_reset(context);
+ else if (transport_is_swd() || transport_is_stlink_swd())
+ e = swd_init_reset(context);
+
+ if (e != ERROR_OK) {
+ Jim_Obj *eObj = Jim_NewIntObj(goi.interp, e);
+ Jim_SetResultFormatted(goi.interp, "error: %#s", eObj);
+ Jim_FreeNewObj(goi.interp, eObj);
+ return JIM_ERR;
+ }
+ return JIM_OK;
+}
+
+static int jim_arp_adapter_reset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ Jim_GetOptInfo goi;
+ Jim_GetOpt_Setup(&goi, interp, argc - 1, argv + 1);
+
+ if (goi.argc != 1) {
+ Jim_WrongNumArgs(interp, 0, argv, "([tT]|[fF]|assert|deassert)");
+ return JIM_ERR;
+ }
+
+ Jim_Nvp *n;
+ int e = Jim_GetOpt_Nvp(&goi, nvp_assert, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(&goi, nvp_assert, 1);
+ return e;
+ }
+
+ if (jtag_get_reset_config() & RESET_HAS_SRST) {
+ if (n->value == NVP_ASSERT)
+ adapter_assert_reset();
+ else
+ adapter_deassert_reset();
+ }
+
+ return JIM_OK;
+}
+
static const struct command_registration interface_command_handlers[] = {
{
.name = "adapter_khz",
@@ -509,6 +617,21 @@ static const struct command_registration interface_command_handlers[] = {
.help = "List all built-in debug adapter interfaces (drivers)",
},
{
+ .name = "arp_init_reset",
+ .mode = COMMAND_ANY,
+ .jim_handler = jim_arp_init_reset,
+ .help = "Uses TRST and SRST to try resetting everything on the JTAG scan chain."
+ " If SWD transport is selected command uses SRST only."
+ " SRST is left asserted if 'reset_config srst_no_gating'."
+ },
+ {
+ .name = "arp_adapter_reset",
+ .mode = COMMAND_ANY,
+ .jim_handler = jim_arp_adapter_reset,
+ .help = "Controls SRST line.",
+ .usage = "[assert|deassert]"
+ },
+ {
.name = "reset_config",
.handler = handle_reset_config_command,
.mode = COMMAND_ANY,
@@ -520,6 +643,15 @@ static const struct command_registration interface_command_handlers[] = {
"[srst_push_pull|srst_open_drain] "
"[connect_deassert_srst|connect_assert_srst]",
},
+ {
+ .name = "reset_config_includes",
+ .jim_handler = jim_reset_config_includes,
+ .mode = COMMAND_ANY,
+ .help = "Test adapter reset configuration",
+ .usage = "[trst] [srst] [srst_nogate] [connect_assert_srst]"
+ " [srst_pulls_trst_or_combined] [trst_pulls_srst_or_combined]"
+ " [independent_trst]"
+ },
COMMAND_REGISTRATION_DONE
};
diff --git a/src/jtag/core.c b/src/jtag/core.c
index f90ae99..e11a633 100644
--- a/src/jtag/core.c
+++ b/src/jtag/core.c
@@ -160,7 +160,7 @@ bool is_jtag_poll_safe(void)
* It is also implicitly disabled while TRST is active and
* while SRST is gating the JTAG clock.
*/
- if (!transport_is_jtag())
+ if (!transport_is_jtag() && !transport_is_stlink_jtag())
return jtag_poll;
if (!jtag_poll || jtag_trst != 0)
@@ -640,11 +640,11 @@ void swd_add_reset(int req_srst)
/* SRST resets everything hooked up to that signal */
jtag_srst = req_srst;
if (jtag_srst) {
- LOG_DEBUG("SRST line asserted");
+ LOG_INFO("SRST line asserted");
if (adapter_nsrst_assert_width)
jtag_add_sleep(adapter_nsrst_assert_width * 1000);
} else {
- LOG_DEBUG("SRST line released");
+ LOG_INFO("SRST line released");
if (adapter_nsrst_delay)
jtag_add_sleep(adapter_nsrst_delay * 1000);
}
@@ -722,11 +722,11 @@ void jtag_add_reset(int req_tlr_or_trst, int req_srst)
if (jtag_srst != new_srst) {
jtag_srst = new_srst;
if (jtag_srst) {
- LOG_DEBUG("SRST line asserted");
+ LOG_INFO("SRST line asserted");
if (adapter_nsrst_assert_width)
jtag_add_sleep(adapter_nsrst_assert_width * 1000);
} else {
- LOG_DEBUG("SRST line released");
+ LOG_INFO("SRST line released");
if (adapter_nsrst_delay)
jtag_add_sleep(adapter_nsrst_delay * 1000);
}
@@ -1404,7 +1404,7 @@ int jtag_init_inner(struct command_context *cmd_ctx)
int retval;
bool issue_setup = true;
- LOG_DEBUG("Init JTAG chain");
+ LOG_INFO("Init JTAG chain");
tap = jtag_tap_next_enabled(NULL);
if (tap == NULL) {
@@ -1505,9 +1505,10 @@ int swd_init_reset(struct command_context *cmd_ctx)
LOG_DEBUG("Initializing with hard SRST reset");
- if (jtag_reset_config & RESET_HAS_SRST)
+ if ((jtag_reset_config & (RESET_HAS_SRST|RESET_SRST_NO_GATING)) == (RESET_HAS_SRST|RESET_SRST_NO_GATING))
swd_add_reset(1);
- swd_add_reset(0);
+ /* leave SRST asserted if SWD trasport is usable in reset state */
+
retval = jtag_execute_queue();
return retval;
}
@@ -1544,28 +1545,30 @@ int jtag_init_reset(struct command_context *cmd_ctx)
* REVISIT once Tcl code can read the reset_config modes, this won't
* need to be a C routine at all...
*/
- if (jtag_reset_config & RESET_HAS_SRST) {
+ if ((jtag_reset_config & RESET_HAS_SRST)
+ && (jtag_reset_config & RESET_SRST_NO_GATING)) {
jtag_add_reset(1, 1);
- if ((jtag_reset_config & RESET_SRST_PULLS_TRST) == 0)
- jtag_add_reset(0, 1);
} else {
jtag_add_reset(1, 0); /* TAP_RESET, using TMS+TCK or TRST */
}
/* some targets enable us to connect with srst asserted */
- if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
- if (jtag_reset_config & RESET_SRST_NO_GATING)
- jtag_add_reset(0, 1);
- else {
+ if (jtag_reset_config & RESET_SRST_NO_GATING)
+ jtag_add_reset(0, 1);
+ else {
+ if (jtag_reset_config & RESET_CNCT_UNDER_SRST)
LOG_WARNING("\'srst_nogate\' reset_config option is required");
- jtag_add_reset(0, 0);
- }
- } else
jtag_add_reset(0, 0);
+ }
+
retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
+ /* ST-Link cannot scan the JTAG chain */
+ if (transport_is_stlink_jtag())
+ return ERROR_OK;
+
/* Check that we can communication on the JTAG chain + eventually we want to
* be able to perform enumeration only after OpenOCD has started
* telnet and GDB server
@@ -1816,30 +1819,33 @@ bool transport_is_jtag(void)
void adapter_assert_reset(void)
{
- if (transport_is_jtag()) {
+ if (transport_is_jtag() || transport_is_stlink_jtag()) {
if (jtag_reset_config & RESET_SRST_PULLS_TRST)
jtag_add_reset(1, 1);
else
jtag_add_reset(0, 1);
- } else if (transport_is_swd())
+ } else if (transport_is_swd() || transport_is_stlink_swd()) {
swd_add_reset(1);
- else if (get_current_transport() != NULL)
- LOG_ERROR("reset is not supported on %s",
- get_current_transport()->name);
- else
+ } else if (get_current_transport() != NULL) {
+ const char *name = get_current_transport()->name;
+ /* hla transports control srst through target assert_reset
+ * and deassert_reset calls so no action here
+ * Show error for other transports */
+ if (strcmp(name, "hla_swd") && strcmp(name, "hla_jtag")
+ && strcmp(name, "stlink_swim"))
+ LOG_ERROR("reset is not supported on %s", name);
+ } else {
LOG_ERROR("transport is not selected");
+ }
}
void adapter_deassert_reset(void)
{
- if (transport_is_jtag())
+ if (transport_is_jtag() || transport_is_stlink_jtag())
jtag_add_reset(0, 0);
- else if (transport_is_swd())
+ else if (transport_is_swd() || transport_is_stlink_swd())
swd_add_reset(0);
- else if (get_current_transport() != NULL)
- LOG_ERROR("reset is not supported on %s",
- get_current_transport()->name);
- else
+ else if (get_current_transport() == NULL)
LOG_ERROR("transport is not selected");
}
diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c
index d9ca53e..f55a608 100644
--- a/src/jtag/drivers/stlink_usb.c
+++ b/src/jtag/drivers/stlink_usb.c
@@ -30,17 +30,21 @@
#endif
/* project specific includes */
+#include <helper/bits.h>
#include <helper/binarybuffer.h>
#include <jtag/interface.h>
#include <jtag/hla/hla_layout.h>
#include <jtag/hla/hla_transport.h>
#include <jtag/hla/hla_interface.h>
+#include <jtag/swd.h>
#include <target/target.h>
#include <target/cortex_m.h>
#include "libusb_common.h"
+#define HLA_DEFAULT_APNUN (0)
+
#define ENDPOINT_IN 0x80
#define ENDPOINT_OUT 0x00
@@ -60,13 +64,32 @@
#define STLINK_CMD_SIZE_V2 (16)
#define STLINK_CMD_SIZE_V1 (10)
+#define STLINK_VID (0x0483)
#define STLINK_V1_PID (0x3744)
#define STLINK_V2_PID (0x3748)
#define STLINK_V2_1_PID (0x374B)
+#define STLINK_V2_1_NO_MSD_PID (0x3752)
+#define STLINK_V3_DFU_PID (0x374D)
+#define STLINK_V3E_PID (0x374E)
+#define STLINK_V3S_PID (0x374F)
+#define STLINK_V3_2VCP_PID (0x3753)
/* the current implementation of the stlink limits
- * 8bit read/writes to max 64 bytes. */
-#define STLINK_MAX_RW8 (64)
+ * 8bit read/writes to max 64 bytes.
+ * The limit is 512 bytes with stlink v3 on usb 2.0 high speed */
+/*
+ * ST-Link/V1, ST-Link/V2 and ST-Link/V2.1 are full-speed USB devices and
+ * this limits the bulk packet size and the 8bit read/writes to max 64 bytes.
+ * ST-Link/V3 is a high speed USB 2.0 and the limit is 512 bytes.
+ */
+#define STLINKV2_MAX_RW8 (64)
+#define STLINKV3_MAX_RW8 (512)
+
+/*
+ * ARM IHI 0031E: TAR Automatic address increment is only guaranteed to
+ * operate on the 10 least significant bits of the address
+ */
+#define TAR_AUTOINCR_BLOCK (1 << 10)
/* "WAIT" responses will be retried (with exponential backoff) at
* most this many times before failing to caller.
@@ -76,6 +99,7 @@
enum stlink_jtag_api_version {
STLINK_JTAG_API_V1 = 1,
STLINK_JTAG_API_V2,
+ STLINK_JTAG_API_V3,
};
/** */
@@ -134,35 +158,57 @@ struct stlink_usb_handle_s {
bool reconnect_pending;
};
+/* status codes */
#define STLINK_SWIM_ERR_OK 0x00
#define STLINK_SWIM_BUSY 0x01
#define STLINK_DEBUG_ERR_OK 0x80
#define STLINK_DEBUG_ERR_FAULT 0x81
+
+#define STLINK_JTAG_SPI_ERROR 0x02
+#define STLINK_JTAG_DMA_ERROR 0x03
+#define STLINK_JTAG_UNKNOWN_JTAG_CHAIN 0x04
+#define STLINK_JTAG_NO_DEVICE_CONNECTED 0x05
+#define STLINK_JTAG_INTERNAL_ERROR 0x06
+#define STLINK_JTAG_CMD_WAIT 0x07
+#define STLINK_JTAG_CMD_ERROR 0x08
+#define STLINK_JTAG_GET_IDCODE_ERROR 0x09
+#define STLINK_JTAG_ALIGNMENT_ERROR 0x0A
+#define STLINK_JTAG_DBG_POWER_ERROR 0x0B
+#define STLINK_JTAG_WRITE_ERROR 0x0C
+#define STLINK_JTAG_WRITE_VERIF_ERROR 0x0D
+#define STLINK_JTAG_ALREADY_OPENED_IN_OTHER_MODE 0x0E
+
#define STLINK_SWD_AP_WAIT 0x10
#define STLINK_SWD_AP_FAULT 0x11
#define STLINK_SWD_AP_ERROR 0x12
#define STLINK_SWD_AP_PARITY_ERROR 0x13
-#define STLINK_JTAG_WRITE_ERROR 0x0c
-#define STLINK_JTAG_WRITE_VERIF_ERROR 0x0d
#define STLINK_SWD_DP_WAIT 0x14
#define STLINK_SWD_DP_FAULT 0x15
#define STLINK_SWD_DP_ERROR 0x16
#define STLINK_SWD_DP_PARITY_ERROR 0x17
-
#define STLINK_SWD_AP_WDATA_ERROR 0x18
#define STLINK_SWD_AP_STICKY_ERROR 0x19
-#define STLINK_SWD_AP_STICKYORUN_ERROR 0x1a
+#define STLINK_SWD_AP_STICKYORUN_ERROR 0x1A
+#define STLINK_AP_ALREADY_USED 0x1B
+#define STLINK_TRACE_AP_TURNAROUND 0x1C
+#define STLINK_BAD_AP 0x1D
+#define STLINK_SWV_NOT_AVAILABLE 0x20
+#define STLINK_NO_JUMP_TO_USB_LOADER 0x21
+#define STLINK_JTAG_TCPID_NOT_FOUND 0x30
+#define STLINK_JTAG_TCPID_MAX_REACHED 0x31
#define STLINK_CORE_RUNNING 0x80
#define STLINK_CORE_HALTED 0x81
#define STLINK_CORE_STAT_UNKNOWN -1
+/* stlink commands */
#define STLINK_GET_VERSION 0xF1
#define STLINK_DEBUG_COMMAND 0xF2
#define STLINK_DFU_COMMAND 0xF3
#define STLINK_SWIM_COMMAND 0xF4
#define STLINK_GET_CURRENT_MODE 0xF5
#define STLINK_GET_TARGET_VOLTAGE 0xF7
+#define STLINK_APIV3_GET_VERSION_EX 0xFB
#define STLINK_DEV_DFU_MODE 0x00
#define STLINK_DEV_MASS_MODE 0x01
@@ -204,7 +250,6 @@ struct stlink_usb_handle_s {
#define STLINK_SWIM_READMEM 0x0b
#define STLINK_SWIM_READBUF 0x0c
-#define STLINK_DEBUG_ENTER_JTAG 0x00
#define STLINK_DEBUG_GETSTATUS 0x01
#define STLINK_DEBUG_FORCEDEBUG 0x02
#define STLINK_DEBUG_APIV1_RESETSYS 0x03
@@ -224,6 +269,7 @@ struct stlink_usb_handle_s {
#define STLINK_DEBUG_ENTER_JTAG 0x00
#define STLINK_DEBUG_ENTER_SWD 0xa3
+#define STLINK_ENTER_JTAG_NO_CORE_RESET 0xA4
#define STLINK_DEBUG_APIV1_ENTER 0x20
#define STLINK_DEBUG_EXIT 0x21
@@ -236,24 +282,55 @@ struct stlink_usb_handle_s {
#define STLINK_DEBUG_APIV2_WRITEREG 0x34
#define STLINK_DEBUG_APIV2_WRITEDEBUGREG 0x35
#define STLINK_DEBUG_APIV2_READDEBUGREG 0x36
-
+#define STLINK_DEBUG_APIV2_SETWATCHPOINT2 0x37
+#define STLINK_DEBUG_APIV2_SETFP2 0x38
+#define STLINK_DEBUG_APIV2_CLEARFP2 0x39
#define STLINK_DEBUG_APIV2_READALLREGS 0x3A
#define STLINK_DEBUG_APIV2_GETLASTRWSTATUS 0x3B
#define STLINK_DEBUG_APIV2_DRIVE_NRST 0x3C
+#define STLINK_DEBUG_APIV2_READFPUREGS 0x3D
+#define STLINK_DEBUG_APIV3_GETLASTRWSTATUS 0x3E
#define STLINK_DEBUG_APIV2_START_TRACE_RX 0x40
#define STLINK_DEBUG_APIV2_STOP_TRACE_RX 0x41
#define STLINK_DEBUG_APIV2_GET_TRACE_NB 0x42
#define STLINK_DEBUG_APIV2_SWD_SET_FREQ 0x43
+#define STLINK_DEBUG_APIV2_JTAG_SET_FREQ 0x44
+#define STLINK_JTAG_READ_DAP_REG 0x45
+#define STLINK_JTAG_WRITE_DAP_REG 0x46
+#define STLINK_DEBUG_APIV2_READMEM_16BIT 0x47
+#define STLINK_DEBUG_APIV2_WRITEMEM_16BIT 0x48
+#define STLINK_DEBUG_APIV2_BLINK_LED 0x49
+#define STLINK_DEBUG_APIV2_GET_DISK_NAME 0x4A
+#define STLINK_DEBUG_APIV2_INIT_AP 0x4B
+#define STLINK_DEBUG_APIV2_CLOSE_AP_DBG 0x4C
+
+#define STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC 0x50
+#define STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC 0x54
+
+#define STLINK_APIV3_SET_COM_FREQ 0x61
+#define STLINK_APIV3_GET_COM_FREQ 0x62
+#define STLINK_APIV3_SWITCH_STLINK_FREQ 0x63
+
+/* parameters */
#define STLINK_DEBUG_APIV2_DRIVE_NRST_LOW 0x00
#define STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH 0x01
#define STLINK_DEBUG_APIV2_DRIVE_NRST_PULSE 0x02
+#define STLINK_DEBUG_FLASHPATCH_LOWER 0
+#define STLINK_DEBUG_FLASHPATCH_UPPER 1
+#define STLINK_DEBUG_FLASHPATCH_ALL 2
+
+#define JTAG_AP_CORTEXM_CORE 1
+#define JTAG_AP_NO_CORE 0
+
#define STLINK_TRACE_SIZE 4096
#define STLINK_TRACE_MAX_HZ 2000000
#define STLINK_TRACE_MIN_VERSION 13
+#define STLINK_V3_MAX_FREQ_NB 10
+
/** */
enum stlink_mode {
STLINK_MODE_UNKNOWN = 0,
@@ -267,10 +344,13 @@ enum stlink_mode {
#define REQUEST_SENSE 0x03
#define REQUEST_SENSE_LENGTH 18
-static const struct {
+struct speed_map {
int speed;
int speed_divisor;
-} stlink_khz_to_speed_map[] = {
+};
+
+/* SWD clock speed */
+static const struct speed_map stlink_khz_to_speed_map[] = {
{4000, 0},
{1800, 1}, /* default */
{1200, 2},
@@ -285,8 +365,37 @@ static const struct {
{5, 798}
};
+/* JTAG clock speed */
+static const struct speed_map stlink_khz_to_speed_map_jtag[] = {
+ {18000, 2},
+ {9000, 4},
+ {4500, 8},
+ {2250, 16},
+ {1125, 32}, /* default */
+ {562, 64},
+ {281, 128},
+ {140, 256}
+};
+
static void stlink_usb_init_buffer(void *handle, uint8_t direction, uint32_t size);
static int stlink_swim_status(void *handle);
+static int stlink_usb_reset(void *handle);
+
+
+/** */
+__attribute__((unused))
+static unsigned int stlink_usb_block(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ if (h->version.stlink == 3)
+ return STLINKV3_MAX_RW8;
+ else
+ return STLINKV2_MAX_RW8;
+}
+
/** */
static int stlink_usb_xfer_v1_get_status(void *handle)
@@ -453,6 +562,36 @@ static int stlink_usb_error_check(void *handle)
case STLINK_SWD_DP_WAIT:
LOG_DEBUG("wait status SWD_DP_WAIT (0x%x)", STLINK_SWD_DP_WAIT);
return ERROR_WAIT;
+ case STLINK_JTAG_SPI_ERROR:
+ LOG_DEBUG("JTAG_INTERNAL_ERROR (SPI)");
+ return ERROR_FAIL;
+ case STLINK_JTAG_DMA_ERROR:
+ LOG_DEBUG("JTAG_INTERNAL_ERROR (DMA)");
+ return ERROR_FAIL;
+ case STLINK_JTAG_UNKNOWN_JTAG_CHAIN:
+ LOG_DEBUG("UNKNOWN_JTAG_CHAIN");
+ return ERROR_FAIL;
+ case STLINK_JTAG_NO_DEVICE_CONNECTED:
+ LOG_DEBUG("NO_DEVICE_CONNECTED");
+ return ERROR_FAIL;
+ case STLINK_JTAG_INTERNAL_ERROR:
+ LOG_DEBUG("JTAG_INTERNAL_ERROR");
+ return ERROR_FAIL;
+ case STLINK_JTAG_CMD_WAIT:
+ LOG_DEBUG("wait status STLINK_JTAG_CMD_WAIT");
+ return ERROR_WAIT;
+ case STLINK_JTAG_CMD_ERROR:
+ LOG_DEBUG("JTAG_CMD_ERROR");
+ return ERROR_FAIL;
+ case STLINK_JTAG_GET_IDCODE_ERROR:
+ LOG_DEBUG("JTAG_GET_IDCODE_ERROR");
+ return ERROR_FAIL;
+ case STLINK_JTAG_ALIGNMENT_ERROR:
+ LOG_DEBUG("JTAG_ALIGNMENT_ERROR");
+ return ERROR_FAIL;
+ case STLINK_JTAG_DBG_POWER_ERROR:
+ LOG_DEBUG("JTAG_DBG_POWER_ERROR");
+ return ERROR_FAIL;
case STLINK_JTAG_WRITE_ERROR:
LOG_DEBUG("Write error");
return ERROR_FAIL;
@@ -466,7 +605,7 @@ static int stlink_usb_error_check(void *handle)
* This fix allows CDT plugin to visualize memory.
*/
LOG_DEBUG("STLINK_SWD_AP_FAULT");
- return ERROR_FAIL;
+ return ERROR_OK;
case STLINK_SWD_AP_ERROR:
LOG_DEBUG("STLINK_SWD_AP_ERROR");
return ERROR_FAIL;
@@ -487,10 +626,25 @@ static int stlink_usb_error_check(void *handle)
return ERROR_FAIL;
case STLINK_SWD_AP_STICKY_ERROR:
LOG_DEBUG("STLINK_SWD_AP_STICKY_ERROR");
+ /* Seen when reading address out of range (0xFFFFFFFF) */
+ /* Seems usb_reset helps the following r/w accesses */
+ stlink_usb_reset(h);
return ERROR_FAIL;
case STLINK_SWD_AP_STICKYORUN_ERROR:
LOG_DEBUG("STLINK_SWD_AP_STICKYORUN_ERROR");
return ERROR_FAIL;
+ case STLINK_JTAG_ALREADY_OPENED_IN_OTHER_MODE:
+ LOG_DEBUG("JTAG_ALREADY_OPENED_IN_OTHER_MODE");
+ return ERROR_FAIL;
+ case STLINK_AP_ALREADY_USED:
+ LOG_DEBUG("AP_ALREADY_USED");
+ return ERROR_FAIL;
+ case STLINK_TRACE_AP_TURNAROUND:
+ LOG_DEBUG("TRACE_AP_TURNAROUND");
+ return ERROR_FAIL;
+ case STLINK_BAD_AP:
+ LOG_DEBUG("BAD_AP");
+ return ERROR_FAIL;
default:
LOG_DEBUG("unknown/unexpected STLINK status code 0x%x", h->databuf[0]);
return ERROR_FAIL;
@@ -603,40 +757,62 @@ static int stlink_usb_version(void *handle)
{
int res;
uint16_t v;
+ uint8_t m_version = 0;
struct stlink_usb_handle_s *h = handle;
assert(handle != NULL);
- stlink_usb_init_buffer(handle, h->rx_ep, 6);
+ /* FIXME: don't use PID, but first old command to get V3, then new command */
+ if (h->pid == STLINK_V3E_PID || h->pid == STLINK_V3S_PID || h->pid == STLINK_V3_2VCP_PID) {
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_VERSION_EX;
+ res = stlink_usb_xfer(handle, h->databuf, 12);
- h->cmdbuf[h->cmdidx++] = STLINK_GET_VERSION;
+ if (res != ERROR_OK)
+ return res;
- res = stlink_usb_xfer(handle, h->databuf, 6);
+ h->version.stlink = h->databuf[0];
+ h->version.swim = h->databuf[1];
+ h->version.jtag = h->databuf[2];
+ m_version = h->databuf[3];
- if (res != ERROR_OK)
- return res;
+ h->vid = (h->databuf[9] << 8) | h->databuf[8];
+ h->pid = (h->databuf[11] << 8) | h->databuf[10];
- v = (h->databuf[0] << 8) | h->databuf[1];
+ h->version.jtag_api_max = STLINK_JTAG_API_V3;
+ } else {
+ stlink_usb_init_buffer(handle, h->rx_ep, 6);
+ h->cmdbuf[h->cmdidx++] = STLINK_GET_VERSION;
- h->version.stlink = (v >> 12) & 0x0f;
- h->version.jtag = (v >> 6) & 0x3f;
- h->version.swim = v & 0x3f;
- h->vid = buf_get_u32(h->databuf, 16, 16);
- h->pid = buf_get_u32(h->databuf, 32, 16);
+ res = stlink_usb_xfer(handle, h->databuf, 6);
- /* set the supported jtag api version
- * API V2 is supported since JTAG V11
- */
- if (h->version.jtag >= 11)
- h->version.jtag_api_max = STLINK_JTAG_API_V2;
- else
- h->version.jtag_api_max = STLINK_JTAG_API_V1;
+ if (res != ERROR_OK)
+ return res;
+
+ v = (h->databuf[0] << 8) | h->databuf[1];
+
+ h->version.stlink = (v >> 12) & 0x0f;
+ h->version.jtag = (v >> 6) & 0x3f;
+ h->version.swim = v & 0x3f;
+ h->vid = buf_get_u32(h->databuf, 16, 16);
+ h->pid = buf_get_u32(h->databuf, 32, 16);
+
+ /* set the supported jtag api version
+ * API V2 is supported since JTAG V11
+ */
+ if (h->version.jtag >= 11)
+ h->version.jtag_api_max = STLINK_JTAG_API_V2;
+ else
+ h->version.jtag_api_max = STLINK_JTAG_API_V1;
+ }
- LOG_INFO("STLINK v%d JTAG v%d API v%d SWIM v%d VID 0x%04X PID 0x%04X",
+ LOG_INFO("STLINK v%d%s JTAG v%d API v%d %s%d VID 0x%04X PID 0x%04X",
h->version.stlink,
+ (h->pid == STLINK_V2_1_PID) ? ".1" : "",
h->version.jtag,
- (h->version.jtag_api_max == STLINK_JTAG_API_V1) ? 1 : 2,
- h->version.swim,
+ h->version.jtag_api_max,
+ (h->pid == STLINK_V2_1_PID || (h->version.stlink == 3 && h->version.swim == 0)) ? "M" : "SWIM v",
+ (h->version.stlink == 3 && h->version.swim == 0) ? m_version : h->version.swim,
h->vid,
h->pid);
@@ -648,8 +824,8 @@ static int stlink_usb_check_voltage(void *handle, float *target_voltage)
struct stlink_usb_handle_s *h = handle;
uint32_t adc_results[2];
- /* only supported by stlink/v2 and for firmware >= 13 */
- if (h->version.stlink == 1 || h->version.jtag < 13)
+ /* only supported by stlink v2/v3 and for firmware >= 13 */
+ if (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 13))
return ERROR_COMMAND_NOTFOUND;
stlink_usb_init_buffer(handle, h->rx_ep, 8);
@@ -682,7 +858,8 @@ static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor)
assert(handle != NULL);
/* only supported by stlink/v2 and for firmware >= 22 */
- if (h->version.stlink == 1 || h->version.jtag < 22)
+ if (h->version.stlink == 1 || h->version.stlink == 3 || \
+ (h->version.stlink == 2 && h->version.jtag < 22))
return ERROR_COMMAND_NOTFOUND;
stlink_usb_init_buffer(handle, h->rx_ep, 2);
@@ -700,6 +877,32 @@ static int stlink_usb_set_swdclk(void *handle, uint16_t clk_divisor)
return ERROR_OK;
}
+static int stlink_usb_set_jtagclk(void *handle, uint16_t clk_divisor)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* only supported by stlink/v2 and for firmware >= 24 */
+ if (h->version.stlink == 1 || h->version.stlink == 3 || \
+ (h->version.stlink == 2 && h->version.jtag < 24))
+ return ERROR_COMMAND_NOTFOUND;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 2);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_JTAG_SET_FREQ;
+ h_u16_to_le(h->cmdbuf+h->cmdidx, clk_divisor);
+ h->cmdidx += 2;
+
+ int result = stlink_cmd_allow_retry(handle, h->databuf, 2);
+
+ if (result != ERROR_OK)
+ return result;
+
+ return ERROR_OK;
+}
+
/** */
static int stlink_usb_current_mode(void *handle, uint8_t *mode)
{
@@ -734,21 +937,23 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
* status
* TODO: we need the test on api V1 too
*/
- if (h->jtag_api == STLINK_JTAG_API_V2)
+ if (h->jtag_api == STLINK_JTAG_API_V2 || h->jtag_api == STLINK_JTAG_API_V3)
rx_size = 2;
stlink_usb_init_buffer(handle, h->rx_ep, rx_size);
switch (type) {
case STLINK_MODE_DEBUG_JTAG:
+ LOG_DEBUG("stlink_usb_mode_enter(JTAG)");
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
if (h->jtag_api == STLINK_JTAG_API_V1)
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_ENTER;
else
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_ENTER;
- h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_JTAG;
+ h->cmdbuf[h->cmdidx++] = STLINK_ENTER_JTAG_NO_CORE_RESET;
break;
case STLINK_MODE_DEBUG_SWD:
+ LOG_DEBUG("stlink_usb_mode_enter(SWD)");
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
if (h->jtag_api == STLINK_JTAG_API_V1)
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV1_ENTER;
@@ -757,6 +962,7 @@ static int stlink_usb_mode_enter(void *handle, enum stlink_mode type)
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_ENTER_SWD;
break;
case STLINK_MODE_DEBUG_SWIM:
+ LOG_DEBUG("stlink_usb_mode_enter(SWIM)");
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_COMMAND;
h->cmdbuf[h->cmdidx++] = STLINK_SWIM_ENTER;
/* no answer for this function... */
@@ -825,12 +1031,11 @@ static enum stlink_mode stlink_get_mode(enum hl_transports t)
}
/** */
-static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
+static int stlink_exit_mode (void *handle)
{
int res;
uint8_t mode;
enum stlink_mode emode;
- struct stlink_usb_handle_s *h = handle;
assert(handle != NULL);
@@ -860,17 +1065,35 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
}
if (emode != STLINK_MODE_UNKNOWN) {
+ LOG_DEBUG("E-MODE: 0x%02X", emode);
res = stlink_usb_mode_leave(handle, emode);
-
if (res != ERROR_OK)
return res;
}
- res = stlink_usb_current_mode(handle, &mode);
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
+{
+ int res;
+ uint8_t mode;
+ enum stlink_mode emode;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ res = stlink_exit_mode(handle);
+ if (res != ERROR_OK)
+ return res;
+ res = stlink_usb_current_mode(handle, &mode);
if (res != ERROR_OK)
return res;
+ LOG_DEBUG("MODE: 0x%02X", mode);
+
/* we check the target voltage here as an aid to debugging connection problems.
* the stlink requires the target Vdd to be connected for reliable debugging.
* this cmd is supported in all modes except DFU
@@ -921,7 +1144,7 @@ static int stlink_usb_init_mode(void *handle, bool connect_under_reset)
return res;
/* assert SRST again: a little bit late but now the adapter knows for sure what pin to use */
- if (connect_under_reset) {
+ if (h->transport == HL_TRANSPORT_SWIM && connect_under_reset) {
res = stlink_usb_assert_srst(handle, 0);
if (res != ERROR_OK)
return res;
@@ -1174,6 +1397,7 @@ static int stlink_usb_v2_read_debug_reg(void *handle, uint32_t addr, uint32_t *v
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READDEBUGREG;
h_u32_to_le(h->cmdbuf+h->cmdidx, addr);
h->cmdidx += 4;
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
res = stlink_cmd_allow_retry(handle, h->databuf, 8);
if (res != ERROR_OK)
@@ -1200,6 +1424,7 @@ static int stlink_usb_write_debug_reg(void *handle, uint32_t addr, uint32_t val)
h->cmdidx += 4;
h_u32_to_le(h->cmdbuf+h->cmdidx, val);
h->cmdidx += 4;
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
return stlink_cmd_allow_retry(handle, h->databuf, 2);
}
@@ -1211,7 +1436,8 @@ static int stlink_usb_trace_read(void *handle, uint8_t *buf, size_t *size)
assert(handle != NULL);
- if (h->trace.enabled && h->version.jtag >= STLINK_TRACE_MIN_VERSION) {
+ if (h->trace.enabled &&
+ (h->version.jtag >= STLINK_TRACE_MIN_VERSION || h->jtag_api == STLINK_JTAG_API_V3)) {
int res;
stlink_usb_init_buffer(handle, h->rx_ep, 10);
@@ -1284,7 +1510,7 @@ static enum target_state stlink_usb_state(void *handle)
h->reconnect_pending = false;
}
- if (h->jtag_api == STLINK_JTAG_API_V2) {
+ if (h->jtag_api == STLINK_JTAG_API_V2 || h->jtag_api == STLINK_JTAG_API_V3) {
res = stlink_usb_v2_get_status(handle);
if (res == TARGET_UNKNOWN)
h->reconnect_pending = true;
@@ -1340,7 +1566,7 @@ static void stlink_usb_trace_disable(void *handle)
assert(handle != NULL);
- assert(h->version.jtag >= STLINK_TRACE_MIN_VERSION);
+ assert(h->version.jtag >= STLINK_TRACE_MIN_VERSION || h->jtag_api == STLINK_JTAG_API_V3);
LOG_DEBUG("Tracing: disable");
@@ -1362,7 +1588,7 @@ static int stlink_usb_trace_enable(void *handle)
assert(handle != NULL);
- if (h->version.jtag >= STLINK_TRACE_MIN_VERSION) {
+ if (h->version.jtag >= STLINK_TRACE_MIN_VERSION || h->jtag_api == STLINK_JTAG_API_V3) {
stlink_usb_init_buffer(handle, h->rx_ep, 10);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
@@ -1426,7 +1652,7 @@ static int stlink_usb_run(void *handle)
assert(handle != NULL);
- if (h->jtag_api == STLINK_JTAG_API_V2) {
+ if (h->jtag_api == STLINK_JTAG_API_V2 || h->jtag_api == STLINK_JTAG_API_V3) {
res = stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_DEBUGEN);
return res;
@@ -1448,7 +1674,7 @@ static int stlink_usb_halt(void *handle)
assert(handle != NULL);
- if (h->jtag_api == STLINK_JTAG_API_V2) {
+ if (h->jtag_api == STLINK_JTAG_API_V2 || h->jtag_api == STLINK_JTAG_API_V3) {
res = stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_DEBUGEN);
return res;
@@ -1469,7 +1695,7 @@ static int stlink_usb_step(void *handle)
assert(handle != NULL);
- if (h->jtag_api == STLINK_JTAG_API_V2) {
+ if (h->jtag_api == STLINK_JTAG_API_V2 || h->jtag_api == STLINK_JTAG_API_V3) {
/* TODO: this emulates the v1 api, it should really use a similar auto mask isr
* that the Cortex-M3 currently does. */
stlink_usb_write_debug_reg(handle, DCB_DHCSR, DBGKEY|C_HALT|C_MASKINTS|C_DEBUGEN);
@@ -1501,6 +1727,9 @@ static int stlink_usb_read_regs(void *handle)
else
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READALLREGS;
+ if (h->jtag_api >= STLINK_JTAG_API_V2 || h->jtag_api == STLINK_JTAG_API_V3)
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
+
res = stlink_usb_xfer(handle, h->databuf, 84);
if (res != ERROR_OK)
@@ -1526,6 +1755,9 @@ static int stlink_usb_read_reg(void *handle, int num, uint32_t *val)
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_READREG;
h->cmdbuf[h->cmdidx++] = num;
+ if ((h->jtag_api == STLINK_JTAG_API_V2 && h->version.jtag >= 28) || h->jtag_api == STLINK_JTAG_API_V3)
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
+
if (h->jtag_api == STLINK_JTAG_API_V1) {
res = stlink_usb_xfer(handle, h->databuf, 4);
if (res != ERROR_OK)
@@ -1559,6 +1791,9 @@ static int stlink_usb_write_reg(void *handle, int num, uint32_t val)
h_u32_to_le(h->cmdbuf+h->cmdidx, val);
h->cmdidx += 4;
+ if ((h->jtag_api == STLINK_JTAG_API_V2 && h->version.jtag >= 28) || h->jtag_api == STLINK_JTAG_API_V3)
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
+
return stlink_cmd_allow_retry(handle, h->databuf, 2);
}
@@ -1572,12 +1807,20 @@ static int stlink_usb_get_rw_status(void *handle)
if (h->jtag_api == STLINK_JTAG_API_V1)
return ERROR_OK;
- stlink_usb_init_buffer(handle, h->rx_ep, 2);
+ stlink_usb_init_buffer(handle, h->rx_ep, 3);
h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
- h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS;
-
- res = stlink_usb_xfer(handle, h->databuf, 2);
+ /* FIXME: this is incorrect */
+ if (h->jtag_api == STLINK_JTAG_API_V3) {
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV3_GETLASTRWSTATUS;
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
+ res = stlink_usb_xfer(handle, h->databuf, 12);
+ } else {
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_GETLASTRWSTATUS;
+ if (h->version.jtag >= 28)
+ h->cmdbuf[h->cmdidx++] = HLA_DEFAULT_APNUN;
+ res = stlink_usb_xfer(handle, h->databuf, 2);
+ }
if (res != ERROR_OK)
return res;
@@ -1586,7 +1829,7 @@ static int stlink_usb_get_rw_status(void *handle)
}
/** */
-static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
+static int stlink_usb_read_mem8(void *handle, uint8_t ap_num, uint32_t addr, uint16_t len,
uint8_t *buffer)
{
int res;
@@ -1595,9 +1838,9 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
assert(handle != NULL);
- /* max 8bit read/write is 64bytes */
- if (len > STLINK_MAX_RW8) {
- LOG_DEBUG("max buffer length exceeded");
+ /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */
+ if (len > stlink_usb_block(h)) {
+ LOG_DEBUG("max buffer (%d) length exceeded", stlink_usb_block(h));
return ERROR_FAIL;
}
@@ -1609,6 +1852,7 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
h->cmdidx += 4;
h_u16_to_le(h->cmdbuf+h->cmdidx, len);
h->cmdidx += 2;
+ h->cmdbuf[h->cmdidx++] = ap_num;
/* we need to fix read length for single bytes */
if (read_len == 1)
@@ -1625,7 +1869,7 @@ static int stlink_usb_read_mem8(void *handle, uint32_t addr, uint16_t len,
}
/** */
-static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
+static int stlink_usb_write_mem8(void *handle, uint8_t ap_num, uint32_t addr, uint16_t len,
const uint8_t *buffer)
{
int res;
@@ -1633,9 +1877,9 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
assert(handle != NULL);
- /* max 8bit read/write is 64bytes */
- if (len > STLINK_MAX_RW8) {
- LOG_DEBUG("max buffer length exceeded");
+ /* max 8 bit read/write is 64 bytes or 512 bytes for v3 */
+ if (len > stlink_usb_block(h)) {
+ LOG_DEBUG("max buffer length (%d) exceeded", stlink_usb_block(h));
return ERROR_FAIL;
}
@@ -1647,6 +1891,7 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
h->cmdidx += 4;
h_u16_to_le(h->cmdbuf+h->cmdidx, len);
h->cmdidx += 2;
+ h->cmdbuf[h->cmdidx++] = ap_num;
res = stlink_usb_xfer(handle, buffer, len);
@@ -1657,7 +1902,7 @@ static int stlink_usb_write_mem8(void *handle, uint32_t addr, uint16_t len,
}
/** */
-static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
+static int stlink_usb_read_mem32(void *handle, uint8_t ap_num, uint32_t addr, uint16_t len,
uint8_t *buffer)
{
int res;
@@ -1679,6 +1924,7 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
h->cmdidx += 4;
h_u16_to_le(h->cmdbuf+h->cmdidx, len);
h->cmdidx += 2;
+ h->cmdbuf[h->cmdidx++] = ap_num;
res = stlink_usb_xfer(handle, h->databuf, len);
@@ -1691,7 +1937,7 @@ static int stlink_usb_read_mem32(void *handle, uint32_t addr, uint16_t len,
}
/** */
-static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
+static int stlink_usb_write_mem32(void *handle, uint8_t ap_num, uint32_t addr, uint16_t len,
const uint8_t *buffer)
{
int res;
@@ -1713,6 +1959,75 @@ static int stlink_usb_write_mem32(void *handle, uint32_t addr, uint16_t len,
h->cmdidx += 4;
h_u16_to_le(h->cmdbuf+h->cmdidx, len);
h->cmdidx += 2;
+ h->cmdbuf[h->cmdidx++] = ap_num;
+
+ res = stlink_usb_xfer(handle, buffer, len);
+
+ if (res != ERROR_OK)
+ return res;
+
+ return stlink_usb_get_rw_status(handle);
+}
+
+/** */
+static int stlink_usb_read_mem32_noaddrinc(void *handle, uint8_t ap_num, uint32_t addr, uint16_t len,
+ uint8_t *buffer)
+{
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* data must be a multiple of 4 and word aligned */
+ if (len % 4 || addr % 4) {
+ LOG_DEBUG("Invalid data alignment");
+ return ERROR_TARGET_UNALIGNED_ACCESS;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, len);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_READMEM_32BIT_NO_ADDR_INC;
+ h_u32_to_le(h->cmdbuf+h->cmdidx, addr);
+ h->cmdidx += 4;
+ h_u16_to_le(h->cmdbuf+h->cmdidx, len);
+ h->cmdidx += 2;
+ h->cmdbuf[h->cmdidx++] = ap_num;
+
+ res = stlink_usb_xfer(handle, h->databuf, len);
+
+ if (res != ERROR_OK)
+ return res;
+
+ memcpy(buffer, h->databuf, len);
+
+ return stlink_usb_get_rw_status(handle);
+}
+
+/** */
+static int stlink_usb_write_mem32_noaddrinc(void *handle, uint8_t ap_num, uint32_t addr, uint16_t len,
+ const uint8_t *buffer)
+{
+ int res;
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* data must be a multiple of 4 and word aligned */
+ if (len % 4 || addr % 4) {
+ LOG_DEBUG("Invalid data alignment");
+ return ERROR_TARGET_UNALIGNED_ACCESS;
+ }
+
+ stlink_usb_init_buffer(handle, h->tx_ep, len);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_WRITEMEM_32BIT_NO_ADDR_INC;
+ h_u32_to_le(h->cmdbuf+h->cmdidx, addr);
+ h->cmdidx += 4;
+ h_u16_to_le(h->cmdbuf+h->cmdidx, len);
+ h->cmdidx += 2;
+ h->cmdbuf[h->cmdidx++] = ap_num;
res = stlink_usb_xfer(handle, buffer, len);
@@ -1730,7 +2045,7 @@ static uint32_t stlink_max_block_size(uint32_t tar_autoincr_block, uint32_t addr
return max_tar_block;
}
-static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
+static int stlink_usb_read_ap_mem(void *handle, uint8_t ap_num, uint32_t addr, uint32_t size,
uint32_t count, uint8_t *buffer)
{
int retval = ERROR_OK;
@@ -1744,7 +2059,7 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
while (count) {
bytes_remaining = (size == 4) ? \
- stlink_max_block_size(h->max_mem_packet, addr) : STLINK_MAX_RW8;
+ stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h);
if (count < bytes_remaining)
bytes_remaining = count;
@@ -1770,7 +2085,7 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
if (addr % 4) {
uint32_t head_bytes = 4 - (addr % 4);
- retval = stlink_usb_read_mem8(handle, addr, head_bytes, buffer);
+ retval = stlink_usb_read_mem8(handle, ap_num, addr, head_bytes, buffer);
if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
usleep((1<<retries++) * 1000);
continue;
@@ -1784,11 +2099,11 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
}
if (bytes_remaining % 4)
- retval = stlink_usb_read_mem(handle, addr, 1, bytes_remaining, buffer);
+ retval = stlink_usb_read_ap_mem(handle, ap_num, addr, 1, bytes_remaining, buffer);
else
- retval = stlink_usb_read_mem32(handle, addr, bytes_remaining, buffer);
+ retval = stlink_usb_read_mem32(handle, ap_num, addr, bytes_remaining, buffer);
} else
- retval = stlink_usb_read_mem8(handle, addr, bytes_remaining, buffer);
+ retval = stlink_usb_read_mem8(handle, ap_num, addr, bytes_remaining, buffer);
if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
usleep((1<<retries++) * 1000);
@@ -1805,7 +2120,13 @@ static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
return retval;
}
-static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
+static int stlink_usb_read_mem(void *handle, uint32_t addr, uint32_t size,
+ uint32_t count, uint8_t *buffer)
+{
+ return stlink_usb_read_ap_mem(handle, HLA_DEFAULT_APNUN, addr, size, count, buffer);
+}
+
+static int stlink_usb_write_ap_mem(void *handle, uint8_t ap_num, uint32_t addr, uint32_t size,
uint32_t count, const uint8_t *buffer)
{
int retval = ERROR_OK;
@@ -1819,7 +2140,7 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
while (count) {
bytes_remaining = (size == 4) ? \
- stlink_max_block_size(h->max_mem_packet, addr) : STLINK_MAX_RW8;
+ stlink_max_block_size(h->max_mem_packet, addr) : stlink_usb_block(h);
if (count < bytes_remaining)
bytes_remaining = count;
@@ -1845,7 +2166,7 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
if (addr % 4) {
uint32_t head_bytes = 4 - (addr % 4);
- retval = stlink_usb_write_mem8(handle, addr, head_bytes, buffer);
+ retval = stlink_usb_write_mem8(handle, ap_num, addr, head_bytes, buffer);
if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
usleep((1<<retries++) * 1000);
continue;
@@ -1859,12 +2180,12 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
}
if (bytes_remaining % 4)
- retval = stlink_usb_write_mem(handle, addr, 1, bytes_remaining, buffer);
+ retval = stlink_usb_write_ap_mem(handle, ap_num, addr, 1, bytes_remaining, buffer);
else
- retval = stlink_usb_write_mem32(handle, addr, bytes_remaining, buffer);
+ retval = stlink_usb_write_mem32(handle, ap_num, addr, bytes_remaining, buffer);
} else
- retval = stlink_usb_write_mem8(handle, addr, bytes_remaining, buffer);
+ retval = stlink_usb_write_mem8(handle, ap_num, addr, bytes_remaining, buffer);
if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
usleep((1<<retries++) * 1000);
continue;
@@ -1880,60 +2201,65 @@ static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
return retval;
}
+static int stlink_usb_write_mem(void *handle, uint32_t addr, uint32_t size,
+ uint32_t count, const uint8_t *buffer)
+{
+ return stlink_usb_write_ap_mem(handle, HLA_DEFAULT_APNUN, addr, size, count, buffer);
+}
+
/** */
static int stlink_usb_override_target(const char *targetname)
{
return !strcmp(targetname, "cortex_m");
}
-static int stlink_speed(void *handle, int khz, bool query)
+static int stlink_speed_swim(void *handle, int khz, bool query)
{
- unsigned i;
- int speed_index = -1;
- int speed_diff = INT_MAX;
- struct stlink_usb_handle_s *h = handle;
-
- if (h && (h->transport == HL_TRANSPORT_SWIM)) {
- /*
+ /*
we dont care what the khz rate is
we only have low and high speed...
before changing speed the SWIM_CSR HS bit
must be updated
- */
- if (khz == 0)
- stlink_swim_speed(handle, 0);
- else
- stlink_swim_speed(handle, 1);
- return khz;
- }
+ */
+ if (khz == 0)
+ stlink_swim_speed(handle, 0);
+ else
+ stlink_swim_speed(handle, 1);
+ return khz;
+}
- /* only supported by stlink/v2 and for firmware >= 22 */
- if (h && (h->version.stlink == 1 || h->version.jtag < 22))
- return khz;
+static int stlink_match_speed_map(const struct speed_map *map, unsigned int map_size, int khz, bool query)
+{
+ unsigned int i;
+ int speed_index = -1;
+ int speed_diff = INT_MAX;
+ int last_valid_speed = -1;
+ bool match = true;
- for (i = 0; i < ARRAY_SIZE(stlink_khz_to_speed_map); i++) {
- if (khz == stlink_khz_to_speed_map[i].speed) {
+ for (i = 0; i < map_size; i++) {
+ if (!map[i].speed)
+ continue;
+ last_valid_speed = i;
+ if (khz == map[i].speed) {
speed_index = i;
break;
} else {
- int current_diff = khz - stlink_khz_to_speed_map[i].speed;
+ int current_diff = khz - map[i].speed;
/* get abs value for comparison */
current_diff = (current_diff > 0) ? current_diff : -current_diff;
- if ((current_diff < speed_diff) && khz >= stlink_khz_to_speed_map[i].speed) {
+ if ((current_diff < speed_diff) && khz >= map[i].speed) {
speed_diff = current_diff;
speed_index = i;
}
}
}
- bool match = true;
-
if (speed_index == -1) {
/* this will only be here if we cannot match the slow speed.
* use the slowest speed we support.*/
- speed_index = ARRAY_SIZE(stlink_khz_to_speed_map) - 1;
+ speed_index = last_valid_speed;
match = false;
- } else if (i == ARRAY_SIZE(stlink_khz_to_speed_map))
+ } else if (i == map_size)
match = false;
if (!match && query) {
@@ -1941,7 +2267,22 @@ static int stlink_speed(void *handle, int khz, bool query)
khz, stlink_khz_to_speed_map[speed_index].speed);
}
- if (h && !query) {
+ return speed_index;
+}
+
+static int stlink_speed_swd(void *handle, int khz, bool query)
+{
+ int speed_index;
+ struct stlink_usb_handle_s *h = handle;
+
+ /* only supported by stlink/v2 and for firmware >= 22 */
+ if (h->version.stlink == 1 || h->version.jtag < 22)
+ return khz;
+
+ speed_index = stlink_match_speed_map(stlink_khz_to_speed_map,
+ ARRAY_SIZE(stlink_khz_to_speed_map), khz, query);
+
+ if (!query) {
int result = stlink_usb_set_swdclk(h, stlink_khz_to_speed_map[speed_index].speed_divisor);
if (result != ERROR_OK) {
LOG_ERROR("Unable to set adapter speed");
@@ -1952,62 +2293,157 @@ static int stlink_speed(void *handle, int khz, bool query)
return stlink_khz_to_speed_map[speed_index].speed;
}
-/** */
-static int stlink_usb_close(void *handle)
+static int stlink_speed_jtag(void *handle, int khz, bool query)
{
- int res;
- uint8_t mode;
- enum stlink_mode emode;
+ int speed_index;
struct stlink_usb_handle_s *h = handle;
- if (h && h->fd)
- res = stlink_usb_current_mode(handle, &mode);
- else
- res = ERROR_FAIL;
- /* do not exit if return code != ERROR_OK,
- it prevents us from closing jtag_libusb */
-
- if (res == ERROR_OK) {
- /* try to exit current mode */
- switch (mode) {
- case STLINK_DEV_DFU_MODE:
- emode = STLINK_MODE_DFU;
- break;
- case STLINK_DEV_DEBUG_MODE:
- emode = STLINK_MODE_DEBUG_SWD;
- break;
- case STLINK_DEV_SWIM_MODE:
- emode = STLINK_MODE_DEBUG_SWIM;
- break;
- case STLINK_DEV_BOOTLOADER_MODE:
- case STLINK_DEV_MASS_MODE:
- default:
- emode = STLINK_MODE_UNKNOWN;
- break;
- }
+ /* only supported by stlink/v2 and for firmware >= 24 */
+ if (h->version.stlink == 1 || h->version.jtag < 24)
+ return khz;
- if (emode != STLINK_MODE_UNKNOWN)
- stlink_usb_mode_leave(handle, emode);
- /* do not check return code, it prevent
- us from closing jtag_libusb */
+ speed_index = stlink_match_speed_map(stlink_khz_to_speed_map_jtag,
+ ARRAY_SIZE(stlink_khz_to_speed_map_jtag), khz, query);
+
+ if (!query) {
+ int result = stlink_usb_set_jtagclk(h, stlink_khz_to_speed_map_jtag[speed_index].speed_divisor);
+ if (result != ERROR_OK) {
+ LOG_ERROR("Unable to set adapter speed");
+ return khz;
+ }
}
- if (h && h->fd)
- jtag_libusb_close(h->fd);
+ return stlink_khz_to_speed_map_jtag[speed_index].speed;
+}
- free(h);
+void stlink_dump_speed_map(const struct speed_map *map, unsigned int map_size)
+{
+ unsigned int i;
- return ERROR_OK;
+ LOG_DEBUG("Supported clock speeds are:");
+ for (i = 0; i < map_size; i++)
+ if (map[i].speed)
+ LOG_DEBUG("%d kHz", map[i].speed);
}
-/** */
-static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
+static int stlink_get_com_freq(void *handle, bool is_jtag, struct speed_map *map)
{
- int err, retry_count = 1;
- struct stlink_usb_handle_s *h;
- enum stlink_jtag_api_version api;
+ struct stlink_usb_handle_s *h = handle;
+ int i;
- LOG_DEBUG("stlink_usb_open");
+ if (h->version.stlink < 3) {
+ LOG_ERROR("Unknown command");
+ return 0;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_APIV3_GET_COM_FREQ;
+ h->cmdbuf[h->cmdidx++] = is_jtag ? 1 : 0;
+
+ int res = stlink_usb_xfer(handle, h->databuf, 52);
+
+ int size = h->databuf[8];
+
+ if (size > STLINK_V3_MAX_FREQ_NB)
+ size = STLINK_V3_MAX_FREQ_NB;
+
+ for (i = 0; i < size; i++) {
+ map[i].speed = le_to_h_u32(&h->databuf[12 + 4 * i]);
+ map[i].speed_divisor = i;
+ }
+
+ /* set to zero all the next entries */
+ for (i = size; i < STLINK_V3_MAX_FREQ_NB; i++)
+ map[i].speed = 0;
+
+ return res;
+}
+
+static int stlink_set_com_freq(void *handle, bool is_jtag, unsigned int frequency)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (h->version.stlink < 3) {
+ LOG_ERROR("Unknown command");
+ return 0;
+ }
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_APIV3_SET_COM_FREQ;
+ h->cmdbuf[h->cmdidx++] = is_jtag ? 1 : 0;
+ h->cmdbuf[h->cmdidx++] = 0;
+
+ h_u32_to_le(&h->cmdbuf[4], frequency);
+
+ return stlink_usb_xfer(handle, h->databuf, 8);
+}
+
+static int stlink_speed_v3(void *handle, int khz, bool query)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int speed_index;
+ struct speed_map map[STLINK_V3_MAX_FREQ_NB];
+
+ stlink_get_com_freq(h, (h->transport == HL_TRANSPORT_JTAG), map);
+
+ speed_index = stlink_match_speed_map(map, ARRAY_SIZE(map), khz, query);
+
+ if (!query) {
+ int result = stlink_set_com_freq(h, (h->transport == HL_TRANSPORT_JTAG), map[speed_index].speed);
+ if (result != ERROR_OK) {
+ LOG_ERROR("Unable to set adapter speed");
+ return khz;
+ }
+ }
+ return map[speed_index].speed;
+}
+
+static int stlink_speed(void *handle, int khz, bool query)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (!handle)
+ return khz;
+
+ if (h->transport == HL_TRANSPORT_SWIM)
+ return stlink_speed_swim(handle, khz, query);
+ else if (h->version.stlink == 3)
+ return stlink_speed_v3(handle, khz, query);
+ else if (h->transport == HL_TRANSPORT_SWD)
+ return stlink_speed_swd(handle, khz, query);
+ else if (h->transport == HL_TRANSPORT_JTAG)
+ return stlink_speed_jtag(handle, khz, query);
+
+ return khz;
+}
+
+/** */
+static int stlink_usb_close(void *handle)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ if (h && h->fd) {
+ stlink_exit_mode(h);
+ jtag_libusb_close(h->fd);
+ }
+
+ free(h);
+
+ return ERROR_OK;
+}
+
+/** */
+static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
+{
+ int err, retry_count = 1;
+ struct stlink_usb_handle_s *h;
+ enum stlink_jtag_api_version api;
+
+ LOG_DEBUG("stlink_usb_open");
h = calloc(1, sizeof(struct stlink_usb_handle_s));
@@ -2062,7 +2498,16 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
h->tx_ep = STLINK_TX_EP;
h->trace_ep = STLINK_TRACE_EP;
break;
+ case STLINK_V3_DFU_PID:
+ case STLINK_V3E_PID:
+ case STLINK_V3S_PID:
+ case STLINK_V3_2VCP_PID:
+ h->version.stlink = 3;
+ h->tx_ep = STLINK_V2_1_TX_EP;
+ h->trace_ep = STLINK_V2_1_TRACE_EP;
+ break;
case STLINK_V2_1_PID:
+ case STLINK_V2_1_NO_MSD_PID:
h->version.stlink = 2;
h->tx_ep = STLINK_V2_1_TX_EP;
h->trace_ep = STLINK_V2_1_TRACE_EP;
@@ -2075,6 +2520,7 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
h->trace_ep = STLINK_TRACE_EP;
break;
}
+ h->pid = pid;
/* get the device version */
err = stlink_usb_version(h);
@@ -2157,13 +2603,25 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
return ERROR_OK;
}
- /* clock speed only supported by stlink/v2 and for firmware >= 22 */
- if (h->version.stlink >= 2 && h->version.jtag >= 22) {
- LOG_DEBUG("Supported clock speeds are:");
+ if (h->transport == HL_TRANSPORT_JTAG) {
+ /* jtag clock speed only supported by stlink/v2 and for firmware >= 24 */
+ if (h->version.stlink == 2 && h->version.jtag >= 24) {
+ stlink_dump_speed_map(stlink_khz_to_speed_map_jtag, ARRAY_SIZE(stlink_khz_to_speed_map_jtag));
+ stlink_speed(h, param->initial_interface_speed, false);
+ }
+ } else if (h->transport == HL_TRANSPORT_SWD) {
+ /* clock speed only supported by stlink/v2 and for firmware >= 22 */
+ if (h->version.stlink == 2 && h->version.jtag >= 22) {
+ stlink_dump_speed_map(stlink_khz_to_speed_map, ARRAY_SIZE(stlink_khz_to_speed_map));
+ stlink_speed(h, param->initial_interface_speed, false);
+ }
+ }
- for (unsigned i = 0; i < ARRAY_SIZE(stlink_khz_to_speed_map); i++)
- LOG_DEBUG("%d kHz", stlink_khz_to_speed_map[i].speed);
+ if (h->version.stlink == 3) {
+ struct speed_map map[STLINK_V3_MAX_FREQ_NB];
+ stlink_get_com_freq(h, (h->transport == HL_TRANSPORT_JTAG), map);
+ stlink_dump_speed_map(map, ARRAY_SIZE(map));
stlink_speed(h, param->initial_interface_speed, false);
}
@@ -2171,8 +2629,9 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
* start with a safe default */
h->max_mem_packet = (1 << 10);
+#if 0
uint8_t buffer[4];
- err = stlink_usb_read_mem32(h, CPUID, 4, buffer);
+ err = stlink_usb_read_mem32(h, 0, CPUID, 4, buffer);
if (err == ERROR_OK) {
uint32_t cpuid = le_to_h_u32(buffer);
int i = (cpuid >> 4) & 0xf;
@@ -2181,6 +2640,7 @@ static int stlink_usb_open(struct hl_interface_param_s *param, void **fd)
h->max_mem_packet = (1 << 12);
}
}
+#endif
LOG_DEBUG("Using TAR autoincrement: %" PRIu32, h->max_mem_packet);
@@ -2226,6 +2686,122 @@ int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_p
}
/** */
+static int stlink_usb_init_access_point(void *handle,
+ unsigned char access_point_id, unsigned char resource)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* only required by stlink/v2 and for firmware >= 28 */
+ if (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 28))
+ return ERROR_OK;
+
+ LOG_DEBUG_IO("init apnum = %d, resource %d", access_point_id, resource);
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_INIT_AP;
+ h->cmdbuf[h->cmdidx++] = access_point_id;
+ h->cmdbuf[h->cmdidx++] = resource;
+ h_u32_to_le(&h->cmdbuf[12], 0);
+
+ return stlink_usb_xfer(handle, h->databuf, 2);
+}
+
+/** */
+static int stlink_usb_close_access_point(void *handle,
+ unsigned char access_point_id)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* only required by stlink/v2 and for firmware >= 28 */
+ if (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 28))
+ return ERROR_OK;
+
+ LOG_DEBUG_IO("close apnum = %d", access_point_id);
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_APIV2_CLOSE_AP_DBG;
+ h->cmdbuf[h->cmdidx++] = access_point_id;
+ h_u32_to_le(&h->cmdbuf[12], 0);
+
+ return stlink_usb_xfer(handle, h->databuf, 2);
+}
+
+/** */
+static int stlink_internal_read_dap_register(void *handle, unsigned short dap_port,
+ unsigned short addr, uint32_t *val)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int retval;
+
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_JTAG_READ_DAP_REG;
+ h_u16_to_le(&h->cmdbuf[2], dap_port);
+ h_u16_to_le(&h->cmdbuf[4], addr);
+ h_u32_to_le(&h->cmdbuf[12], 0);
+
+ retval = stlink_usb_xfer(handle, h->databuf, 8);
+ *val = le_to_h_u32(h->databuf + 4);
+ return retval;
+}
+
+#define STLINK_DEBUG_PORT 0xffff
+static int stlink_read_dap_register(void *handle, unsigned short dap_port,
+ unsigned short addr, uint32_t *val)
+{
+ struct stlink_usb_handle_s *h = handle;
+ int retval;
+ uint32_t dummy;
+
+ assert(handle != NULL);
+
+ /* only supported by stlink/v2 and for firmware >= 24 */
+ if (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 24))
+ return ERROR_COMMAND_NOTFOUND;
+
+ if (h->transport == HL_TRANSPORT_JTAG && dap_port == STLINK_DEBUG_PORT
+ && h->version.stlink == 2 && h->version.jtag < 32) {
+ /* workaround for V2J24 ~ V2J31 */
+ retval = stlink_internal_read_dap_register(handle, STLINK_DEBUG_PORT, addr, &dummy);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = stlink_internal_read_dap_register(handle, STLINK_DEBUG_PORT, DP_RDBUFF, val);
+ } else {
+ retval = stlink_internal_read_dap_register(handle, dap_port, addr, val);
+ }
+
+ LOG_DEBUG_IO("dap_port_read = %d, addr = 0x%x, value = 0x%x", dap_port, addr, *val);
+ return retval;
+}
+
+/** */
+static int stlink_write_dap_register(void *handle, unsigned short dap_port,
+ unsigned short addr, uint32_t val)
+{
+ struct stlink_usb_handle_s *h = handle;
+
+ assert(handle != NULL);
+
+ /* only supported by stlink/v2 and for firmware >= 24 */
+ if (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 24))
+ return ERROR_COMMAND_NOTFOUND;
+
+ LOG_DEBUG_IO("dap_port_write = %d, addr = 0x%x, value = 0x%x", dap_port, addr, val);
+ stlink_usb_init_buffer(handle, h->rx_ep, 16);
+ h->cmdbuf[h->cmdidx++] = STLINK_DEBUG_COMMAND;
+ h->cmdbuf[h->cmdidx++] = STLINK_JTAG_WRITE_DAP_REG;
+ h_u16_to_le(&h->cmdbuf[2], dap_port);
+ h_u16_to_le(&h->cmdbuf[4], addr);
+ h_u32_to_le(&h->cmdbuf[6], val);
+ h_u32_to_le(&h->cmdbuf[12], 0);
+ return stlink_usb_xfer(handle, h->databuf, 2);
+}
+
+/** */
struct hl_layout_api_s stlink_usb_layout_api = {
/** */
.open = stlink_usb_open,
@@ -2266,3 +2842,536 @@ struct hl_layout_api_s stlink_usb_layout_api = {
/** */
.poll_trace = stlink_usb_trace_read,
};
+
+/* Low-level interface */
+
+static struct stlink_usb_handle_s *stlink_dap_handle;
+static struct hl_interface_param_s stlink_dap_param = {
+ .transport = HL_TRANSPORT_JTAG,
+ .vid = {STLINK_VID, STLINK_VID, STLINK_VID, STLINK_VID, STLINK_VID, STLINK_VID, STLINK_VID, STLINK_VID, 0},
+ .pid = {STLINK_V1_PID, STLINK_V2_PID, STLINK_V2_1_PID, STLINK_V2_1_NO_MSD_PID, STLINK_V3_DFU_PID, STLINK_V3E_PID, STLINK_V3S_PID, STLINK_V3_2VCP_PID, 0},
+};
+static DECLARE_BITMAP(opened_ap, DP_APSEL_MAX + 1);
+static uint8_t ap_csw_size_cached[DP_APSEL_MAX + 1];
+
+static int stlink_dap_open_ap(unsigned short apsel)
+{
+ int retval;
+
+ if (apsel > DP_APSEL_MAX)
+ return ERROR_OK;
+
+ if (test_bit(apsel, opened_ap))
+ return ERROR_OK;
+
+ retval = stlink_usb_init_access_point(stlink_dap_handle, apsel, 0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ LOG_DEBUG("AP %d enabled", apsel);
+ set_bit(apsel, opened_ap);
+ return ERROR_OK;
+}
+
+static int stlink_dap_closeall_ap(void)
+{
+ int retval, apsel;
+
+ for (apsel = 0; apsel <= DP_APSEL_MAX; apsel++) {
+ if (!test_bit(apsel, opened_ap))
+ continue;
+ retval = stlink_usb_close_access_point(stlink_dap_handle, apsel);
+ if (retval != ERROR_OK)
+ return retval;
+ clear_bit(apsel, opened_ap);
+ }
+ return ERROR_OK;
+}
+
+int stlink_dap_dap_read(unsigned short dap_port, unsigned short addr, uint32_t *val)
+{
+ uint32_t dummy;
+ int retval;
+
+ /* Skip opening DAP if we are only scanning to look for an AP */
+ if (dap_port <= DP_APSEL_MAX && addr != AP_REG_IDR) {
+ retval = stlink_dap_open_ap(dap_port);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
+ if (!val)
+ val = &dummy;
+ return stlink_read_dap_register(stlink_dap_handle, dap_port, addr, val);
+}
+
+int stlink_dap_dap_write(unsigned short dap_port, unsigned short addr, uint32_t val)
+{
+ int retval;
+
+ retval = stlink_dap_open_ap(dap_port);
+ if (retval != ERROR_OK)
+ return retval;
+
+ return stlink_write_dap_register(stlink_dap_handle, dap_port, addr, val);
+}
+
+/*
+ * Workaround for setting CSW with ST-Link pre-versions V2J32 and V3J2.
+ * Before ST-Link versions above, high level API does not provide a method to
+ * set CSW. Same issue on every version of ST-Link V1. This forced us using
+ * the un-efficient low-level AP register API for every memory read/write.
+ *
+ * This workaround leverage the CSW caching operated by ST-Link. At every
+ * memory R/W, ST-Link computes the new CSW value based on word size. If it
+ * match the previous CSW value than it has wrote in CSW register, ST-Link
+ * will not write in CSW register again.
+ *
+ * Here we track the word size used in the last memory R/W. If it does not
+ * match with current word size, we first force ST-Link to update CSW register
+ * and its internal cache accordingly to the new size. Then we overwrite CSW
+ * register with the value we need.
+ * The following memory R/W will operate base on the new CSW and ST-Link will
+ * not change it.
+ */
+static int stlink_dap_set_csw(struct adiv5_ap *ap, uint32_t size, bool addrinc)
+{
+ uint32_t csw;
+ uint8_t dummy[4], ap_num;
+ int retval;
+
+ ap_num = ap->ap_num;
+
+ struct stlink_usb_handle_s *h = stlink_dap_handle;
+ if ((h->version.stlink == 2 && h->version.jtag >= 32) || (h->version.stlink == 3 && h->version.jtag >= 2)) {
+ csw = ap->csw_default;
+ if (csw != (ap->csw_value & ~(CSW_SIZE_MASK | CSW_ADDRINC_MASK))) {
+ retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw);
+ if (retval != ERROR_OK) {
+ ap->csw_value = 0;
+ return retval;
+ }
+ ap->csw_value = csw;
+ }
+ return ERROR_OK;
+ }
+
+ switch (size) {
+ case 2:
+ /* current implementation only use 8 and 32 bits */
+ size = 1;
+ /* fallthrough */
+ case 1:
+ csw = CSW_8BIT;
+ break;
+ case 4:
+ default:
+ /* ST-Link sets autoinc only in 32 bits mode */
+ /*
+ * ARM IHI 0031D: Note:
+ * It is IMPLEMENTATION DEFINED whether a MEM-AP supports transfer sizes other than Word. If a
+ * MEM-AP only supports word transfers and Increment single is selected, the TAR always
+ * increments by four after a successful DRW transaction.
+ */
+ csw = addrinc ? (CSW_32BIT | CSW_ADDRINC_SINGLE) : CSW_32BIT;
+ break;
+ }
+ csw |= ap->csw_default;
+
+ if (ap_csw_size_cached[ap_num] != size) {
+ ap_csw_size_cached[ap_num] = size;
+
+ /* The mem read below will change CSW */
+ ap->csw_value = 0;
+ stlink_usb_read_ap_mem(stlink_dap_handle, ap_num, 0x00000000, size, 1, dummy);
+ }
+
+ if (ap->csw_value != csw) {
+ retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw);
+ if (retval != ERROR_OK) {
+ ap->csw_value = 0;
+ return retval;
+ }
+ ap->csw_value = csw;
+ }
+ return ERROR_OK;
+}
+
+static int stlink_dap_reset_csw(struct adiv5_ap *ap)
+{
+ uint32_t csw;
+ int retval;
+
+ csw = ap->csw_value;
+ if (csw & CSW_ADDRINC_SINGLE)
+ return ERROR_OK;
+
+ csw &= ~CSW_ADDRINC_SINGLE;
+ retval = dap_queue_ap_write(ap, MEM_AP_REG_CSW, csw);
+ if (retval != ERROR_OK)
+ return retval;
+ ap->csw_value = csw;
+ return ERROR_OK;
+}
+
+int stlink_dap_ap_mem_read(struct adiv5_ap *ap, uint8_t *buffer,
+ uint32_t size, uint32_t count, uint32_t address, bool addrinc)
+{
+ int retval;
+ uint8_t ap_num;
+ uint32_t partial;
+ int retries = 0;
+ uint32_t bytes_remaining;
+
+ if (!addrinc && size != 4)
+ return ERROR_OP_NOT_SUPPORTED;
+
+ struct stlink_usb_handle_s *h = stlink_dap_handle;
+ if (!addrinc && (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 24)))
+ return ERROR_OP_NOT_SUPPORTED;
+
+ ap_num = ap->ap_num;
+ retval = stlink_dap_open_ap(ap_num);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (size == 4 && address & 3) {
+ /* unaligned 32-bits read could be split in mix 32 & 8-bits. Force 8-bis only */
+ count *= 4;
+ size = 1;
+ }
+
+ /* here we do not track TAR, and next calls will change it */
+ ap->tar_valid = false;
+
+ retval = stlink_dap_set_csw(ap, size, addrinc);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (addrinc)
+ return stlink_usb_read_ap_mem(stlink_dap_handle, ap_num, address, size,
+ count, buffer);
+
+ /* !addrinc && size == 4 */
+
+ if ((h->version.stlink == 2 && h->version.jtag >= 32) || (h->version.stlink == 3 && h->version.jtag >= 2)) {
+ count *= 4;
+ while (count) {
+ bytes_remaining = stlink_usb_block(h);
+
+ if (count < bytes_remaining)
+ bytes_remaining = count;
+
+ retval = stlink_usb_read_mem32_noaddrinc(stlink_dap_handle, ap_num, address, bytes_remaining, buffer);
+ if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+ usleep((1<<retries++) * 1000);
+ continue;
+ }
+ if (retval != ERROR_OK)
+ return retval;
+
+ retries = 0;
+ buffer += bytes_remaining;
+ count -= bytes_remaining;
+ }
+ return ERROR_OK;
+ }
+
+ /* workaround for V2J24..V2J31 and V3J1 */
+ partial = (TAR_AUTOINCR_BLOCK - (address & (TAR_AUTOINCR_BLOCK - 1))) / 4;
+ if (partial > stlink_usb_block(stlink_dap_handle) / 4)
+ partial = stlink_usb_block(stlink_dap_handle) / 4;
+ while (count) {
+ if (partial > count)
+ partial = count;
+ retval = stlink_usb_read_mem32(stlink_dap_handle, ap_num, address, 4 * partial, buffer);
+ if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+ usleep((1<<retries++) * 1000);
+ continue;
+ }
+ if (retval != ERROR_OK)
+ return retval;
+ retries = 0;
+ count -= partial;
+ buffer += 4 * partial;
+ }
+ return stlink_dap_reset_csw(ap);
+}
+
+int stlink_dap_ap_mem_write(struct adiv5_ap *ap, const uint8_t *buffer,
+ uint32_t size, uint32_t count, uint32_t address, bool addrinc)
+{
+ int retval;
+ uint8_t ap_num;
+ uint32_t partial;
+ int retries = 0;
+ uint32_t bytes_remaining;
+
+ if (!addrinc && size != 4)
+ return ERROR_OP_NOT_SUPPORTED;
+
+ struct stlink_usb_handle_s *h = stlink_dap_handle;
+ if (!addrinc && (h->version.stlink == 1 || (h->version.stlink == 2 && h->version.jtag < 24)))
+ return ERROR_OP_NOT_SUPPORTED;
+
+ ap_num = ap->ap_num;
+ retval = stlink_dap_open_ap(ap_num);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (size == 4 && address & 3) {
+ /* unaligned 32-bits write could be split in mix 32 & 8-bits. Force 8-bis only */
+ count *= 4;
+ size = 1;
+ }
+
+ /* here we do not track TAR, and next calls will change it */
+ ap->tar_valid = false;
+
+ retval = stlink_dap_set_csw(ap, size, addrinc);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (addrinc)
+ return stlink_usb_write_ap_mem(stlink_dap_handle, ap_num, address, size,
+ count, buffer);
+
+ /* !addrinc && size == 4 */
+
+ if ((h->version.stlink == 2 && h->version.jtag >= 32) || (h->version.stlink == 3 && h->version.jtag >= 2)) {
+ count *= 4;
+ while (count) {
+ bytes_remaining = stlink_usb_block(h);
+
+ if (count < bytes_remaining)
+ bytes_remaining = count;
+
+ retval = stlink_usb_write_mem32_noaddrinc(stlink_dap_handle, ap_num, address, bytes_remaining, buffer);
+ if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+ usleep((1<<retries++) * 1000);
+ continue;
+ }
+ if (retval != ERROR_OK)
+ return retval;
+
+ retries = 0;
+ buffer += bytes_remaining;
+ count -= bytes_remaining;
+ }
+ return ERROR_OK;
+ }
+
+ /* workaround for V2J24..V2J31 and V3J1 */
+ partial = (TAR_AUTOINCR_BLOCK - (address & (TAR_AUTOINCR_BLOCK - 1))) / 4;
+ if (partial > stlink_usb_block(stlink_dap_handle) / 4)
+ partial = stlink_usb_block(stlink_dap_handle) / 4;
+ while (count) {
+ if (partial > count)
+ partial = count;
+ retval = stlink_usb_write_mem32(stlink_dap_handle, ap_num, address, 4 * partial, buffer);
+ if (retval == ERROR_WAIT && retries < MAX_WAIT_RETRIES) {
+ usleep((1<<retries++) * 1000);
+ continue;
+ }
+ if (retval != ERROR_OK)
+ return retval;
+ retries = 0;
+ count -= partial;
+ buffer += 4 * partial;
+ }
+ return stlink_dap_reset_csw(ap);
+}
+
+static int stlink_dap_swd_init(void)
+{
+ LOG_DEBUG("stlink_dap_swd_init()");
+ stlink_dap_param.transport = HL_TRANSPORT_SWD;
+ return ERROR_OK;
+}
+
+static int stlink_dap_speed(int speed)
+{
+ if (speed == 0) {
+ LOG_ERROR("RTCK not supported. Set nonzero adapter_khz.");
+ return ERROR_JTAG_NOT_IMPLEMENTED;
+ }
+
+ stlink_dap_param.initial_interface_speed = speed;
+ stlink_speed(stlink_dap_handle, speed, false);
+ return ERROR_OK;
+}
+
+static int stlink_dap_speed_div(int speed, int *khz)
+{
+ *khz = speed;
+ return ERROR_OK;
+}
+
+static int stlink_dap_khz(int khz, int *jtag_speed)
+{
+ *jtag_speed = khz;
+ return ERROR_OK;
+}
+
+static int_least32_t stlink_dap_swd_frequency(int_least32_t hz)
+{
+ if (hz > 0)
+ stlink_dap_speed(hz / 1000);
+
+ return hz;
+}
+
+static int stlink_dap_swd_switch_seq(enum swd_special_seq seq)
+{
+ LOG_ERROR("stlink_dap_swd_switch_seq()");
+ assert(0);
+ return ERROR_FAIL;
+}
+
+static void stlink_dap_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
+{
+ LOG_ERROR("stlink_dap_swd_read_reg()");
+ assert(0);
+}
+
+static void stlink_dap_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
+{
+ LOG_ERROR("stlink_dap_swd_write_reg()");
+ assert(0);
+}
+
+static int stlink_dap_swd_run_queue(void)
+{
+ LOG_ERROR("stlink_dap_swd_run_queue()");
+ assert(0);
+ return ERROR_FAIL;
+}
+
+static void stlink_dap_execute_command(struct jtag_command *cmd)
+{
+ switch (cmd->type) {
+ case JTAG_RESET:
+ LOG_DEBUG("stlink_usb_assert trst(%d) srst(%d)", !cmd->cmd.reset->trst, !cmd->cmd.reset->srst);
+ if (cmd->cmd.reset->trst)
+ stlink_usb_reset(stlink_dap_handle);
+ stlink_usb_assert_srst(stlink_dap_handle, cmd->cmd.reset->srst ? STLINK_DEBUG_APIV2_DRIVE_NRST_LOW : STLINK_DEBUG_APIV2_DRIVE_NRST_HIGH);
+ break;
+ case JTAG_SLEEP:
+ jtag_sleep(cmd->cmd.sleep->us);
+ break;
+ default:
+ LOG_ERROR("stlink_dap_execute_queue(%d)", cmd->type);
+ assert(0);
+ break;
+ }
+}
+
+static int stlink_dap_execute_queue(void)
+{
+ struct jtag_command *cmd = jtag_command_queue;
+
+ LOG_DEBUG_IO("stlink_dap_execute_queue()");
+
+ while (cmd != NULL) {
+ stlink_dap_execute_command(cmd);
+ cmd = cmd->next;
+ }
+
+ return ERROR_OK;
+}
+
+static int stlink_dap_init(void)
+{
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+ LOG_DEBUG("stlink_dap_init()");
+
+ if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
+ if (jtag_reset_config & RESET_SRST_NO_GATING)
+ stlink_dap_param.connect_under_reset = true;
+ else
+ LOG_WARNING("\'srst_nogate\' reset_config option is required");
+ }
+ return stlink_usb_open(&stlink_dap_param, (void **)&stlink_dap_handle);
+}
+
+static int stlink_dap_quit(void)
+{
+ int retval;
+
+ LOG_DEBUG("stlink_dap_quit()");
+
+ retval = stlink_dap_closeall_ap();
+ if (retval != ERROR_OK)
+ return retval;
+
+ free((void *)stlink_dap_param.serial);
+ stlink_dap_param.serial = NULL;
+
+ return stlink_usb_close(stlink_dap_handle);
+}
+
+COMMAND_HANDLER(stlink_dap_serial_command)
+{
+ LOG_DEBUG("stlink_dap_serial_command");
+
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Expected exactly one argument for \"st-link serial <serial-number>\".");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (stlink_dap_param.serial) {
+ LOG_WARNING("Command \"stlink serial\" already used. Replace previous value");
+ free((void *)stlink_dap_param.serial);
+ }
+
+ stlink_dap_param.serial = strdup(CMD_ARGV[0]);
+ return ERROR_OK;
+}
+
+static const struct command_registration stlink_dap_subcommand_handlers[] = {
+ {
+ .name = "serial",
+ .handler = &stlink_dap_serial_command,
+ .mode = COMMAND_CONFIG,
+ .help = "set the serial number of the device that should be used",
+ .usage = "<serial number>",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration stlink_dap_command_handlers[] = {
+ {
+ .name = "st-link",
+ .mode = COMMAND_ANY,
+ .help = "perform st-link management",
+ .chain = stlink_dap_subcommand_handlers,
+ .usage = "",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct swd_driver stlink_dap_swd_driver = {
+ .init = stlink_dap_swd_init,
+ .frequency = stlink_dap_swd_frequency,
+ .switch_seq = stlink_dap_swd_switch_seq,
+ .read_reg = stlink_dap_swd_read_reg,
+ .write_reg = stlink_dap_swd_write_reg,
+ .run = stlink_dap_swd_run_queue,
+};
+
+static const char *const stlink_dap_transport[] = { "stlink_swd", "stlink_jtag", NULL };
+
+struct jtag_interface stlink_dap_interface = {
+ .name = "st-link",
+ .commands = stlink_dap_command_handlers,
+ .swd = &stlink_dap_swd_driver,
+ .transports = stlink_dap_transport,
+
+ .execute_queue = stlink_dap_execute_queue,
+ .speed = stlink_dap_speed,
+ .speed_div = stlink_dap_speed_div,
+ .khz = stlink_dap_khz,
+ .init = stlink_dap_init,
+ .quit = stlink_dap_quit,
+};
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index 286a73a..1ef422f 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -135,6 +135,9 @@ extern struct jtag_interface imx_gpio_interface;
#if BUILD_XDS110 == 1
extern struct jtag_interface xds110_interface;
#endif
+#if BUILD_HLADAPTER_STLINK == 1
+extern struct jtag_interface stlink_dap_interface;
+#endif
#endif /* standard drivers */
/**
@@ -240,6 +243,9 @@ struct jtag_interface *jtag_interfaces[] = {
#if BUILD_XDS110 == 1
&xds110_interface,
#endif
+#if BUILD_HLADAPTER_STLINK == 1
+ &stlink_dap_interface,
+#endif
#endif /* standard drivers */
NULL,
};
diff --git a/src/jtag/startup.tcl b/src/jtag/startup.tcl
index d57cafb..765c417 100644
--- a/src/jtag/startup.tcl
+++ b/src/jtag/startup.tcl
@@ -21,11 +21,19 @@ proc jtag_init {} {
# startup (at OpenOCD server startup, when JTAG may not yet work); and
# potentially more (for reset types like cold, warm, etc)
proc init_reset { mode } {
- if {[using_jtag]} {
- jtag arp_init-reset
+ if {![using_hla]} {
+ arp_init_reset
}
}
+proc reset_assert_final { mode } {
+ arp_adapter_reset assert
+}
+
+proc reset_deassert_initial { mode } {
+ arp_adapter_reset deassert
+}
+
#########
# TODO: power_restore and power_dropout are currently neither
diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c
index e32f0ca..734ef1e 100644
--- a/src/jtag/tcl.c
+++ b/src/jtag/tcl.c
@@ -549,7 +549,7 @@ static int jim_newtap_cmd(Jim_GetOptInfo *goi)
LOG_DEBUG("Creating New Tap, Chip: %s, Tap: %s, Dotted: %s, %d params",
pTap->chip, pTap->tapname, pTap->dotted_name, goi->argc);
- if (!transport_is_jtag()) {
+ if (!transport_is_jtag() && !transport_is_stlink_jtag()) {
/* SWD doesn't require any JTAG tap parameters */
pTap->enabled = true;
jtag_tap_init(pTap);
@@ -684,9 +684,9 @@ static int jim_jtag_arp_init_reset(Jim_Interp *interp, int argc, Jim_Obj *const
return JIM_ERR;
}
struct command_context *context = current_command_context(interp);
- if (transport_is_jtag())
+ if (transport_is_jtag() || transport_is_stlink_jtag())
e = jtag_init_reset(context);
- else if (transport_is_swd())
+ else if (transport_is_swd() || transport_is_stlink_swd())
e = swd_init_reset(context);
if (e != ERROR_OK) {
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index a451273..fc5f967 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -1395,11 +1395,6 @@ static int gdb_error(struct connection *connection, int retval)
return ERROR_OK;
}
-/* We don't have to worry about the default 2 second timeout for GDB packets,
- * because GDB breaks up large memory reads into smaller reads.
- *
- * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????
- */
static int gdb_read_memory_packet(struct connection *connection,
char const *packet, int packet_size)
{
@@ -3089,6 +3084,42 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line,
gdb_output_con(connection, string);
}
+/*
+ * During long memory read/write, the default 2 seconds timeout of GDB can
+ * expire due to slow JTAG interface combined with high traffic on the USB bus.
+ * The usual O packets cannot be used during memory read/write.
+ * To restart the GDB timeout counter, send a custom notification packet. It
+ * would be silently dropped because is not recognized by GDB.
+ */
+static void gdb_keepalive_callback(void *priv, const char *file, unsigned line,
+ const char *function, const char *string)
+{
+ static unsigned int count = 0;
+ struct connection *connection = priv;
+ struct gdb_connection *gdb_con = connection->priv;
+ int i, len;
+ unsigned int my_checksum = 0;
+ char buf[17];
+
+ /* keep_alive() sends empty strings */
+ if (gdb_con->busy || string[0])
+ return;
+
+ len = sprintf(buf, "%%keepalive:%2.2x", count);
+ count = (count + 1) & 255;
+ for (i = 1; i < len; i++)
+ my_checksum += buf[i];
+ len += sprintf(buf + len, "#%2.2x", my_checksum & 255);
+
+#ifdef _DEBUG_GDB_IO_
+ LOG_DEBUG("sending packet '%s'", buf);
+#endif
+
+ gdb_con->busy = true;
+ gdb_write(connection, buf, len);
+ gdb_con->busy = false;
+}
+
static void gdb_sig_halted(struct connection *connection)
{
char sig_reply[4];
@@ -3172,10 +3203,14 @@ static int gdb_input_inner(struct connection *connection)
retval = gdb_set_register_packet(connection, packet, packet_size);
break;
case 'm':
+ log_add_callback(gdb_keepalive_callback, connection);
retval = gdb_read_memory_packet(connection, packet, packet_size);
+ log_remove_callback(gdb_keepalive_callback, connection);
break;
case 'M':
+ log_add_callback(gdb_keepalive_callback, connection);
retval = gdb_write_memory_packet(connection, packet, packet_size);
+ log_remove_callback(gdb_keepalive_callback, connection);
break;
case 'z':
case 'Z':
@@ -3261,9 +3296,9 @@ static int gdb_input_inner(struct connection *connection)
extended_protocol = 0;
break;
case 'X':
+ log_add_callback(gdb_keepalive_callback, connection);
retval = gdb_write_memory_binary_packet(connection, packet, packet_size);
- if (retval != ERROR_OK)
- return retval;
+ log_remove_callback(gdb_keepalive_callback, connection);
break;
case 'k':
if (extended_protocol != 0) {
diff --git a/src/target/Makefile.am b/src/target/Makefile.am
index 4b7c8c0..bb305b7 100644
--- a/src/target/Makefile.am
+++ b/src/target/Makefile.am
@@ -105,6 +105,10 @@ ARM_DEBUG_SRC = \
%D%/etm_dummy.c \
%D%/arm_cti.c
+if HLADAPTER
+ARM_DEBUG_SRC += %D%/adi_v5_stlink.c
+endif
+
AVR32_SRC = \
%D%/avr32_ap7k.c \
%D%/avr32_jtag.c \
diff --git a/src/target/adi_v5_stlink.c b/src/target/adi_v5_stlink.c
new file mode 100644
index 0000000..9e7ab0f
--- /dev/null
+++ b/src/target/adi_v5_stlink.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author(s): Antonio Borneo <borneo.antonio@gmail.com> for STMicroelectronics
+ *
+ * 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/>.
+ */
+
+/**
+ * @file
+ * Utilities to support STMicroelectronics in-circuit debugger and programmer
+ * "ST-Link" (only V2 and newer versions) to access ARM DAP through either
+ * JTAG or ARM "Serial Wire Debug" (SWD).
+ *
+ * This implementation is complementary to the existing "hla" transport, which
+ * is too much focused at Cortex-M, and supports also Cortex-A and SMP nodes.
+ *
+ * Single-DAP support only.
+ *
+ * for details, see "ARM IHI 0031A"
+ * ARM Debug Interface v5 Architecture Specification
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arm.h"
+#include "arm_adi_v5.h"
+#include <helper/time_support.h>
+
+#include <transport/transport.h>
+#include <jtag/interface.h>
+
+#include <jtag/swd.h>
+
+#define STLINK_DEBUG_PORT 0xffff
+
+int stlink_dap_dap_read(unsigned short dap_port, unsigned short addr, uint32_t *val);
+int stlink_dap_dap_write(unsigned short dap_port, unsigned short addr, uint32_t val);
+int stlink_dap_ap_mem_read(struct adiv5_ap *ap, uint8_t *buffer,
+ uint32_t size, uint32_t count, uint32_t address, bool addrinc);
+int stlink_dap_ap_mem_write(struct adiv5_ap *ap, const uint8_t *buffer,
+ uint32_t size, uint32_t count, uint32_t address, bool addrinc);
+
+static int stlink_check_reconnect(struct adiv5_dap *dap);
+
+static int stlink_swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
+ uint32_t *data)
+{
+ int retval = stlink_check_reconnect(dap);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stlink_dap_dap_read(STLINK_DEBUG_PORT, reg, data);
+ if (retval != ERROR_OK)
+ dap->do_reconnect = true;
+ return retval;
+}
+
+static int stlink_swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
+ uint32_t data)
+{
+ int retval = stlink_check_reconnect(dap);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* ST-Link does not like that we set CORUNDETECT */
+ if (reg == DP_CTRL_STAT)
+ data &= ~CORUNDETECT;
+
+ retval = stlink_dap_dap_write(STLINK_DEBUG_PORT, reg, data);
+ if (retval != ERROR_OK)
+ dap->do_reconnect = true;
+ return retval;
+}
+
+static int stlink_swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
+ uint32_t *data)
+{
+ struct adiv5_dap *dap = ap->dap;
+ int retval = stlink_check_reconnect(dap);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stlink_dap_dap_read(ap->ap_num, reg, data);
+ if (retval != ERROR_OK)
+ dap->do_reconnect = true;
+ return retval;
+}
+
+static int stlink_swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
+ uint32_t data)
+{
+ struct adiv5_dap *dap = ap->dap;
+ int retval = stlink_check_reconnect(dap);
+ if (retval != ERROR_OK)
+ return retval;
+
+ retval = stlink_dap_dap_write(ap->ap_num, reg, data);
+ if (retval != ERROR_OK)
+ dap->do_reconnect = true;
+ return retval;
+}
+
+static int stlink_connect(struct adiv5_dap *dap)
+{
+ uint32_t dpidr;
+ int retval;
+
+ LOG_INFO("stlink_connect(%sconnect)", dap->do_reconnect ? "re" : "");
+
+ /* Check if we should reset srst already when connecting, but not if reconnecting. */
+ if (!dap->do_reconnect) {
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+ if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
+ if (jtag_reset_config & RESET_SRST_NO_GATING)
+ adapter_assert_reset();
+ else
+ LOG_WARNING("\'srst_nogate\' reset_config option is required");
+ }
+ }
+
+ dap->do_reconnect = false;
+ dap_invalidate_cache(dap);
+
+ retval = dap->ops->queue_dp_read(dap, DP_DPIDR, &dpidr);
+ if (retval == ERROR_OK) {
+ LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr);
+ retval = dap_dp_init(dap);
+ } else
+ dap->do_reconnect = true;
+
+ return retval;
+}
+
+static int stlink_check_reconnect(struct adiv5_dap *dap)
+{
+ if (dap->do_reconnect)
+ return stlink_connect(dap);
+
+ return ERROR_OK;
+}
+
+static int stlink_swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
+{
+ LOG_ERROR("stlink_swd_queue_ap_abort()");
+ return ERROR_OK;
+}
+
+static int stlink_swd_run(struct adiv5_dap *dap)
+{
+ /* Here no LOG_DEBUG. This is called continuously! */
+
+ /*
+ * ST-Link returns immediately after a DAP write, without waiting for it
+ * to complete.
+ * FIXME: Here we should check if the last operation is a read or a
+ * write, and issue the dummy read only to complete a write!
+ *
+ * Run a dummy read to DP_RDBUFF, as suggested in
+ * http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka16363.html
+ */
+ return stlink_swd_queue_dp_read(dap, DP_RDBUFF, NULL);
+}
+
+#define stlink_ap_mem_read stlink_dap_ap_mem_read
+#define stlink_ap_mem_write stlink_dap_ap_mem_write
+
+const struct dap_ops stlink_dap_swd_ops = {
+ .connect = stlink_connect,
+ .queue_dp_read = stlink_swd_queue_dp_read,
+ .queue_dp_write = stlink_swd_queue_dp_write,
+ .queue_ap_read = stlink_swd_queue_ap_read,
+ .queue_ap_write = stlink_swd_queue_ap_write,
+ .queue_ap_abort = stlink_swd_queue_ap_abort,
+ .ap_mem_read = stlink_ap_mem_read,
+ .ap_mem_write = stlink_ap_mem_write,
+ .run = stlink_swd_run,
+};
+
+const struct dap_ops stlink_dap_jtag_ops = {
+ .connect = stlink_connect,
+ .queue_dp_read = stlink_swd_queue_dp_read,
+ .queue_dp_write = stlink_swd_queue_dp_write,
+ .queue_ap_read = stlink_swd_queue_ap_read,
+ .queue_ap_write = stlink_swd_queue_ap_write,
+ .queue_ap_abort = stlink_swd_queue_ap_abort,
+ .ap_mem_read = stlink_ap_mem_read,
+ .ap_mem_write = stlink_ap_mem_write,
+ .run = stlink_swd_run,
+};
+
+static const struct command_registration stlink_commands[] = {
+ {
+ /*
+ * Set up SWD and JTAG targets identically, unless/until
+ * infrastructure improves ... meanwhile, ignore all
+ * JTAG-specific stuff like IR length for SWD.
+ *
+ * REVISIT can we verify "just one SWD DAP" here/early?
+ */
+ .name = "newdap",
+ .jim_handler = jim_jtag_newtap,
+ .mode = COMMAND_CONFIG,
+ .help = "declare a new DAP"
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration stlink_handlers[] = {
+ {
+ .name = "stlink_dap",
+ .mode = COMMAND_ANY,
+ .help = "ST-Link command group",
+ .chain = stlink_commands,
+ .usage = "",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static int stlink_jtag_select(struct command_context *ctx)
+{
+ LOG_DEBUG("stlink_jtag_select()");
+
+ return register_commands(ctx, NULL, stlink_handlers);
+}
+
+static int stlink_swd_select(struct command_context *ctx)
+{
+ /* FIXME: only place where global 'jtag_interface' is still needed */
+ extern struct jtag_interface *jtag_interface;
+ const struct swd_driver *swd = jtag_interface->swd;
+ int retval;
+
+ LOG_DEBUG("stlink_swd_select()");
+
+ retval = register_commands(ctx, NULL, stlink_handlers);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* be sure driver is in SWD mode; start
+ * with hardware default TRN (1), it can be changed later
+ */
+ if (!swd || !swd->read_reg || !swd->write_reg || !swd->init) {
+ LOG_DEBUG("no SWD driver?");
+ return ERROR_FAIL;
+ }
+
+ retval = swd->init();
+ if (retval != ERROR_OK) {
+ LOG_DEBUG("can't init SWD driver");
+ return retval;
+ }
+
+ return retval;
+}
+
+static int stlink_init(struct command_context *ctx)
+{
+ LOG_DEBUG("stlink_init()");
+
+ adapter_deassert_reset();
+ return ERROR_OK;
+}
+
+static struct transport stlink_jtag_transport = {
+ .name = "stlink_jtag",
+ .select = stlink_jtag_select,
+ .init = stlink_init,
+};
+
+static struct transport stlink_swd_transport = {
+ .name = "stlink_swd",
+ .select = stlink_swd_select,
+ .init = stlink_init,
+};
+
+static void stlink_constructor(void) __attribute__((constructor));
+static void stlink_constructor(void)
+{
+ transport_register(&stlink_jtag_transport);
+ transport_register(&stlink_swd_transport);
+}
+
+/** Returns true if the current debug session
+ * is using SWD as its transport.
+ */
+bool transport_is_stlink_jtag(void)
+{
+ return get_current_transport() == &stlink_jtag_transport;
+}
+
+/** Returns true if the current debug session
+ * is using SWD as its transport.
+ */
+bool transport_is_stlink_swd(void)
+{
+ return get_current_transport() == &stlink_swd_transport;
+}
diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c
index d9f3bd7..51d228b 100644
--- a/src/target/arm_adi_v5.c
+++ b/src/target/arm_adi_v5.c
@@ -331,6 +331,7 @@ static int mem_ap_write(struct adiv5_ap *ap, const uint8_t *buffer, uint32_t siz
const uint32_t csw_addrincr = addrinc ? CSW_ADDRINC_SINGLE : CSW_ADDRINC_OFF;
uint32_t csw_size;
uint32_t addr_xor;
+ uint32_t j = 0;
int retval = ERROR_OK;
/* TI BE-32 Quirks mode:
@@ -424,6 +425,9 @@ static int mem_ap_write(struct adiv5_ap *ap, const uint8_t *buffer, uint32_t siz
mem_ap_update_tar_cache(ap);
if (addrinc)
address += this_size;
+
+ if ((j++ % 32) == 0)
+ keep_alive();
}
/* REVISIT: Might want to have a queued version of this function that does not run. */
@@ -461,6 +465,7 @@ static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint
const uint32_t csw_addrincr = addrinc ? CSW_ADDRINC_SINGLE : CSW_ADDRINC_OFF;
uint32_t csw_size;
uint32_t address = adr;
+ uint32_t j = 0;
int retval = ERROR_OK;
/* TI BE-32 Quirks mode:
@@ -523,6 +528,9 @@ static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint
address += this_size;
mem_ap_update_tar_cache(ap);
+
+ if ((j++ % 32) == 0)
+ keep_alive();
}
if (retval == ERROR_OK)
@@ -594,24 +602,44 @@ static int mem_ap_read(struct adiv5_ap *ap, uint8_t *buffer, uint32_t size, uint
int mem_ap_read_buf(struct adiv5_ap *ap,
uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address)
{
+ if (ap->dap->ops->ap_mem_read) {
+ int retval = ap->dap->ops->ap_mem_read(ap, buffer, size, count, address, true);
+ if (retval != ERROR_OP_NOT_SUPPORTED)
+ return retval;
+ }
return mem_ap_read(ap, buffer, size, count, address, true);
}
int mem_ap_write_buf(struct adiv5_ap *ap,
const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address)
{
+ if (ap->dap->ops->ap_mem_write) {
+ int retval = ap->dap->ops->ap_mem_write(ap, buffer, size, count, address, true);
+ if (retval != ERROR_OP_NOT_SUPPORTED)
+ return retval;
+ }
return mem_ap_write(ap, buffer, size, count, address, true);
}
int mem_ap_read_buf_noincr(struct adiv5_ap *ap,
uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address)
{
+ if (ap->dap->ops->ap_mem_read) {
+ int retval = ap->dap->ops->ap_mem_read(ap, buffer, size, count, address, false);
+ if (retval != ERROR_OP_NOT_SUPPORTED)
+ return retval;
+ }
return mem_ap_read(ap, buffer, size, count, address, false);
}
int mem_ap_write_buf_noincr(struct adiv5_ap *ap,
const uint8_t *buffer, uint32_t size, uint32_t count, uint32_t address)
{
+ if (ap->dap->ops->ap_mem_write) {
+ int retval = ap->dap->ops->ap_mem_write(ap, buffer, size, count, address, false);
+ if (retval != ERROR_OP_NOT_SUPPORTED)
+ return retval;
+ }
return mem_ap_write(ap, buffer, size, count, address, false);
}
diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h
index a340b76..3b04d0f 100644
--- a/src/target/arm_adi_v5.h
+++ b/src/target/arm_adi_v5.h
@@ -292,6 +292,16 @@ struct dap_ops {
/** Optional; called at OpenOCD exit */
void (*quit)(struct adiv5_dap *dap);
+
+ /** Optional interface-specific optimized AP memory read */
+ int (*ap_mem_read)(struct adiv5_ap *ap, uint8_t *buffer,
+ uint32_t size, uint32_t count, uint32_t address,
+ bool addrinc);
+
+ /** Optional interface-specific optimized AP memory write */
+ int (*ap_mem_write)(struct adiv5_ap *ap, const uint8_t *buffer,
+ uint32_t size, uint32_t count, uint32_t address,
+ bool addrinc);
};
/*
diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c
index 3adb4ed..277a804 100644
--- a/src/target/arm_dap.c
+++ b/src/target/arm_dap.c
@@ -34,6 +34,8 @@ static LIST_HEAD(all_dap);
extern const struct dap_ops swd_dap_ops;
extern const struct dap_ops jtag_dp_ops;
+extern const struct dap_ops stlink_dap_swd_ops;
+extern const struct dap_ops stlink_dap_jtag_ops;
extern struct jtag_interface *jtag_interface;
/* DAP command support */
@@ -118,6 +120,13 @@ static int dap_init_all(void)
if (transport_is_swd()) {
dap->ops = &swd_dap_ops;
obj->swd = jtag_interface->swd;
+#if BUILD_HLADAPTER_STLINK == 1
+ } else if (transport_is_stlink_swd()) {
+ dap->ops = &stlink_dap_swd_ops;
+ obj->swd = jtag_interface->swd;
+ } else if (transport_is_stlink_jtag()) {
+ dap->ops = &stlink_dap_jtag_ops;
+#endif
} else
dap->ops = &jtag_dp_ops;
diff --git a/src/target/arm_dpm.h b/src/target/arm_dpm.h
index f8d1248..ac70ce3 100644
--- a/src/target/arm_dpm.h
+++ b/src/target/arm_dpm.h
@@ -188,8 +188,6 @@ void arm_dpm_report_wfar(struct arm_dpm *, uint32_t wfar);
#define DSCR_DTR_RX_FULL (0x1 << 30) /* bit 31 is reserved */
#define DSCR_ENTRY(dscr) ((dscr) & 0x3f)
-#define DSCR_RUN_MODE(dscr) ((dscr) & 0x03)
-
/* Methods of entry into debug mode */
#define DSCR_ENTRY_HALT_REQ (0x03)
@@ -240,4 +238,31 @@ void arm_dpm_report_dscr(struct arm_dpm *dpm, uint32_t dcsr);
#define OSLSR_OSLM1 (1 << 3)
#define OSLSR_OSLM (OSLSR_OSLM0|OSLSR_OSLM1)
+/* VCR (Vector Catch Register) bits */
+#define VCR_RESET (1 << 0)
+#define VCR_UNDEFINED (1 << 1)
+#define VCR_SUPERVISOR_CALL (1 << 2)
+#define VCR_PREFETCH_ABORT (1 << 3)
+#define VCR_DATA_ABORT (1 << 4)
+#define VCR_IRQ (1 << 6)
+#define VCR_FIQ (1 << 7)
+#define VCR_MONITOR_SECURE_CALL (1 << 10)
+#define VCR_MONITOR_PREFETCH_ABORT (1 << 11)
+#define VCR_MONITOR_DATA_ABORT (1 << 12)
+#define VCR_MONITOR_IRQ (1 << 14)
+#define VCR_MONITOR_FIQ (1 << 15)
+#define VCR_HYP_UNDEFINED (1 << 17)
+#define VCR_HYPERVISOR_CALL (1 << 18)
+#define VCR_HYPERVISOR_PREFETCH_ABORT (1 << 19)
+#define VCR_HYPERVISOR_DATA_ABORT (1 << 20)
+#define VCR_HYPERVISOR_TRAP_ENTRY (1 << 21)
+#define VCR_HYPERVISOR_IRQ (1 << 22)
+#define VCR_HYPERVISOR_FIQ (1 << 23)
+#define VCR_NONSEC_UNDEFINED (1 << 25)
+#define VCR_NONSEC_SUPERVISOR_CALL (1 << 26)
+#define VCR_NONSEC_PREFETCH_ABORT (1 << 27)
+#define VCR_NONSEC_DATA_ABORT (1 << 28)
+#define VCR_NONSEC_IRQ (1 << 30)
+#define VCR_NONSEC_FIQ (1 << 31)
+
#endif /* OPENOCD_TARGET_ARM_DPM_H */
diff --git a/src/target/armv7a.c b/src/target/armv7a.c
index eecfa70..c4a3b05 100644
--- a/src/target/armv7a.c
+++ b/src/target/armv7a.c
@@ -226,7 +226,8 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val)
LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor);
if ((first_lvl_descriptor & 0x3) == 0) {
- LOG_ERROR("Address translation failure");
+ /* Avoid LOG_ERROR, probably GDB is guessing the stack frame */
+ LOG_WARNING("Address translation failure [1]: va %8.8" PRIx32 "", va);
return ERROR_TARGET_TRANSLATION_FAULT;
}
@@ -258,7 +259,8 @@ int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val)
LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor);
if ((second_lvl_descriptor & 0x3) == 0) {
- LOG_ERROR("Address translation failure");
+ /* Avoid LOG_ERROR, probably GDB is guessing the stack frame */
+ LOG_WARNING("Address translation failure [2]: va %8.8" PRIx32 "", va);
return ERROR_TARGET_TRANSLATION_FAULT;
}
@@ -665,6 +667,19 @@ done:
}
+/**
+ * Clears register cache during reset process
+ */
+int armv7a_reset_clear_internal_state(struct target *target)
+{
+ struct armv7a_common *armv7a = target_to_armv7a(target);
+
+ if (target_was_examined(target))
+ register_cache_invalidate(armv7a->arm.core_cache);
+
+ return target_reset_clear_internal_state_default(target);
+}
+
static int armv7a_setup_semihosting(struct target *target, int enable)
{
struct armv7a_common *armv7a = target_to_armv7a(target);
diff --git a/src/target/armv7a.h b/src/target/armv7a.h
index 57779c6..1a83522 100644
--- a/src/target/armv7a.h
+++ b/src/target/armv7a.h
@@ -196,6 +196,8 @@ int armv7a_handle_cache_info_command(struct command_context *cmd_ctx,
struct armv7a_cache_common *armv7a_cache);
int armv7a_read_ttbcr(struct target *target);
+int armv7a_reset_clear_internal_state(struct target *target);
+
extern const struct command_registration armv7a_command_handlers[];
#endif /* OPENOCD_TARGET_ARMV7A_H */
diff --git a/src/target/armv7m.c b/src/target/armv7m.c
index 7d3bd73..af394f8 100644
--- a/src/target/armv7m.c
+++ b/src/target/armv7m.c
@@ -657,6 +657,19 @@ void armv7m_free_reg_cache(struct target *target)
arm->core_cache = NULL;
}
+/**
+ * Clears register cache during reset process
+ */
+int armv7m_reset_clear_internal_state(struct target *target)
+{
+ struct armv7m_common *armv7m = target_to_armv7m(target);
+
+ if (target_was_examined(target))
+ register_cache_invalidate(armv7m->arm.core_cache);
+
+ return target_reset_clear_internal_state_default(target);
+}
+
static int armv7m_setup_semihosting(struct target *target, int enable)
{
/* nothing todo for armv7m */
diff --git a/src/target/armv7m.h b/src/target/armv7m.h
index 01bf19e..f424c6c 100644
--- a/src/target/armv7m.h
+++ b/src/target/armv7m.h
@@ -218,7 +218,7 @@ int armv7m_wait_algorithm(struct target *target,
target_addr_t exit_point, int timeout_ms,
void *arch_info);
-int armv7m_invalidate_core_regs(struct target *target);
+int armv7m_reset_clear_internal_state(struct target *target);
int armv7m_restore_context(struct target *target);
diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c
index bc4aa33..f17eddd 100644
--- a/src/target/cortex_a.c
+++ b/src/target/cortex_a.c
@@ -878,7 +878,7 @@ static int cortex_a_poll(struct target *target)
return retval;
cortex_a->cpudbg_dscr = dscr;
- if (DSCR_RUN_MODE(dscr) == (DSCR_CORE_HALTED | DSCR_CORE_RESTARTED)) {
+ if (dscr & DSCR_CORE_HALTED) {
if (prev_target_state != TARGET_HALTED) {
/* We have a halting debug event */
LOG_DEBUG("Target halted");
@@ -1420,6 +1420,7 @@ static int cortex_a_step(struct target *target, int current, target_addr_t addre
/* Setup single step breakpoint */
stepbreakpoint.address = address;
+ stepbreakpoint.asid = 0;
stepbreakpoint.length = (arm->core_state == ARM_STATE_THUMB)
? 2 : 4;
stepbreakpoint.type = BKPT_HARD;
@@ -1901,76 +1902,165 @@ static int cortex_a_remove_breakpoint(struct target *target, struct breakpoint *
return ERROR_OK;
}
-/*
- * Cortex-A Reset functions
+/**
+ * Prepares debug state before reset or under active SRST if possible.
+ *
+ * @param halt sets vector catch to stop core at reset
+ * @param trigger triggers PRCR_WARM_RESET.
*/
-
-static int cortex_a_assert_reset(struct target *target)
+static int cortex_a_reset_prepare_trigger(struct target *target, bool halt, bool trigger)
{
struct armv7a_common *armv7a = target_to_armv7a(target);
+ uint32_t reg;
+ int retval;
- LOG_DEBUG(" ");
+ /* cannot talk to target if it wasn't examined yet */
+ if (!target_was_examined(target))
+ return ERROR_OK;
- /* FIXME when halt is requested, make it work somehow... */
+ /*
+ * Some cores support connecting while SRST is asserted.
+ * If RESET_HAS_SRST and RESET_SRST_NO_GATING are configured, core is
+ * under SRST now
+ */
- /* This function can be called in "target not examined" state */
+ if (halt) {
+ /* Enable debug requests */
+ retval = mem_ap_read_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DSCR, &reg);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DSCR, reg | DSCR_HALT_DBG_MODE);
+ if (retval != ERROR_OK)
+ return retval;
- /* Issue some kind of warm reset. */
- if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT))
- target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
- else if (jtag_get_reset_config() & RESET_HAS_SRST) {
- /* REVISIT handle "pulls" cases, if there's
- * hardware that needs them to work.
- */
+ /* set the reset vector catch */
+ retval = mem_ap_read_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_VCR, &reg);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_VCR, reg | VCR_RESET);
+ if (retval != ERROR_OK)
+ return retval;
- /*
- * FIXME: fix reset when transport is SWD. This is a temporary
- * work-around for release v0.10 that is not intended to stay!
- */
- if (transport_is_swd() ||
- (target->reset_halt && (jtag_get_reset_config() & RESET_SRST_NO_GATING)))
- jtag_add_reset(0, 1);
+ /* halt the core */
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DRCR, DRCR_HALT);
+ if (retval != ERROR_OK)
+ return retval;
+ /* hold core in reset */
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_PRCR, PRCR_HOLD_NON_DEBUG_RESET);
+ if (retval != ERROR_OK)
+ return retval;
} else {
- LOG_ERROR("%s: how to reset?", target_name(target));
- return ERROR_FAIL;
+ /* clear the reset vector catch */
+ retval = mem_ap_read_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_VCR, &reg);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_VCR, reg & (~VCR_RESET));
+
+ /* resume the core if it was halted */
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DRCR, DRCR_RESTART);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* resume the core if it was holding the reset */
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_PRCR, 0);
+ if (retval != ERROR_OK)
+ return retval;
}
- /* registers are now invalid */
- if (target_was_examined(target))
- register_cache_invalidate(armv7a->arm.core_cache);
+ if (trigger) {
+ /*
+ * Use a standard Cortex-A software reset mechanism.
+ * PRCR_WARM_RESET is 'recommended' but 'implementation defined'.
+ * See DDI0406C ARMv7-A/R Architecture Reference Manual.
+ * Usually this has the disadvantage of not resetting the peripherals,
+ * so a reset-init event handler is needed to perform any peripheral
+ * resets.
+ */
+ reg = (halt ? PRCR_HOLD_NON_DEBUG_RESET : 0) | PRCR_WARM_RESET;
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_PRCR, reg);
+ if (retval != ERROR_OK)
+ return retval;
+ }
- target->state = TARGET_RESET;
+ armv7a_reset_clear_internal_state(target);
return ERROR_OK;
}
-static int cortex_a_deassert_reset(struct target *target)
+static int cortex_a_post_deassert_reset(struct target *target)
{
+ struct armv7a_common *armv7a = target_to_armv7a(target);
+ uint32_t reg;
int retval;
- LOG_DEBUG(" ");
+ /* cannot talk to target if it wasn't examined yet */
+ if (!target_was_examined(target))
+ return ERROR_OK;
+
+ if (!target->reset_halt)
+ return ERROR_OK;
+
+ /* Enable debug requests */
+ retval = mem_ap_read_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DSCR, &reg);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DSCR, reg | DSCR_HALT_DBG_MODE);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* halt the core while reset is on hold */
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_DRCR, DRCR_HALT);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* resume the core from reset hold */
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_PRCR, 0);
+ if (retval != ERROR_OK)
+ return retval;
+
+ /* clear the reset vector-catch */
+ retval = mem_ap_read_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_VCR, &reg);
+ if (retval != ERROR_OK)
+ return retval;
+ retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
+ armv7a->debug_base + CPUDBG_VCR, reg & (~VCR_RESET));
+ if (retval != ERROR_OK)
+ return retval;
- /* be certain SRST is off */
- jtag_add_reset(0, 0);
+ retval = cortex_a_poll(target);
+ if (retval != ERROR_OK)
+ return retval;
- if (target_was_examined(target)) {
+ for (int i = 0; i < 50 && target->state != TARGET_HALTED; i++) {
+ alive_sleep(1);
retval = cortex_a_poll(target);
if (retval != ERROR_OK)
return retval;
}
- if (target->reset_halt) {
- if (target->state != TARGET_HALTED) {
- LOG_WARNING("%s: ran after reset and before halt ...",
- target_name(target));
- if (target_was_examined(target)) {
- retval = target_halt(target);
- if (retval != ERROR_OK)
- return retval;
- } else
- target->state = TARGET_UNKNOWN;
- }
+ 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;
}
return ERROR_OK;
@@ -2749,6 +2839,10 @@ static int cortex_a_read_memory_ahb(struct target *target, target_addr_t address
LOG_DEBUG("Reading memory at address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32,
address, size, count);
+ /* flush the data cache */
+ if (armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
+ armv7a_l1_d_cache_clean_virt(target, address, size * count);
+
/* determine if MMU was enabled on target stop */
if (!armv7a->is_armv7r) {
retval = cortex_a_mmu(target, &mmu_enabled);
@@ -2756,24 +2850,42 @@ static int cortex_a_read_memory_ahb(struct target *target, target_addr_t address
return retval;
}
- if (mmu_enabled) {
+ if (!count || !buffer)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (!mmu_enabled)
+ return mem_ap_read_buf(armv7a->memory_ap, buffer, size, count, address);
+
+ /*
+ * TODO: here we are pessimistic and we use the smaller page of 4kB, but
+ * cortex_a_virt2phys() does a full table walk, so can return the actual
+ * size of page (4kB or 64kB) or section (1MB or 16Mb).
+ */
+ while (count) {
+ target_addr_t page_size = 0x1000;
+ uint32_t current_count;
+
virt = address;
retval = cortex_a_virt2phys(target, virt, &phys);
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("Reading at virtual address. "
- "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT,
- virt, phys);
- address = phys;
- }
-
- if (!count || !buffer)
- return ERROR_COMMAND_SYNTAX_ERROR;
+ current_count = (page_size - (address & (page_size - 1))) / size;
+ if (current_count > count)
+ current_count = count;
- retval = mem_ap_read_buf(armv7a->memory_ap, buffer, size, count, address);
+ LOG_DEBUG("Reading at virtual address 0x%" PRIx32 " bytes. "
+ "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT,
+ size * current_count, virt, phys);
- return retval;
+ retval = mem_ap_read_buf(armv7a->memory_ap, buffer, size, current_count, phys);
+ if (retval != ERROR_OK)
+ return retval;
+ count -= current_count;
+ address += size * current_count;
+ buffer += size * current_count;
+ }
+ return ERROR_OK;
}
static int cortex_a_write_phys_memory(struct target *target,
@@ -2837,6 +2949,12 @@ static int cortex_a_write_memory_ahb(struct target *target, target_addr_t addres
LOG_DEBUG("Writing memory at address " TARGET_ADDR_FMT "; size %" PRId32 "; count %" PRId32,
address, size, count);
+ /* flush and invalidate the data cache */
+ if (armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
+ armv7a_cache_flush_virt(target, address, size * count);
+ /* invalidate instruction cache */
+ armv7a_l1_i_cache_inval_virt(target, address, size * count);
+
/* determine if MMU was enabled on target stop */
if (!armv7a->is_armv7r) {
retval = cortex_a_mmu(target, &mmu_enabled);
@@ -2844,25 +2962,42 @@ static int cortex_a_write_memory_ahb(struct target *target, target_addr_t addres
return retval;
}
- if (mmu_enabled) {
+ if (!count || !buffer)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (!mmu_enabled)
+ return mem_ap_write_buf(armv7a->memory_ap, buffer, size, count, address);
+
+ /*
+ * TODO: here we are pessimistic and we use the smaller page of 4kB, but
+ * cortex_a_virt2phys() does a full table walk, so can return the actual
+ * size of page (4kB or 64kB) or section (1MB or 16Mb).
+ */
+ while (count) {
+ target_addr_t page_size = 0x1000;
+ uint32_t current_count;
+
virt = address;
retval = cortex_a_virt2phys(target, virt, &phys);
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("Writing to virtual address. "
- "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT,
- virt,
- phys);
- address = phys;
- }
-
- if (!count || !buffer)
- return ERROR_COMMAND_SYNTAX_ERROR;
+ current_count = (page_size - (address & (page_size - 1))) / size;
+ if (current_count > count)
+ current_count = count;
- retval = mem_ap_write_buf(armv7a->memory_ap, buffer, size, count, address);
+ LOG_DEBUG("Writing to virtual address 0x%" PRIx32 " bytes. "
+ "Translating v:" TARGET_ADDR_FMT " to r:" TARGET_ADDR_FMT,
+ size * current_count, virt, phys);
- return retval;
+ retval = mem_ap_write_buf(armv7a->memory_ap, buffer, size, current_count, phys);
+ if (retval != ERROR_OK)
+ return retval;
+ count -= current_count;
+ address += size * current_count;
+ buffer += size * current_count;
+ }
+ return ERROR_OK;
}
static int cortex_a_read_buffer(struct target *target, target_addr_t address,
@@ -2982,7 +3117,21 @@ static int cortex_a_examine_first(struct target *target)
int i;
int retval = ERROR_OK;
- uint32_t didr, cpuid, dbg_osreg;
+ uint32_t didr, cpuid, dbg_osreg, dp_ctrl_stat;
+
+ /*
+ * Some device turn off CSYSPWRUPACK or CDBGPWRUPACK at reset.
+ * Check them and eventually re init the DP.
+ */
+ retval = dap_queue_dp_read(swjdp, DP_CTRL_STAT, &dp_ctrl_stat);
+ if (retval != ERROR_OK
+ || ((dp_ctrl_stat & CDBGPWRUPACK) == 0)
+ || (!swjdp->ignore_syspwrupack && ((dp_ctrl_stat & CSYSPWRUPACK) == 0))) {
+ LOG_INFO("DP disabled after reset. Re-initialize it!");
+ retval = dap_dp_init(swjdp);
+ if (retval != ERROR_OK)
+ LOG_ERROR("DP initialization failed");
+ }
/* Search for the APB-AP - it is needed for access to debug registers */
retval = dap_find_ap(swjdp, AP_TYPE_APB_AP, &armv7a->debug_ap);
@@ -2999,19 +3148,23 @@ static int cortex_a_examine_first(struct target *target)
armv7a->debug_ap->memaccess_tck = 80;
- /* Search for the AHB-AB.
- * REVISIT: We should search for AXI-AP as well and make sure the AP's MEMTYPE says it
+ /* Search for the AXI-AP or the AHB-AP.
+ * REVISIT: We should make sure the AP's MEMTYPE says it
* can access system memory. */
armv7a->memory_ap_available = false;
- retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7a->memory_ap);
- if (retval == ERROR_OK) {
+ retval = dap_find_ap(swjdp, AP_TYPE_AXI_AP, &armv7a->memory_ap);
+ if (retval == ERROR_OK)
retval = mem_ap_init(armv7a->memory_ap);
+ if (retval != ERROR_OK) {
+ retval = dap_find_ap(swjdp, AP_TYPE_AHB_AP, &armv7a->memory_ap);
if (retval == ERROR_OK)
- armv7a->memory_ap_available = true;
+ retval = mem_ap_init(armv7a->memory_ap);
}
if (retval != ERROR_OK) {
- /* AHB-AP not found or unavailable - use the CPU */
- LOG_DEBUG("No AHB-AP available for memory access");
+ /* AHB-AP and AXI-AP not found or unavailable - use the CPU */
+ LOG_DEBUG("Nor AHB-AP nor AXI-AP available for memory access");
+ } else {
+ armv7a->memory_ap_available = true;
}
if (!target->dbgbase_set) {
@@ -3508,8 +3661,9 @@ struct target_type cortexa_target = {
.resume = cortex_a_resume,
.step = cortex_a_step,
- .assert_reset = cortex_a_assert_reset,
- .deassert_reset = cortex_a_deassert_reset,
+ .reset_clear_internal_state = armv7a_reset_clear_internal_state,
+ .reset_prepare_trigger = cortex_a_reset_prepare_trigger,
+ .deassert_reset = cortex_a_post_deassert_reset,
/* REVISIT allow exporting VFP3 registers ... */
.get_gdb_reg_list = arm_get_gdb_reg_list,
@@ -3587,8 +3741,9 @@ struct target_type cortexr4_target = {
.resume = cortex_a_resume,
.step = cortex_a_step,
- .assert_reset = cortex_a_assert_reset,
- .deassert_reset = cortex_a_deassert_reset,
+ .reset_clear_internal_state = armv7a_reset_clear_internal_state,
+ .reset_prepare_trigger = cortex_a_reset_prepare_trigger,
+ .deassert_reset = cortex_a_post_deassert_reset,
/* REVISIT allow exporting VFP3 registers ... */
.get_gdb_reg_list = arm_get_gdb_reg_list,
diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c
index 07fea51..42eb4b2 100644
--- a/src/target/cortex_m.c
+++ b/src/target/cortex_m.c
@@ -948,49 +948,24 @@ static int cortex_m_step(struct target *target, int current,
return ERROR_OK;
}
-static int cortex_m_assert_reset(struct target *target)
+/**
+ * Prepares debug state before reset or under active SRST if possible.
+ *
+ * @param halt sets vector catch to stop core at reset
+ * @param trigger triggers SYSRESETREQ or VECTRESET.
+ */
+int cortex_m_reset_prepare_trigger(struct target *target, bool halt, bool trigger)
{
struct cortex_m_common *cortex_m = target_to_cm(target);
struct armv7m_common *armv7m = &cortex_m->armv7m;
enum cortex_m_soft_reset_config reset_config = cortex_m->soft_reset_config;
- LOG_DEBUG("target->state: %s",
- target_state_name(target));
-
- enum reset_types jtag_reset_config = jtag_get_reset_config();
-
- if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
- /* allow scripts to override the reset event */
-
- target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
- register_cache_invalidate(cortex_m->armv7m.arm.core_cache);
- target->state = TARGET_RESET;
-
+ /* cannot talk to target if it wasn't examined yet */
+ if (!target_was_examined(target))
return ERROR_OK;
- }
-
- /* some cores support connecting while srst is asserted
- * use that mode is it has been configured */
-
- bool srst_asserted = false;
-
- if (!target_was_examined(target)) {
- if (jtag_reset_config & RESET_HAS_SRST) {
- adapter_assert_reset();
- if (target->reset_halt)
- LOG_ERROR("Target not examined, will not halt after reset!");
- return ERROR_OK;
- } else {
- LOG_ERROR("Target not examined, reset NOT asserted!");
- return ERROR_FAIL;
- }
- }
- if ((jtag_reset_config & RESET_HAS_SRST) &&
- (jtag_reset_config & RESET_SRST_NO_GATING)) {
- adapter_assert_reset();
- srst_asserted = true;
- }
+ /* Some cores support connecting while srst is asserted.
+ * If RESET_HAS_SRST and RESET_SRST_NO_GATING are configured, core is under SRST now */
/* Enable debug requests */
int retval;
@@ -1008,17 +983,7 @@ static int cortex_m_assert_reset(struct target *target)
mem_ap_write_u32(armv7m->debug_ap, DCB_DCRDR, 0);
/* Ignore less important errors */
- if (!target->reset_halt) {
- /* Set/Clear C_MASKINTS in a separate operation */
- if (cortex_m->dcb_dhcsr & C_MASKINTS)
- cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
-
- /* clear any debug flags before resuming */
- cortex_m_clear_halt(target);
-
- /* clear C_HALT in dhcsr reg */
- cortex_m_write_debug_halt_mask(target, 0, C_HALT);
- } else {
+ if (halt) {
/* Halt in debug on reset; endreset_event() restores DEMCR.
*
* REVISIT catching BUSERR presumably helps to defend against
@@ -1030,16 +995,19 @@ static int cortex_m_assert_reset(struct target *target)
TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET);
if (retval != ERROR_OK || retval2 != ERROR_OK)
LOG_INFO("AP write error, reset will not halt");
- }
+ } else {
+ /* Set/Clear C_MASKINTS in a separate operation */
+ if (cortex_m->dcb_dhcsr & C_MASKINTS)
+ cortex_m_write_debug_halt_mask(target, 0, C_MASKINTS);
- if (jtag_reset_config & RESET_HAS_SRST) {
- /* default to asserting srst */
- if (!srst_asserted)
- adapter_assert_reset();
+ /* clear any debug flags before resuming */
+ cortex_m_clear_halt(target);
- /* srst is asserted, ignore AP access errors */
- retval = ERROR_OK;
- } else {
+ /* clear C_HALT in dhcsr reg */
+ cortex_m_write_debug_halt_mask(target, 0, C_HALT);
+ }
+
+ if (trigger) {
/* Use a standard Cortex-M3 software reset mechanism.
* We default to using VECRESET as it is supported on all current cores.
* This has the disadvantage of not resetting the peripherals, so a
@@ -1059,7 +1027,7 @@ static int cortex_m_assert_reset(struct target *target)
? AIRCR_SYSRESETREQ : AIRCR_VECTRESET));
if (retval3 != ERROR_OK)
LOG_DEBUG("Ignoring AP write error right after reset");
-
+#if 0
retval3 = dap_dp_init(armv7m->debug_ap->dap);
if (retval3 != ERROR_OK)
LOG_ERROR("DP initialisation failed");
@@ -1072,41 +1040,66 @@ static int cortex_m_assert_reset(struct target *target)
uint32_t tmp;
mem_ap_read_atomic_u32(armv7m->debug_ap, NVIC_AIRCR, &tmp);
}
+#endif
+ } else {
+ /* srst is asserted, ignore AP access errors */
+ retval = ERROR_OK;
}
- target->state = TARGET_RESET;
- jtag_add_sleep(50000);
-
- register_cache_invalidate(cortex_m->armv7m.arm.core_cache);
+ armv7m_reset_clear_internal_state(target);
/* now return stored error code if any */
if (retval != ERROR_OK)
return retval;
-
- if (target->reset_halt) {
+#if 0
+ if (halt) {
retval = target_halt(target);
if (retval != ERROR_OK)
return retval;
}
-
+#endif
return ERROR_OK;
}
-static int cortex_m_deassert_reset(struct target *target)
+#if 0
+static int cortex_m_assert_reset(struct target *target)
+{
+ LOG_DEBUG("target->state: %s",
+ target_state_name(target));
+
+ if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
+ /* allow scripts to override the reset event */
+
+ target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
+
+ struct cortex_m_common *cortex_m = target_to_cm(target);
+ register_cache_invalidate(cortex_m->armv7m.arm.core_cache);
+ target->state = TARGET_RESET;
+
+ return ERROR_OK;
+ }
+
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+ return cortex_m_prepare_reset(target, target->reset_halt,
+ (jtag_reset_config & RESET_HAS_SRST) == 0);
+}
+
+static int cortex_m_post_deassert_reset(struct target *target)
{
struct armv7m_common *armv7m = &target_to_cm(target)->armv7m;
LOG_DEBUG("target->state: %s",
target_state_name(target));
- /* deassert reset lines */
- adapter_deassert_reset();
+ /* SRST was deasserted in the previous Tcl call to reset_deassert_initial */
+ /* adapter_deassert_reset(); is no more needed here */
enum reset_types jtag_reset_config = jtag_get_reset_config();
- if ((jtag_reset_config & RESET_HAS_SRST) &&
- !(jtag_reset_config & RESET_SRST_NO_GATING) &&
- target_was_examined(target)) {
+ if ((jtag_reset_config & RESET_HAS_SRST)
+ && target->dbg_under_srst != DBG_UNDER_SRST_WORKING
+ && target_was_examined(target)) {
int retval = dap_dp_init(armv7m->debug_ap->dap);
if (retval != ERROR_OK) {
LOG_ERROR("DP initialisation failed");
@@ -1116,6 +1109,7 @@ static int cortex_m_deassert_reset(struct target *target)
return ERROR_OK;
}
+#endif
int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint)
{
@@ -2495,8 +2489,10 @@ struct target_type cortexm_target = {
.resume = cortex_m_resume,
.step = cortex_m_step,
- .assert_reset = cortex_m_assert_reset,
- .deassert_reset = cortex_m_deassert_reset,
+ .reset_clear_internal_state = armv7m_reset_clear_internal_state,
+ .reset_prepare_trigger = cortex_m_reset_prepare_trigger,
+/* .assert_reset = cortex_m_assert_reset,*/
+/* .deassert_reset = cortex_m_post_deassert_reset,*/
.soft_reset_halt = cortex_m_soft_reset_halt,
.get_gdb_reg_list = armv7m_get_gdb_reg_list,
diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h
index 2daf4cb..e6a2ab3 100644
--- a/src/target/cortex_m.h
+++ b/src/target/cortex_m.h
@@ -200,6 +200,7 @@ target_to_cm(struct target *target)
}
int cortex_m_examine(struct target *target);
+int cortex_m_prepare_reset(struct target *target, bool halt, bool without_srst);
int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint);
int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoint);
int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint);
diff --git a/src/target/startup.tcl b/src/target/startup.tcl
index cf844e1..10ee27c 100644
--- a/src/target/startup.tcl
+++ b/src/target/startup.tcl
@@ -10,12 +10,14 @@ set in_process_reset 0
# Catch reset recursion
proc ocd_process_reset { MODE } {
global in_process_reset
+ global arp_reset_mode
if {$in_process_reset} {
set in_process_reset 0
return -code error "'reset' can not be invoked recursively"
}
set in_process_reset 1
+ set arp_reset_mode $MODE
set success [expr [catch {ocd_process_reset_inner $MODE} result]==0]
set in_process_reset 0
@@ -26,6 +28,158 @@ proc ocd_process_reset { MODE } {
}
}
+
+proc arp_is_tap_enabled { target } {
+ if (![using_jtag]) {
+ return 1
+ }
+ return [jtag tapisenabled [$target cget -chain-position]]
+}
+
+# duplicate of target_examine_one(), keep in sync
+proc arp_examine_one { target } {
+ if [arp_is_tap_enabled $target] {
+ $target invoke-event examine-start
+ set err [catch "$target arp_examine allow-defer"]
+ if { $err } {
+ $target invoke-event examine-fail
+ } else {
+ $target invoke-event examine-end
+ }
+ }
+}
+
+
+proc arp_reset_plan_no_srst { phase target } {
+ global arp_reset_mode
+ switch $phase {
+ pre {
+ arp_examine_one $target
+ }
+ middle {
+ $target arp_reset trigger $arp_reset_mode
+ }
+ post {
+ $target arp_reset post_deassert $arp_reset_mode
+ }
+ }
+}
+
+proc arp_reset_plan_srst_dbg_Working { phase target } {
+ global arp_reset_mode
+ switch $phase {
+ pre {
+ # srst_nogate: SRST is asserted now
+ # srst_gates_jtag: SRST has not been asserted yet
+ }
+ middle {
+ # SRST is asserted, target is responsive
+ arp_examine_one $target
+ $target arp_reset prepare $arp_reset_mode
+ }
+ post {
+ $target arp_reset post_deassert $arp_reset_mode
+ }
+ }
+}
+
+proc arp_reset_plan_srst_dbg_gated { phase target } {
+ global arp_reset_mode
+ switch $phase {
+ pre {
+ # SRST has not been asserted yet
+ # srst_nogate mode is not supported
+ $target arp_reset prepare $arp_reset_mode
+ }
+ middle {
+ # SRST is asserted, target debug gated
+ }
+ post {
+ arp_examine_one $target
+ $target arp_reset post_deassert $arp_reset_mode
+ }
+ }
+}
+
+proc arp_reset_plan_srst_dbg_cleared { phase target } {
+ global arp_reset_mode
+ switch $phase {
+ pre {
+ # target debug is going to be cleared
+ # no point to pre-configure is
+ }
+ middle {
+ # SRST is asserted, target debug not accessible
+ if { [using_hla] } {
+ # hla_target controls SRST on its own and even worse
+ # an hl adapter may not export SRST control in API (ST-Link 1)
+ # The only possible workaround is to let the target control SRST
+ $target arp_reset assert $arp_reset_mode
+ } else {
+ $target arp_reset clear_internal_state $arp_reset_mode
+ }
+ }
+ post {
+ arp_examine_one $target
+ $target arp_reset post_deassert $arp_reset_mode
+ }
+ }
+}
+
+proc arp_reset_default_handler { phase target } {
+ if [arp_is_tap_enabled $target] {
+ set dbg_u_srst [$target cget -dbg-under-srst]
+ if [reset_config_includes srst] {
+ switch $dbg_u_srst {
+ working {
+ arp_reset_plan_srst_dbg_Working $phase $target
+ }
+ gated {
+ arp_reset_plan_srst_dbg_gated $phase $target
+ }
+ cleared {
+ arp_reset_plan_srst_dbg_cleared $phase $target
+ }
+ unknown {
+ if [reset_config_includes srst_nogate] {
+ arp_reset_plan_srst_dbg_Working $phase $target
+ } else {
+ arp_reset_plan_srst_dbg_gated $phase $target
+ }
+ }
+ }
+ } else {
+ arp_reset_plan_no_srst $phase $target
+ }
+ }
+}
+
+proc arp_reset_halt_default_handler { target } {
+ # Wait upto 1 second for target to halt. Why 1sec? Cause
+ # the JTAG tap reset signal might be hooked to a slow
+ # resistor/capacitor circuit - and it might take a while
+ # to charge
+ catch { $target arp_waitstate halted 1000 }
+}
+
+# Utility to make 'reset halt' work as reset;halt on a target
+# It does not prevent running code after reset
+proc arp_reset_simple_halter { target } {
+ $target arp_poll
+ set st [$target curstate]
+ if { $st eq "reset" } {
+ # we assume running state follows
+ # if reset accidentaly halt, waiting is useless
+ catch { $target arp_waitstate running 1000 }
+ set st [$target curstate]
+ }
+ if { $st eq "running" } {
+ echo "$target: Ran after reset and before halt..."
+ $target arp_halt
+ }
+ arp_reset_halt_default_handler $target
+}
+
proc ocd_process_reset_inner { MODE } {
set targets [target names]
@@ -53,51 +207,53 @@ proc ocd_process_reset_inner { MODE } {
# relative to a previous restrictive scheme
foreach t $targets {
- # New event script.
$t invoke-event reset-start
}
- # Use TRST or TMS/TCK operations to reset all the tap controllers.
- # TAP reset events get reported; they might enable some taps.
- init_reset $MODE
-
- # Examine all targets on enabled taps.
- foreach t $targets {
- if {![using_jtag] || [jtag tapisenabled [$t cget -chain-position]]} {
- $t invoke-event examine-start
- set err [catch "$t arp_examine allow-defer"]
- if { $err == 0 } {
- $t invoke-event examine-end
+ # If srst_nogate is set, check all targets whether they support it
+ if {[reset_config_includes srst srst_nogate]} {
+ foreach t $targets {
+ set dbg_u_srst [$t cget -dbg-under-srst]
+ if {$dbg_u_srst eq "gated"} {
+ reset_config srst_gates_jtag
+ echo "'srst_nogate' is not supported by at least target $t"
+ echo "Reset config changed to 'srst_gates_jtag'"
+ break;
}
}
}
+ set early_reset_init [expr {[reset_config_includes independent_trst]
+ || [reset_config_includes srst srst_nogate]}]
- # Assert SRST, and report the pre/post events.
- # Note: no target sees SRST before "pre" or after "post".
- foreach t $targets {
- $t invoke-event reset-assert-pre
+ if $early_reset_init {
+ # We have an independent trst or no-gating srst
+
+ # Use TRST or TMS/TCK operations to reset all the tap controllers.
+ # TAP reset events get reported; they might enable some taps.
+ init_reset $MODE
}
+
foreach t $targets {
- # C code needs to know if we expect to 'halt'
- if {![using_jtag] || [jtag tapisenabled [$t cget -chain-position]]} {
- $t arp_reset assert $halt
- }
+ $t invoke-event reset-assert-pre
}
+
+ # Assert SRST
+ reset_assert_final $MODE
+
foreach t $targets {
$t invoke-event reset-assert-post
}
- # Now de-assert SRST, and report the pre/post events.
- # Note: no target sees !SRST before "pre" or after "post".
foreach t $targets {
$t invoke-event reset-deassert-pre
}
- foreach t $targets {
- # Again, de-assert code needs to know if we 'halt'
- if {![using_jtag] || [jtag tapisenabled [$t cget -chain-position]]} {
- $t arp_reset deassert $halt
- }
+
+ # Deassert SRST
+ reset_deassert_initial $MODE
+ if { !$early_reset_init } {
+ if [using_jtag] { jtag arp_init }
}
+
foreach t $targets {
$t invoke-event reset-deassert-post
}
@@ -107,7 +263,7 @@ proc ocd_process_reset_inner { MODE } {
# first executing any instructions.
if { $halt } {
foreach t $targets {
- if {[using_jtag] && ![jtag tapisenabled [$t cget -chain-position]]} {
+ if {![arp_is_tap_enabled $t]} {
continue
}
@@ -117,18 +273,10 @@ proc ocd_process_reset_inner { MODE } {
continue
}
- # Wait upto 1 second for target to halt. Why 1sec? Cause
- # the JTAG tap reset signal might be hooked to a slow
- # resistor/capacitor circuit - and it might take a while
- # to charge
-
- # Catch, but ignore any errors.
- catch { $t arp_waitstate halted 1000 }
+ $t invoke-event reset-halt
# Did we succeed?
- set s [$t curstate]
-
- if { 0 != [string compare $s "halted" ] } {
+ if { [$t curstate] ne "halted" } {
return -code error [format "TARGET: %s - Not halted" $t]
}
}
@@ -137,7 +285,7 @@ proc ocd_process_reset_inner { MODE } {
#Pass 2 - if needed "init"
if { 0 == [string compare init $MODE] } {
foreach t $targets {
- if {[using_jtag] && ![jtag tapisenabled [$t cget -chain-position]]} {
+ if {![arp_is_tap_enabled $t]} {
continue
}
@@ -147,11 +295,7 @@ proc ocd_process_reset_inner { MODE } {
continue
}
- set err [catch "$t arp_waitstate halted 5000"]
- # Did it halt?
- if { $err == 0 } {
- $t invoke-event reset-init
- }
+ $t invoke-event reset-init
}
}
@@ -162,12 +306,12 @@ proc ocd_process_reset_inner { MODE } {
proc using_jtag {} {
set _TRANSPORT [ transport select ]
- expr { [ string first "jtag" $_TRANSPORT ] != -1 }
+ expr { [ string equal "jtag" $_TRANSPORT ] != 0 || [ string equal "hla_jtag" $_TRANSPORT ] != 0 }
}
proc using_swd {} {
set _TRANSPORT [ transport select ]
- expr { [ string first "swd" $_TRANSPORT ] != -1 }
+ expr { [ string equal "swd" $_TRANSPORT ] != 0 || [ string equal "hla_swd" $_TRANSPORT ] != 0}
}
proc using_hla {} {
@@ -175,6 +319,16 @@ proc using_hla {} {
expr { [ string first "hla" $_TRANSPORT ] != -1 }
}
+proc using_stlink_jtag {} {
+ set _TRANSPORT [ transport select ]
+ expr { [ string equal "stlink_jtag" $_TRANSPORT ] != 0 }
+}
+
+proc using_stlink_swd {} {
+ set _TRANSPORT [ transport select ]
+ expr { [ string equal "stlink_swd" $_TRANSPORT ] != 0 }
+}
+
#########
# Temporary migration aid. May be removed starting in January 2011.
@@ -204,6 +358,10 @@ proc init_target_events {} {
set_default_target_event $t gdb-flash-erase-start "reset init"
set_default_target_event $t gdb-flash-write-end "reset halt"
set_default_target_event $t gdb-attach "halt"
+ set_default_target_event $t reset-assert-pre "arp_reset_default_handler pre $t"
+ set_default_target_event $t reset-assert-post "arp_reset_default_handler middle $t"
+ set_default_target_event $t reset-deassert-post "arp_reset_default_handler post $t"
+ set_default_target_event $t reset-halt "arp_reset_halt_default_handler $t"
}
}
diff --git a/src/target/target.c b/src/target/target.c
index 478c39d..906f1be 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -74,6 +74,8 @@ static int target_gdb_fileio_end_default(struct target *target, int retcode,
int fileio_errno, bool ctrl_c);
static int target_profiling_default(struct target *target, uint32_t *samples,
uint32_t max_num_samples, uint32_t *num_samples, uint32_t seconds);
+static void target_free_all_working_areas_restore(struct target *target, int restore);
+static void target_reset_examined(struct target *target);
/* targets */
extern struct target_type arm7tdmi_target;
@@ -163,6 +165,10 @@ static const Jim_Nvp nvp_assert[] = {
{ .name = "F", NVP_DEASSERT },
{ .name = "t", NVP_ASSERT },
{ .name = "f", NVP_DEASSERT },
+ { .name = "prepare", NVP_PREPARE },
+ { .name = "trigger", NVP_TRIGGER },
+ { .name = "post_deassert", NVP_POST_DEASSERT },
+ { .name = "clear_internal_state", NVP_CLEAR_INTERNAL_STATE },
{ .name = NULL, .value = -1 }
};
@@ -209,10 +215,12 @@ static const Jim_Nvp nvp_target_event[] = {
{ .value = TARGET_EVENT_RESET_ASSERT_POST, .name = "reset-assert-post" },
{ .value = TARGET_EVENT_RESET_DEASSERT_PRE, .name = "reset-deassert-pre" },
{ .value = TARGET_EVENT_RESET_DEASSERT_POST, .name = "reset-deassert-post" },
+ { .value = TARGET_EVENT_RESET_HALT, .name = "reset-halt" },
{ .value = TARGET_EVENT_RESET_INIT, .name = "reset-init" },
{ .value = TARGET_EVENT_RESET_END, .name = "reset-end" },
{ .value = TARGET_EVENT_EXAMINE_START, .name = "examine-start" },
+ { .value = TARGET_EVENT_EXAMINE_FAIL, .name = "examine-fail" },
{ .value = TARGET_EVENT_EXAMINE_END, .name = "examine-end" },
{ .value = TARGET_EVENT_DEBUG_HALTED, .name = "debug-halted" },
@@ -269,6 +277,14 @@ static const Jim_Nvp nvp_reset_modes[] = {
{ .name = NULL , .value = -1 },
};
+static const Jim_Nvp nvp_dbg_under_srst[] = {
+ { .name = "unknown", .value = DBG_UNDER_SRST_UNKNOWN },
+ { .name = "working", .value = DBG_UNDER_SRST_WORKING },
+ { .name = "gated", .value = DBG_UNDER_SRST_GATED },
+ { .name = "cleared", .value = DBG_UNDER_SRST_CLEARED },
+ { .name = NULL , .value = -1 },
+};
+
const char *debug_reason_name(struct target *t)
{
const char *cp;
@@ -697,18 +713,23 @@ static int default_examine(struct target *target)
}
/* no check by default */
+/*TODO: remove and use reset_deassert_post or reset_end event */
static int default_check_reset(struct target *target)
{
return ERROR_OK;
}
+/* Equvivalent Tcl code arp_examine_one is in src/target/startup.tcl
+ * Keep in sync */
int target_examine_one(struct target *target)
{
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START);
int retval = target->type->examine(target);
- if (retval != ERROR_OK)
+ if (retval != ERROR_OK) {
+ target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_FAIL);
return retval;
+ }
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
@@ -760,6 +781,41 @@ const char *target_type_name(struct target *target)
return target->type->name;
}
+int target_reset_prepare_trigger(struct target *target, bool halt, bool trigger)
+{
+ if (!target->type->reset_prepare_trigger) {
+ if (halt || trigger) {
+ LOG_ERROR("Target %s does not support reset_prepare_trigger",
+ target_name(target));
+ return ERROR_FAIL;
+ }
+ LOG_DEBUG("Target %s does not support reset_prepare",
+ target_name(target));
+ return ERROR_OK;
+ }
+ return target->type->reset_prepare_trigger(target, halt, trigger);
+}
+
+int target_reset_clear_internal_state_default(struct target *target)
+{
+ if (target->defer_examine)
+ target_reset_examined(target);
+
+ target_free_all_working_areas_restore(target, 0);
+ target->state = TARGET_RESET;
+ return ERROR_OK;
+}
+
+int target_reset_clear_internal_state(struct target *target)
+{
+ if (!target->type->reset_clear_internal_state) {
+ LOG_DEBUG("Target %s does not support reset_clear_internal_state",
+ target_name(target));
+ return ERROR_OK;
+ }
+ return target->type->reset_clear_internal_state(target);
+}
+
static int target_soft_reset_halt(struct target *target)
{
if (!target_was_examined(target)) {
@@ -1252,7 +1308,6 @@ int target_profiling(struct target *target, uint32_t *samples,
/**
* Reset the @c examined flag for the given target.
- * Pure paranoia -- targets are zeroed on allocation.
*/
static void target_reset_examined(struct target *target)
{
@@ -4551,6 +4606,7 @@ enum target_cfg_param {
TCFG_RTOS,
TCFG_DEFER_EXAMINE,
TCFG_GDB_PORT,
+ TCFG_DBG_UNDER_SRST,
};
static Jim_Nvp nvp_config_opts[] = {
@@ -4567,6 +4623,7 @@ static Jim_Nvp nvp_config_opts[] = {
{ .name = "-rtos", .value = TCFG_RTOS },
{ .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE },
{ .name = "-gdb-port", .value = TCFG_GDB_PORT },
+ { .name = "-dbg-under-srst", .value = TCFG_DBG_UNDER_SRST },
{ .name = NULL, .value = -1 }
};
@@ -4868,6 +4925,27 @@ no_params:
Jim_SetResultString(goi->interp, target->gdb_port_override ? : "undefined", -1);
/* loop for more */
break;
+
+ case TCFG_DBG_UNDER_SRST:
+ if (goi->isconfigure) {
+ e = Jim_GetOpt_Nvp(goi, nvp_dbg_under_srst, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(goi, nvp_dbg_under_srst, 1);
+ return e;
+ }
+ target->dbg_under_srst = n->value;
+ } else {
+ if (goi->argc != 0)
+ goto no_params;
+ }
+ n = Jim_Nvp_value2name_simple(nvp_dbg_under_srst, target->dbg_under_srst);
+ if (n->name == NULL) {
+ target->dbg_under_srst = DBG_UNDER_SRST_UNKNOWN;
+ n = Jim_Nvp_value2name_simple(nvp_dbg_under_srst, target->dbg_under_srst);
+ }
+ Jim_SetResultString(goi->interp, n->name, -1);
+ /* loop for more */
+ break;
}
} /* while (goi->argc) */
@@ -5228,7 +5306,7 @@ static int jim_target_reset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
if (goi.argc != 2) {
Jim_WrongNumArgs(interp, 0, argv,
- "([tT]|[fF]|assert|deassert) BOOL");
+ "[prepare|trigger|post_deassert|clear_internal_state|assert|deassert] [run|halt|BOOL]");
return JIM_ERR;
}
@@ -5239,35 +5317,106 @@ static int jim_target_reset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return e;
}
/* the halt or not param */
- jim_wide a;
- e = Jim_GetOpt_Wide(&goi, &a);
+ Jim_Obj *o;
+ e = Jim_GetOpt_Obj(&goi, &o);
if (e != JIM_OK)
return e;
struct target *target = Jim_CmdPrivData(goi.interp);
+ Jim_Nvp *n2;
+ e = Jim_Nvp_name2value_obj(interp, nvp_reset_modes, o, &n2);
+ if (e == JIM_OK) {
+ target->reset_halt = n2->value == RESET_HALT || n2->value == RESET_INIT;
+ } else {
+ jim_wide a;
+ e = Jim_GetWide(interp, o, &a);
+ if (e == JIM_OK)
+ target->reset_halt = a != 0;
+ }
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(&goi, nvp_reset_modes, 1);
+ return e;
+ }
+
if (!target->tap->enabled)
return jim_target_tap_disabled(interp);
- if (!target->type->assert_reset || !target->type->deassert_reset) {
- Jim_SetResultFormatted(interp,
- "No target-specific reset for %s",
+ enum reset_types jtag_reset_config = jtag_get_reset_config();
+
+ e = ERROR_FAIL;
+ switch (n->value) {
+ case NVP_ASSERT:
+ if (!target->type->assert_reset) {
+ Jim_SetResultFormatted(interp,
+ "No target-specific reset assert for %s",
target_name(target));
- return JIM_ERR;
- }
+ return JIM_ERR;
+ }
+ e = target->type->assert_reset(target);
+ break;
- if (target->defer_examine)
- target_reset_examined(target);
+ case NVP_PREPARE:
+ if (target->type->reset_prepare_trigger) {
+ e = target->type->reset_prepare_trigger(target, target->reset_halt, false);
+ } else if (target->type->assert_reset
+ && jtag_reset_config & RESET_HAS_SRST) {
+ /* old target compatibility */
+ LOG_DEBUG("No target-specific reset prepare for %s, using assert_reset",
+ target_name(target));
+ e = target->type->assert_reset(target);
+ } else {
+ LOG_DEBUG("No target-specific reset prepare for %s",
+ target_name(target));
+ e = ERROR_OK;
+ }
+ break;
- /* determine if we should halt or not. */
- target->reset_halt = !!a;
- /* When this happens - all workareas are invalid. */
- target_free_all_working_areas_restore(target, 0);
+ case NVP_TRIGGER:
+ if (target->type->reset_prepare_trigger) {
+ e = target->type->reset_prepare_trigger(target, target->reset_halt, true);
+ } else if (target->type->assert_reset
+ && (jtag_reset_config & RESET_HAS_SRST) == 0) {
+ /* old target compatibility */
+ LOG_DEBUG("No target-specific reset trigger for %s, using assert_reset",
+ target_name(target));
+ e = target->type->assert_reset(target);
+ } else {
+ Jim_SetResultFormatted(interp,
+ "No target-specific reset trigger for %s",
+ target_name(target));
+ return JIM_ERR;
+ }
+ break;
- /* do the assert */
- if (n->value == NVP_ASSERT)
- e = target->type->assert_reset(target);
- else
+ case NVP_DEASSERT:
+ if (!target->type->deassert_reset) {
+ Jim_SetResultFormatted(interp,
+ "No target-specific reset deassert for %s",
+ target_name(target));
+ return JIM_ERR;
+ }
e = target->type->deassert_reset(target);
+ break;
+
+ case NVP_POST_DEASSERT:
+ if (target->type->deassert_reset) {
+ e = target->type->deassert_reset(target);
+ } else {
+ LOG_DEBUG("No target-specific reset post_deassert for %s",
+ target_name(target));
+ e = ERROR_OK;
+ }
+ break;
+
+ case NVP_CLEAR_INTERNAL_STATE:
+ if (target->type->reset_clear_internal_state) {
+ e = target->type->reset_clear_internal_state(target);
+ } else {
+ LOG_DEBUG("No target-specific reset clear internal state for %s",
+ target_name(target));
+ e = target_reset_clear_internal_state_default(target);
+ }
+ }
return (e == ERROR_OK) ? JIM_OK : JIM_ERR;
}
diff --git a/src/target/target.h b/src/target/target.h
index fe7e1a7..b24abc0 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -67,6 +67,20 @@ enum target_state {
enum nvp_assert {
NVP_DEASSERT,
NVP_ASSERT,
+ NVP_PREPARE,
+ NVP_TRIGGER,
+ NVP_POST_DEASSERT,
+ NVP_CLEAR_INTERNAL_STATE,
+};
+
+enum target_dbg_under_srst {
+ DBG_UNDER_SRST_UNKNOWN = 0,
+ DBG_UNDER_SRST_WORKING = 1,
+ /* Debug circuitry is working with SRST asserted */
+ DBG_UNDER_SRST_GATED = 2,
+ /* Debug circuitry holds the setting but access to it is blocked under SRST */
+ DBG_UNDER_SRST_CLEARED = 3,
+ /* SRST clears debug circuitry */
};
enum target_reset_mode {
@@ -152,7 +166,8 @@ struct target {
struct target_event_action *event_action;
- int reset_halt; /* attempt resetting the CPU into the halted mode? */
+ enum target_dbg_under_srst dbg_under_srst; /* how SRST signal influences the debug circuitry */
+ int reset_halt; /* attempt resetting the CPU into the halted mode */
target_addr_t working_area; /* working area (initialised RAM). Evaluated
* upon first allocation from virtual/physical address. */
bool working_area_virt_spec; /* virtual address specified? */
@@ -259,6 +274,7 @@ enum target_event {
TARGET_EVENT_RESET_ASSERT_POST,
TARGET_EVENT_RESET_DEASSERT_PRE,
TARGET_EVENT_RESET_DEASSERT_POST,
+ TARGET_EVENT_RESET_HALT,
TARGET_EVENT_RESET_INIT,
TARGET_EVENT_RESET_END,
@@ -266,6 +282,7 @@ enum target_event {
TARGET_EVENT_DEBUG_RESUMED, /* target resumed to execute on behalf of the debugger */
TARGET_EVENT_EXAMINE_START,
+ TARGET_EVENT_EXAMINE_FAIL,
TARGET_EVENT_EXAMINE_END,
TARGET_EVENT_GDB_ATTACH,
@@ -369,6 +386,9 @@ int target_poll(struct target *target);
int target_resume(struct target *target, int current, target_addr_t address,
int handle_breakpoints, int debug_execution);
int target_halt(struct target *target);
+int target_reset_prepare_trigger(struct target *target, bool halt, bool trigger);
+int target_reset_clear_internal_state_default(struct target *target);
+int target_reset_clear_internal_state(struct target *target);
int target_call_event_callbacks(struct target *target, enum target_event event);
int target_call_reset_callbacks(struct target *target, enum target_reset_mode reset_mode);
int target_call_trace_callbacks(struct target *target, size_t len, uint8_t *data);
diff --git a/src/target/target_type.h b/src/target/target_type.h
index fbbd57d..24b29ea 100644
--- a/src/target/target_type.h
+++ b/src/target/target_type.h
@@ -86,7 +86,23 @@ struct target_type {
* reset run; halt
*/
int (*deassert_reset)(struct target *target);
+ /**
+ * Substitutes assert_reset
+ * Prepares debug circuitry before or during SRST
+ * Optionally triggers reset using a debug register
+ * DOES NOT CONTROL SRST LINE!!!
+ *
+ * @param target The target to work on
+ * @param halt Prepare halt after reset
+ * @param trigger Trigger reset by setting a debug register
+ */
+ int (*reset_prepare_trigger)(struct target *target, bool halt, bool trigger);
int (*soft_reset_halt)(struct target *target);
+ /**
+ * Internal target adjustment after reset
+ * Typically sets target state and clears register cache
+ */
+ int (*reset_clear_internal_state)(struct target *target);
/**
* Target register access for GDB. Do @b not call this function
diff --git a/src/transport/transport.h b/src/transport/transport.h
index 140ef50..8fb2585 100644
--- a/src/transport/transport.h
+++ b/src/transport/transport.h
@@ -96,6 +96,8 @@ bool transports_are_declared(void);
bool transport_is_jtag(void);
bool transport_is_swd(void);
+bool transport_is_stlink_jtag(void);
+bool transport_is_stlink_swd(void);
#if BUILD_HLADAPTER
bool transport_is_hla(void);
diff --git a/tcl/board/stm32mp15x_dk2.cfg b/tcl/board/stm32mp15x_dk2.cfg
new file mode 100644
index 0000000..82617c5
--- /dev/null
+++ b/tcl/board/stm32mp15x_dk2.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board MB1272B.
+
+source [find interface/stlink-dap.cfg]
+
+transport select stlink_swd
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config srst_only
diff --git a/tcl/board/stm32mp15x_ev1_jlink_jtag.cfg b/tcl/board/stm32mp15x_ev1_jlink_jtag.cfg
new file mode 100644
index 0000000..7d74fa6
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev1_jlink_jtag.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1263B with PMIC STPMU1.
+
+source [find interface/jlink.cfg]
+
+transport select jtag
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config trst_and_srst separate
diff --git a/tcl/board/stm32mp15x_ev1_jlink_swd.cfg b/tcl/board/stm32mp15x_ev1_jlink_swd.cfg
new file mode 100644
index 0000000..33e6ef5
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev1_jlink_swd.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1263B with PMIC STPMU1.
+
+source [find interface/jlink.cfg]
+
+transport select swd
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config srst_only
diff --git a/tcl/board/stm32mp15x_ev1_stlink_jtag.cfg b/tcl/board/stm32mp15x_ev1_stlink_jtag.cfg
new file mode 100644
index 0000000..6fb1e31
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev1_stlink_jtag.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1263B with PMIC STPMU1.
+
+source [find interface/stlink-dap.cfg]
+
+transport select stlink_jtag
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config trst_and_srst separate
diff --git a/tcl/board/stm32mp15x_ev1_stlink_swd.cfg b/tcl/board/stm32mp15x_ev1_stlink_swd.cfg
new file mode 100644
index 0000000..3c561c9
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev1_stlink_swd.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1263B with PMIC STPMU1.
+
+source [find interface/stlink-dap.cfg]
+
+transport select stlink_swd
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config srst_only
diff --git a/tcl/board/stm32mp15x_ev1_ulink2_jtag.cfg b/tcl/board/stm32mp15x_ev1_ulink2_jtag.cfg
new file mode 100644
index 0000000..6b10351
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev1_ulink2_jtag.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1263B with PMIC STPMU1.
+
+source [find interface/cmsis-dap.cfg]
+
+transport select jtag
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config trst_and_srst separate
diff --git a/tcl/board/stm32mp15x_ev1_ulink2_swd.cfg b/tcl/board/stm32mp15x_ev1_ulink2_swd.cfg
new file mode 100644
index 0000000..5495bc3
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev1_ulink2_swd.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1263B with PMIC STPMU1.
+
+source [find interface/cmsis-dap.cfg]
+
+transport select swd
+
+source [find target/stm32mp15x_stpmu1.cfg]
+
+reset_config srst_only
diff --git a/tcl/board/stm32mp15x_ev2_jlink_jtag.cfg b/tcl/board/stm32mp15x_ev2_jlink_jtag.cfg
new file mode 100644
index 0000000..fcb0064
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev2_jlink_jtag.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1264A.
+
+source [find interface/jlink.cfg]
+
+transport select jtag
+
+source [find target/stm32mp15x.cfg]
+
+reset_config trst_and_srst separate
diff --git a/tcl/board/stm32mp15x_ev2_jlink_swd.cfg b/tcl/board/stm32mp15x_ev2_jlink_swd.cfg
new file mode 100644
index 0000000..bbf981c
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev2_jlink_swd.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1264A.
+
+source [find interface/jlink.cfg]
+
+transport select swd
+
+source [find target/stm32mp15x.cfg]
+
+reset_config srst_only
diff --git a/tcl/board/stm32mp15x_ev2_stlink_jtag.cfg b/tcl/board/stm32mp15x_ev2_stlink_jtag.cfg
new file mode 100644
index 0000000..4f2e0df
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev2_stlink_jtag.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1264A.
+
+source [find interface/stlink-dap.cfg]
+
+transport select stlink_jtag
+
+source [find target/stm32mp15x.cfg]
+
+reset_config trst_and_srst separate
diff --git a/tcl/board/stm32mp15x_ev2_stlink_swd.cfg b/tcl/board/stm32mp15x_ev2_stlink_swd.cfg
new file mode 100644
index 0000000..182ed4b
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev2_stlink_swd.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1264A.
+
+source [find interface/stlink-dap.cfg]
+
+transport select stlink_swd
+
+source [find target/stm32mp15x.cfg]
+
+reset_config srst_only
diff --git a/tcl/board/stm32mp15x_ev2_ulink2_jtag.cfg b/tcl/board/stm32mp15x_ev2_ulink2_jtag.cfg
new file mode 100644
index 0000000..8c320a4
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev2_ulink2_jtag.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1264A.
+
+source [find interface/cmsis-dap.cfg]
+
+transport select jtag
+
+source [find target/stm32mp15x.cfg]
+
+reset_config trst_and_srst separate
diff --git a/tcl/board/stm32mp15x_ev2_ulink2_swd.cfg b/tcl/board/stm32mp15x_ev2_ulink2_swd.cfg
new file mode 100644
index 0000000..25b87cc
--- /dev/null
+++ b/tcl/board/stm32mp15x_ev2_ulink2_swd.cfg
@@ -0,0 +1,9 @@
+# This is a STM32MP15x board set MB1262C + MB1264A.
+
+source [find interface/cmsis-dap.cfg]
+
+transport select swd
+
+source [find target/stm32mp15x.cfg]
+
+reset_config srst_only
diff --git a/tcl/interface/stlink-dap.cfg b/tcl/interface/stlink-dap.cfg
new file mode 100644
index 0000000..78a1b24
--- /dev/null
+++ b/tcl/interface/stlink-dap.cfg
@@ -0,0 +1,18 @@
+#
+# STMicroelectronics ST-LINK/V1, ST-LINK/V2, ST-LINK/V2-1 in-circuit
+# debugger/programmer
+#
+# This new interface drive creates a ST-Link wrapper for ARM-DAP
+#
+
+interface st-link
+
+# transport select stlink_swd
+# transport select stlink_jtag
+
+# Optionally specify the serial number of ST-LINK/V2 usb device. ST-LINK/V2
+# devices seem to have serial numbers with unreadable characters. ST-LINK/V2
+# firmware version >= V2.J21.S4 recommended to avoid issues with adapter serial
+# number reset issues.
+# e.g.
+# st-link serial "\xaa\xbc\x6e\x06\x50\x75\xff\x55\x17\x42\x19\x3f"
diff --git a/tcl/target/atsamv.cfg b/tcl/target/atsamv.cfg
index 1d026aa..6626a3d 100644
--- a/tcl/target/atsamv.cfg
+++ b/tcl/target/atsamv.cfg
@@ -35,7 +35,7 @@ swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPU
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
+target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap -dbg-under-srst gated
$_TARGETNAME configure -work-area-phys 0x20400000 -work-area-size $_WORKAREASIZE -work-area-backup 0
diff --git a/tcl/target/imx6.cfg b/tcl/target/imx6.cfg
index 5b59ecf..0b0e87d 100644
--- a/tcl/target/imx6.cfg
+++ b/tcl/target/imx6.cfg
@@ -6,6 +6,12 @@ if { [info exists CHIPNAME] } {
set _CHIPNAME imx6
}
+if { [info exists SMP] } {
+ set _SMP $SMP
+} else {
+ set _SMP 4
+}
+
# CoreSight Debug Access Port
if { [info exists DAP_TAPID] } {
set _DAP_TAPID $DAP_TAPID
@@ -39,22 +45,40 @@ jtag newtap $_CHIPNAME sjc -irlen 5 -ircapture 0x01 -irmask 0x1f \
# core 1 - 0x82152000
# core 2 - 0x82154000
# core 3 - 0x82156000
-set _TARGETNAME $_CHIPNAME.cpu.0
+set _TARGETNAME $_CHIPNAME.cpu
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
-target create $_TARGETNAME cortex_a -dap $_CHIPNAME.dap \
- -coreid 0 -dbgbase 0x82150000
+target create $_TARGETNAME.0 cortex_a -dap $_CHIPNAME.dap \
+ -coreid 0 -dbgbase 0x82150000 -dbg-under-srst cleared
-# some TCK cycles are required to activate the DEBUG power domain
-jtag configure $_CHIPNAME.sjc -event post-reset "runtest 100"
+set _CORES $_TARGETNAME.0
-proc imx6_dbginit {target} {
- # General Cortex-A8/A9 debug initialisation
- cortex_a dbginit
+if { $_SMP >= 2 } {
+ target create $_TARGETNAME.1 cortex_a -chain-position $_CHIPNAME.dap \
+ -coreid 1 -dbgbase 0x82152000 -defer-examine \
+ -dbg-under-srst cleared
+}
+if { $_SMP == 2 } {
+ set _CORES [list $_TARGETNAME.1 $_TARGETNAME.0]
+ target smp {*}$_CORES
}
+if { $_SMP == 4 } {
+ target create $_TARGETNAME.2 cortex_a -chain-position $_CHIPNAME.dap \
+ -coreid 2 -dbgbase 0x82154000 -defer-examine \
+ -dbg-under-srst cleared
+ target create $_TARGETNAME.3 cortex_a -chain-position $_CHIPNAME.dap \
+ -coreid 3 -dbgbase 0x82156000 -defer-examine \
+ -dbg-under-srst cleared
-# Slow speed to be sure it will work
-adapter_khz 1000
-$_TARGETNAME configure -event reset-start { adapter_khz 1000 }
+ set _CORES [list $_TARGETNAME.3 $_TARGETNAME.2 \
+ $_TARGETNAME.1 $_TARGETNAME.0 ]
+ target smp {*}$_CORES
+}
-$_TARGETNAME configure -event reset-assert-post "imx6_dbginit $_TARGETNAME"
-$_TARGETNAME configure -event gdb-attach { halt }
+# some TCK cycles are required to activate the DEBUG power domain
+jtag configure $_CHIPNAME.sjc -event post-reset "runtest 100"
+
+foreach t $_CORES {
+ $t configure -event reset-halt \
+ "arp_reset_simple_halter $t"
+ $t configure -event gdb-attach { halt }
+}
diff --git a/tcl/target/kx.cfg b/tcl/target/kx.cfg
index 73ee62a..f16e772 100644
--- a/tcl/target/kx.cfg
+++ b/tcl/target/kx.cfg
@@ -33,7 +33,7 @@ swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPU
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap
+target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap -dbg-under-srst working
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
@@ -60,7 +60,10 @@ if {[using_hla]} {
echo ""
} {
# Detect secured MCU or boot lock-up in RESET/WDOG loop
- $_CHIPNAME.cpu configure -event examine-start {
+ $_CHIPNAME.cpu configure -event examine-fail {
+ kinetis mdm check_security
+ }
+ $_CHIPNAME.cpu configure -event examine-end {
kinetis mdm check_security
}
diff --git a/tcl/target/stm32f7x.cfg b/tcl/target/stm32f7x.cfg
index e06a345..486d137 100755
--- a/tcl/target/stm32f7x.cfg
+++ b/tcl/target/stm32f7x.cfg
@@ -43,7 +43,7 @@ if {[using_jtag]} {
}
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
+target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap -dbg-under-srst working
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
diff --git a/tcl/target/stm32mp15x.cfg b/tcl/target/stm32mp15x.cfg
new file mode 100644
index 0000000..0def780
--- /dev/null
+++ b/tcl/target/stm32mp15x.cfg
@@ -0,0 +1,165 @@
+# script for stm32mp15x family
+
+#
+# stm32 devices support both JTAG and SWD transports.
+#
+source [find target/swj-dp.tcl]
+source [find mem_helper.tcl]
+
+if { [info exists CHIPNAME] } {
+ set _CHIPNAME $CHIPNAME
+} else {
+ set _CHIPNAME stm32mp15x
+}
+
+set _ENDIAN little
+
+# Work-area is a space in RAM used for flash programming
+# By default use 32kB
+if { [info exists WORKAREASIZE] } {
+ set _WORKAREASIZE $WORKAREASIZE
+} else {
+ set _WORKAREASIZE 0x8000
+}
+
+# jtag scan chain
+if { [info exists CPUTAPID] } {
+ set _CPUTAPID $CPUTAPID
+} else {
+ if { [using_jtag] } {
+ # See STM Document RM0436
+ # Section 66.8.3
+ set _CPUTAPID 0x6ba00477
+ } {
+ set _CPUTAPID 0x6ba02477
+ }
+}
+
+swj_newdap $_CHIPNAME tap -expected-id $_CPUTAPID -irlen 4 -ircapture 0x01 -irmask 0x0f
+dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.tap
+target create $_CHIPNAME.cpu0 cortex_a -dap $_CHIPNAME.dap -coreid 0 -dbgbase 0xE00D0000
+target create $_CHIPNAME.cpu1 cortex_a -dap $_CHIPNAME.dap -coreid 1 -dbgbase 0xE00D2000
+
+target smp $_CHIPNAME.cpu0 $_CHIPNAME.cpu1
+$_CHIPNAME.cpu0 cortex_a maskisr on
+$_CHIPNAME.cpu1 cortex_a maskisr on
+$_CHIPNAME.cpu0 cortex_a dacrfixup on
+$_CHIPNAME.cpu1 cortex_a dacrfixup on
+
+# Only in ENG_MODE the CM4 is available at reset
+if { [info exists ENG_MODE] } {
+ target create $_CHIPNAME.cpu2 cortex_m -dap $_CHIPNAME.dap
+} else {
+ target create $_CHIPNAME.cpu2 cortex_m -dap $_CHIPNAME.dap -defer-examine
+}
+
+$_CHIPNAME.cpu0 configure -work-area-phys 0x2ffc0000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+$_CHIPNAME.cpu1 configure -work-area-phys 0x2ffc0000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+$_CHIPNAME.cpu2 configure -work-area-phys 0x2ffc0000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+if { [info exists GDB_PORT] } {
+ set _GDB_PORT $GDB_PORT
+} else {
+ set _GDB_PORT [ocd_gdb_port]
+}
+
+# The two CA7 will require one or two gdb ports, depending on smp enabled or not
+# Set a fixed gdb port of CM4
+$_CHIPNAME.cpu0 configure -gdb-port [expr $_GDB_PORT + 1]
+$_CHIPNAME.cpu1 configure -gdb-port [expr $_GDB_PORT + 2]
+$_CHIPNAME.cpu2 configure -gdb-port [expr $_GDB_PORT + 0]
+
+$_CHIPNAME.cpu0 configure -event reset-deassert-post { delayed_reset_halt_cpu0 }
+
+# Reset-halt stops in bootrom when only cpu0 can be attached.
+# Set a breakpoint to stop cpu0 when cpu1 can be attached too.
+proc delayed_reset_halt_cpu0 { } {
+ global _CHIPNAME
+ global arp_reset_mode
+
+ set rom_halt_pc 0x0000688c
+
+ # only "reset halt" or "reset init"
+ if { 0 != [ string compare "$arp_reset_mode" "run" ] } {
+ cortex_a smp_off
+ targets $_CHIPNAME.cpu0
+ arp_reset_default_handler post $_CHIPNAME.cpu0
+
+ poll on
+ catch { $_CHIPNAME.cpu0 arp_waitstate halted 1000 }
+ bp $rom_halt_pc 2 hw
+ resume
+ catch { $_CHIPNAME.cpu0 arp_waitstate halted 1000 }
+ rbp $rom_halt_pc
+
+ cortex_a smp_on
+ }
+}
+
+targets $_CHIPNAME.cpu0
+
+# interface does not work while srst is asserted
+# this is target specific, valid for every board
+reset_config srst_gates_jtag
+
+if { [info exists CLOCK_FREQ] } {
+ set _CLOCK_FREQ $CLOCK_FREQ
+} else {
+ set _CLOCK_FREQ 5000
+}
+adapter_khz $_CLOCK_FREQ
+adapter_nsrst_assert_width 200
+adapter_nsrst_delay 200
+
+
+set clients_num 0
+proc clients_inc {} {
+ global clients_num
+
+ incr clients_num
+}
+
+proc clients_dec_and_shutdown {} {
+ global clients_num
+
+ incr clients_num -1
+ if { $clients_num <= 0 } { shutdown }
+}
+
+$_CHIPNAME.cpu0 configure -event gdb-attach { clients_inc; halt }
+$_CHIPNAME.cpu1 configure -event gdb-attach { clients_inc; halt }
+
+$_CHIPNAME.cpu2 configure -event gdb-attach {
+ global _CHIPNAME
+ global ENG_MODE
+
+ clients_inc
+
+ # Only in ENG_MODE the CM4 is available at reset
+ if { ! [info exists ENG_MODE] } {
+ $_CHIPNAME.cpu2 arp_examine
+ $_CHIPNAME.cpu2 arp_poll
+ }
+ halt
+}
+
+$_CHIPNAME.cpu0 configure -event gdb-detach { catch { resume }; clients_dec_and_shutdown }
+$_CHIPNAME.cpu1 configure -event gdb-detach { catch { resume }; clients_dec_and_shutdown }
+$_CHIPNAME.cpu2 configure -event gdb-detach { catch { resume }; clients_dec_and_shutdown }
+
+proc ap0_secure_access {} {
+ global _CHIPNAME
+
+ $_CHIPNAME.dap apsel 0
+ $_CHIPNAME.dap apcsw 0x10006000
+}
+
+proc ap0_non_secure_access {} {
+ global _CHIPNAME
+
+ $_CHIPNAME.dap apsel 0
+ $_CHIPNAME.dap apcsw 0x30006000
+}
+
+$_CHIPNAME.cpu0 configure -event examine-end { ap0_secure_access }
+$_CHIPNAME.cpu1 configure -event examine-end { ap0_secure_access }
diff --git a/tcl/target/stm32mp15x_stpmu1.cfg b/tcl/target/stm32mp15x_stpmu1.cfg
new file mode 100644
index 0000000..413abf2
--- /dev/null
+++ b/tcl/target/stm32mp15x_stpmu1.cfg
@@ -0,0 +1,113 @@
+# script for stm32mp15x family with companion PMIC STPMU1
+
+source [find target/stm32mp15x.cfg]
+
+$_CHIPNAME.cpu0 configure -event reset-halt { catch { pmic_init }}
+
+# Wait for expression to be true with a timeout of 200ms
+proc wait_state {condition} {
+ for {set t 0} {$t < 200} {incr t} {
+ if {[uplevel 1 $condition] == 0} {
+ return
+ }
+ sleep 1
+ }
+ return -code 1 "Timed out"
+}
+
+proc mrw_phys {reg} {
+ set value ""
+ mem2array value 32 $reg 1 phys
+ return $value(0)
+}
+
+proc mmw_phys {reg setbits clearbits} {
+ set old [mrw_phys $reg]
+ set new [expr ($old & ~$clearbits) | $setbits]
+ mww phys $reg $new
+}
+
+# Set mask_reset bits in PMIC to keep all powers on at next reset event
+# Only the mask_reset of SD-card power is not enabled to guarantee boot from SD
+# This procedure requires Secure memory access
+proc pmic_init {} {
+ global _CHIPNAME
+
+ targets $_CHIPNAME.cpu0
+ $_CHIPNAME.dap apsel 0
+
+ set RCC_BASE 0x50000000
+ set GPIO_BASE 0x54004000
+ set I2C_BASE 0x5c002000
+
+ # i2c @ 100kHz, rise 100ns, fall 100ns
+ # timing depending from HSIDIV[1:0] = {0 1 2 3} means hsi_ker_ck = {64 32 16 8} MHz
+ set I2C_TIMING {0x10b17db5 0x00b07cb4 0x00503d58 0x00201d2b}
+
+ mww phys [expr $RCC_BASE + 0x00c] 1; # set HSION = 1 (enable HSI)
+ mww phys [expr $RCC_BASE + 0x0c0] 2; # set I2C46SRC[2:0] = 2 (hsi_ker_ck)
+
+ mww phys [expr $RCC_BASE + 0x208] 4; # set I2C4EN = 1
+ mww phys [expr $RCC_BASE + 0x188] 4; # assert I2C4RST
+ mww phys [expr $RCC_BASE + 0x18c] 4; # deassert I2C4RST
+
+ mww phys [expr $RCC_BASE + 0x210] 1; # set GPIOZEN = 1 (enable GPIOZ)
+ mmw_phys [expr $GPIO_BASE + 0x00] 0x00000a00 0x00000f00; # GPIOZ4/5 AF
+ mmw_phys [expr $GPIO_BASE + 0x04] 0x00000030 0x00000000; # GPIOZ4/5 open drain
+ mmw_phys [expr $GPIO_BASE + 0x08] 0x00000000 0x00000f00; # GPIOZ4/5 low speed
+ mmw_phys [expr $GPIO_BASE + 0x0c] 0x00000000 0x00000f00; # GPIOZ4/5 no pull-up/down
+ mmw_phys [expr $GPIO_BASE + 0x20] 0x00660000 0x00ff0000; # GPIOZ4/5 AF6
+
+ set hsidiv [expr [mrw_phys [expr $RCC_BASE + 0x018]] & 0x3]
+ mww phys [expr $I2C_BASE + 0x010] [lindex $I2C_TIMING $hsidiv]
+ mww phys [expr $I2C_BASE + 0x000] 1; # set PE = 1
+ mww phys [expr $I2C_BASE + 0x004] 0x02043066
+ wait_state {expr {([mrw_phys [expr $I2C_BASE + 0x018]] & 0x00000002) == 0}}; # wait TXIS
+ mww phys [expr $I2C_BASE + 0x028] 0x18
+ wait_state {expr {([mrw_phys [expr $I2C_BASE + 0x018]] & 0x00000002) == 0}}; # wait TXIS
+ mww phys [expr $I2C_BASE + 0x028] 0x0f
+ wait_state {expr {([mrw_phys [expr $I2C_BASE + 0x018]] & 0x00000002) == 0}}; # wait TXIS
+ mww phys [expr $I2C_BASE + 0x028] 0x00
+ wait_state {expr {([mrw_phys [expr $I2C_BASE + 0x018]] & 0x00000002) == 0}}; # wait TXIS
+ mww phys [expr $I2C_BASE + 0x028] 0x6f
+ wait_state {expr {([mrw_phys [expr $I2C_BASE + 0x018]] & 0x00000020) == 0}}; # wait STOPF
+ mww phys [expr $I2C_BASE + 0x01c] 0x00000020
+ mww phys [expr $I2C_BASE + 0x004] 0x01ff14fe
+
+# mww phys [expr $RCC_BASE + 0x20c] 4; # set I2C4EN = 0 (disable I2C4)
+# mww phys [expr $RCC_BASE + 0x214] 1; # set GPIOZEN = 0 (disable GPIOZ)
+}
+
+# Set bit 16 in TAMP_BACKUP_REGISTER(20) to signal to SPL and TF-A that we
+# are in a debug session. This will force them (at next reboot) to program
+# the PMIC for keeping powered-on the debug unit during reset.
+proc set_pmic_in_backup_reg {} {
+ global _CHIPNAME
+
+ targets $_CHIPNAME.cpu0
+ $_CHIPNAME.dap apsel 0
+
+ mmw_phys 0x5c00a150 0x00010000 0x00000000
+}
+
+proc clear_pmic_in_backup_reg {} {
+ global _CHIPNAME
+
+ targets $_CHIPNAME.cpu0
+ $_CHIPNAME.dap apsel 0
+
+ mmw_phys 0x5c00a150 0x00000000 0x00010000
+}
+
+# Wrap around init/shutdown. Attention: CTRL-C will not invoke shutdown
+rename init _init
+proc init {} {
+ _init
+ set_pmic_in_backup_reg
+}
+
+rename shutdown _shutdown
+proc shutdown {} {
+ clear_pmic_in_backup_reg
+ _shutdown
+}
diff --git a/tcl/target/swj-dp.tcl b/tcl/target/swj-dp.tcl
index 1d274cb..e42a168 100644
--- a/tcl/target/swj-dp.tcl
+++ b/tcl/target/swj-dp.tcl
@@ -30,5 +30,7 @@ proc swj_newdap {chip tag args} {
eval jtag newtap $chip $tag $args
} elseif [using_swd] {
eval swd newdap $chip $tag $args
+ } elseif { [using_stlink_jtag] || [using_stlink_swd] } {
+ eval stlink_dap newdap $chip $tag $args
}
}
--
2.7.4