Skip to content

Instantly share code, notes, and snippets.

@cr0sh
Last active December 19, 2021 03:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cr0sh/1920833b7b7ecd40f3c605f49dd32c84 to your computer and use it in GitHub Desktop.
Save cr0sh/1920833b7b7ecd40f3c605f49dd32c84 to your computer and use it in GitHub Desktop.
Buildroot package patch for OpenOCD raspberrypi fork
diff --git a/configure.ac b/configure.ac
index a5c9c9276..a25477407 100644
--- a/configure.ac
+++ b/configure.ac
@@ -112,6 +112,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])])
m4_define([USB1_ADAPTERS],
[[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
+ [[picoprobe], [Raspberry Pi Pico Probe], [PICOPROBE]],
[[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
diff --git a/contrib/60-openocd.rules b/contrib/60-openocd.rules
index e0864b827..57006a4c6 100644
--- a/contrib/60-openocd.rules
+++ b/contrib/60-openocd.rules
@@ -175,4 +175,7 @@ ATTRS{idVendor}=="c251", ATTRS{idProduct}=="2750", MODE="660", GROUP="plugdev",
# CMSIS-DAP compatible adapters
ATTRS{product}=="*CMSIS-DAP*", MODE="660", GROUP="plugdev", TAG+="uaccess"
+# Raspberry Pi Picoprobe
+ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", MODE="660", GROUP="plugdev", TAG+="uaccess"
+
LABEL="openocd_rules_end"
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index 8c9b2b7ab..532670436 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -52,6 +52,7 @@ NOR_DRIVERS = \
%D%/psoc5lp.c \
%D%/psoc6.c \
%D%/renesas_rpchf.c \
+ %D%/rp2040.c \
%D%/sfdp.c \
%D%/sh_qspi.c \
%D%/sim3x.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index 570861ec5..6eadc756b 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -67,6 +67,7 @@ extern const struct flash_driver psoc5lp_eeprom_flash;
extern const struct flash_driver psoc5lp_nvl_flash;
extern const struct flash_driver psoc6_flash;
extern const struct flash_driver renesas_rpchf_flash;
+extern const struct flash_driver rp2040_flash;
extern const struct flash_driver sh_qspi_flash;
extern const struct flash_driver sim3x_flash;
extern const struct flash_driver stellaris_flash;
@@ -140,6 +141,7 @@ static const struct flash_driver * const flash_drivers[] = {
&psoc5lp_nvl_flash,
&psoc6_flash,
&renesas_rpchf_flash,
+ &rp2040_flash,
&sh_qspi_flash,
&sim3x_flash,
&stellaris_flash,
diff --git a/src/flash/nor/rp2040.c b/src/flash/nor/rp2040.c
new file mode 100644
index 000000000..74f4ad568
--- /dev/null
+++ b/src/flash/nor/rp2040.c
@@ -0,0 +1,420 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+// NOTE THAT THIS CODE REQUIRES FLASH ROUTINES in BOOTROM WITH FUNCTION TABLE PTR AT 0x00000010
+// Your gdbinit should load the bootrom.elf if appropriate
+
+// this is 'M' 'u', 1 (version)
+#define BOOTROM_MAGIC 0x01754d
+#define BOOTROM_MAGIC_ADDR 0x00000010
+
+// Call a ROM function via the debug trampoline
+// Up to four arguments passed in r0...r3 as per ABI
+// Function address is passed in r7
+// FIXME the trampoline is needed because OpenOCD "algorithm" code insists on sw breakpoints.
+
+char *regnames[4] = {
+ "r0", "r1", "r2", "r3"
+}; // FIXME pretty sure this is defined elsewhere too
+
+#define MAKE_TAG(a, b) (((b)<<8) | a)
+#define FUNC_FLASH_EXIT_XIP MAKE_TAG('E', 'X')
+#define FUNC_DEBUG_TRAMPOLINE MAKE_TAG('D', 'T')
+#define FUNC_DEBUG_TRAMPOLINE_END MAKE_TAG('D', 'E')
+#define FUNC_CONNECT_INTERNAL_FLASH MAKE_TAG('I', 'F')
+#define FUNC_FLASH_RANGE_ERASE MAKE_TAG('R', 'E')
+#define FUNC_FLASH_RANGE_PROGRAM MAKE_TAG('R', 'P')
+#define FUNC_FLASH_FLUSH_CACHE MAKE_TAG('F', 'C')
+#define FUNC_FLASH_ENTER_CMD_XIP MAKE_TAG('C', 'X')
+
+static uint32_t rp2040_lookup_symbol(struct target *target, uint32_t tag, uint16_t *symbol) {
+ uint32_t magic;
+ int err = target_read_u32(target, BOOTROM_MAGIC_ADDR, &magic);
+ if (err != ERROR_OK)
+ return err;
+
+ magic &= 0xffffff; // ignore bootrom version
+ if (magic != BOOTROM_MAGIC) {
+ if (!((magic ^ BOOTROM_MAGIC)&0xffff))
+ LOG_ERROR("Incorrect RP2040 BOOT ROM version");
+ else
+ LOG_ERROR("RP2040 BOOT ROM not found");
+ return ERROR_FAIL;
+ }
+
+ // dereference the table pointer
+ uint16_t table_entry;
+ err = target_read_u16(target, BOOTROM_MAGIC_ADDR + 4, &table_entry);
+ if (err != ERROR_OK)
+ return err;
+
+ uint16_t entry_tag;
+ do {
+ err = target_read_u16(target, table_entry, &entry_tag);
+ if (err != ERROR_OK)
+ return err;
+ if (entry_tag == tag) {
+ // 16 bit symbol is next
+ return target_read_u16(target, table_entry + 2, symbol);
+ }
+ table_entry += 4;
+ } while (entry_tag);
+ return ERROR_FAIL;
+}
+
+static int rp2040_call_rom_func(struct target *target, target_addr_t stack, uint32_t func_tag, uint32_t argdata[], int n_args)
+{
+ const int max_args = 4; // only allow register arguments
+ if (n_args > max_args)
+ {
+ LOG_ERROR("Max of 4 arguments permitted when calling RP2040 ROM functions.");
+ return ERROR_FAIL;
+ }
+ uint16_t debug_trampoline;
+ int err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE, &debug_trampoline);
+ if (err != ERROR_OK) {
+ LOG_ERROR("Debug trampoline not found in RP2040 ROM.");
+ return err;
+ }
+ debug_trampoline &= ~1u; // mask off thumb bit
+
+ uint16_t debug_trampoline_end;
+ err = rp2040_lookup_symbol(target, FUNC_DEBUG_TRAMPOLINE_END, &debug_trampoline_end);
+ if (err != ERROR_OK) {
+ LOG_ERROR("Debug trampoline end not found in RP2040 ROM.");
+ return err;
+ }
+ debug_trampoline_end &= ~1u; // mask off thumb bit
+
+ LOG_DEBUG("Calling ROM func %c%c with %d arguments", (char)func_tag, (char)(func_tag>>8), n_args);
+ LOG_DEBUG("Calling on core \"%s\"", target->cmd_name);
+
+ uint16_t func;
+ err = rp2040_lookup_symbol(target, func_tag, &func);
+ if (err != ERROR_OK) {
+ LOG_ERROR("Function %c%c not found in RP2040 ROM.", (char)func_tag, (char)(func_tag>>8));
+ return err;
+ }
+
+ struct reg_param args[max_args + 2];
+ struct armv7m_algorithm alg_info;
+
+ for (int i = 0; i < n_args; ++i)
+ {
+ init_reg_param(&args[i], regnames[i], 32, PARAM_OUT);
+ buf_set_u32(args[i].value, 0, 32, argdata[i]);
+ }
+ // Pass function pointer in r7
+ init_reg_param(&args[n_args], "r7", 32, PARAM_OUT);
+ buf_set_u32(args[n_args].value, 0, 32, func);
+ init_reg_param(&args[n_args + 1], "sp", 32, PARAM_OUT);
+ buf_set_u32(args[n_args + 1].value, 0, 32, stack);
+
+
+ for (int i = 0; i < n_args + 2; ++i)
+ LOG_DEBUG("Set %s = %08x", args[i].reg_name, buf_get_u32(args[i].value, 0, 32));
+
+ // Actually call the function
+ alg_info.common_magic = ARMV7M_COMMON_MAGIC;
+ alg_info.core_mode = ARM_MODE_THREAD;
+ err = target_run_algorithm(
+ target,
+ 0, NULL, // No memory arguments
+ n_args + 1, args, // User arguments + r7
+ debug_trampoline, debug_trampoline_end,
+ 3000, // 3s timeout
+ &alg_info
+ );
+ for (int i = 0; i < n_args + 1; ++i)
+ destroy_reg_param(&args[i]);
+ if (err != ERROR_OK)
+ LOG_ERROR("Failed to invoke ROM function %c%c (@%08x)\n", (char)func_tag, (char)(func_tag>>8), func);
+ return err;
+
+}
+
+// -----------------------------------------------------------------------------
+// Flash access code
+
+// FIXME: support other flash geometries; where to get these consts from?
+// These are common values, and are accurate for W25X10CL, W25Q16JV and friends
+#define BLOCK_SIZE (1ul << 16)
+#define BLOCK_ERASE_CMD 0xd8
+#define SECTOR_SIZE 4096
+#define PAGE_SIZE 256
+
+struct rp2040_flash_bank {
+ int probed;
+ // Infrastructure for calling into ROM code:
+ struct working_area *stack;
+ target_addr_t stacktop;
+};
+
+static int rp2040_flash_exit_xip(struct flash_bank *bank)
+{
+ struct rp2040_flash_bank *priv = bank->driver_priv;
+ int err = ERROR_OK;
+
+ LOG_DEBUG("Connecting internal flash");
+ err = rp2040_call_rom_func(bank->target, priv->stacktop, FUNC_CONNECT_INTERNAL_FLASH, NULL, 0);
+ if (err != ERROR_OK)
+ {
+ LOG_ERROR("RP2040 exit xip: failed to connect internal flash");
+ return err;
+ }
+
+ LOG_DEBUG("Kicking flash out of XIP mode");
+ err = rp2040_call_rom_func(bank->target, priv->stacktop, FUNC_FLASH_EXIT_XIP, NULL, 0);
+ if (err != ERROR_OK)
+ {
+ LOG_ERROR("RP2040 exit xip: failed to exit flash XIP mode");
+ return err;
+ }
+
+ return err;
+}
+
+static int rp2040_flash_enter_xip(struct flash_bank *bank)
+{
+ struct rp2040_flash_bank *priv = bank->driver_priv;
+
+ // Always flush before returning to execute-in-place, to invalidate stale cache contents.
+ // The flush call also restores regular hardware-controlled chip select following a rp2040_flash_exit_xip().
+ LOG_DEBUG("Flushing flash cache after write behind");
+ int err = rp2040_call_rom_func(bank->target, priv->stacktop, FUNC_FLASH_FLUSH_CACHE, NULL, 0);
+ if (err != ERROR_OK)
+ {
+ LOG_ERROR("RP2040 enter xip: failed to flush flash cache");
+ return err;
+ }
+
+ LOG_DEBUG("Configuring SSI for execute-in-place");
+ err = rp2040_call_rom_func(bank->target, priv->stacktop, FUNC_FLASH_ENTER_CMD_XIP, NULL, 0);
+ if (err != ERROR_OK)
+ {
+ LOG_ERROR("RP2040 enter xip: failed to enter flash XIP mode");
+ }
+ return err;
+}
+
+static int rp2040_flash_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ LOG_INFO("Writing %d bytes starting at 0x%x", count, offset);
+
+ // todo fixme hard coded chunk size
+ struct rp2040_flash_bank *priv = bank->driver_priv;
+ const unsigned int chunk_size = 16 * 1024;
+ struct target *target = bank->target;
+ struct working_area *bounce;
+ int err = ERROR_OK;
+
+ if (offset % PAGE_SIZE) {
+ LOG_ERROR("RP2040 flash writes must be page-aligned (%d bytes). Can't continue", PAGE_SIZE);
+ return ERROR_TARGET_UNALIGNED_ACCESS;
+ }
+
+ err = rp2040_flash_exit_xip(bank);
+ if (err != ERROR_OK)
+ {
+ return err;
+ }
+
+ if (target_alloc_working_area(target, chunk_size, &bounce) != ERROR_OK) {
+ LOG_ERROR("Could not allocate bounce buffer for flash programming. Can't continue");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ LOG_DEBUG("Allocated flash bounce buffer @%08x", (unsigned)bounce->address);
+
+ while (count > 0)
+ {
+ uint32_t write_size = count > chunk_size ? chunk_size : count;
+ LOG_DEBUG("Writing %d bytes to offset %08x", write_size, offset);
+ err = target_write_buffer(target, bounce->address, write_size, buffer);
+ if (err != ERROR_OK) {
+ LOG_ERROR("Could not load data into target bounce buffer");
+ break;
+ }
+ uint32_t args[3] = {
+ offset,
+ bounce->address,
+ write_size
+ };
+ err = rp2040_call_rom_func(target, priv->stacktop, FUNC_FLASH_RANGE_PROGRAM, args, 3);
+ if (err != ERROR_OK) {
+ LOG_ERROR("Failed to invoke flash programming code on target");
+ break;
+ }
+
+ buffer += write_size;
+ offset += write_size;
+ count -= write_size;
+ }
+ target_free_working_area(target, bounce);
+
+ if (err != ERROR_OK)
+ return err;
+
+ // Flash is successfully programmed. We can now do a bit of poking to make the flash
+ // contents visible to us via memory-mapped (XIP) interface in the 0x1... memory region
+ LOG_DEBUG("Flushing flash cache after write behind");
+ err = rp2040_call_rom_func(bank->target, priv->stacktop, FUNC_FLASH_FLUSH_CACHE, NULL, 0);
+ if (err != ERROR_OK)
+ {
+ LOG_ERROR("RP2040 write: failed to flush flash cache");
+ return err;
+ }
+
+ err = rp2040_flash_enter_xip(bank);
+
+ return err;
+}
+
+static int rp2040_flash_erase(struct flash_bank *bank, unsigned int first, unsigned int last)
+{
+ struct rp2040_flash_bank *priv = bank->driver_priv;
+ uint32_t start_addr = bank->sectors[first].offset;
+ uint32_t length = bank->sectors[last].offset + bank->sectors[last].size - start_addr;
+ int err = ERROR_OK;
+
+ LOG_DEBUG("RP2040 erase %d bytes starting at 0x%08x", length, start_addr);
+
+ err = rp2040_flash_exit_xip(bank);
+ if (err != ERROR_OK)
+ {
+ return err;
+ }
+
+ LOG_DEBUG("Remote call flash_range_erase");
+
+ // Erase in naturally-aligned chunks of n sectors per call. Get speed of
+ // block erases, without timeout on large erase ranges:
+ const unsigned int erase_sectors_per_call = 4 * BLOCK_SIZE / SECTOR_SIZE;
+ unsigned int first_of_call, last_of_call;
+ for (first_of_call = first; first_of_call <= last; first_of_call = last_of_call + 1)
+ {
+ // Try to keep our erase calls block-aligned, to avoid degeneration to
+ // sector erase at start and end of each call (RP2040ch slower)
+ last_of_call = first_of_call + erase_sectors_per_call;
+ last_of_call -= (last_of_call % erase_sectors_per_call) + 1;
+ if (last_of_call > last)
+ last_of_call = last;
+ uint32_t args[4] = {
+ bank->sectors[first_of_call].offset,
+ bank->sectors[last_of_call].offset + bank->sectors[last_of_call].size - bank->sectors[first_of_call].offset,
+ BLOCK_SIZE,
+ BLOCK_ERASE_CMD
+ };
+ err = rp2040_call_rom_func(bank->target, priv->stacktop, FUNC_FLASH_RANGE_ERASE, args, 4);
+ if (err != ERROR_OK)
+ {
+ LOG_ERROR("RP2040 erase: flash_range_erase failed");
+ break;
+ }
+ }
+
+ err = rp2040_flash_enter_xip(bank);
+
+ return err;
+}
+
+static int rp2040_flash_protect_check(struct flash_bank *bank)
+{
+ LOG_WARNING("RP2040 Flash Protect Check (ignored)");
+ return ERROR_OK;
+}
+
+static int rp2040_flash_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last)
+{
+ LOG_WARNING("RP2040 Flash Protect (ignored)");
+ return ERROR_OK;
+}
+
+// -----------------------------------------------------------------------------
+// Driver probing etc
+
+static int rp2040_flash_probe(struct flash_bank *bank)
+{
+ struct rp2040_flash_bank *flash_info = bank->driver_priv;
+
+ bank->num_sectors = bank->size / SECTOR_SIZE;
+ LOG_INFO("RP2040 B0 Flash Probe: %d bytes @%08x, in %d sectors\n", bank->size, (uint32_t)bank->base, bank->num_sectors);
+ bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors);
+
+ for (unsigned int i = 0; i < bank->num_sectors; i++) {
+ bank->sectors[i].offset = i * SECTOR_SIZE;
+ bank->sectors[i].size = SECTOR_SIZE;
+ bank->sectors[i].is_erased = -1;
+ bank->sectors[i].is_protected = 1;
+ }
+
+ // target_alloc_working_area always allocates RP2040ltiple of 4 bytes, so no worry about alignment
+ const int STACK_SIZE = 256;
+ if (target_alloc_working_area(bank->target, STACK_SIZE, &flash_info->stack) != ERROR_OK) {
+ LOG_ERROR("Could not allocate stack for flash programming code");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ flash_info->stacktop = flash_info->stack->address + flash_info->stack->size;
+ LOG_DEBUG("Allocated flash algorithm stack @%08x size %d bytes",
+ (uint32_t)flash_info->stack->address,
+ flash_info->stack->size);
+
+ flash_info->probed = 1;
+
+ return ERROR_OK;
+}
+
+static int rp2040_flash_auto_probe(struct flash_bank *bank)
+{
+ struct rp2040_flash_bank *flash_info = bank->driver_priv;
+
+ if (flash_info->probed)
+ return ERROR_OK;
+
+ return rp2040_flash_probe(bank);
+}
+
+static int rp2040_flash_info(struct flash_bank *bank, char *buf, int buf_size)
+{
+ LOG_INFO("RP2040 Flash Info");
+ return ERROR_OK;
+}
+
+// -----------------------------------------------------------------------------
+// Driver boilerplate
+
+FLASH_BANK_COMMAND_HANDLER(rp2040_flash_bank_command)
+{
+ LOG_INFO("RP2040 Flash Bank Command");
+ struct rp2040_flash_bank *flash_info;
+
+ flash_info = malloc(sizeof(struct rp2040_flash_bank));
+ flash_info->probed = 0;
+
+ // Set up bank info
+ bank->driver_priv = flash_info;
+
+ return ERROR_OK;
+}
+
+struct flash_driver rp2040_flash = {
+ .name = "rp2040_flash",
+ .commands = NULL,
+ .flash_bank_command = rp2040_flash_bank_command,
+ .erase = rp2040_flash_erase,
+ .protect = rp2040_flash_protect,
+ .write = rp2040_flash_write,
+ .read = default_flash_read,
+ .probe = rp2040_flash_probe,
+ .auto_probe = rp2040_flash_auto_probe,
+ .erase_check = default_flash_blank_check,
+ .protect_check = rp2040_flash_protect_check,
+ .info = rp2040_flash_info,
+ .free_driver_priv = default_flash_free_driver_priv // FIXME free working areas etc
+};
diff --git a/src/helper/log.h b/src/helper/log.h
index f2ba0daa6..93b6cddce 100644
--- a/src/helper/log.h
+++ b/src/helper/log.h
@@ -155,6 +155,10 @@ extern int debug_level;
/* ERROR_TIMEOUT is already taken by winerror.h. */
#define ERROR_TIMEOUT_REACHED (-6)
#define ERROR_NOT_IMPLEMENTED (-7)
+/*
+ * there was a sticky error, but it has been cleared. safe to retry
+ */
+#define ERROR_STICKY_CLEARED (-8)
#endif /* OPENOCD_HELPER_LOG_H */
diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am
index f7a54b003..a11b76a58 100644
--- a/src/jtag/drivers/Makefile.am
+++ b/src/jtag/drivers/Makefile.am
@@ -186,6 +186,9 @@ endif
if KITPROG
DRIVERFILES += %D%/kitprog.c
endif
+if PICOPROBE
+DRIVERFILES += %D%/picoprobe.c
+endif
if XDS110
DRIVERFILES += %D%/xds110.c
endif
diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c
index 40cb5aa0b..9116e131e 100644
--- a/src/jtag/drivers/bcm2835gpio.c
+++ b/src/jtag/drivers/bcm2835gpio.c
@@ -115,10 +115,15 @@ static int bcm2835gpio_swd_write(int swclk, int swdio)
{
uint32_t set = swclk << swclk_gpio | swdio << swdio_gpio;
uint32_t clear = !swclk << swclk_gpio | !swdio << swdio_gpio;
-
+ int temp;
+
GPIO_SET = set;
GPIO_CLR = clear;
+ do {
+ temp = GPIO_LEV & ( 1 << swclk_gpio );
+ } while ( temp != swclk << swclk_gpio );
+
for (unsigned int i = 0; i < jtag_delay; i++)
asm volatile ("");
diff --git a/src/jtag/drivers/bitbang.c b/src/jtag/drivers/bitbang.c
index df1d601b8..25d53c75a 100644
--- a/src/jtag/drivers/bitbang.c
+++ b/src/jtag/drivers/bitbang.c
@@ -427,6 +427,8 @@ static int bitbang_swd_switch_seq(enum swd_special_seq seq)
switch (seq) {
case LINE_RESET:
+ LOG_DEBUG_IO("SWD line reset");
+ bitbang_swd_exchange(false, (uint8_t *)swd_seq_line_reset, 0, swd_seq_line_reset_len);
LOG_DEBUG("SWD line reset");
bitbang_swd_exchange(false, (uint8_t *)swd_seq_line_reset, 0, swd_seq_line_reset_len);
break;
@@ -438,6 +440,14 @@ static int bitbang_swd_switch_seq(enum swd_special_seq seq)
LOG_DEBUG("SWD-to-JTAG");
bitbang_swd_exchange(false, (uint8_t *)swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len);
break;
+ case DORMANT_TO_SWD:
+ LOG_DEBUG("DORMANT-to-SWD");
+ bitbang_swd_exchange(false, (uint8_t *)swd_seq_dormant_to_swd, 0, swd_seq_dormant_to_swd_len);
+ break;
+ case SWD_TO_DORMANT:
+ LOG_DEBUG("SWD-to-DORMANT");
+ bitbang_swd_exchange(false, (uint8_t *) swd_seq_swd_to_dormant, 0, swd_seq_swd_to_dormant_len);
+ break;
default:
LOG_ERROR("Sequence %d not supported", seq);
return ERROR_FAIL;
@@ -531,6 +541,11 @@ static void bitbang_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay
bitbang_interface->swdio_drive(false);
bitbang_swd_exchange(true, trn_ack_data_parity_trn, 0, 1 + 3 + 1);
+ if (0 == ((cmd ^ swd_cmd(false, false, DP_TARGETSEL)) &
+ (SWD_CMD_APnDP|SWD_CMD_RnW|SWD_CMD_A32))) {
+ /* Targetsel has no ack so force it */
+ buf_set_u32(trn_ack_data_parity_trn, 1, 3, SWD_ACK_OK);
+ }
bitbang_interface->swdio_drive(true);
bitbang_swd_exchange(false, trn_ack_data_parity_trn, 1 + 3 + 1, 32 + 1);
diff --git a/src/jtag/drivers/cmsis_dap.c b/src/jtag/drivers/cmsis_dap.c
index 16480ae1e..f49eeb0d7 100644
--- a/src/jtag/drivers/cmsis_dap.c
+++ b/src/jtag/drivers/cmsis_dap.c
@@ -136,6 +136,7 @@ static bool swd_mode;
/* CMSIS-DAP SWD Commands */
#define CMD_DAP_SWD_CONFIGURE 0x13
+#define CMD_DAP_SWD_SEQUENCE 0x1D
/* CMSIS-DAP JTAG Commands */
#define CMD_DAP_JTAG_SEQ 0x14
@@ -538,6 +539,47 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us)
}
#endif
+static int cmsis_dap_metacmd_targetsel(uint32_t instance_id)
+{
+ int retval;
+ uint8_t *buffer = cmsis_dap_handle->packet_buffer;
+ const uint32_t SEQ_RD = 0x80, SEQ_WR = 0x00;
+
+ /* SWD multi-drop requires a transfer ala CMD_DAP_TFER,
+ but with no expectation of an SWD ACK response. In
+ CMSIS-DAP v1.20 and v2.00, CMD_DAP_SWD_SEQUENCE was
+ added to allow this special sequence to be generated.
+ The purpose of this operation is to select the target
+ corresponding to the instance_id that is written */
+
+ int idx = 0;
+ buffer[idx++] = 0; /* report number */
+ buffer[idx++] = CMD_DAP_SWD_SEQUENCE;
+ buffer[idx++] = 3; /* sequence count */
+
+ /* sequence 0: packet request for TARGETSEL */
+ buffer[idx++] = SEQ_WR | 8;
+ buffer[idx++] = 0x99;
+ /* sequence 1: no expectation for target to ACK */
+ buffer[idx++] = SEQ_RD | 5;
+ /* sequence 2: WDATA plus parity */
+ buffer[idx++] = SEQ_WR | 33;
+ buffer[idx++] = (uint8_t)(instance_id >> 0);
+ buffer[idx++] = (uint8_t)(instance_id >> 8);
+ buffer[idx++] = (uint8_t)(instance_id >> 16);
+ buffer[idx++] = (uint8_t)(instance_id >> 24);
+ buffer[idx++] = parity_u32(instance_id);
+
+ retval = cmsis_dap_xfer(cmsis_dap_handle, idx);
+
+ if (retval != ERROR_OK || buffer[1] != DAP_OK) {
+ LOG_ERROR("CMSIS-DAP command CMD_SWD_Configure failed.");
+ return ERROR_JTAG_DEVICE_ERROR;
+ }
+
+ return ERROR_OK;
+}
+
static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
{
uint8_t *buffer = dap->packet_buffer;
@@ -704,7 +746,9 @@ static int cmsis_dap_swd_run_queue(void)
static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
{
- if (pending_fifo[pending_fifo_put_idx].transfer_count == pending_queue_len) {
+ bool targetsel_cmd = swd_cmd(false, false, DP_TARGETSEL) == cmd;
+
+ if ((pending_fifo[pending_fifo_put_idx].transfer_count == pending_queue_len) || targetsel_cmd) {
if (pending_fifo_block_count)
cmsis_dap_swd_read_process(cmsis_dap_handle, 0);
@@ -718,6 +762,11 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
if (queued_retval != ERROR_OK)
return;
+ if (targetsel_cmd) {
+ cmsis_dap_metacmd_targetsel(data);
+ return;
+ }
+
struct pending_request_block *block = &pending_fifo[pending_fifo_put_idx];
struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]);
transfer->data = data;
@@ -849,6 +898,16 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
s = swd_seq_swd_to_jtag;
s_len = swd_seq_swd_to_jtag_len;
break;
+ case DORMANT_TO_SWD:
+ LOG_DEBUG("DORMANT-to-SWD");
+ s = swd_seq_dormant_to_swd;
+ s_len = swd_seq_dormant_to_swd_len;
+ break;
+ case SWD_TO_DORMANT:
+ LOG_DEBUG("SWD-to-DORMANT");
+ s = swd_seq_swd_to_dormant;
+ s_len = swd_seq_swd_to_dormant_len;
+ break;
default:
LOG_ERROR("Sequence %d not supported", seq);
return ERROR_FAIL;
diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c
index 9e47d3cad..0b31cc855 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -1092,6 +1092,12 @@ static int ftdi_swd_run_queue(void)
}
for (size_t i = 0; i < swd_cmd_queue_length; i++) {
+ if (0 == ((swd_cmd_queue[i].cmd ^ swd_cmd(false, false, DP_TARGETSEL)) &
+ (SWD_CMD_APnDP|SWD_CMD_RnW|SWD_CMD_A32))) {
+ /* Targetsel has no ack so force it */
+ buf_set_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1, 3, SWD_ACK_OK);
+ }
+
int ack = buf_get_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1, 3);
LOG_DEBUG_IO("%s %s %s reg %X = %08"PRIx32,
@@ -1122,6 +1128,10 @@ static int ftdi_swd_run_queue(void)
}
skip:
+ /* Defensive cleanup - seems like a bad idea to have potentially stale pointers sticking around */
+ for (size_t i = 0; i < swd_cmd_queue_length; i++)
+ swd_cmd_queue[i].dst = NULL;
+
swd_cmd_queue_length = 0;
retval = queued_retval;
queued_retval = ERROR_OK;
@@ -1202,7 +1212,7 @@ static int ftdi_swd_switch_seq(enum swd_special_seq seq)
{
switch (seq) {
case LINE_RESET:
- LOG_DEBUG("SWD line reset");
+ LOG_DEBUG_IO("SWD line reset");
ftdi_swd_swdio_en(true);
mpsse_clock_data_out(mpsse_ctx, swd_seq_line_reset, 0, swd_seq_line_reset_len, SWD_MODE);
break;
@@ -1216,6 +1226,16 @@ static int ftdi_swd_switch_seq(enum swd_special_seq seq)
ftdi_swd_swdio_en(true);
mpsse_clock_data_out(mpsse_ctx, swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len, SWD_MODE);
break;
+ case DORMANT_TO_SWD:
+ LOG_DEBUG("DORMANT-to-SWD");
+ ftdi_swd_swdio_en(true);
+ mpsse_clock_data_out(mpsse_ctx, swd_seq_dormant_to_swd, 0, swd_seq_dormant_to_swd_len, SWD_MODE);
+ break;
+ case SWD_TO_DORMANT:
+ LOG_DEBUG("SWD-to-DORMANT");
+ ftdi_swd_swdio_en(true);
+ mpsse_clock_data_out(mpsse_ctx, swd_seq_swd_to_dormant, 0, swd_seq_swd_to_dormant_len, SWD_MODE);
+ break;
default:
LOG_ERROR("Sequence %d not supported", seq);
return ERROR_FAIL;
diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c
index 15d252cfb..22ef72635 100644
--- a/src/jtag/drivers/jlink.c
+++ b/src/jtag/drivers/jlink.c
@@ -2008,6 +2008,8 @@ struct pending_scan_result {
void *buffer;
/** Offset in the destination buffer */
unsigned buffer_offset;
+ /** true if the command has nmo acknowledgement */
+ bool no_ack;
};
#define MAX_PENDING_SCAN_RESULTS 256
@@ -2155,6 +2157,16 @@ static int jlink_swd_switch_seq(enum swd_special_seq seq)
s = swd_seq_swd_to_jtag;
s_len = swd_seq_swd_to_jtag_len;
break;
+ case DORMANT_TO_SWD:
+ LOG_DEBUG("DORMANT-to-SWD");
+ s = swd_seq_dormant_to_swd;
+ s_len = swd_seq_dormant_to_swd_len;
+ break;
+ case SWD_TO_DORMANT:
+ LOG_DEBUG("SWD-to-DORMANT");
+ s = swd_seq_swd_to_dormant;
+ s_len = swd_seq_swd_to_dormant_len;
+ break;
default:
LOG_ERROR("Sequence %d not supported.", seq);
return ERROR_FAIL;
@@ -2191,7 +2203,9 @@ static int jlink_swd_run_queue(void)
}
for (i = 0; i < pending_scan_results_length; i++) {
- int ack = buf_get_u32(tdo_buffer, pending_scan_results_buffer[i].first, 3);
+ int ack = pending_scan_results_buffer[i].no_ack ?
+ SWD_ACK_OK :
+ buf_get_u32(tdo_buffer, pending_scan_results_buffer[i].first, 3);
if (ack != SWD_ACK_OK) {
LOG_DEBUG("SWD ack not OK: %d %s", ack,
@@ -2255,6 +2269,9 @@ static void jlink_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint3
jlink_queue_data_out(data_parity_trn, 32 + 1);
}
+ pending_scan_results_buffer[pending_scan_results_length].no_ack =
+ (0 == ((cmd ^ swd_cmd(false, false, DP_TARGETSEL)) &
+ (SWD_CMD_APnDP|SWD_CMD_RnW|SWD_CMD_A32)));
pending_scan_results_length++;
diff --git a/src/jtag/drivers/picoprobe.c b/src/jtag/drivers/picoprobe.c
new file mode 100644
index 000000000..fc73b1ee3
--- /dev/null
+++ b/src/jtag/drivers/picoprobe.c
@@ -0,0 +1,595 @@
+/***************************************************************************
+ * Copyright (C) 2020 by Liam Fraser *
+ * liam@raspberrypi.com *
+ * *
+ * Based on: kitprog.c, ftdi.c, mpsse.c *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <jtag/interface.h>
+#include <jtag/swd.h>
+#include <jtag/commands.h>
+#include <helper/command.h>
+
+#include "libusb_helper.h"
+
+#define VID 0x2E8A /* Raspberry Pi */
+#define PID 0x0004 /* Picoprobe */
+
+#define BULK_EP_OUT 4
+#define BULK_EP_IN 5
+#define PICOPROBE_INTERFACE 2
+
+#define PICOPROBE_MAX_PACKET_LENGTH 512
+#define LIBUSB_TIMEOUT 10000
+
+struct picoprobe {
+ struct libusb_device_handle *usb_handle;
+ uint8_t *packet_buffer;
+ int freq;
+};
+
+static struct swd_cmd_queue_entry {
+ uint8_t cmd;
+ uint32_t *dst;
+ uint8_t trn_ack_data_parity_trn[DIV_ROUND_UP(4 + 3 + 32 + 1 + 4, 8)];
+} *swd_cmd_queue;
+static size_t swd_cmd_queue_length;
+static size_t swd_cmd_queue_alloced;
+static int queued_retval;
+
+static struct picoprobe *picoprobe_handle;
+
+static int picoprobe_init(void);
+static int picoprobe_quit(void);
+
+enum PROBE_CMDS {
+ PROBE_INVALID = 0, /* Invalid */
+ PROBE_WRITE_BITS = 1, /* Host wants us to write bits */
+ PROBE_READ_BITS = 2, /* Host wants us to read bits */
+ PROBE_SET_FREQ = 3, /* Set TCK freq */
+ PROBE_RESET = 4
+};
+
+struct __attribute__((__packed__)) probe_cmd_hdr {
+ uint8_t id;
+ uint8_t cmd;
+ uint32_t bits;
+};
+
+struct __attribute__((__packed__)) probe_pkt_hdr {
+ uint32_t total_packet_length;
+};
+
+/* Separate queue to swd_cmd_queue because we sometimes insert idle cycles not described
+ * there */
+#define PICOPROBE_QUEUE_SIZE 64
+static struct picoprobe_queue_entry {
+ uint8_t id;
+ uint8_t cmd; /* PROBE_CMDS */
+ unsigned bits;
+ unsigned offset;
+ const uint8_t *buf;
+} *picoprobe_queue;
+static size_t picoprobe_queue_length;
+static size_t picoprobe_queue_alloced;
+
+static inline unsigned packet_length(uint8_t *pkt)
+{
+ return pkt - picoprobe_handle->packet_buffer;
+}
+
+static int picoprobe_bulk_write(struct probe_pkt_hdr *pkt_hdr, uint8_t *pkt)
+{
+ pkt_hdr->total_packet_length = packet_length(pkt);
+ assert(pkt_hdr->total_packet_length <= PICOPROBE_MAX_PACKET_LENGTH);
+ int ret = 0;
+ jtag_libusb_bulk_write(picoprobe_handle->usb_handle,
+ BULK_EP_OUT, (char *)picoprobe_handle->packet_buffer, packet_length(pkt), LIBUSB_TIMEOUT,
+ &ret);
+ if (ret < 0)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ return ERROR_OK;
+}
+
+static int picoprobe_flush(void)
+{
+ LOG_DEBUG_IO("Flush %d transactions", (int)picoprobe_queue_length);
+ int ret = ERROR_OK;
+
+ struct probe_pkt_hdr *pkt_hdr = (struct probe_pkt_hdr *)picoprobe_handle->packet_buffer;
+
+ /* Chain pending write and read commands together */
+ uint8_t *pkt = picoprobe_handle->packet_buffer + sizeof(struct probe_pkt_hdr);
+
+ unsigned total_reads = 0;
+ unsigned total_read_bytes = 0;
+
+ for (unsigned i = 0; i < picoprobe_queue_length; i++) {
+ /* Copy header regardless of read or write */
+ struct picoprobe_queue_entry *q = &picoprobe_queue[i];
+ struct probe_cmd_hdr *hdr = (struct probe_cmd_hdr *)pkt;
+ if (q->id != i) {
+ LOG_ERROR("Wrong queue id. q->id %d != %d", q->id, i);
+ return ERROR_JTAG_DEVICE_ERROR;
+ }
+ hdr->id = q->id;
+ hdr->cmd = q->cmd;
+ hdr->bits = q->bits;
+ pkt += sizeof(struct probe_cmd_hdr);
+ unsigned length_bytes = DIV_ROUND_UP(q->bits, 8);
+
+ if (q->cmd == PROBE_WRITE_BITS) {
+ /* Copy the data to write into the packet buffer */
+ if (q->buf) {
+ bit_copy(pkt, 0, q->buf, q->offset, q->bits);
+ } else {
+ /* Make sure the packet buffer is zerod to clock zeros */
+ assert(q->offset == 0);
+ memset(pkt, 0, length_bytes);
+ }
+ pkt += length_bytes;
+ } else if (q->cmd == PROBE_READ_BITS) {
+ /* Nothing to do for a read as we have already copied the header
+ * Will process the data later in one go */
+ total_reads++;
+ total_read_bytes += length_bytes;
+ } else {
+ /* Unexpected cmd to flush */
+ return ERROR_FAIL;
+ }
+ }
+
+ /* Send all read/write commands + write data */
+ ret = picoprobe_bulk_write(pkt_hdr, pkt);
+ if (ret < 0)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ /* If no reads we can bail */
+ if (total_reads == 0) {
+ picoprobe_queue_length = 0;
+ return ret;
+ }
+
+ /* Now get any read responses */
+ unsigned rx_pkt_len = sizeof(struct probe_pkt_hdr) +
+ (sizeof(struct probe_cmd_hdr) * total_reads) +
+ total_read_bytes;
+ jtag_libusb_bulk_read(picoprobe_handle->usb_handle,
+ BULK_EP_IN | LIBUSB_ENDPOINT_IN, (char *)picoprobe_handle->packet_buffer,
+ rx_pkt_len, LIBUSB_TIMEOUT, &ret);
+
+ if (ret < 0)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ /* Now time to process the rx data */
+ LOG_DEBUG_IO("Read %d bytes from probe", ret);
+
+ /* If we didn't get length we expected */
+ if ((int)rx_pkt_len != ret)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ struct probe_pkt_hdr *response_hdr = (struct probe_pkt_hdr *)picoprobe_handle->packet_buffer;
+ if (rx_pkt_len != response_hdr->total_packet_length)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ pkt = picoprobe_handle->packet_buffer + sizeof(struct probe_pkt_hdr);
+
+ /* Now go through read responses */
+ for (unsigned i = 0; i < total_reads; i++) {
+ struct probe_cmd_hdr *read_hdr = (struct probe_cmd_hdr *)pkt;
+ pkt += sizeof(struct probe_cmd_hdr);
+ unsigned read_bytes = DIV_ROUND_UP(read_hdr->bits, 8);
+
+ if (read_hdr->cmd != PROBE_READ_BITS)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ uint8_t id = read_hdr->id;
+ struct picoprobe_queue_entry *q = &picoprobe_queue[id];
+ assert(read_hdr->cmd == q->cmd);
+ assert(read_hdr->id == q->id);
+ assert(read_hdr->bits == q->bits);
+ LOG_DEBUG_IO("Processing read of %d bits", read_hdr->bits);
+
+ /* Copy data back to swd cmd queue */
+ memcpy((void *)q->buf, pkt, read_bytes);
+ pkt += read_bytes;
+ }
+
+ unsigned processed_len = (pkt - picoprobe_handle->packet_buffer);
+ if (processed_len != rx_pkt_len)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ picoprobe_queue_length = 0;
+
+ /* Keep gdb alive */
+ keep_alive();
+
+ return ERROR_OK;
+}
+
+static int picoprobe_read_write_bits(const uint8_t *buf, unsigned offset, unsigned length, uint8_t cmd)
+{
+ if (picoprobe_queue_length == picoprobe_queue_alloced) {
+ LOG_ERROR("Picoprobe queue full");
+ return ERROR_BUF_TOO_SMALL;
+ } else {
+ LOG_DEBUG_IO("Picoprobe queue len %d -> %d", (int)picoprobe_queue_length,
+ (int)picoprobe_queue_length + 1);
+ }
+
+ struct picoprobe_queue_entry *q = &picoprobe_queue[picoprobe_queue_length];
+ q->id = picoprobe_queue_length++;
+ q->cmd = cmd;
+ q->bits = length;
+ q->offset = offset;
+ q->buf = buf;
+
+ return ERROR_OK;
+}
+
+static int picoprobe_write_bits(const uint8_t *buf, unsigned offset, unsigned length)
+{
+ LOG_DEBUG_IO("Write %d bits @ offset %d", length, offset);
+ return picoprobe_read_write_bits(buf, offset, length, PROBE_WRITE_BITS);
+}
+
+static int picoprobe_read_bits(const uint8_t *buf, unsigned offset, unsigned length)
+{
+ LOG_DEBUG_IO("Read %d bits @ offset %d", length, offset);
+
+ if (picoprobe_queue_length == picoprobe_queue_alloced)
+ return ERROR_BUF_TOO_SMALL;
+
+ return picoprobe_read_write_bits(buf, offset, length, PROBE_READ_BITS);
+}
+
+static int picoprobe_swd_run_queue(void)
+{
+ LOG_DEBUG_IO("Executing %zu queued transactions", swd_cmd_queue_length);
+ int retval;
+
+ queued_retval = picoprobe_flush();
+
+ if (queued_retval != ERROR_OK) {
+ LOG_DEBUG_IO("Skipping due to previous errors: %d", queued_retval);
+ goto skip;
+ }
+
+ for (size_t i = 0; i < swd_cmd_queue_length; i++) {
+ if (0 == ((swd_cmd_queue[i].cmd ^ swd_cmd(false, false, DP_TARGETSEL)) &
+ (SWD_CMD_APnDP|SWD_CMD_RnW|SWD_CMD_A32))) {
+ /* Targetsel has no ack so force it */
+ buf_set_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1, 3, SWD_ACK_OK);
+ }
+
+
+ LOG_DEBUG_IO("trn_ack_data_parity_trn:");
+ for (size_t y = 0; y < sizeof(swd_cmd_queue[i].trn_ack_data_parity_trn); y++)
+ LOG_DEBUG_IO("BYTE %d 0x%x", (int)y, swd_cmd_queue[i].trn_ack_data_parity_trn[y]);
+
+ int ack = buf_get_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1, 3);
+
+ LOG_DEBUG_IO("%s %s %s reg %X = %08"PRIx32,
+ ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK",
+ swd_cmd_queue[i].cmd & SWD_CMD_APnDP ? "AP" : "DP",
+ swd_cmd_queue[i].cmd & SWD_CMD_RnW ? "read" : "write",
+ (swd_cmd_queue[i].cmd & SWD_CMD_A32) >> 1,
+ buf_get_u32(swd_cmd_queue[i].trn_ack_data_parity_trn,
+ 1 + 3 + (swd_cmd_queue[i].cmd & SWD_CMD_RnW ? 0 : 1), 32));
+
+ if (ack != SWD_ACK_OK) {
+ queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL;
+ goto skip;
+
+ } else if (swd_cmd_queue[i].cmd & SWD_CMD_RnW) {
+ uint32_t data = buf_get_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1 + 3, 32);
+ int parity = buf_get_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1 + 3 + 32, 1);
+
+ if (parity != parity_u32(data)) {
+ LOG_ERROR("SWD Read data parity mismatch");
+ queued_retval = ERROR_FAIL;
+ goto skip;
+ }
+
+ if (swd_cmd_queue[i].dst != NULL)
+ *swd_cmd_queue[i].dst = data;
+ }
+ }
+
+skip:
+ /* Defensive cleanup - seems like a bad idea to have potentially stale pointers sticking around */
+ for (size_t i = 0; i < swd_cmd_queue_length; i++)
+ swd_cmd_queue[i].dst = NULL;
+
+ swd_cmd_queue_length = 0;
+ retval = queued_retval;
+ queued_retval = ERROR_OK;
+
+ return retval;
+}
+
+static void picoprobe_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint32_t ap_delay_clk)
+{
+ if (swd_cmd_queue_length == swd_cmd_queue_alloced)
+ queued_retval = picoprobe_swd_run_queue();
+
+ if (queued_retval != ERROR_OK)
+ return;
+
+ size_t i = swd_cmd_queue_length++;
+ swd_cmd_queue[i].cmd = cmd | SWD_CMD_START | SWD_CMD_PARK;
+
+ picoprobe_write_bits(&swd_cmd_queue[i].cmd, 0, 8);
+
+ if (swd_cmd_queue[i].cmd & SWD_CMD_RnW) {
+ /* Queue a read transaction */
+ swd_cmd_queue[i].dst = dst;
+
+ picoprobe_read_bits(swd_cmd_queue[i].trn_ack_data_parity_trn,
+ 0, 1 + 3 + 32 + 1 + 1);
+ } else {
+ /* Queue a write transaction */
+ picoprobe_read_bits(swd_cmd_queue[i].trn_ack_data_parity_trn,
+ 0, 1 + 3 + 1);
+
+ buf_set_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1 + 3 + 1, 32, data);
+ buf_set_u32(swd_cmd_queue[i].trn_ack_data_parity_trn, 1 + 3 + 1 + 32, 1, parity_u32(data));
+
+ picoprobe_write_bits(swd_cmd_queue[i].trn_ack_data_parity_trn,
+ 1 + 3 + 1, 32 + 1);
+ }
+
+ /* Insert idle cycles after AP accesses to avoid WAIT */
+ if (cmd & SWD_CMD_APnDP) {
+ if (ap_delay_clk == 0)
+ return;
+ LOG_DEBUG("Add %d idle cycles", ap_delay_clk);
+ picoprobe_write_bits(NULL, 0, ap_delay_clk);
+ }
+
+}
+
+static void picoprobe_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk)
+{
+ assert(cmd & SWD_CMD_RnW);
+ picoprobe_swd_queue_cmd(cmd, value, 0, ap_delay_clk);
+}
+
+static void picoprobe_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk)
+{
+ assert(!(cmd & SWD_CMD_RnW));
+ picoprobe_swd_queue_cmd(cmd, NULL, value, ap_delay_clk);
+}
+
+static int_least32_t picoprobe_set_frequency(int_least32_t hz)
+{
+ int ret;
+ struct probe_pkt_hdr *pkt_hdr = (struct probe_pkt_hdr *)picoprobe_handle->packet_buffer;
+
+ /* Assert this is a standalone command with nothing else queued */
+ assert(picoprobe_queue_length == 0);
+
+ /* Chain writes and read commands together */
+ uint8_t *pkt = picoprobe_handle->packet_buffer + sizeof(struct probe_pkt_hdr);
+ struct probe_cmd_hdr *hdr = (struct probe_cmd_hdr *)pkt;
+ hdr->id = 0;
+ hdr->cmd = PROBE_SET_FREQ;
+ hdr->bits = hz / 1000;
+ pkt += sizeof(struct probe_cmd_hdr);
+
+ /* Send all read/write commands + write data */
+ ret = picoprobe_bulk_write(pkt_hdr, pkt);
+ if (ret < 0)
+ return ERROR_JTAG_DEVICE_ERROR;
+
+ return hz;
+}
+
+static int_least32_t picoprobe_speed(int_least32_t hz)
+{
+ int ret = picoprobe_set_frequency(hz);
+
+ if (ret < 0)
+ LOG_ERROR("Couldn't set picoprobe speed");
+ else
+ picoprobe_handle->freq = ret;
+
+ return ERROR_OK;
+}
+
+static int picoprobe_khz(int khz, int *jtag_speed)
+{
+ *jtag_speed = khz * 1000;
+ return ERROR_OK;
+}
+
+static int picoprobe_speed_div(int speed, int *khz)
+{
+ *khz = speed / 1000;
+ return ERROR_OK;
+}
+
+static int picoprobe_swd_init(void)
+{
+ return ERROR_OK;
+}
+
+static int picoprobe_swd_switch_seq(enum swd_special_seq seq)
+{
+ int ret = ERROR_OK;
+
+ switch (seq) {
+ case LINE_RESET:
+ LOG_DEBUG_IO("SWD line reset");
+ ret = picoprobe_write_bits(swd_seq_line_reset, 0, swd_seq_line_reset_len);
+ break;
+ case JTAG_TO_SWD:
+ LOG_DEBUG("JTAG-to-SWD");
+ ret = picoprobe_write_bits(swd_seq_jtag_to_swd, 0, swd_seq_jtag_to_swd_len);
+ break;
+ case SWD_TO_JTAG:
+ LOG_DEBUG("SWD-to-JTAG");
+ ret = picoprobe_write_bits(swd_seq_swd_to_jtag, 0, swd_seq_swd_to_jtag_len);
+ break;
+ case DORMANT_TO_SWD:
+ LOG_DEBUG("DORMANT-to-SWD");
+ ret = picoprobe_write_bits(swd_seq_dormant_to_swd, 0, swd_seq_dormant_to_swd_len);
+ break;
+ case SWD_TO_DORMANT:
+ LOG_DEBUG("SWD-to-DORMANT");
+ ret = picoprobe_write_bits(swd_seq_swd_to_dormant, 0, swd_seq_swd_to_dormant_len);
+ break;
+ default:
+ LOG_ERROR("Sequence %d not supported", seq);
+ return ERROR_FAIL;
+ }
+
+ return ret;
+}
+
+static int picoprobe_reset(int trst, int srst)
+{
+ return ERROR_OK;
+}
+
+static const struct swd_driver picoprobe_swd = {
+ .init = picoprobe_swd_init,
+ .switch_seq = picoprobe_swd_switch_seq,
+ .read_reg = picoprobe_swd_read_reg,
+ .write_reg = picoprobe_swd_write_reg,
+ .run = picoprobe_swd_run_queue,
+};
+
+const char *probe_serial_number = NULL;
+
+static COMMAND_HELPER(handle_serialnum_args, const char **serialNumber)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("%s: need single argument with serial number", CMD_NAME);
+ *serialNumber = NULL;
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ } else {
+ *serialNumber = CMD_ARGV[0];
+ return ERROR_OK;
+ }
+}
+
+COMMAND_HANDLER(handle_serialnum_command)
+{
+ const char *serialNumber = NULL;
+ int retval = CALL_COMMAND_HANDLER(handle_serialnum_args, &serialNumber);
+ if (ERROR_OK == retval) {
+ probe_serial_number = malloc(strlen(serialNumber) + 1);
+ if (probe_serial_number) {
+ strcpy((char *) probe_serial_number, (char *) serialNumber);
+ command_print(CMD, "Using serial number : %s", serialNumber);
+ }
+ }
+ return retval;
+}
+
+static const struct command_registration serialnum_command_handlers[] = {
+ {
+ .name = "pico_serialnum",
+ .mode = COMMAND_ANY,
+ .handler = handle_serialnum_command,
+ .help = "use picoprobe with this serial number",
+ .usage = "'serial number'",
+ },
+ COMMAND_REGISTRATION_DONE
+};
+static const char * const picoprobe_transports[] = { "swd", NULL };
+
+struct adapter_driver picoprobe_adapter_driver = {
+ .name = "picoprobe",
+ .commands = serialnum_command_handlers,
+ .transports = picoprobe_transports,
+ .swd_ops = &picoprobe_swd,
+ .init = picoprobe_init,
+ .quit = picoprobe_quit,
+ .reset = picoprobe_reset,
+ .speed = picoprobe_speed,
+ .speed_div = picoprobe_speed_div,
+ .khz = picoprobe_khz,
+};
+
+static int picoprobe_usb_open(void)
+{
+ const uint16_t vids[] = { VID, 0 };
+ const uint16_t pids[] = { PID, 0 };
+
+ if (jtag_libusb_open(vids, pids, probe_serial_number,
+ &picoprobe_handle->usb_handle, NULL) != ERROR_OK) {
+ LOG_ERROR("Failed to open or find the device");
+ return ERROR_FAIL;
+ }
+
+ if (libusb_claim_interface(picoprobe_handle->usb_handle, PICOPROBE_INTERFACE) != ERROR_OK) {
+ LOG_ERROR("Failed to claim picoprobe interface");
+ return ERROR_FAIL;
+ }
+
+ return ERROR_OK;
+}
+
+static void picoprobe_usb_close(void)
+{
+ jtag_libusb_close(picoprobe_handle->usb_handle);
+}
+
+static int picoprobe_init(void)
+{
+ picoprobe_handle = malloc(sizeof(struct picoprobe));
+ if (picoprobe_handle == NULL) {
+ LOG_ERROR("Failed to allocate memory");
+ return ERROR_FAIL;
+ }
+
+ if (picoprobe_usb_open() != ERROR_OK) {
+ LOG_ERROR("Can't find a picoprobe device! Please check device connections and permissions.");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+
+ /* Allocate packet buffers and queues */
+ picoprobe_handle->packet_buffer = malloc(PICOPROBE_MAX_PACKET_LENGTH);
+ if (picoprobe_handle->packet_buffer == NULL) {
+ LOG_ERROR("Failed to allocate memory for the packet buffer");
+ return ERROR_FAIL;
+ }
+
+ picoprobe_queue_alloced = PICOPROBE_QUEUE_SIZE;
+ picoprobe_queue_length = 0;
+ picoprobe_queue = malloc(picoprobe_queue_alloced * sizeof(*picoprobe_queue));
+ if (picoprobe_queue == NULL)
+ return ERROR_FAIL;
+
+ swd_cmd_queue_alloced = 10;
+ swd_cmd_queue = malloc(swd_cmd_queue_alloced * sizeof(*swd_cmd_queue));
+
+ return swd_cmd_queue != NULL ? ERROR_OK : ERROR_FAIL;
+}
+
+
+static int picoprobe_quit(void)
+{
+ picoprobe_usb_close();
+ return ERROR_OK;
+}
diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c
index 061a78f9c..3e1b0d0d7 100644
--- a/src/jtag/interfaces.c
+++ b/src/jtag/interfaces.c
@@ -134,6 +134,9 @@ extern struct adapter_driver aice_adapter_driver;
#if BUILD_BCM2835GPIO == 1
extern struct adapter_driver bcm2835gpio_adapter_driver;
#endif
+#if BUILD_PICOPROBE == 1
+extern struct adapter_driver picoprobe_adapter_driver;
+#endif
#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1
extern struct adapter_driver cmsis_dap_adapter_driver;
#endif
@@ -254,6 +257,9 @@ struct adapter_driver *adapter_drivers[] = {
#if BUILD_BCM2835GPIO == 1
&bcm2835gpio_adapter_driver,
#endif
+#if BUILD_PICOPROBE == 1
+ &picoprobe_adapter_driver,
+#endif
#if BUILD_CMSIS_DAP_USB == 1 || BUILD_CMSIS_DAP_HID == 1
&cmsis_dap_adapter_driver,
#endif
diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h
index 2fa580223..20669c3e4 100644
--- a/src/jtag/jtag.h
+++ b/src/jtag/jtag.h
@@ -152,6 +152,10 @@ struct jtag_tap {
struct jtag_tap_event_action *event_action;
+ /* Value to use with DP_TARGETSEL in SWD multidrop mode, otherwise
+ * DP_TARGETSEL_INVALID*/
+ uint32_t multidrop_targetsel;
+
struct jtag_tap *next_tap;
/* private pointer to support none-jtag specific functions */
void *priv;
diff --git a/src/jtag/swd.h b/src/jtag/swd.h
index 487cb85bf..e499128ca 100644
--- a/src/jtag/swd.h
+++ b/src/jtag/swd.h
@@ -277,5 +277,8 @@ struct swd_driver {
};
int swd_init_reset(struct command_context *cmd_ctx);
+void swd_add_reset(int req_srst);
+int swd_create_transport_session(struct adiv5_dap *dap, const struct dap_ops **dap_ops,
+ void **transport_private);
#endif /* OPENOCD_JTAG_SWD_H */
diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c
index 2fa162e56..5d14f950d 100644
--- a/src/jtag/tcl.c
+++ b/src/jtag/tcl.c
@@ -475,6 +475,9 @@ static int jim_newtap_expected_id(Jim_Nvp *n, Jim_GetOptInfo *goi,
#define NTAP_OPT_DISABLED 4
#define NTAP_OPT_EXPECTED_ID 5
#define NTAP_OPT_VERSION 6
+#define NTAP_OPT_DP_ID 7
+#define NTAP_OPT_INSTANCE_ID 8
+
static int jim_newtap_ir_param(Jim_Nvp *n, Jim_GetOptInfo *goi,
struct jtag_tap *pTap)
@@ -522,6 +525,38 @@ static int jim_newtap_ir_param(Jim_Nvp *n, Jim_GetOptInfo *goi,
return JIM_OK;
}
+static int jim_newtap_md_param(Jim_Nvp *n, Jim_GetOptInfo *goi, struct jtag_tap *pTap)
+{
+ jim_wide w;
+ int e = Jim_GetOpt_Wide(goi, &w);
+ if (e != JIM_OK) {
+ Jim_SetResultFormatted(goi->interp,
+ "option: %s bad parameter", n->name);
+ return e;
+ }
+ switch (n->value) {
+ case NTAP_OPT_INSTANCE_ID:
+ if (w < 0 || w > 15) {
+ LOG_ERROR("%s: invalid multidrop instance-id %d",
+ pTap->dotted_name, (int) w);
+ return JIM_ERR;
+ }
+ pTap->multidrop_targetsel = (pTap->multidrop_targetsel & DP_TARGETSEL_DPID_MASK) |
+ (w << DP_TARGETSEL_INSTANCEID_SHIFT);
+ break;
+ case NTAP_OPT_DP_ID:
+ if (w < 0 || w > DP_TARGETSEL_DPID_MASK) {
+ LOG_ERROR("%s: invalid multidrop target-id %d",
+ pTap->dotted_name, (int) w);
+ }
+ pTap->multidrop_targetsel = (pTap->multidrop_targetsel & DP_TARGETSEL_INSTANCEID_MASK) | w;
+ break;
+ default:
+ return JIM_ERR;
+ }
+ return JIM_OK;
+}
+
static int jim_newtap_cmd(Jim_GetOptInfo *goi)
{
struct jtag_tap *pTap;
@@ -537,6 +572,8 @@ static int jim_newtap_cmd(Jim_GetOptInfo *goi)
{ .name = "-disable", .value = NTAP_OPT_DISABLED },
{ .name = "-expected-id", .value = NTAP_OPT_EXPECTED_ID },
{ .name = "-ignore-version", .value = NTAP_OPT_VERSION },
+ { .name = "-dp-id", .value = NTAP_OPT_DP_ID },
+ { .name = "-instance-id", .value = NTAP_OPT_INSTANCE_ID },
{ .name = NULL, .value = -1 },
};
@@ -567,12 +604,57 @@ static int jim_newtap_cmd(Jim_GetOptInfo *goi)
cp = malloc(x);
sprintf(cp, "%s.%s", pTap->chip, pTap->tapname);
pTap->dotted_name = cp;
+ pTap->multidrop_targetsel = DP_TARGETSEL_INVALID;
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()) {
- /* SWD doesn't require any JTAG tap parameters */
+ bool target_id_specified = false;
+ bool instance_id_specified = false;
+ while (goi->argc) {
+ e = Jim_GetOpt_Nvp(goi, opts, &n);
+ if (e != JIM_OK) {
+ Jim_GetOpt_NvpUnknown(goi, opts, 0);
+ free(cp);
+ free(pTap);
+ return e;
+ }
+ LOG_DEBUG("Processing option: %s", n->name);
+ switch (n->value) {
+ case NTAP_OPT_DP_ID:
+ target_id_specified = true;
+ e = jim_newtap_md_param(n, goi, pTap);
+ break;
+ case NTAP_OPT_INSTANCE_ID:
+ instance_id_specified = true;
+ e = jim_newtap_md_param(n, goi, pTap);
+ break;
+ /* SWD doesn't require any JTAG tap parameters; we allow (but ignore) them */
+ case NTAP_OPT_EXPECTED_ID:
+ case NTAP_OPT_IRLEN:
+ case NTAP_OPT_IRMASK:
+ case NTAP_OPT_IRCAPTURE:
+ /* dummy read to ignore the next argument */
+ Jim_GetOpt_Wide(goi, NULL);
+ break;
+ default:
+ e = JIM_OK;
+ break;
+ } /* switch (n->value) */
+ if (JIM_OK != e) {
+ free(cp);
+ free(pTap);
+ return e;
+ }
+ } /* while (goi->argc) */
+
+ if (instance_id_specified != target_id_specified) {
+ LOG_ERROR("%s: -dp-id and -instance-id must both be specified", pTap->dotted_name);
+ free(cp);
+ free(pTap);
+ return JIM_ERR;
+ }
pTap->enabled = true;
jtag_tap_init(pTap);
return JIM_OK;
diff --git a/src/target/adi_v5_swd.c b/src/target/adi_v5_swd.c
index b25181e21..0f3f07ce2 100644
--- a/src/target/adi_v5_swd.c
+++ b/src/target/adi_v5_swd.c
@@ -55,6 +55,37 @@
static bool do_sync;
+enum queued_request_type {
+ dp_read,
+ dp_write,
+ ap_read,
+ ap_write,
+ ap_abort
+};
+
+struct queued_request {
+ enum queued_request_type type;
+ unsigned reg;
+ struct adiv5_ap *ap;
+ uint32_t write;
+ uint32_t *read;
+ uint8_t *ack;
+};
+
+struct swd_multidrop_session_state {
+ struct queued_request *request_queue;
+ int request_queue_size;
+ int queued_request_count;
+ bool active_on_wire;
+};
+
+static inline struct swd_multidrop_session_state *adiv5_dap_swd_multidrop_session_state(struct adiv5_dap *dap)
+{
+ return (struct swd_multidrop_session_state *)adiv5_dap_swd_transport_private(dap);
+}
+
+static int swd_multidrop_select_target(struct adiv5_dap *dap, uint32_t *dpidr, uint32_t *dlpidr, bool reconnecting);
+
static void swd_finish_read(struct adiv5_dap *dap)
{
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
@@ -69,6 +100,11 @@ static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
uint32_t *data);
+static bool swd_dap_is_multidrop(struct adiv5_dap *dap)
+{
+ return dap->tap->multidrop_targetsel != DP_TARGETSEL_INVALID;
+}
+
static void swd_clear_sticky_errors(struct adiv5_dap *dap)
{
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
@@ -185,6 +221,63 @@ static int swd_connect(struct adiv5_dap *dap)
return status;
}
+static uint32_t last_target_id;
+
+static int swd_multidrop_connect(struct adiv5_dap *dap)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(dap);
+ if (state->active_on_wire) {
+ /* No recursion for you */
+ return ERROR_FAIL;
+ }
+ state->active_on_wire = true;
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
+ uint32_t dpidr = 0xdeadbeef, dlpidr = 0xdeadbeef;
+ int status;
+
+ /* FIXME validate transport config ... is the
+ * configured DAP present (check IDCODE)?
+ * Is *only* one DAP configured?
+ *
+ * MUST READ DPIDR
+ */
+
+ /* 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");
+ }
+ }
+
+ /* Note, debugport_init() does setup too */
+ swd->switch_seq(DORMANT_TO_SWD);
+
+ /* Clear link state, including the SELECT cache. */
+ dap->do_reconnect = false;
+ dap_invalidate_cache(dap);
+ last_target_id = DP_TARGETSEL_INVALID;
+
+ /* Pass true for reconnect, clearing sticky errors */
+ status = swd_multidrop_select_target(dap, &dpidr, &dlpidr, true);
+
+ if (status == ERROR_OK) {
+ LOG_INFO("SWD DPIDR %#8.8" PRIx32, dpidr);
+ LOG_INFO("SWD DLPIDR %#8.8" PRIx32, dlpidr);
+ dap->do_reconnect = false;
+ status = dap_dp_init(dap);
+ } else {
+ dap->do_reconnect = true;
+ }
+
+ state->active_on_wire = false;
+ return status;
+}
+
static int swd_send_sequence(struct adiv5_dap *dap, enum swd_special_seq seq)
{
const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
@@ -200,10 +293,15 @@ static inline int check_sync(struct adiv5_dap *dap)
static int swd_check_reconnect(struct adiv5_dap *dap)
{
- if (dap->do_reconnect)
- return swd_connect(dap);
+ int status = ERROR_OK;
- return ERROR_OK;
+ /* check_reconnect is a noop for multidrop as everything is queued */
+ if (!swd_dap_is_multidrop(dap)) {
+ if (dap->do_reconnect)
+ status = swd_connect(dap);
+ }
+
+ return status;
}
static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
@@ -367,6 +465,7 @@ static void swd_quit(struct adiv5_dap *dap)
swd->switch_seq(SWD_TO_JTAG);
/* flush the queue before exit */
swd->run();
+ free(adiv5_dap_swd_transport_private(dap));
}
const struct dap_ops swd_dap_ops = {
@@ -444,6 +543,283 @@ static int swd_init(struct command_context *ctx)
return ERROR_OK;
}
+static int swd_multidrop_queue_request(struct swd_multidrop_session_state *state, struct queued_request **request)
+{
+ if (state->queued_request_count == state->request_queue_size) {
+ int new_size = state->request_queue_size * 2;
+ struct queued_request *new_queue = calloc(new_size, sizeof(struct queued_request));
+ if (!new_queue)
+ return ERROR_FAIL;
+ memcpy(new_queue, state->request_queue, state->request_queue_size * sizeof(struct queued_request));
+ free(state->request_queue);
+ state->request_queue = new_queue;
+ state->request_queue_size = new_size;
+ }
+ *request = &state->request_queue[state->queued_request_count++];
+ return ERROR_OK;
+}
+
+static int swd_multidrop_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
+ uint32_t *data)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(dap);
+ int retval;
+ if (state->active_on_wire) {
+ retval = swd_queue_dp_read(dap, reg, data);
+ } else {
+ struct queued_request *r = NULL;
+ retval = swd_multidrop_queue_request(state, &r);
+ if (retval == ERROR_OK) {
+ r->type = dp_read;
+ r->reg = reg;
+ r->read = data;
+ }
+ }
+ return retval;
+}
+
+static int swd_multidrop_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
+ uint32_t data)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(dap);
+ int retval;
+ if (state->active_on_wire) {
+ retval = swd_queue_dp_write(dap, reg, data);
+ } else {
+ struct queued_request *r = NULL;
+ retval = swd_multidrop_queue_request(state, &r);
+ if (retval == ERROR_OK) {
+ r->type = dp_write;
+ r->reg = reg;
+ r->write = data;
+ }
+ }
+ return retval;
+}
+
+static int swd_multidrop_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
+ uint32_t *data)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(ap->dap);
+ int retval;
+ if (state->active_on_wire) {
+ retval = swd_queue_ap_read(ap, reg, data);
+ } else {
+ struct queued_request *r = NULL;
+ retval = swd_multidrop_queue_request(state, &r);
+ if (retval == ERROR_OK) {
+ r->type = ap_read;
+ r->ap = ap;
+ r->reg = reg;
+ r->read = data;
+ }
+ }
+ return retval;
+}
+
+static int swd_multidrop_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
+ uint32_t data)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(ap->dap);
+ int retval;
+
+ if (state->active_on_wire) {
+ retval = swd_queue_ap_write(ap, reg, data);
+ } else {
+ struct queued_request *r = NULL;
+ retval = swd_multidrop_queue_request(state, &r);
+ if (retval == ERROR_OK) {
+ r->type = ap_write;
+ r->ap = ap;
+ r->reg = reg;
+ r->write = data;
+ }
+ }
+ return retval;
+}
+
+static int swd_multidrop_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(dap);
+ int retval;
+ if (state->active_on_wire) {
+ retval = swd_queue_ap_abort(dap, ack);
+ } else {
+ struct queued_request *r = NULL;
+ retval = swd_multidrop_queue_request(state, &r);
+ if (retval == ERROR_OK) {
+ r->type = ap_abort;
+ r->ack = ack;
+ }
+ }
+ return retval;
+}
+
+/* todo: remove this (only used to reset link to dormant on last quit) or find it a better home */
+static int32_t swd_multidrop_session_count;
+
+static int swd_multidrop_select_target(struct adiv5_dap *dap, uint32_t *dpidr, uint32_t *dlpidr, bool reconnecting)
+{
+ assert(adiv5_dap_swd_multidrop_session_state(dap)->active_on_wire);
+ assert(swd_dap_is_multidrop(dap));
+
+ int retval = ERROR_OK;
+ if (last_target_id != dap->tap->multidrop_targetsel || dap->do_reconnect) {
+ const struct swd_driver *driver = adiv5_dap_swd_driver(dap);
+ const int MAX_TRIES = 3;
+ for (int retry = MAX_TRIES; retry > 0; retry--) {
+ driver->switch_seq(LINE_RESET);
+ swd_queue_dp_write(dap, DP_TARGETSEL, dap->tap->multidrop_targetsel);
+ *dpidr = *dlpidr = 0;
+ swd_queue_dp_read(dap, DP_DPIDR, dpidr);
+ if (!reconnecting && retry == MAX_TRIES) {
+ /* Ideally just clear ORUN flag which is set by reset */
+ swd_queue_dp_write(dap, DP_ABORT, ORUNERRCLR);
+ } else {
+ /* Clear all sticky errors during (including ORUN) */
+ swd_clear_sticky_errors(dap);
+ }
+ swd_queue_dp_read(dap, DP_DLPIDR, dlpidr);
+ retval = swd_run(dap);
+ if (retval == ERROR_OK) {
+ if (1 != (*dlpidr & DP_TARGETSEL_DPID_MASK) ||
+ (dap->tap->multidrop_targetsel & DP_TARGETSEL_INSTANCEID_MASK)
+ != (*dlpidr & DP_TARGETSEL_INSTANCEID_MASK)) {
+ LOG_INFO("Read incorrect DLIPDR %08x (possibly CTRL/STAT value) when selecting coreid %d", *dlpidr,
+ dap->tap->multidrop_targetsel >> DP_TARGETSEL_INSTANCEID_SHIFT);
+ retval = ERROR_FAIL;
+ } else {
+ LOG_DEBUG_IO("Selected core %d\n", dap->tap->multidrop_targetsel >> DP_TARGETSEL_INSTANCEID_SHIFT);
+ last_target_id = dap->tap->multidrop_targetsel;
+ }
+ break;
+ } else {
+ last_target_id = DP_TARGETSEL_INVALID;
+ LOG_DEBUG("Failed to select core %d%s", dap->tap->multidrop_targetsel >> DP_TARGETSEL_INSTANCEID_SHIFT,
+ retry > 1 ? ", retrying..." : "");
+ }
+ }
+ }
+ return retval;
+}
+
+static int swd_multidrop_deselect_target(struct adiv5_dap *dap)
+{
+ assert(adiv5_dap_swd_multidrop_session_state(dap)->active_on_wire);
+ int retval = ERROR_OK;
+ static const bool unselect_target; /* = false */
+ /* todo: decide if we really want to deselect the target */
+ if (unselect_target) {
+ const struct swd_driver *driver = adiv5_dap_swd_driver(dap);
+ driver->switch_seq(LINE_RESET);
+ swd_queue_dp_write(dap, DP_TARGETSEL, DP_TARGETSEL_INVALID);
+ retval = swd_run(dap);
+ }
+ return retval;
+}
+
+/** Executes all queued DAP operations. */
+static int swd_multidrop_run(struct adiv5_dap *dap)
+{
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(dap);
+ int retval;
+ if (state->active_on_wire) {
+ retval = swd_run(dap);
+ } else {
+ if (dap->do_reconnect) {
+ retval = swd_multidrop_connect(dap);
+ } else {
+ uint32_t dpidr, dlpidr;
+ state->active_on_wire = true;
+ retval = swd_multidrop_select_target(dap, &dpidr, &dlpidr, false);
+ state->active_on_wire = false;
+ }
+
+ state->active_on_wire = true;
+ /* Send the queued command if all is well */
+ for (int n = 0; n < state->queued_request_count && retval == ERROR_OK; n++) {
+ struct queued_request *request = state->request_queue + n;
+ switch (request->type) {
+ case dp_write:
+ retval = swd_queue_dp_write(dap, request->reg, request->write);
+ break;
+ case dp_read:
+ retval = swd_queue_dp_read(dap, request->reg, request->read);
+ break;
+ case ap_write:
+ retval = swd_queue_ap_write(request->ap, request->reg, request->write);
+ break;
+ case ap_read:
+ retval = swd_queue_ap_read(request->ap, request->reg, request->read);
+ break;
+ case ap_abort:
+ retval = swd_queue_ap_abort(dap, request->ack);
+ break;
+ default:
+ assert(false);
+ }
+ }
+ state->queued_request_count = 0;
+
+ if (retval == ERROR_OK)
+ retval = swd_run(dap);
+
+ if (retval == ERROR_OK)
+ swd_multidrop_deselect_target(dap);
+
+ state->active_on_wire = false;
+ }
+ return retval;
+}
+
+/** Put the SWJ-DP back to dormant mode */
+static void swd_multidrop_quit(struct adiv5_dap *dap)
+{
+ const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
+
+ if (0 == --swd_multidrop_session_count)
+ swd->switch_seq(SWD_TO_DORMANT);
+
+ /* flush the queue before exit */
+ swd_multidrop_run(dap);
+
+ struct swd_multidrop_session_state *state = adiv5_dap_swd_multidrop_session_state(dap);
+ free(state->request_queue);
+ free(state);
+}
+
+const struct dap_ops swd_multidrop_dap_ops = {
+ .connect = swd_multidrop_connect,
+ .queue_dp_read = swd_multidrop_queue_dp_read,
+ .queue_dp_write = swd_multidrop_queue_dp_write,
+ .queue_ap_read = swd_multidrop_queue_ap_read,
+ .queue_ap_write = swd_multidrop_queue_ap_write,
+ .queue_ap_abort = swd_multidrop_queue_ap_abort,
+ .run = swd_multidrop_run,
+ .quit = swd_multidrop_quit,
+};
+
+int swd_create_transport_session(struct adiv5_dap *dap, const struct dap_ops **dap_ops, void **transport_private)
+{
+ if (swd_dap_is_multidrop(dap)) {
+ struct swd_multidrop_session_state *state =
+ (struct swd_multidrop_session_state *)calloc(1, sizeof(struct swd_multidrop_session_state));
+ if (state) {
+ *dap_ops = &swd_multidrop_dap_ops;
+ state->request_queue_size = 8;
+ state->request_queue = calloc(state->request_queue_size, sizeof(struct queued_request));
+ *transport_private = state;
+ swd_multidrop_session_count++;
+ return ERROR_OK;
+ } else {
+ return ERROR_FAIL;
+ }
+ } else {
+ *dap_ops = &swd_dap_ops;
+ return ERROR_OK;
+ }
+}
+
static struct transport swd_transport = {
.name = "swd",
.select = swd_select,
@@ -456,8 +832,9 @@ static void swd_constructor(void)
transport_register(&swd_transport);
}
-/** Returns true if the current debug session
- * is using SWD as its transport.
+/**
+ * Returns true if the current debug session
+ * is using SWD (or SWD-multidrop) as its transport.
*/
bool transport_is_swd(void)
{
diff --git a/src/target/arm_adi_v5.h b/src/target/arm_adi_v5.h
index 8edfaa816..65f725b74 100644
--- a/src/target/arm_adi_v5.h
+++ b/src/target/arm_adi_v5.h
@@ -155,6 +155,11 @@
#define DP_SELECT_DPBANK 0x0000000F
#define DP_SELECT_INVALID 0x00FFFF00 /* Reserved bits one */
+#define DP_TARGETSEL_INVALID 0xFFFFFFFF
+#define DP_TARGETSEL_DPID_MASK 0x0FFFFFFF
+#define DP_TARGETSEL_INSTANCEID_MASK 0xF0000000
+#define DP_TARGETSEL_INSTANCEID_SHIFT 28
+
#define DP_APSEL_MAX (255)
#define DP_APSEL_INVALID (-1)
@@ -592,6 +597,7 @@ extern int dap_info_command(struct command_invocation *cmd,
extern int dap_register_commands(struct command_context *cmd_ctx);
extern const char *adiv5_dap_name(struct adiv5_dap *self);
extern const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self);
+extern void *adiv5_dap_swd_transport_private(struct adiv5_dap *self);
extern int dap_cleanup_all(void);
struct adiv5_private_config {
diff --git a/src/target/arm_dap.c b/src/target/arm_dap.c
index 56442f183..9ebcf76bc 100644
--- a/src/target/arm_dap.c
+++ b/src/target/arm_dap.c
@@ -29,10 +29,12 @@
#include "helper/command.h"
#include "transport/transport.h"
#include "jtag/interface.h"
+#include "jtag/swd.h"
static LIST_HEAD(all_dap);
extern const struct dap_ops swd_dap_ops;
+extern const struct dap_ops swd_multidrop_dap_ops;
extern const struct dap_ops jtag_dp_ops;
extern struct adapter_driver *adapter_driver;
@@ -41,7 +43,8 @@ struct arm_dap_object {
struct list_head lh;
struct adiv5_dap dap;
char *name;
- const struct swd_driver *swd;
+ const struct swd_driver *driver;
+ void *transport_private;
};
static void dap_instance_init(struct adiv5_dap *dap)
@@ -71,13 +74,20 @@ const char *adiv5_dap_name(struct adiv5_dap *self)
const struct swd_driver *adiv5_dap_swd_driver(struct adiv5_dap *self)
{
struct arm_dap_object *obj = container_of(self, struct arm_dap_object, dap);
- return obj->swd;
+ return obj->driver;
+}
+
+void *adiv5_dap_swd_transport_private(struct adiv5_dap *self)
+{
+ struct arm_dap_object *obj = container_of(self, struct arm_dap_object, dap);
+ return obj->transport_private;
}
struct adiv5_dap *adiv5_get_dap(struct arm_dap_object *obj)
{
return &obj->dap;
}
+
struct adiv5_dap *dap_instance_by_jim_obj(Jim_Interp *interp, Jim_Obj *o)
{
struct arm_dap_object *obj = NULL;
@@ -105,6 +115,8 @@ static int dap_init_all(void)
LOG_DEBUG("Initializing all DAPs ...");
+ bool had_multidrop = false;
+ bool had_non_multidrop = false;
list_for_each_entry(obj, &all_dap, lh) {
struct adiv5_dap *dap = &obj->dap;
@@ -117,8 +129,19 @@ static int dap_init_all(void)
continue;
if (transport_is_swd()) {
- dap->ops = &swd_dap_ops;
- obj->swd = adapter_driver->swd_ops;
+ if (dap->tap->multidrop_targetsel == DP_TARGETSEL_INVALID)
+ had_non_multidrop = true;
+ else
+ had_multidrop = true;
+
+ if (had_multidrop && had_non_multidrop) {
+ LOG_ERROR("Mixture of SWD multidrop DAPs and non multidrop DAPs is not currently supported");
+ return ERROR_FAIL;
+ }
+ retval = swd_create_transport_session(dap, &dap->ops, &obj->transport_private);
+ if (retval != ERROR_OK)
+ return retval;
+ obj->driver = adapter_driver->swd_ops;
} else if (transport_is_dapdirect_swd()) {
dap->ops = adapter_driver->dap_swd_ops;
} else if (transport_is_dapdirect_jtag()) {
@@ -308,7 +331,12 @@ static int jim_dap_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
COMMAND_HANDLER(handle_dap_init)
{
- return dap_init_all();
+ int retval;
+ retval = dap_init_all();
+ if (retval != ERROR_OK)
+ LOG_INFO("DAP init failed");
+
+ return retval;
}
COMMAND_HANDLER(handle_dap_info_command)
diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c
index ce2c426ce..a30dc6a81 100644
--- a/src/target/cortex_m.c
+++ b/src/target/cortex_m.c
@@ -38,6 +38,7 @@
#include "register.h"
#include "arm_opcodes.h"
#include "arm_semihosting.h"
+#include "smp.h"
#include <helper/time_support.h>
#include <rtt/rtt.h>
@@ -570,6 +571,78 @@ static int cortex_m_debug_entry(struct target *target)
return ERROR_OK;
}
+static struct target *get_cortex_m(struct target *target, int32_t coreid)
+{
+ struct target_list *head;
+ struct target *curr;
+
+ foreach_smp_target(head, target->head) {
+ curr = head->target;
+ if ((curr->coreid == coreid) && (curr->state == TARGET_HALTED))
+ return curr;
+ }
+ return target;
+}
+
+static int cortex_m_halt(struct target *target);
+static int cortex_m_poll(struct target *target);
+
+static int cortex_m_halt_smp(struct target *target)
+{
+ int retval = 0;
+ struct target_list *head;
+ struct target *curr;
+ foreach_smp_target(head, target->head) {
+ curr = head->target;
+ if ((curr != target) && (curr->state != TARGET_HALTED)
+ && target_was_examined(curr))
+ retval += cortex_m_halt(curr);
+ }
+ return retval;
+}
+
+static int update_halt_gdb(struct target *target)
+{
+ struct target *gdb_target = NULL;
+ struct target_list *head;
+ struct target *curr;
+ int retval = 0;
+
+ if (target->gdb_service && target->gdb_service->core[0] == -1) {
+ target->gdb_service->target = target;
+ target->gdb_service->core[0] = target->coreid;
+ retval += cortex_m_halt_smp(target);
+ }
+
+ if (target->gdb_service)
+ gdb_target = target->gdb_service->target;
+
+ foreach_smp_target(head, target->head) {
+ curr = head->target;
+ /* skip calling context */
+ if (curr == target)
+ continue;
+ if (!target_was_examined(curr))
+ continue;
+ /* skip targets that were already halted */
+ if (curr->state == TARGET_HALTED)
+ continue;
+ /* Skip gdb_target; it alerts GDB so has to be polled as last one */
+ if (curr == gdb_target)
+ continue;
+
+ /* avoid recursion in cortex_m_poll() */
+ curr->smp = 0;
+ cortex_m_poll(curr);
+ curr->smp = 1;
+ }
+
+ /* after all targets were updated, poll the gdb serving target */
+ if (gdb_target != NULL && gdb_target != target)
+ cortex_m_poll(gdb_target);
+ return retval;
+}
+
static int cortex_m_poll(struct target *target)
{
int detected_failure = ERROR_OK;
@@ -578,6 +651,18 @@ static int cortex_m_poll(struct target *target)
struct cortex_m_common *cortex_m = target_to_cm(target);
struct armv7m_common *armv7m = &cortex_m->armv7m;
+ /* toggle to another core is done by gdb as follow */
+ /* maint packet J core_id */
+ /* continue */
+ /* the next polling trigger an halt event sent to gdb */
+ if ((target->state == TARGET_HALTED) && (target->smp) &&
+ (target->gdb_service) &&
+ (target->gdb_service->target == NULL)) {
+ target->gdb_service->target =
+ get_cortex_m(target, target->gdb_service->core[1]);
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ return retval;
+ }
/* Read from Debug Halting Control and Status Register */
retval = mem_ap_read_atomic_u32(armv7m->debug_ap, DCB_DHCSR, &cortex_m->dcb_dhcsr);
if (retval != ERROR_OK) {
@@ -597,7 +682,7 @@ static int cortex_m_poll(struct target *target)
/* We have to execute the rest (the "finally" equivalent, but
* still throw this exception again).
*/
- detected_failure = ERROR_FAIL;
+ detected_failure = ERROR_STICKY_CLEARED;
/* refresh status bits */
retval = mem_ap_read_atomic_u32(armv7m->debug_ap, DCB_DHCSR, &cortex_m->dcb_dhcsr);
@@ -609,6 +694,7 @@ static int cortex_m_poll(struct target *target)
if (target->state != TARGET_RESET) {
target->state = TARGET_RESET;
LOG_INFO("%s: external reset detected", target_name(target));
+ register_cache_invalidate(armv7m->arm.core_cache);
}
return ERROR_OK;
}
@@ -636,6 +722,12 @@ static int cortex_m_poll(struct target *target)
if (retval != ERROR_OK)
return retval;
+ if (target->smp) {
+ retval = update_halt_gdb(target);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
if (arm_semihosting(target, &retval) != 0)
return retval;
@@ -647,6 +739,12 @@ static int cortex_m_poll(struct target *target)
if (retval != ERROR_OK)
return retval;
+ if (target->smp) {
+ retval = update_halt_gdb(target);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+
target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
}
}
@@ -789,8 +887,8 @@ void cortex_m_enable_breakpoints(struct target *target)
}
}
-static int cortex_m_resume(struct target *target, int current,
- target_addr_t address, int handle_breakpoints, int debug_execution)
+static int cortex_m_internal_restore(struct target *target, int current,
+ target_addr_t *address, int handle_breakpoints, int debug_execution)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
struct breakpoint *breakpoint = NULL;
@@ -840,7 +938,7 @@ static int cortex_m_resume(struct target *target, int current,
/* current = 1: continue on current pc, otherwise continue at <address> */
r = armv7m->arm.pc;
if (!current) {
- buf_set_u32(r->value, 0, 32, address);
+ buf_set_u32(r->value, 0, 32, *address);
r->dirty = true;
r->valid = true;
}
@@ -854,6 +952,8 @@ static int cortex_m_resume(struct target *target, int current,
armv7m_maybe_skip_bkpt_inst(target, NULL);
resume_pc = buf_get_u32(r->value, 0, 32);
+ if (current)
+ *address = resume_pc;
armv7m_restore_context(target);
@@ -871,15 +971,67 @@ static int cortex_m_resume(struct target *target, int current,
}
}
+ return ERROR_OK;
+}
+
+static int cortex_m_internal_restart(struct target *target)
+{
+ struct armv7m_common *armv7m = target_to_armv7m(target);
+
/* Restart core */
cortex_m_set_maskints_for_run(target);
cortex_m_write_debug_halt_mask(target, 0, C_HALT);
target->debug_reason = DBG_REASON_NOTHALTED;
+ target->state = TARGET_RUNNING;
/* registers are now invalid */
register_cache_invalidate(armv7m->arm.core_cache);
+ return ERROR_OK;
+}
+
+static int cortex_m_restore_smp(struct target *target, int handle_breakpoints)
+{
+ int retval = 0;
+ struct target_list *head;
+ struct target *curr;
+ target_addr_t address;
+ foreach_smp_target(head, target->head) {
+ curr = head->target;
+ if ((curr != target) && (curr->state != TARGET_RUNNING)
+ && target_was_examined(curr)) {
+ /* resume current address , not in step mode */
+ retval += cortex_m_internal_restore(curr, 1, &address,
+ handle_breakpoints, 0);
+ retval += cortex_m_internal_restart(curr);
+ }
+ }
+ return retval;
+}
+
+static int cortex_m_resume(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int debug_execution)
+{
+ int retval = 0;
+ /* dummy resume for smp toggle in order to reduce gdb impact */
+ if ((target->smp) && (target->gdb_service->core[1] != -1)) {
+ /* simulate a start and halt of target */
+ target->gdb_service->target = NULL;
+ target->gdb_service->core[0] = target->gdb_service->core[1];
+ /* fake resume at next poll we play the target core[1], see poll*/
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ return 0;
+ }
+ cortex_m_internal_restore(target, current, &address, handle_breakpoints, debug_execution);
+ if (target->smp) {
+ target->gdb_service->core[0] = -1;
+ retval = cortex_m_restore_smp(target, handle_breakpoints);
+ if (retval != ERROR_OK)
+ return retval;
+ }
+ cortex_m_internal_restart(target);
+ uint32_t resume_pc = (uint32_t)address;
if (!debug_execution) {
target->state = TARGET_RUNNING;
target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
@@ -1950,8 +2102,10 @@ int cortex_m_examine(struct target *target)
retval = cortex_m_find_mem_ap(swjdp, &armv7m->debug_ap);
if (retval != ERROR_OK) {
LOG_ERROR("Could not find MEM-AP to control the core");
+ swjdp->do_reconnect = true;
return retval;
}
+
} else {
armv7m->debug_ap = dap_ap(swjdp, cortex_m->apsel);
}
@@ -2409,6 +2563,27 @@ COMMAND_HANDLER(handle_cortex_m_mask_interrupts_command)
return ERROR_OK;
}
+COMMAND_HANDLER(cortex_m_handle_smp_gdb_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ int retval = ERROR_OK;
+ struct target_list *head;
+ head = target->head;
+ if (head != (struct target_list *)NULL) {
+ if (CMD_ARGC == 1) {
+ int coreid = 0;
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], coreid);
+ if (ERROR_OK != retval)
+ return retval;
+ target->gdb_service->core[1] = coreid;
+
+ }
+ command_print(CMD, "gdb coreid %" PRId32 " -> %" PRId32, target->gdb_service->core[0]
+ , target->gdb_service->core[1]);
+ }
+ return ERROR_OK;
+}
+
COMMAND_HANDLER(handle_cortex_m_reset_config_command)
{
struct target *target = get_current_target(CMD_CTX);
@@ -2455,6 +2630,13 @@ COMMAND_HANDLER(handle_cortex_m_reset_config_command)
}
static const struct command_registration cortex_m_exec_command_handlers[] = {
+ {
+ .name = "smp_gdb",
+ .handler = cortex_m_handle_smp_gdb_command,
+ .mode = COMMAND_EXEC,
+ .help = "display/fix current core played to gdb",
+ .usage = "",
+ },
{
.name = "maskisr",
.handler = handle_cortex_m_mask_interrupts_command,
@@ -2476,6 +2658,9 @@ static const struct command_registration cortex_m_exec_command_handlers[] = {
.help = "configure software reset handling",
.usage = "['sysresetreq'|'vectreset']",
},
+ {
+ .chain = smp_command_handlers,
+ },
COMMAND_REGISTRATION_DONE
};
static const struct command_registration cortex_m_command_handlers[] = {
diff --git a/src/target/target.c b/src/target/target.c
index cab84b06b..a8f604462 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -3010,6 +3010,10 @@ static int handle_target(void *priv)
if (!powerDropout && !srstAsserted) {
/* polling may fail silently until the target has been examined */
retval = target_poll(target);
+ if (retval == ERROR_STICKY_CLEARED) {
+ /* retry if a sticky error was cleared */
+ retval = target_poll(target);
+ }
if (retval != ERROR_OK) {
/* 100ms polling interval. Increase interval between polling up to 5000ms */
if (target->backoff.times * polling_interval < 5000) {
@@ -3230,6 +3234,10 @@ int target_wait_state(struct target *target, enum target_state state, int ms)
for (;;) {
retval = target_poll(target);
+ if (retval == ERROR_STICKY_CLEARED) {
+ /* retry if a sticky error was cleared */
+ retval = target_poll(target);
+ }
if (retval != ERROR_OK)
return retval;
if (target->state == state)
diff --git a/src/transport/transport.h b/src/transport/transport.h
index 6bf6aaced..98c288772 100644
--- a/src/transport/transport.h
+++ b/src/transport/transport.h
@@ -93,6 +93,9 @@ COMMAND_HELPER(transport_list_parse, char ***vector);
int allow_transports(struct command_context *ctx, const char * const *vector);
bool transport_is_jtag(void);
+/**
+ * Note this returns true for swd multidrop too
+ */
bool transport_is_swd(void);
bool transport_is_dapdirect_jtag(void);
bool transport_is_dapdirect_swd(void);
diff --git a/tcl/board/pico-debug.cfg b/tcl/board/pico-debug.cfg
new file mode 100644
index 000000000..ba59f860a
--- /dev/null
+++ b/tcl/board/pico-debug.cfg
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# pico-debug is a virtual CMSIS-DAP debug adapter
+# it runs on the very same RP2040 target being debugged without additional hardware
+# https://github.com/majbthrd/pico-debug
+
+source [find interface/cmsis-dap.cfg]
+adapter speed 4000
+
+set CHIPNAME rp2040
+source [find target/rp2040-core0.cfg]
diff --git a/tcl/interface/picoprobe.cfg b/tcl/interface/picoprobe.cfg
new file mode 100644
index 000000000..7090050e7
--- /dev/null
+++ b/tcl/interface/picoprobe.cfg
@@ -0,0 +1,3 @@
+# Adapter section
+adapter driver picoprobe
+adapter speed 5000
diff --git a/tcl/interface/raspberrypi-swd.cfg b/tcl/interface/raspberrypi-swd.cfg
new file mode 100644
index 000000000..78f181676
--- /dev/null
+++ b/tcl/interface/raspberrypi-swd.cfg
@@ -0,0 +1,12 @@
+# Use RPI GPIO pins
+adapter driver bcm2835gpio
+
+bcm2835gpio_speed_coeffs 146203 36
+
+# SWD swclk swdio
+# Header pin numbers
+bcm2835gpio_swd_nums 25 24
+
+transport select swd
+
+adapter speed 1000
diff --git a/tcl/target/rp2040-core0.cfg b/tcl/target/rp2040-core0.cfg
new file mode 100644
index 000000000..79b80f9ea
--- /dev/null
+++ b/tcl/target/rp2040-core0.cfg
@@ -0,0 +1,39 @@
+source [find target/swj-dp.tcl]
+source [find mem_helper.tcl]
+
+set _CHIPNAME rp2040
+set _CPUTAPID 0x01002927
+set _ENDIAN little
+
+swj_newdap $_CHIPNAME.core0 cpu -dp-id $_CPUTAPID -instance-id 0
+
+# NOTE target smp makes both targets act a single virtual target on one port for gdb
+# (without it you should be able to debug separately on two ports)
+# NOTE: "-rtos hwthread" creates a thread per core in smp mode (otherwise it is a single thread for the virtual target)
+
+# Give OpenOCD SRAM1 (64k) to use for e.g. flash programming bounce buffers (should avoid algo stack etc)
+# Don't require save/restore, because this isn't something we'd do whilst a user app is running
+set _WORKSIZE 0x10000
+set _WORKBASE 0x20010000
+
+#core 0
+set _TARGETNAME_0 $_CHIPNAME.core0
+dap create $_TARGETNAME_0.dap -chain-position $_CHIPNAME.core0.cpu
+target create $_TARGETNAME_0 cortex_m -endian $_ENDIAN -coreid 0 -dap $_TARGETNAME_0.dap -rtos hwthread
+$_TARGETNAME_0 configure -work-area-phys $_WORKBASE -work-area-size $_WORKSIZE -work-area-backup 0
+cortex_m reset_config sysresetreq
+
+target smp $_TARGETNAME_0
+
+set _FLASHNAME $_CHIPNAME.flash
+set _FLASHSIZE 0x200000
+set _FLASHBASE 0x10000000
+# name driver base, size in bytes, chip_width, bus_width, target used to access
+flash bank $_FLASHNAME rp2040_flash $_FLASHBASE $_FLASHSIZE 1 32 $_TARGETNAME_0
+
+# srst is not fitted so use SYSRESETREQ to perform a soft reset
+reset_config srst_nogate
+
+gdb_flash_program enable
+gdb_memory_map enable
+
diff --git a/tcl/target/rp2040-core1.cfg b/tcl/target/rp2040-core1.cfg
new file mode 100644
index 000000000..cc4ff3888
--- /dev/null
+++ b/tcl/target/rp2040-core1.cfg
@@ -0,0 +1,39 @@
+source [find target/swj-dp.tcl]
+source [find mem_helper.tcl]
+
+set _CHIPNAME rp2040
+set _CPUTAPID 0x01002927
+set _ENDIAN little
+
+swj_newdap $_CHIPNAME.core1 cpu -dp-id $_CPUTAPID -instance-id 1
+
+# NOTE target smp makes both targets act a single virtual target on one port for gdb
+# (without it you should be able to debug separately on two ports)
+# NOTE: "-rtos hwthread" creates a thread per core in smp mode (otherwise it is a single thread for the virtual target)
+
+# Give OpenOCD SRAM1 (64k) to use for e.g. flash programming bounce buffers (should avoid algo stack etc)
+# Don't require save/restore, because this isn't something we'd do whilst a user app is running
+set _WORKSIZE 0x10000
+set _WORKBASE 0x20010000
+
+#core 1
+set _TARGETNAME_1 $_CHIPNAME.core1
+dap create $_TARGETNAME_1.dap -chain-position $_CHIPNAME.core1.cpu
+target create $_TARGETNAME_1 cortex_m -endian $_ENDIAN -coreid 1 -dap $_TARGETNAME_1.dap -rtos hwthread
+$_TARGETNAME_1 configure -work-area-phys $_WORKBASE -work-area-size $_WORKSIZE -work-area-backup 0
+cortex_m reset_config sysresetreq
+
+target smp $_TARGETNAME_1
+
+set _FLASHNAME $_CHIPNAME.flash
+set _FLASHSIZE 0x200000
+set _FLASHBASE 0x10000000
+# name driver base, size in bytes, chip_width, bus_width, target used to access
+flash bank $_FLASHNAME rp2040_flash $_FLASHBASE $_FLASHSIZE 1 32 $_TARGETNAME_1
+
+# srst is not fitted so use SYSRESETREQ to perform a soft reset
+reset_config srst_nogate
+
+gdb_flash_program enable
+gdb_memory_map enable
+
diff --git a/tcl/target/rp2040-rescue.cfg b/tcl/target/rp2040-rescue.cfg
new file mode 100644
index 000000000..56298ba02
--- /dev/null
+++ b/tcl/target/rp2040-rescue.cfg
@@ -0,0 +1,28 @@
+source [find target/swj-dp.tcl]
+source [find mem_helper.tcl]
+
+set _CHIPNAME rp2040
+set _CPUTAPID 0x01002927
+set _ENDIAN little
+
+swj_newdap $_CHIPNAME.rescue_dp cpu -dp-id $_CPUTAPID -instance-id 0xf
+set _TARGETNAME_0 $_CHIPNAME.rescue
+dap create $_TARGETNAME_0.dap -chain-position $_CHIPNAME.rescue_dp.cpu -ignore-syspwrupack
+
+# Have to init before we can do dpreg commands
+init
+
+# The rescue debug port uses the AP CTRL/STAT bit DBGPWRUPREQ to reset the
+# PSM (power on state machine) of the RP2040 with a flag set in the
+# VREG_AND_POR_CHIP_RESET register. Once the reset is released
+# (by clearing the DBGPWRUPREQ flag), the bootrom will run, see this flag,
+# and halt. Allowing the user to load some fresh code, rather than loading
+# the potentially broken code stored in flash
+
+# Clear DBGPWRUPREQ
+$_TARGETNAME_0.dap dpreg 0x4 0x00000000
+
+# Verifying CTRL/STAT is 0
+$_TARGETNAME_0.dap dpreg 0x4
+
+echo "Now attach a debugger to your RP2040 and load some code"
diff --git a/tcl/target/rp2040.cfg b/tcl/target/rp2040.cfg
new file mode 100644
index 000000000..b032f0567
--- /dev/null
+++ b/tcl/target/rp2040.cfg
@@ -0,0 +1,53 @@
+source [find target/swj-dp.tcl]
+source [find mem_helper.tcl]
+
+set _CHIPNAME rp2040
+set _CPUTAPID 0x01002927
+set _ENDIAN little
+
+swj_newdap $_CHIPNAME.core0 cpu -dp-id $_CPUTAPID -instance-id 0
+swj_newdap $_CHIPNAME.core1 cpu -dp-id $_CPUTAPID -instance-id 1
+
+# NOTE target smp makes both targets act a single virtual target on one port for gdb
+# (without it you should be able to debug separately on two ports)
+# NOTE: "-rtos hwthread" creates a thread per core in smp mode (otherwise it is a single thread for the virtual target)
+
+# Give OpenOCD SRAM1 (64k) to use for e.g. flash programming bounce buffers (should avoid algo stack etc)
+# Don't require save/restore, because this isn't something we'd do whilst a user app is running
+set _WORKSIZE 0x10000
+set _WORKBASE 0x20010000
+
+#core 0
+set _TARGETNAME_0 $_CHIPNAME.core0
+dap create $_TARGETNAME_0.dap -chain-position $_CHIPNAME.core0.cpu
+target create $_TARGETNAME_0 cortex_m -endian $_ENDIAN -coreid 0 -dap $_TARGETNAME_0.dap -rtos hwthread
+$_TARGETNAME_0 configure -work-area-phys $_WORKBASE -work-area-size $_WORKSIZE -work-area-backup 0
+cortex_m reset_config sysresetreq
+
+#core 1
+set _TARGETNAME_1 $_CHIPNAME.core1
+dap create $_TARGETNAME_1.dap -chain-position $_CHIPNAME.core1.cpu
+target create $_TARGETNAME_1 cortex_m -endian $_ENDIAN -coreid 1 -dap $_TARGETNAME_1.dap -rtos hwthread
+$_TARGETNAME_1 configure -work-area-phys $_WORKBASE -work-area-size $_WORKSIZE -work-area-backup 0
+cortex_m reset_config sysresetreq
+
+target smp $_TARGETNAME_0 $_TARGETNAME_1
+
+set _FLASHNAME $_CHIPNAME.flash
+set _FLASHSIZE 0x200000
+set _FLASHBASE 0x10000000
+# name driver base, size in bytes, chip_width, bus_width, target used to access
+flash bank $_FLASHNAME rp2040_flash $_FLASHBASE $_FLASHSIZE 1 32 $_TARGETNAME_0
+
+# Openocd associates a flash bank with a target (in our case a core) and
+# running `openocd -c "program foo.elf"` just grabs the last selected core
+# from the TCL context. Select `core0` by default so that flash probe
+# succeeds. Note gdb understands there are 2 cores -- this is only for TCL.
+targets rp2040.core0
+
+# srst is not fitted so use SYSRESETREQ to perform a soft reset
+reset_config srst_nogate
+
+gdb_flash_program enable
+gdb_memory_map enable
+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment