Merge commit '16e9b9c44fa62ea6eec99d1fb7bc43a8f1cc2f7e' into from_upstream
Conflicts: configure.ac tcl/target/gd32vf103.cfg Change-Id: I72bbb973249b7bbfa720696fa2c76a87a41a2e9c
This commit is contained in:
commit
e4a0658dff
4
HACKING
4
HACKING
|
@ -181,10 +181,6 @@ topics. It is possible because @c for/master is not a traditional Git
|
|||
branch.
|
||||
-# You will need to install this hook, we will look into a better solution:
|
||||
@code
|
||||
scp -p -P 29418 USERNAME@review.openocd.org:hooks/commit-msg .git/hooks/
|
||||
@endcode
|
||||
Or with http only:
|
||||
@code
|
||||
wget https://review.openocd.org/tools/hooks/commit-msg
|
||||
mv commit-msg .git/hooks
|
||||
chmod +x .git/hooks/commit-msg
|
||||
|
|
|
@ -383,7 +383,7 @@ AC_ARG_ENABLE([internal-libjaylink],
|
|||
[use_internal_libjaylink=$enableval], [use_internal_libjaylink=no])
|
||||
|
||||
AC_ARG_ENABLE([remote-bitbang],
|
||||
AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang jtag driver]),
|
||||
AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang driver]),
|
||||
[build_remote_bitbang=$enableval], [build_remote_bitbang=yes])
|
||||
|
||||
AS_CASE(["${host_cpu}"],
|
||||
|
@ -599,9 +599,9 @@ AS_IF([test "x$use_internal_jimtcl" = "xyes"], [
|
|||
|
||||
AS_IF([test "x$build_remote_bitbang" = "xyes"], [
|
||||
build_bitbang=yes
|
||||
AC_DEFINE([BUILD_REMOTE_BITBANG], [1], [1 if you want the Remote Bitbang JTAG driver.])
|
||||
AC_DEFINE([BUILD_REMOTE_BITBANG], [1], [1 if you want the Remote Bitbang driver.])
|
||||
], [
|
||||
AC_DEFINE([BUILD_REMOTE_BITBANG], [0], [0 if you don't want the Remote Bitbang JTAG driver.])
|
||||
AC_DEFINE([BUILD_REMOTE_BITBANG], [0], [0 if you don't want the Remote Bitbang driver.])
|
||||
])
|
||||
|
||||
AS_IF([test "x$build_sysfsgpio" = "xyes"], [
|
||||
|
|
|
@ -19,6 +19,7 @@ void repeated_start(void);
|
|||
void stop_cd(void);
|
||||
void clock_cd(void);
|
||||
void send_ack(void);
|
||||
void send_nack(void);
|
||||
bool get_ack(void);
|
||||
|
||||
uint8_t get_address(uint8_t adr, uint8_t rdwr);
|
||||
|
|
|
@ -60,6 +60,16 @@ void send_ack(void)
|
|||
delay_us(1);
|
||||
}
|
||||
|
||||
void send_nack(void)
|
||||
{
|
||||
PIN_SDA = 1;
|
||||
delay_us(1);
|
||||
PIN_SCL = 1;
|
||||
delay_us(1);
|
||||
PIN_SCL = 0;
|
||||
delay_us(1);
|
||||
}
|
||||
|
||||
bool get_ack(void)
|
||||
{
|
||||
PIN_SDA_DIR = 1;
|
||||
|
|
|
@ -757,14 +757,13 @@ void i2c_recieve(void)
|
|||
PIN_SDA_DIR = 0;
|
||||
if (EP6FIFOBUF[0] == 1) {
|
||||
uint8_t rdwr = EP6FIFOBUF[0]; //read
|
||||
uint8_t reg_adr_check = EP6FIFOBUF[1];
|
||||
uint8_t count = EP6FIFOBUF[2]; //request data count
|
||||
uint8_t data_count = EP6FIFOBUF[1]; //data sent count
|
||||
uint8_t count = EP6FIFOBUF[2]; //requested data count
|
||||
uint8_t adr = EP6FIFOBUF[3]; //address
|
||||
uint8_t reg_adr = EP6FIFOBUF[4];
|
||||
uint8_t address = get_address(adr, rdwr); //address byte (read command)
|
||||
uint8_t address_2 = get_address(adr, 0); //address byte 2 (write command)
|
||||
|
||||
printf("%d\n", address);
|
||||
printf("%d\n", address - 1);
|
||||
|
||||
/* start: */
|
||||
start_cd();
|
||||
|
@ -773,17 +772,15 @@ void i2c_recieve(void)
|
|||
/* ack: */
|
||||
uint8_t ack = get_ack();
|
||||
|
||||
delay_us(10);
|
||||
|
||||
/* send data */
|
||||
if (reg_adr_check) { //if there is a byte reg
|
||||
send_byte(reg_adr);
|
||||
/* ack(): */
|
||||
ack = get_ack();
|
||||
if (data_count) { //if there is a byte reg
|
||||
for (uint8_t i = 0; i < data_count; i++) {
|
||||
send_byte(EP6FIFOBUF[i + 4]);
|
||||
/* ack(): */
|
||||
ack = get_ack();
|
||||
}
|
||||
}
|
||||
|
||||
delay_us(10);
|
||||
|
||||
/* repeated start: */
|
||||
repeated_start();
|
||||
/* address: */
|
||||
|
@ -791,23 +788,22 @@ void i2c_recieve(void)
|
|||
/* get ack: */
|
||||
ack = get_ack();
|
||||
|
||||
delay_us(10);
|
||||
|
||||
/* receive data */
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
for (uint8_t i = 0; i < count - 1; i++) {
|
||||
EP8FIFOBUF[i] = receive_byte();
|
||||
|
||||
/* send ack: */
|
||||
/* send ack: */
|
||||
send_ack();
|
||||
}
|
||||
|
||||
delay_ms(1);
|
||||
EP8FIFOBUF[count - 1] = receive_byte();
|
||||
|
||||
/* send Nack: */
|
||||
send_nack();
|
||||
|
||||
/* stop */
|
||||
stop_cd();
|
||||
|
||||
delay_us(10);
|
||||
|
||||
EP8BCH = 0; //EP8
|
||||
syncdelay(3);
|
||||
EP8BCL = count; //EP8
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Espressif Xtensa Makefile to compile flasher stub wrapper
|
||||
# Copyright (C) 2023 Espressif Systems Ltd.
|
||||
|
||||
# Prefix for Espressif xtensa cross compilers (can include a directory path)
|
||||
CROSS ?= xtensa-esp32-elf-
|
||||
|
||||
APP_ARCH := xtensa
|
||||
APP_CHIP_PATH := $(shell pwd)
|
||||
SRCS := $(APP_CHIP_PATH)/esp_xtensa_stub_tramp_win.S
|
||||
|
||||
BIN2C = ../../../../../src/helper/bin2char.sh
|
||||
BUILD_DIR = build
|
||||
|
||||
APP = esp_xtensa_stub_tramp_win
|
||||
APP_OBJ = $(BUILD_DIR)/$(APP).o
|
||||
APP_BIN = $(BUILD_DIR)/$(APP).bin
|
||||
APP_CODE = $(APP).inc
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(BUILD_DIR) $(APP_OBJ) $(APP_CODE)
|
||||
|
||||
$(BUILD_DIR):
|
||||
$(Q) mkdir $@
|
||||
|
||||
$(APP_OBJ): $(SRCS)
|
||||
@echo " CC $^ -> $@"
|
||||
$(Q) $(CROSS)gcc -c $(CFLAGS) -o $@ $^
|
||||
|
||||
$(APP_CODE): $(APP_OBJ)
|
||||
@echo " CC $^ -> $@"
|
||||
$(Q) $(CROSS)objcopy -O binary -j.text $^ $(APP_BIN)
|
||||
$(Q) $(BIN2C) < $(APP_BIN) > $@
|
||||
|
||||
clean:
|
||||
$(Q) rm -rf $(BUILD_DIR)
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/***************************************************************************
|
||||
* Xtensa flasher stub wrapper *
|
||||
* Copyright (C) 2017 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Expects :
|
||||
* a0 = zero
|
||||
* a1 = stack_base + stack_size - 16, 16 bytes aligned
|
||||
* a8 = address of the function to call
|
||||
* Params :
|
||||
* a2 = command arg0, result (out)
|
||||
* a3 = command arg1
|
||||
* a4 = command arg2
|
||||
* a5 = command arg3
|
||||
* a6 = command arg4
|
||||
* Maximum 5 user args
|
||||
*/
|
||||
.text
|
||||
|
||||
.align 4
|
||||
_stub_enter:
|
||||
/* initialize initial stack frame for callx8 */
|
||||
addi a9, sp, 32 /* point 16 past extra save area */
|
||||
s32e a9, sp, -12 /* access to extra save area */
|
||||
/* prepare args */
|
||||
mov a10, a2
|
||||
mov a11, a3
|
||||
mov a12, a4
|
||||
mov a13, a5
|
||||
mov a14, a6
|
||||
/* call stub */
|
||||
callx8 a8
|
||||
/* prepare return value */
|
||||
mov a2, a10
|
||||
break 0,0
|
||||
|
||||
_idle_loop:
|
||||
j _idle_loop
|
|
@ -0,0 +1,3 @@
|
|||
/* Autogenerated with ../../../../../src/helper/bin2char.sh */
|
||||
0x92,0xc1,0x20,0x90,0xd1,0x49,0xad,0x02,0xbd,0x03,0xcd,0x04,0xdd,0x05,0x60,0xe6,
|
||||
0x20,0xe0,0x08,0x00,0x2d,0x0a,0x00,0x40,0x00,0x06,0xff,0xff,
|
|
@ -1,30 +1,31 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2021 by Manuel Wick <manuel@matronix.de> *
|
||||
* Copyright (C) 2013 Paul Fertser <fercerpav@gmail.com> *
|
||||
* Copyright (C) 2012 by Creative Product Design, marc @ cpdesign.com.au *
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
This is a test application to be used as a remote bitbang server for
|
||||
the OpenOCD remote_bitbang interface driver.
|
||||
|
||||
To compile run:
|
||||
gcc -Wall -ansi -pedantic -std=c99 -o remote_bitbang_sysfsgpio remote_bitbang_sysfsgpio.c
|
||||
|
||||
|
||||
Usage example:
|
||||
|
||||
On Raspberry Pi run:
|
||||
socat TCP6-LISTEN:7777,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
|
||||
|
||||
On host run:
|
||||
openocd -c "interface remote_bitbang; remote_bitbang host raspberrypi; remote_bitbang port 7777" \
|
||||
-f target/stm32f1x.cfg
|
||||
|
||||
Or if you want to test UNIX sockets, run both on Raspberry Pi:
|
||||
socat UNIX-LISTEN:/tmp/remotebitbang-socket,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
|
||||
openocd -c "interface remote_bitbang; remote_bitbang host /tmp/remotebitbang-socket" -f target/stm32f1x.cfg
|
||||
* This is a test application to be used as a remote bitbang server for
|
||||
* the OpenOCD remote_bitbang interface driver.
|
||||
*
|
||||
* To compile run:
|
||||
* gcc -Wall -ansi -pedantic -std=c99 -o remote_bitbang_sysfsgpio remote_bitbang_sysfsgpio.c
|
||||
*
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* On Raspberry Pi run:
|
||||
* socat TCP6-LISTEN:7777,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
|
||||
*
|
||||
* On host run:
|
||||
* openocd -c "adapter driver remote_bitbang; remote_bitbang host raspberrypi; remote_bitbang port 7777" \
|
||||
* -f target/stm32f1x.cfg
|
||||
*
|
||||
* Or if you want to test UNIX sockets, run both on Raspberry Pi:
|
||||
* socat UNIX-LISTEN:/tmp/remotebitbang-socket,fork EXEC:"sudo ./remote_bitbang_sysfsgpio tck 11 tms 25 tdo 9 tdi 10"
|
||||
* openocd -c "adapter driver remote_bitbang; remote_bitbang host /tmp/remotebitbang-socket" -f target/stm32f1x.cfg
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
@ -97,11 +98,14 @@ static void unexport_sysfs_gpio(int gpio)
|
|||
* If the gpio is an output, it is initialized according to init_high,
|
||||
* otherwise it is ignored.
|
||||
*
|
||||
* When open_rw is set, the file descriptor will be open as read and write,
|
||||
* e.g. for SWDIO (TMS) that is used as input and output.
|
||||
*
|
||||
* If the gpio is already exported we just show a warning and continue; if
|
||||
* openocd happened to crash (or was killed by user) then the gpios will not
|
||||
* have been cleaned up.
|
||||
*/
|
||||
static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
|
||||
static int setup_sysfs_gpio(int gpio, int is_output, int init_high, int open_rw)
|
||||
{
|
||||
char buf[40];
|
||||
char gpiostr[4];
|
||||
|
@ -132,7 +136,9 @@ static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
|
|||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", gpio);
|
||||
if (is_output)
|
||||
if (open_rw)
|
||||
ret = open(buf, O_RDWR | O_NONBLOCK | O_SYNC);
|
||||
else if (is_output)
|
||||
ret = open(buf, O_WRONLY | O_NONBLOCK | O_SYNC);
|
||||
else
|
||||
ret = open(buf, O_RDONLY | O_NONBLOCK | O_SYNC);
|
||||
|
@ -143,6 +149,37 @@ static int setup_sysfs_gpio(int gpio, int is_output, int init_high)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change direction for gpio.
|
||||
*/
|
||||
static int change_dir_sysfs_gpio(int gpio, int is_output, int init_high)
|
||||
{
|
||||
char buf[40];
|
||||
int ret;
|
||||
|
||||
if (!is_gpio_valid(gpio))
|
||||
return ERROR_OK;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", gpio);
|
||||
ret = open_write_close(buf, is_output ? (init_high ? "high" : "low") : "in");
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("Couldn't set direction for gpio %d", gpio);
|
||||
perror("sysfsgpio: ");
|
||||
unexport_sysfs_gpio(gpio);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/* gpio numbers for each gpio. Negative values are invalid */
|
||||
static int tck_gpio = -1;
|
||||
static int tms_gpio = -1;
|
||||
static int tdi_gpio = -1;
|
||||
static int tdo_gpio = -1;
|
||||
static int trst_gpio = -1;
|
||||
static int srst_gpio = -1;
|
||||
|
||||
/*
|
||||
* file descriptors for /sys/class/gpio/gpioXX/value
|
||||
* Set up during init.
|
||||
|
@ -154,6 +191,15 @@ static int tdo_fd = -1;
|
|||
static int trst_fd = -1;
|
||||
static int srst_fd = -1;
|
||||
|
||||
/*
|
||||
* GPIO state of /sys/class/gpio/gpioXX/value
|
||||
*/
|
||||
static int last_tck = -1;
|
||||
static int last_tms = -1;
|
||||
static int last_tms_drive = -1;
|
||||
static int last_tdi = -1;
|
||||
static int last_initialized = -1;
|
||||
|
||||
/*
|
||||
* Bitbang interface read of TDO
|
||||
*
|
||||
|
@ -179,26 +225,22 @@ static int sysfsgpio_read(void)
|
|||
/*
|
||||
* Bitbang interface write of TCK, TMS, TDI
|
||||
*
|
||||
* Seeing as this is the only function where the outputs are changed,
|
||||
* we can cache the old value to avoid needlessly writing it.
|
||||
* Output states are changed here and in sysfsgpio_write_swd,
|
||||
* which are not used simultaneously, so we can cache the old
|
||||
* value to avoid needlessly writing it.
|
||||
*/
|
||||
static void sysfsgpio_write(int tck, int tms, int tdi)
|
||||
{
|
||||
const char one[] = "1";
|
||||
const char zero[] = "0";
|
||||
|
||||
static int last_tck;
|
||||
static int last_tms;
|
||||
static int last_tdi;
|
||||
|
||||
static int first_time;
|
||||
size_t bytes_written;
|
||||
|
||||
if (!first_time) {
|
||||
if (!last_initialized) {
|
||||
last_tck = !tck;
|
||||
last_tms = !tms;
|
||||
last_tdi = !tdi;
|
||||
first_time = 1;
|
||||
last_initialized = 1;
|
||||
}
|
||||
|
||||
if (tdi != last_tdi) {
|
||||
|
@ -251,13 +293,81 @@ static void sysfsgpio_reset(int trst, int srst)
|
|||
}
|
||||
}
|
||||
|
||||
/* gpio numbers for each gpio. Negative values are invalid */
|
||||
static int tck_gpio = -1;
|
||||
static int tms_gpio = -1;
|
||||
static int tdi_gpio = -1;
|
||||
static int tdo_gpio = -1;
|
||||
static int trst_gpio = -1;
|
||||
static int srst_gpio = -1;
|
||||
/*
|
||||
* Bitbang interface set direction of SWDIO (TMS)
|
||||
*/
|
||||
static void sysfsgpio_swdio_drive(int is_output)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (is_output != 0 && last_tms == -1)
|
||||
last_tms = 0;
|
||||
|
||||
ret = change_dir_sysfs_gpio(tms_gpio, (is_output != 0) ? 1 : 0, last_tms);
|
||||
if (ret != ERROR_OK)
|
||||
LOG_WARNING("Failed to change SWDIO (TMS) direction to output");
|
||||
else
|
||||
last_tms_drive = (is_output != 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitbang interface read of SWDIO (TMS)
|
||||
*
|
||||
* The sysfs value will read back either '0' or '1'. The trick here is to call
|
||||
* lseek to bypass buffering in the sysfs kernel driver.
|
||||
*/
|
||||
static int sysfsgpio_swdio_read(void)
|
||||
{
|
||||
char buf[1];
|
||||
|
||||
/* important to seek to signal sysfs of new read */
|
||||
lseek(tms_fd, 0, SEEK_SET);
|
||||
int ret = read(tms_fd, &buf, sizeof(buf));
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_WARNING("reading swdio (tms) failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitbang interface write of SWCLK (TCK) and SWDIO (TMS)
|
||||
*
|
||||
* Output states are changed here and in sysfsgpio_write, which
|
||||
* are not used simultaneously, so we can cache the old value
|
||||
* to avoid needlessly writing it.
|
||||
*/
|
||||
static void sysfsgpio_swd_write(int swclk, int swdio)
|
||||
{
|
||||
static const char one[] = "1";
|
||||
static const char zero[] = "0";
|
||||
|
||||
size_t bytes_written;
|
||||
|
||||
if (!last_initialized) {
|
||||
last_tck = !swclk;
|
||||
last_tms = !swdio;
|
||||
last_initialized = 1;
|
||||
}
|
||||
|
||||
if (last_tms_drive == 1 && swdio != last_tms) {
|
||||
bytes_written = write(tms_fd, swdio ? &one : &zero, 1);
|
||||
if (bytes_written != 1)
|
||||
LOG_WARNING("writing swdio (tms) failed");
|
||||
}
|
||||
|
||||
/* write clk last */
|
||||
if (swclk != last_tck) {
|
||||
bytes_written = write(tck_fd, swclk ? &one : &zero, 1);
|
||||
if (bytes_written != 1)
|
||||
LOG_WARNING("writing swclk (tck) failed");
|
||||
}
|
||||
|
||||
last_tms = swdio;
|
||||
last_tck = swclk;
|
||||
}
|
||||
|
||||
/* helper func to close and cleanup files only if they were valid/ used */
|
||||
static void cleanup_fd(int fd, int gpio)
|
||||
|
@ -293,13 +403,21 @@ static void process_remote_protocol(void)
|
|||
char d = c - 'r';
|
||||
sysfsgpio_reset(!!(d & 2),
|
||||
(d & 1));
|
||||
} else if (c >= '0' && c <= '0' + 7) {/* Write */
|
||||
} else if (c >= '0' && c <= '0' + 7) { /* Write */
|
||||
char d = c - '0';
|
||||
sysfsgpio_write(!!(d & 4),
|
||||
!!(d & 2),
|
||||
(d & 1));
|
||||
} else if (c == 'R')
|
||||
putchar(sysfsgpio_read());
|
||||
else if (c == 'c') /* SWDIO read */
|
||||
putchar(sysfsgpio_swdio_read());
|
||||
else if (c == 'o' || c == 'O') /* SWDIO drive */
|
||||
sysfsgpio_swdio_drive(c == 'o' ? 0 : 1);
|
||||
else if (c >= 'd' && c <= 'g') { /* SWD write */
|
||||
char d = c - 'd';
|
||||
sysfsgpio_swd_write((d & 2), (d & 1));
|
||||
}
|
||||
else
|
||||
LOG_ERROR("Unknown command '%c' received", c);
|
||||
}
|
||||
|
@ -307,7 +425,7 @@ static void process_remote_protocol(void)
|
|||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
LOG_WARNING("SysfsGPIO remote_bitbang JTAG driver\n");
|
||||
LOG_WARNING("SysfsGPIO remote_bitbang JTAG+SWD driver\n");
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "tck"))
|
||||
|
@ -349,36 +467,39 @@ int main(int argc, char *argv[])
|
|||
* Configure TDO as an input, and TDI, TCK, TMS, TRST, SRST
|
||||
* as outputs. Drive TDI and TCK low, and TMS/TRST/SRST high.
|
||||
*/
|
||||
tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0);
|
||||
tck_fd = setup_sysfs_gpio(tck_gpio, 1, 0, 0);
|
||||
if (tck_fd < 0)
|
||||
goto out_error;
|
||||
|
||||
tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1);
|
||||
tms_fd = setup_sysfs_gpio(tms_gpio, 1, 1, 1);
|
||||
if (tms_fd < 0)
|
||||
goto out_error;
|
||||
last_tms_drive = 0;
|
||||
|
||||
tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0);
|
||||
tdi_fd = setup_sysfs_gpio(tdi_gpio, 1, 0, 0);
|
||||
if (tdi_fd < 0)
|
||||
goto out_error;
|
||||
|
||||
tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0);
|
||||
tdo_fd = setup_sysfs_gpio(tdo_gpio, 0, 0, 0);
|
||||
if (tdo_fd < 0)
|
||||
goto out_error;
|
||||
|
||||
/* assume active low */
|
||||
if (trst_gpio > 0) {
|
||||
trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1);
|
||||
trst_fd = setup_sysfs_gpio(trst_gpio, 1, 1, 0);
|
||||
if (trst_fd < 0)
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
/* assume active low */
|
||||
if (srst_gpio > 0) {
|
||||
srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1);
|
||||
srst_fd = setup_sysfs_gpio(srst_gpio, 1, 1, 0);
|
||||
if (srst_fd < 0)
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
last_initialized = 0;
|
||||
|
||||
LOG_WARNING("SysfsGPIO nums: tck = %d, tms = %d, tdi = %d, tdo = %d",
|
||||
tck_gpio, tms_gpio, tdi_gpio, tdo_gpio);
|
||||
LOG_WARNING("SysfsGPIO num: srst = %d", srst_gpio);
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
/** @remote_bitbangpage OpenOCD Developer's Guide
|
||||
|
||||
The remote_bitbang JTAG driver is used to drive JTAG from a remote process. The
|
||||
remote_bitbang driver communicates via TCP or UNIX sockets with some remote
|
||||
process using an ASCII encoding of the bitbang interface. The remote process
|
||||
presumably then drives the JTAG however it pleases. The remote process should
|
||||
act as a server, listening for connections from the openocd remote_bitbang
|
||||
driver.
|
||||
The remote_bitbang JTAG+SWD driver is used to drive JTAG and/or SWD from a
|
||||
remote process. The remote_bitbang driver communicates via TCP or UNIX
|
||||
sockets with some remote process using an ASCII encoding of the bitbang
|
||||
interface. The remote process presumably then drives the JTAG/SWD however
|
||||
it pleases. The remote process should act as a server, listening for
|
||||
connections from the openocd remote_bitbang driver.
|
||||
|
||||
The remote bitbang driver is useful for debugging software running on
|
||||
processors which are being simulated.
|
||||
|
||||
There also is an implementation of the server-side protocol for the
|
||||
Glasgow Debug Tool (https://github.com/glasgowEmbedded/Glasgow) through
|
||||
the jtag-openocd applet.
|
||||
|
||||
The bitbang interface consists of the following functions.
|
||||
|
||||
blink on
|
||||
|
@ -24,11 +28,20 @@ write tck tms tdi
|
|||
reset trst srst
|
||||
Set the value of trst, srst.
|
||||
|
||||
swdio_drive
|
||||
Set the output enable of the bidirectional swdio (tms) pin
|
||||
|
||||
swdio_read
|
||||
Sample the value of swdio (tms).
|
||||
|
||||
swd_write
|
||||
Set the value of swclk (tck) and swdio (tms).
|
||||
|
||||
An additional function, quit, is added to the remote_bitbang interface to
|
||||
indicate there will be no more requests and the connection with the remote
|
||||
driver should be closed.
|
||||
|
||||
These five functions are encoded in ASCII by assigning a single character to
|
||||
These eight functions are encoded in ASCII by assigning a single character to
|
||||
each possible request. The assignments are:
|
||||
|
||||
B - Blink on
|
||||
|
@ -47,7 +60,14 @@ each possible request. The assignments are:
|
|||
s - Reset 0 1
|
||||
t - Reset 1 0
|
||||
u - Reset 1 1
|
||||
O - SWDIO drive 1
|
||||
o - SWDIO drive 0
|
||||
c - SWDIO read request
|
||||
d - SWD write 0 0
|
||||
e - SWD write 0 1
|
||||
f - SWD write 1 0
|
||||
g - SWD write 1 1
|
||||
|
||||
The read response is encoded in ASCII as either digit 0 or 1.
|
||||
The read responses are encoded in ASCII as either digit 0 or 1.
|
||||
|
||||
*/
|
||||
|
|
|
@ -2543,32 +2543,44 @@ and a specific set of GPIOs is used.
|
|||
ARM CMSIS-DAP compliant based adapter v1 (USB HID based)
|
||||
or v2 (USB bulk).
|
||||
|
||||
@deffn {Config Command} {cmsis_dap_vid_pid} [vid pid]+
|
||||
@deffn {Config Command} {cmsis-dap vid_pid} [vid pid]+
|
||||
The vendor ID and product ID of the CMSIS-DAP device. If not specified
|
||||
the driver will attempt to auto detect the CMSIS-DAP device.
|
||||
Currently, up to eight [@var{vid}, @var{pid}] pairs may be given, e.g.
|
||||
@example
|
||||
cmsis_dap_vid_pid 0xc251 0xf001 0x0d28 0x0204
|
||||
cmsis-dap vid_pid 0xc251 0xf001 0x0d28 0x0204
|
||||
@end example
|
||||
@end deffn
|
||||
|
||||
@deffn {Config Command} {cmsis_dap_backend} [@option{auto}|@option{usb_bulk}|@option{hid}]
|
||||
@deffn {Config Command} {cmsis-dap backend} [@option{auto}|@option{usb_bulk}|@option{hid}]
|
||||
Specifies how to communicate with the adapter:
|
||||
|
||||
@itemize @minus
|
||||
@item @option{hid} Use HID generic reports - CMSIS-DAP v1
|
||||
@item @option{usb_bulk} Use USB bulk - CMSIS-DAP v2
|
||||
@item @option{auto} First try USB bulk CMSIS-DAP v2, if not found try HID CMSIS-DAP v1.
|
||||
This is the default if @command{cmsis_dap_backend} is not specified.
|
||||
This is the default if @command{cmsis-dap backend} is not specified.
|
||||
@end itemize
|
||||
@end deffn
|
||||
|
||||
@deffn {Config Command} {cmsis_dap_usb interface} [number]
|
||||
@deffn {Config Command} {cmsis-dap usb interface} [number]
|
||||
Specifies the @var{number} of the USB interface to use in v2 mode (USB bulk).
|
||||
In most cases need not to be specified and interfaces are searched by
|
||||
interface string or for user class interface.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {cmsis-dap quirk} [@option{enable}|@option{disable}]
|
||||
Enables or disables the following workarounds of known CMSIS-DAP adapter
|
||||
quirks:
|
||||
@itemize @minus
|
||||
@item disconnect and re-connect before sending a switch sequence
|
||||
@item packets pipelining is suppressed, only one packet at a time is
|
||||
submitted to the adapter
|
||||
@end itemize
|
||||
The quirk workarounds are disabled by default.
|
||||
The command without a parameter displays current setting.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {cmsis-dap info}
|
||||
Display various device information, like hardware version, firmware version, current bus status.
|
||||
@end deffn
|
||||
|
@ -2839,9 +2851,9 @@ If not specified, default 0xFFFF is used.
|
|||
@end deffn
|
||||
|
||||
@deffn {Interface Driver} {remote_bitbang}
|
||||
Drive JTAG from a remote process. This sets up a UNIX or TCP socket connection
|
||||
with a remote process and sends ASCII encoded bitbang requests to that process
|
||||
instead of directly driving JTAG.
|
||||
Drive JTAG and SWD from a remote process. This sets up a UNIX or TCP socket
|
||||
connection with a remote process and sends ASCII encoded bitbang requests to
|
||||
that process instead of directly driving JTAG and SWD.
|
||||
|
||||
The remote_bitbang driver is useful for debugging software running on
|
||||
processors which are being simulated.
|
||||
|
@ -3020,7 +3032,7 @@ This driver is for Cypress Semiconductor's KitProg adapters. The KitProg is an
|
|||
SWD-only adapter that is designed to be used with Cypress's PSoC and PRoC device
|
||||
families, but it is possible to use it with some other devices. If you are using
|
||||
this adapter with a PSoC or a PRoC, you may need to add
|
||||
@command{kitprog_init_acquire_psoc} or @command{kitprog acquire_psoc} to your
|
||||
@command{kitprog init_acquire_psoc} or @command{kitprog acquire_psoc} to your
|
||||
configuration script.
|
||||
|
||||
Note that this driver is for the proprietary KitProg protocol, not the CMSIS-DAP
|
||||
|
@ -3041,14 +3053,14 @@ versions only implement "SWD line reset". Second, due to a firmware quirk, an
|
|||
SWD sequence must be sent after every target reset in order to re-establish
|
||||
communications with the target.
|
||||
@item Due in part to the limitation above, KitProg devices with firmware below
|
||||
version 2.14 will need to use @command{kitprog_init_acquire_psoc} in order to
|
||||
version 2.14 will need to use @command{kitprog init_acquire_psoc} in order to
|
||||
communicate with PSoC 5LP devices. This is because, assuming debug is not
|
||||
disabled on the PSoC, the PSoC 5LP needs its JTAG interface switched to SWD
|
||||
mode before communication can begin, but prior to firmware 2.14, "JTAG to SWD"
|
||||
could only be sent with an acquisition sequence.
|
||||
@end itemize
|
||||
|
||||
@deffn {Config Command} {kitprog_init_acquire_psoc}
|
||||
@deffn {Config Command} {kitprog init_acquire_psoc}
|
||||
Indicate that a PSoC acquisition sequence needs to be run during adapter init.
|
||||
Please be aware that the acquisition sequence hard-resets the target.
|
||||
@end deffn
|
||||
|
@ -9403,7 +9415,7 @@ Remove the breakpoint at @var{address} or all breakpoints.
|
|||
Remove data watchpoint on @var{address} or all watchpoints.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {wp} [address len [(@option{r}|@option{w}|@option{a}) [value [mask]]]]
|
||||
@deffn {Command} {wp} [address length [(@option{r}|@option{w}|@option{a}) [value [mask]]]]
|
||||
With no parameters, lists all active watchpoints.
|
||||
Else sets a data watchpoint on data from @var{address} for @var{length} bytes.
|
||||
The watch point is an "access" watchpoint unless
|
||||
|
@ -9477,8 +9489,9 @@ Return a list of all channels and their properties as Tcl list.
|
|||
The list can be manipulated easily from within scripts.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {rtt server start} port channel
|
||||
Start a TCP server on @var{port} for the channel @var{channel}.
|
||||
@deffn {Command} {rtt server start} port channel [message]
|
||||
Start a TCP server on @var{port} for the channel @var{channel}. When
|
||||
@var{message} is not empty, it will be sent to a client when it connects.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {rtt server stop} port
|
||||
|
@ -9508,7 +9521,7 @@ TCP/IP port 9090.
|
|||
@deffn {Command} {profile} seconds filename [start end]
|
||||
Profiling samples the CPU's program counter as quickly as possible,
|
||||
which is useful for non-intrusive stochastic profiling.
|
||||
Saves up to 10000 samples in @file{filename} using ``gmon.out''
|
||||
Saves up to 1000000 samples in @file{filename} using ``gmon.out''
|
||||
format. Optional @option{start} and @option{end} parameters allow to
|
||||
limit the address range.
|
||||
@end deffn
|
||||
|
@ -11590,16 +11603,18 @@ NOTE: @file{xtensa-core-XXX.cfg} must match the target Xtensa hardware
|
|||
connected to OpenOCD.
|
||||
|
||||
Some example Xtensa configurations are bundled with OpenOCD for reference:
|
||||
@itemize @bullet
|
||||
@enumerate
|
||||
@item Cadence Palladium VDebug emulation target. The user can combine their
|
||||
@file{xtensa-core-XXX.cfg} with the provided
|
||||
@file{board/xtensa-palladium-vdebug.cfg} to debug an emulated Xtensa RTL design.
|
||||
@item NXP MIMXRT685-EVK evaluation kit. The relevant configuration files are
|
||||
@file{board/xtensa-rt685-jlink.cfg} and @file{board/xtensa-core-nxp_rt600.cfg}.
|
||||
Additional information is provided by
|
||||
@uref{https://www.nxp.com/design/development-boards/i-mx-evaluation-and-development-boards/i-mx-rt600-evaluation-kit:MIMXRT685-EVK,
|
||||
NXP}.
|
||||
@item NXP MIMXRT685-EVK evaluation kit. The relevant configuration files are:
|
||||
@itemize @bullet
|
||||
@item @file{board/xtensa-rt685-ext.cfg}
|
||||
@item @file{target/xtensa-core-nxp_rt600.cfg}
|
||||
@end itemize
|
||||
Additional information is available by searching for "i.MX RT600 Evaluation Kit"
|
||||
on @url{https://www.nxp.com}.
|
||||
@end enumerate
|
||||
|
||||
@subsection Xtensa Configuration Commands
|
||||
|
||||
|
@ -11624,6 +11639,11 @@ others may be common to both but have different valid ranges.
|
|||
Configure Xtensa target memory. Memory type determines access rights,
|
||||
where RAMs are read/write while ROMs are read-only. @var{baseaddr} and
|
||||
@var{bytes} are both integers, typically hexadecimal and decimal, respectively.
|
||||
|
||||
NOTE: Some Xtensa memory types, such as system RAM/ROM or MMIO/device regions,
|
||||
can be added or modified after the Xtensa core has been generated. Additional
|
||||
@code{xtensa xtmem} definitions should be manually added to xtensa-core-XXX.cfg
|
||||
to keep OpenOCD's target address map consistent with the Xtensa configuration.
|
||||
@end deffn
|
||||
|
||||
@deffn {Config Command} {xtensa xtmem} (@option{icache}|@option{dcache}) linebytes cachebytes ways [writeback]
|
||||
|
@ -11706,6 +11726,12 @@ Execute arbitrary instruction(s) provided as an ascii string. The string repres
|
|||
number of instruction bytes, thus its length must be even.
|
||||
@end deffn
|
||||
|
||||
@deffn {Command} {xtensa dm} (address) [value]
|
||||
Read or write Xtensa Debug Module (DM) registers. @var{address} is required for both reads
|
||||
and writes and is a 4-byte-aligned value typically between 0 and 0x3ffc. @var{value} is specified
|
||||
only for write accesses.
|
||||
@end deffn
|
||||
|
||||
@subsection Xtensa Performance Monitor Configuration
|
||||
|
||||
@deffn {Command} {xtensa perfmon_enable} <counter_id> <select> [mask] [kernelcnt] [tracelevel]
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later OR GFDL-1.2-no-invariants-or-later
|
||||
|
||||
# Optional comment
|
||||
|
||||
Bus 001 Device 006: ID 03eb:2111 Atmel Corp. Xplained Pro board debugger and programmer
|
||||
Device Descriptor:
|
||||
bLength 18
|
||||
bDescriptorType 1
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 239 Miscellaneous Device
|
||||
bDeviceSubClass 2
|
||||
bDeviceProtocol 1 Interface Association
|
||||
bMaxPacketSize0 64
|
||||
idVendor 0x03eb Atmel Corp.
|
||||
idProduct 0x2111 Xplained Pro board debugger and programmer
|
||||
bcdDevice 1.01
|
||||
iManufacturer 1 Atmel Corp.
|
||||
iProduct 2 EDBG CMSIS-DAP
|
||||
iSerial 3 ATMLxxxxxxxxxxxxxxxx
|
||||
bNumConfigurations 1
|
||||
Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 0x0082
|
||||
bNumInterfaces 4
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0x80
|
||||
(Bus Powered)
|
||||
MaxPower 500mA
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 3 Human Interface Device
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 4 EDBG CMSIS-DAP
|
||||
HID Device Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 33
|
||||
bcdHID 1.11
|
||||
bCountryCode 0 Not supported
|
||||
bNumDescriptors 1
|
||||
bDescriptorType 34 Report
|
||||
wDescriptorLength 35
|
||||
Report Descriptor: (length is 35)
|
||||
Item(Global): Usage Page, data= [ 0x00 0xff ] 65280
|
||||
(null)
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Collection, data= [ 0x01 ] 1
|
||||
Application
|
||||
Item(Global): Logical Minimum, data= [ 0x00 ] 0
|
||||
Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255
|
||||
Item(Global): Report Size, data= [ 0x08 ] 8
|
||||
Item(Global): Report Count, data= [ 0x00 0x02 ] 512
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Input, data= [ 0x02 ] 2
|
||||
Data Variable Absolute No_Wrap Linear
|
||||
Preferred_State No_Null_Position Non_Volatile Bitfield
|
||||
Item(Global): Report Count, data= [ 0x00 0x02 ] 512
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Output, data= [ 0x02 ] 2
|
||||
Data Variable Absolute No_Wrap Linear
|
||||
Preferred_State No_Null_Position Non_Volatile Bitfield
|
||||
Item(Global): Report Count, data= [ 0x04 ] 4
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Feature, data= [ 0x02 ] 2
|
||||
Data Variable Absolute No_Wrap Linear
|
||||
Preferred_State No_Null_Position Non_Volatile Bitfield
|
||||
Item(Main ): End Collection, data=none
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x01 EP 1 OUT
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 1
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x82 EP 2 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 1
|
||||
Interface Association:
|
||||
bLength 8
|
||||
bDescriptorType 11
|
||||
bFirstInterface 1
|
||||
bInterfaceCount 2
|
||||
bFunctionClass 2 Communications
|
||||
bFunctionSubClass 2 Abstract (modem)
|
||||
bFunctionProtocol 1 AT-commands (v.25ter)
|
||||
iFunction 6 EDBG Virtual COM Port
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 1
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 1
|
||||
bInterfaceClass 2 Communications
|
||||
bInterfaceSubClass 2 Abstract (modem)
|
||||
bInterfaceProtocol 1 AT-commands (v.25ter)
|
||||
iInterface 0
|
||||
CDC Header:
|
||||
bcdCDC 1.10
|
||||
CDC ACM:
|
||||
bmCapabilities 0x06
|
||||
sends break
|
||||
line coding and serial state
|
||||
CDC Union:
|
||||
bMasterInterface 1
|
||||
bSlaveInterface 2
|
||||
CDC Call Management:
|
||||
bmCapabilities 0x03
|
||||
call management
|
||||
use DataInterface
|
||||
bDataInterface 2
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x83 EP 3 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 8
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 2
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 10 CDC Data
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x84 EP 4 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x05 EP 5 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 0
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 3
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 255 Vendor Specific Class
|
||||
bInterfaceSubClass 255 Vendor Specific Subclass
|
||||
bInterfaceProtocol 255 Vendor Specific Protocol
|
||||
iInterface 5 EDBG Data Gateway
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x87 EP 7 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 255
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x06 EP 6 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 255
|
||||
Device Qualifier (for other device speed):
|
||||
bLength 10
|
||||
bDescriptorType 6
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 239 Miscellaneous Device
|
||||
bDeviceSubClass 2
|
||||
bDeviceProtocol 1 Interface Association
|
||||
bMaxPacketSize0 64
|
||||
bNumConfigurations 1
|
||||
Device Status: 0x0000
|
||||
(Bus Powered)
|
|
@ -0,0 +1,211 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later OR GFDL-1.2-no-invariants-or-later
|
||||
|
||||
# Optional comment
|
||||
|
||||
Bus 001 Device 005: ID 03eb:2169 Atmel Corp. EDBG CMSIS-DAP
|
||||
Device Descriptor:
|
||||
bLength 18
|
||||
bDescriptorType 1
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 239 Miscellaneous Device
|
||||
bDeviceSubClass 2
|
||||
bDeviceProtocol 1 Interface Association
|
||||
bMaxPacketSize0 64
|
||||
idVendor 0x03eb Atmel Corp.
|
||||
idProduct 0x2169
|
||||
bcdDevice 1.01
|
||||
iManufacturer 1 Atmel Corp.
|
||||
iProduct 2 EDBG CMSIS-DAP
|
||||
iSerial 3 ATMLxxxxxxxxxxxxxxxx
|
||||
bNumConfigurations 1
|
||||
Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 0x0082
|
||||
bNumInterfaces 4
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0x80
|
||||
(Bus Powered)
|
||||
MaxPower 500mA
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 3 Human Interface Device
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 4 EDBG CMSIS-DAP
|
||||
HID Device Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 33
|
||||
bcdHID 1.11
|
||||
bCountryCode 0 Not supported
|
||||
bNumDescriptors 1
|
||||
bDescriptorType 34 Report
|
||||
wDescriptorLength 35
|
||||
Report Descriptor: (length is 35)
|
||||
Item(Global): Usage Page, data= [ 0x00 0xff ] 65280
|
||||
(null)
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Collection, data= [ 0x01 ] 1
|
||||
Application
|
||||
Item(Global): Logical Minimum, data= [ 0x00 ] 0
|
||||
Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255
|
||||
Item(Global): Report Size, data= [ 0x08 ] 8
|
||||
Item(Global): Report Count, data= [ 0x00 0x02 ] 512
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Input, data= [ 0x02 ] 2
|
||||
Data Variable Absolute No_Wrap Linear
|
||||
Preferred_State No_Null_Position Non_Volatile Bitfield
|
||||
Item(Global): Report Count, data= [ 0x00 0x02 ] 512
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Output, data= [ 0x02 ] 2
|
||||
Data Variable Absolute No_Wrap Linear
|
||||
Preferred_State No_Null_Position Non_Volatile Bitfield
|
||||
Item(Global): Report Count, data= [ 0x04 ] 4
|
||||
Item(Local ): Usage, data= [ 0x01 ] 1
|
||||
(null)
|
||||
Item(Main ): Feature, data= [ 0x02 ] 2
|
||||
Data Variable Absolute No_Wrap Linear
|
||||
Preferred_State No_Null_Position Non_Volatile Bitfield
|
||||
Item(Main ): End Collection, data=none
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x01 EP 1 OUT
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 1
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x82 EP 2 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 1
|
||||
Interface Association:
|
||||
bLength 8
|
||||
bDescriptorType 11
|
||||
bFirstInterface 1
|
||||
bInterfaceCount 2
|
||||
bFunctionClass 2 Communications
|
||||
bFunctionSubClass 2 Abstract (modem)
|
||||
bFunctionProtocol 1 AT-commands (v.25ter)
|
||||
iFunction 6 EDBG Virtual COM Port
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 1
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 1
|
||||
bInterfaceClass 2 Communications
|
||||
bInterfaceSubClass 2 Abstract (modem)
|
||||
bInterfaceProtocol 1 AT-commands (v.25ter)
|
||||
iInterface 0
|
||||
CDC Header:
|
||||
bcdCDC 1.10
|
||||
CDC ACM:
|
||||
bmCapabilities 0x06
|
||||
sends break
|
||||
line coding and serial state
|
||||
CDC Union:
|
||||
bMasterInterface 1
|
||||
bSlaveInterface 2
|
||||
CDC Call Management:
|
||||
bmCapabilities 0x03
|
||||
call management
|
||||
use DataInterface
|
||||
bDataInterface 2
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x83 EP 3 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 8
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 2
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 10 CDC Data
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x84 EP 4 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x05 EP 5 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 0
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 3
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 8 Mass Storage
|
||||
bInterfaceSubClass 6 SCSI
|
||||
bInterfaceProtocol 80 Bulk-Only
|
||||
iInterface 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x87 EP 7 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x06 EP 6 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Device Qualifier (for other device speed):
|
||||
bLength 10
|
||||
bDescriptorType 6
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 239 Miscellaneous Device
|
||||
bDeviceSubClass 2
|
||||
bDeviceProtocol 1 Interface Association
|
||||
bMaxPacketSize0 64
|
||||
bNumConfigurations 1
|
||||
Device Status: 0x0000
|
||||
(Bus Powered)
|
|
@ -866,10 +866,8 @@ COMMAND_HANDLER(pic32mx_handle_unlock_command)
|
|||
struct mips_ejtag *ejtag_info;
|
||||
int timeout = 10;
|
||||
|
||||
if (CMD_ARGC < 1) {
|
||||
command_print(CMD, "pic32mx unlock <bank>");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -932,7 +930,7 @@ static const struct command_registration pic32mx_exec_command_handlers[] = {
|
|||
.name = "unlock",
|
||||
.handler = pic32mx_handle_unlock_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "[bank_id]",
|
||||
.usage = "bank_id",
|
||||
.help = "Unlock/Erase entire device.",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
|
|
|
@ -1540,10 +1540,8 @@ static int stm32x_mass_erase(struct flash_bank *bank)
|
|||
|
||||
COMMAND_HANDLER(stm32x_handle_mass_erase_command)
|
||||
{
|
||||
if (CMD_ARGC < 1) {
|
||||
command_print(CMD, "stm32x mass_erase <bank>");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -1566,10 +1564,8 @@ COMMAND_HANDLER(stm32f2x_handle_options_read_command)
|
|||
struct flash_bank *bank;
|
||||
struct stm32x_flash_bank *stm32x_info = NULL;
|
||||
|
||||
if (CMD_ARGC != 1) {
|
||||
command_print(CMD, "stm32f2x options_read <bank>");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
if (retval != ERROR_OK)
|
||||
|
@ -1612,10 +1608,8 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command)
|
|||
struct stm32x_flash_bank *stm32x_info = NULL;
|
||||
uint16_t user_options, boot_addr0, boot_addr1, options_mask;
|
||||
|
||||
if (CMD_ARGC < 1) {
|
||||
command_print(CMD, "stm32f2x options_write <bank> ...");
|
||||
if (CMD_ARGC < 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
if (retval != ERROR_OK)
|
||||
|
@ -1627,19 +1621,14 @@ COMMAND_HANDLER(stm32f2x_handle_options_write_command)
|
|||
|
||||
stm32x_info = bank->driver_priv;
|
||||
if (stm32x_info->has_boot_addr) {
|
||||
if (CMD_ARGC != 4) {
|
||||
command_print(CMD, "stm32f2x options_write <bank> <user_options>"
|
||||
" <boot_addr0> <boot_addr1>");
|
||||
if (CMD_ARGC != 4)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[2], boot_addr0);
|
||||
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], boot_addr1);
|
||||
stm32x_info->option_bytes.boot_addr = boot_addr0 | (((uint32_t) boot_addr1) << 16);
|
||||
} else {
|
||||
if (CMD_ARGC != 2) {
|
||||
command_print(CMD, "stm32f2x options_write <bank> <user_options>");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
} else if (CMD_ARGC != 2) {
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], user_options);
|
||||
|
@ -1674,10 +1663,8 @@ COMMAND_HANDLER(stm32f2x_handle_optcr2_write_command)
|
|||
struct stm32x_flash_bank *stm32x_info = NULL;
|
||||
uint32_t optcr2_pcrop;
|
||||
|
||||
if (CMD_ARGC != 2) {
|
||||
command_print(CMD, "stm32f2x optcr2_write <bank> <optcr2_value>");
|
||||
if (CMD_ARGC != 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
if (retval != ERROR_OK)
|
||||
|
@ -1711,10 +1698,8 @@ COMMAND_HANDLER(stm32f2x_handle_optcr2_write_command)
|
|||
|
||||
COMMAND_HANDLER(stm32x_handle_otp_command)
|
||||
{
|
||||
if (CMD_ARGC < 2) {
|
||||
command_print(CMD, "stm32x otp <bank> (enable|disable|show)");
|
||||
if (CMD_ARGC != 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -1787,7 +1772,7 @@ static const struct command_registration stm32f2x_exec_command_handlers[] = {
|
|||
.name = "otp",
|
||||
.handler = stm32x_handle_otp_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.usage = "bank_id",
|
||||
.usage = "bank_id (enable|disable|show)",
|
||||
.help = "OTP (One Time Programmable) memory write enable/disable.",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
|
|
|
@ -1080,10 +1080,8 @@ flash_lock:
|
|||
|
||||
COMMAND_HANDLER(stm32x_handle_mass_erase_command)
|
||||
{
|
||||
if (CMD_ARGC < 1) {
|
||||
command_print(CMD, "stm32h7x mass_erase <bank>");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -1101,10 +1099,8 @@ COMMAND_HANDLER(stm32x_handle_mass_erase_command)
|
|||
|
||||
COMMAND_HANDLER(stm32x_handle_option_read_command)
|
||||
{
|
||||
if (CMD_ARGC < 2) {
|
||||
command_print(CMD, "stm32h7x option_read <bank> <option_reg offset>");
|
||||
if (CMD_ARGC != 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -1126,10 +1122,8 @@ COMMAND_HANDLER(stm32x_handle_option_read_command)
|
|||
|
||||
COMMAND_HANDLER(stm32x_handle_option_write_command)
|
||||
{
|
||||
if (CMD_ARGC < 3) {
|
||||
command_print(CMD, "stm32h7x option_write <bank> <option_reg offset> <value> [mask]");
|
||||
if (CMD_ARGC != 3 && CMD_ARGC != 4)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
|
|
@ -2218,10 +2218,8 @@ err_lock:
|
|||
|
||||
COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
|
||||
{
|
||||
if (CMD_ARGC < 1) {
|
||||
command_print(CMD, "stm32l4x mass_erase <STM32L4 bank>");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -2239,10 +2237,8 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
|
|||
|
||||
COMMAND_HANDLER(stm32l4_handle_option_read_command)
|
||||
{
|
||||
if (CMD_ARGC < 2) {
|
||||
command_print(CMD, "stm32l4x option_read <STM32L4 bank> <option_reg offset>");
|
||||
if (CMD_ARGC != 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -2266,10 +2262,8 @@ COMMAND_HANDLER(stm32l4_handle_option_read_command)
|
|||
|
||||
COMMAND_HANDLER(stm32l4_handle_option_write_command)
|
||||
{
|
||||
if (CMD_ARGC < 3) {
|
||||
command_print(CMD, "stm32l4x option_write <STM32L4 bank> <option_reg offset> <value> [mask]");
|
||||
if (CMD_ARGC != 3 && CMD_ARGC != 4)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
struct flash_bank *bank;
|
||||
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -2381,7 +2375,7 @@ COMMAND_HANDLER(stm32l4_handle_lock_command)
|
|||
{
|
||||
struct target *target = NULL;
|
||||
|
||||
if (CMD_ARGC < 1)
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
struct flash_bank *bank;
|
||||
|
@ -2416,7 +2410,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command)
|
|||
{
|
||||
struct target *target = NULL;
|
||||
|
||||
if (CMD_ARGC < 1)
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
struct flash_bank *bank;
|
||||
|
@ -2520,7 +2514,7 @@ COMMAND_HANDLER(stm32l4_handle_wrp_info_command)
|
|||
|
||||
COMMAND_HANDLER(stm32l4_handle_otp_command)
|
||||
{
|
||||
if (CMD_ARGC < 2)
|
||||
if (CMD_ARGC != 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
struct flash_bank *bank;
|
||||
|
|
|
@ -646,21 +646,21 @@ COMMAND_HANDLER(stmqspi_handle_set)
|
|||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.size_in_bytes);
|
||||
if (log2u(stmqspi_info->dev.size_in_bytes) < 8) {
|
||||
command_print(CMD, "stmqspi: device size must be 2^n with n >= 8");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[index++], stmqspi_info->dev.pagesize);
|
||||
if (stmqspi_info->dev.pagesize > stmqspi_info->dev.size_in_bytes ||
|
||||
(log2u(stmqspi_info->dev.pagesize) < 0)) {
|
||||
command_print(CMD, "stmqspi: page size must be 2^n and <= device size");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.read_cmd);
|
||||
if ((stmqspi_info->dev.read_cmd != 0x03) &&
|
||||
(stmqspi_info->dev.read_cmd != 0x13)) {
|
||||
command_print(CMD, "stmqspi: only 0x03/0x13 READ cmd allowed");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.qread_cmd);
|
||||
|
@ -678,7 +678,7 @@ COMMAND_HANDLER(stmqspi_handle_set)
|
|||
(stmqspi_info->dev.qread_cmd != 0xEE)) {
|
||||
command_print(CMD, "stmqspi: only 0x0B/0x0C/0x3B/0x3C/"
|
||||
"0x6B/0x6C/0xBB/0xBC/0xEB/0xEC/0xEE QREAD allowed");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u8, CMD_ARGV[index++], stmqspi_info->dev.pprog_cmd);
|
||||
|
@ -686,7 +686,7 @@ COMMAND_HANDLER(stmqspi_handle_set)
|
|||
(stmqspi_info->dev.pprog_cmd != 0x12) &&
|
||||
(stmqspi_info->dev.pprog_cmd != 0x32)) {
|
||||
command_print(CMD, "stmqspi: only 0x02/0x12/0x32 PPRG cmd allowed");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
if (index < CMD_ARGC)
|
||||
|
@ -700,7 +700,7 @@ COMMAND_HANDLER(stmqspi_handle_set)
|
|||
(stmqspi_info->dev.sectorsize < stmqspi_info->dev.pagesize) ||
|
||||
(log2u(stmqspi_info->dev.sectorsize) < 0)) {
|
||||
command_print(CMD, "stmqspi: sector size must be 2^n and <= device size");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
if (index < CMD_ARGC)
|
||||
|
@ -786,7 +786,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
|
|||
num_write = CMD_ARGC - 2;
|
||||
if (num_write > max) {
|
||||
LOG_ERROR("at most %d bytes may be sent", max);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
|
||||
|
@ -811,7 +811,7 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
|
|||
if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) {
|
||||
if ((num_write & 1) == 0) {
|
||||
LOG_ERROR("number of data bytes to write must be even in dual mode");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -819,12 +819,12 @@ COMMAND_HANDLER(stmqspi_handle_cmd)
|
|||
if (stmqspi_info->saved_cr & BIT(SPI_DUAL_FLASH)) {
|
||||
if ((num_read & 1) != 0) {
|
||||
LOG_ERROR("number of bytes to read must be even in dual mode");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
if ((num_write < 1) || (num_write > 5)) {
|
||||
LOG_ERROR("one cmd and up to four addr bytes must be send when reading");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -597,7 +597,7 @@ static int xcf_probe(struct flash_bank *bank)
|
|||
}
|
||||
|
||||
/* check idcode and alloc memory for sector table */
|
||||
if (!bank->target->tap->hasidcode)
|
||||
if (!bank->target->tap->has_idcode)
|
||||
return ERROR_FLASH_OPERATION_FAILED;
|
||||
|
||||
/* guess number of blocks using chip ID */
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* Copyright 2009 David Brownell
|
||||
* Copyright (c) 2005-2011 Jim Tcl Project. All rights reserved.
|
||||
*
|
||||
* This file is extracted from jim_nvp.c, originally part of jim TCL code.
|
||||
* This file is extracted from jim-nvp.c, originally part of jim TCL code.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* Copyright 2009 David Brownell
|
||||
* Copyright (c) 2005-2011 Jim Tcl Project. All rights reserved.
|
||||
*
|
||||
* This file is extracted from jim_nvp.h, originally part of jim TCL code.
|
||||
* This file is extracted from jim-nvp.h, originally part of jim TCL code.
|
||||
*/
|
||||
|
||||
#ifndef OPENOCD_HELPER_NVP_H
|
||||
|
@ -51,7 +51,7 @@
|
|||
* returns &yn[0];
|
||||
* result = nvp_name2value(yn, "no");
|
||||
* returns &yn[1];
|
||||
* result = jim_nvp_name2value(yn, "Blah");
|
||||
* result = nvp_name2value(yn, "Blah");
|
||||
* returns &yn[4];
|
||||
* \endcode
|
||||
*
|
||||
|
|
|
@ -1049,7 +1049,7 @@ static int jtag_reset_callback(enum jtag_event event, void *priv)
|
|||
|
||||
/* current instruction is either BYPASS or IDCODE */
|
||||
buf_set_ones(tap->cur_instr, tap->ir_length);
|
||||
tap->bypass = 1;
|
||||
tap->bypass = true;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
|
@ -1177,7 +1177,7 @@ static bool jtag_examine_chain_end(uint8_t *idcodes, unsigned count, unsigned ma
|
|||
static bool jtag_examine_chain_match_tap(const struct jtag_tap *tap)
|
||||
{
|
||||
|
||||
if (tap->expected_ids_cnt == 0 || !tap->hasidcode)
|
||||
if (tap->expected_ids_cnt == 0 || !tap->has_idcode)
|
||||
return true;
|
||||
|
||||
/* optionally ignore the JTAG version field - bits 28-31 of IDCODE */
|
||||
|
@ -1283,13 +1283,13 @@ static int jtag_examine_chain(void)
|
|||
/* Zero for LSB indicates a device in bypass */
|
||||
LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%" PRIx32 ")",
|
||||
tap->dotted_name, idcode);
|
||||
tap->hasidcode = false;
|
||||
tap->has_idcode = false;
|
||||
tap->idcode = 0;
|
||||
|
||||
bit_count += 1;
|
||||
} else {
|
||||
/* Friendly devices support IDCODE */
|
||||
tap->hasidcode = true;
|
||||
tap->has_idcode = true;
|
||||
tap->idcode = idcode;
|
||||
jtag_examine_chain_display(LOG_LVL_INFO, "tap/device found", tap->dotted_name, idcode);
|
||||
|
||||
|
@ -1464,7 +1464,7 @@ void jtag_tap_init(struct jtag_tap *tap)
|
|||
buf_set_u32(tap->expected_mask, 0, ir_len_bits, tap->ir_capture_mask);
|
||||
|
||||
/* TAP will be in bypass mode after jtag_validate_ircapture() */
|
||||
tap->bypass = 1;
|
||||
tap->bypass = true;
|
||||
buf_set_ones(tap->cur_instr, tap->ir_length);
|
||||
|
||||
/* register the reset callback for the TAP */
|
||||
|
|
|
@ -225,6 +225,12 @@ struct pending_scan_result {
|
|||
unsigned int buffer_offset;
|
||||
};
|
||||
|
||||
/* Read mode */
|
||||
enum cmsis_dap_blocking {
|
||||
CMSIS_DAP_NON_BLOCKING,
|
||||
CMSIS_DAP_BLOCKING
|
||||
};
|
||||
|
||||
/* Each block in FIFO can contain up to pending_queue_len transfers */
|
||||
static unsigned int pending_queue_len;
|
||||
static unsigned int tfer_max_command_size;
|
||||
|
@ -315,7 +321,7 @@ static void cmsis_dap_flush_read(struct cmsis_dap *dap)
|
|||
* USB close/open so we need to flush up to 64 old packets
|
||||
* to be sure all buffers are empty */
|
||||
for (i = 0; i < 64; i++) {
|
||||
int retval = dap->backend->read(dap, 10);
|
||||
int retval = dap->backend->read(dap, 10, NULL);
|
||||
if (retval == ERROR_TIMEOUT_REACHED)
|
||||
break;
|
||||
}
|
||||
|
@ -326,10 +332,13 @@ static void cmsis_dap_flush_read(struct cmsis_dap *dap)
|
|||
/* Send a message and receive the reply */
|
||||
static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
|
||||
{
|
||||
if (dap->write_count + dap->read_count) {
|
||||
LOG_ERROR("internal: queue not empty before xfer");
|
||||
}
|
||||
if (dap->pending_fifo_block_count) {
|
||||
LOG_ERROR("pending %u blocks, flushing", dap->pending_fifo_block_count);
|
||||
while (dap->pending_fifo_block_count) {
|
||||
dap->backend->read(dap, 10);
|
||||
dap->backend->read(dap, 10, NULL);
|
||||
dap->pending_fifo_block_count--;
|
||||
}
|
||||
dap->pending_fifo_put_idx = 0;
|
||||
|
@ -342,7 +351,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
|
|||
return retval;
|
||||
|
||||
/* get reply */
|
||||
retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS);
|
||||
retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, NULL);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
|
@ -356,6 +365,7 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen)
|
|||
LOG_ERROR("CMSIS-DAP command mismatch. Sent 0x%" PRIx8
|
||||
" received 0x%" PRIx8, current_cmd, resp[0]);
|
||||
|
||||
dap->backend->cancel_all(dap);
|
||||
cmsis_dap_flush_read(dap);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
@ -749,6 +759,22 @@ static int cmsis_dap_cmd_dap_swo_data(
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void cmsis_dap_swd_discard_all_pending(struct cmsis_dap *dap)
|
||||
{
|
||||
for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++)
|
||||
dap->pending_fifo[i].transfer_count = 0;
|
||||
|
||||
dap->pending_fifo_put_idx = 0;
|
||||
dap->pending_fifo_get_idx = 0;
|
||||
dap->pending_fifo_block_count = 0;
|
||||
}
|
||||
|
||||
static void cmsis_dap_swd_cancel_transfers(struct cmsis_dap *dap)
|
||||
{
|
||||
dap->backend->cancel_all(dap);
|
||||
cmsis_dap_flush_read(dap);
|
||||
cmsis_dap_swd_discard_all_pending(dap);
|
||||
}
|
||||
|
||||
static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
|
||||
{
|
||||
|
@ -770,8 +796,10 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
|
|||
goto skip;
|
||||
}
|
||||
|
||||
if (block->transfer_count == 0)
|
||||
if (block->transfer_count == 0) {
|
||||
LOG_ERROR("internal: write an empty queue?!");
|
||||
goto skip;
|
||||
}
|
||||
|
||||
bool block_cmd = !cmsis_dap_handle->swd_cmds_differ
|
||||
&& block->transfer_count >= CMD_DAP_TFER_BLOCK_MIN_OPS;
|
||||
|
@ -831,14 +859,13 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap)
|
|||
if (retval < 0) {
|
||||
queued_retval = retval;
|
||||
goto skip;
|
||||
} else {
|
||||
queued_retval = ERROR_OK;
|
||||
}
|
||||
|
||||
dap->pending_fifo_put_idx = (dap->pending_fifo_put_idx + 1) % dap->packet_count;
|
||||
unsigned int packet_count = dap->quirk_mode ? 1 : dap->packet_count;
|
||||
dap->pending_fifo_put_idx = (dap->pending_fifo_put_idx + 1) % packet_count;
|
||||
dap->pending_fifo_block_count++;
|
||||
if (dap->pending_fifo_block_count > dap->packet_count)
|
||||
LOG_ERROR("too much pending writes %u", dap->pending_fifo_block_count);
|
||||
if (dap->pending_fifo_block_count > packet_count)
|
||||
LOG_ERROR("internal: too much pending writes %u", dap->pending_fifo_block_count);
|
||||
|
||||
return;
|
||||
|
||||
|
@ -846,21 +873,47 @@ skip:
|
|||
block->transfer_count = 0;
|
||||
}
|
||||
|
||||
static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
|
||||
static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, enum cmsis_dap_blocking blocking)
|
||||
{
|
||||
int retval;
|
||||
struct pending_request_block *block = &dap->pending_fifo[dap->pending_fifo_get_idx];
|
||||
|
||||
if (dap->pending_fifo_block_count == 0)
|
||||
LOG_ERROR("no pending write");
|
||||
if (dap->pending_fifo_block_count == 0) {
|
||||
LOG_ERROR("internal: no pending write when reading?!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (queued_retval != ERROR_OK) {
|
||||
/* keep reading blocks until the pipeline is empty */
|
||||
retval = dap->backend->read(dap, 10, NULL);
|
||||
if (retval == ERROR_TIMEOUT_REACHED || retval == 0) {
|
||||
/* timeout means that we flushed the pipeline,
|
||||
* we can safely discard remaining pending requests */
|
||||
cmsis_dap_swd_discard_all_pending(dap);
|
||||
return;
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
|
||||
/* get reply */
|
||||
int retval = dap->backend->read(dap, timeout_ms);
|
||||
if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < LIBUSB_TIMEOUT_MS)
|
||||
struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 0
|
||||
};
|
||||
retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS, blocking ? NULL : &tv);
|
||||
bool timeout = (retval == ERROR_TIMEOUT_REACHED || retval == 0);
|
||||
if (timeout && blocking == CMSIS_DAP_NON_BLOCKING)
|
||||
return;
|
||||
|
||||
if (retval <= 0) {
|
||||
LOG_DEBUG("error reading data");
|
||||
LOG_DEBUG("error reading adapter response");
|
||||
queued_retval = ERROR_FAIL;
|
||||
if (timeout) {
|
||||
/* timeout means that we flushed the pipeline,
|
||||
* we can safely discard remaining pending requests */
|
||||
cmsis_dap_swd_discard_all_pending(dap);
|
||||
return;
|
||||
}
|
||||
goto skip;
|
||||
}
|
||||
|
||||
|
@ -868,8 +921,9 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
|
|||
if (resp[0] != block->command) {
|
||||
LOG_ERROR("CMSIS-DAP command mismatch. Expected 0x%x received 0x%" PRIx8,
|
||||
block->command, resp[0]);
|
||||
cmsis_dap_swd_cancel_transfers(dap);
|
||||
queued_retval = ERROR_FAIL;
|
||||
goto skip;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int transfer_count;
|
||||
|
@ -895,12 +949,17 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
|
|||
goto skip;
|
||||
}
|
||||
|
||||
if (block->transfer_count != transfer_count)
|
||||
if (block->transfer_count != transfer_count) {
|
||||
LOG_ERROR("CMSIS-DAP transfer count mismatch: expected %d, got %d",
|
||||
block->transfer_count, transfer_count);
|
||||
cmsis_dap_swd_cancel_transfers(dap);
|
||||
queued_retval = ERROR_FAIL;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %u timeout %i",
|
||||
transfer_count, dap->pending_fifo_get_idx, timeout_ms);
|
||||
LOG_DEBUG_IO("Received results of %d queued transactions FIFO index %u, %s mode",
|
||||
transfer_count, dap->pending_fifo_get_idx,
|
||||
blocking ? "blocking" : "nonblocking");
|
||||
|
||||
for (unsigned int i = 0; i < transfer_count; i++) {
|
||||
struct pending_transfer_result *transfer = &(block->transfers[i]);
|
||||
|
@ -926,19 +985,22 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms)
|
|||
|
||||
skip:
|
||||
block->transfer_count = 0;
|
||||
dap->pending_fifo_get_idx = (dap->pending_fifo_get_idx + 1) % dap->packet_count;
|
||||
if (!dap->quirk_mode && dap->packet_count > 1)
|
||||
dap->pending_fifo_get_idx = (dap->pending_fifo_get_idx + 1) % dap->packet_count;
|
||||
dap->pending_fifo_block_count--;
|
||||
}
|
||||
|
||||
static int cmsis_dap_swd_run_queue(void)
|
||||
{
|
||||
if (cmsis_dap_handle->pending_fifo_block_count)
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, 0);
|
||||
if (cmsis_dap_handle->write_count + cmsis_dap_handle->read_count) {
|
||||
if (cmsis_dap_handle->pending_fifo_block_count)
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_NON_BLOCKING);
|
||||
|
||||
cmsis_dap_swd_write_from_queue(cmsis_dap_handle);
|
||||
cmsis_dap_swd_write_from_queue(cmsis_dap_handle);
|
||||
}
|
||||
|
||||
while (cmsis_dap_handle->pending_fifo_block_count)
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS);
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_BLOCKING);
|
||||
|
||||
cmsis_dap_handle->pending_fifo_put_idx = 0;
|
||||
cmsis_dap_handle->pending_fifo_get_idx = 0;
|
||||
|
@ -979,10 +1041,16 @@ static unsigned int cmsis_dap_tfer_resp_size(unsigned int write_count,
|
|||
|
||||
static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
|
||||
{
|
||||
/* TARGETSEL register write cannot be queued */
|
||||
if (swd_cmd(false, false, DP_TARGETSEL) == cmd) {
|
||||
queued_retval = cmsis_dap_swd_run_queue();
|
||||
|
||||
cmsis_dap_metacmd_targetsel(data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute sizes of the DAP Transfer command and the expected response
|
||||
* for all queued and this operation */
|
||||
bool targetsel_cmd = swd_cmd(false, false, DP_TARGETSEL) == cmd;
|
||||
|
||||
unsigned int write_count = cmsis_dap_handle->write_count;
|
||||
unsigned int read_count = cmsis_dap_handle->read_count;
|
||||
bool block_cmd;
|
||||
|
@ -1003,20 +1071,19 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data)
|
|||
block_cmd);
|
||||
unsigned int max_transfer_count = block_cmd ? 65535 : 255;
|
||||
|
||||
/* Does the DAP Transfer command and the expected response fit into one packet?
|
||||
* Run the queue also before a targetsel - it cannot be queued */
|
||||
/* Does the DAP Transfer command and also its expected response fit into one packet? */
|
||||
if (cmd_size > tfer_max_command_size
|
||||
|| resp_size > tfer_max_response_size
|
||||
|| targetsel_cmd
|
||||
|| write_count + read_count > max_transfer_count) {
|
||||
if (cmsis_dap_handle->pending_fifo_block_count)
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, 0);
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_NON_BLOCKING);
|
||||
|
||||
/* Not enough room in the queue. Run the queue. */
|
||||
cmsis_dap_swd_write_from_queue(cmsis_dap_handle);
|
||||
|
||||
if (cmsis_dap_handle->pending_fifo_block_count >= cmsis_dap_handle->packet_count)
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS);
|
||||
unsigned int packet_count = cmsis_dap_handle->quirk_mode ? 1 : cmsis_dap_handle->packet_count;
|
||||
if (cmsis_dap_handle->pending_fifo_block_count >= packet_count)
|
||||
cmsis_dap_swd_read_process(cmsis_dap_handle, CMSIS_DAP_BLOCKING);
|
||||
}
|
||||
|
||||
assert(cmsis_dap_handle->pending_fifo[cmsis_dap_handle->pending_fifo_put_idx].transfer_count < pending_queue_len);
|
||||
|
@ -1024,11 +1091,6 @@ 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 = &cmsis_dap_handle->pending_fifo[cmsis_dap_handle->pending_fifo_put_idx];
|
||||
struct pending_transfer_result *transfer = &(block->transfers[block->transfer_count]);
|
||||
transfer->data = data;
|
||||
|
@ -1160,7 +1222,10 @@ static int cmsis_dap_swd_switch_seq(enum swd_special_seq seq)
|
|||
unsigned int s_len;
|
||||
int retval;
|
||||
|
||||
if (seq != LINE_RESET &&
|
||||
if (swd_mode)
|
||||
queued_retval = cmsis_dap_swd_run_queue();
|
||||
|
||||
if (cmsis_dap_handle->quirk_mode && seq != LINE_RESET &&
|
||||
(output_pins & (SWJ_PIN_SRST | SWJ_PIN_TRST))
|
||||
== (SWJ_PIN_SRST | SWJ_PIN_TRST)) {
|
||||
/* Following workaround deasserts reset on most adapters.
|
||||
|
@ -1296,7 +1361,7 @@ static int cmsis_dap_init(void)
|
|||
if (data[0] == 2) { /* short */
|
||||
uint16_t pkt_sz = data[1] + (data[2] << 8);
|
||||
if (pkt_sz != cmsis_dap_handle->packet_size) {
|
||||
free(cmsis_dap_handle->packet_buffer);
|
||||
cmsis_dap_handle->backend->packet_buffer_free(cmsis_dap_handle);
|
||||
retval = cmsis_dap_handle->backend->packet_buffer_alloc(cmsis_dap_handle, pkt_sz);
|
||||
if (retval != ERROR_OK)
|
||||
goto init_err;
|
||||
|
@ -2108,12 +2173,12 @@ COMMAND_HANDLER(cmsis_dap_handle_cmd_command)
|
|||
COMMAND_HANDLER(cmsis_dap_handle_vid_pid_command)
|
||||
{
|
||||
if (CMD_ARGC > MAX_USB_IDS * 2) {
|
||||
LOG_WARNING("ignoring extra IDs in cmsis_dap_vid_pid "
|
||||
LOG_WARNING("ignoring extra IDs in cmsis-dap vid_pid "
|
||||
"(maximum is %d pairs)", MAX_USB_IDS);
|
||||
CMD_ARGC = MAX_USB_IDS * 2;
|
||||
}
|
||||
if (CMD_ARGC < 2 || (CMD_ARGC & 1)) {
|
||||
LOG_WARNING("incomplete cmsis_dap_vid_pid configuration directive");
|
||||
LOG_WARNING("incomplete cmsis-dap vid_pid configuration directive");
|
||||
if (CMD_ARGC < 2)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
/* remove the incomplete trailing id */
|
||||
|
@ -2148,15 +2213,29 @@ COMMAND_HANDLER(cmsis_dap_handle_backend_command)
|
|||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("invalid backend argument to cmsis_dap_backend <backend>");
|
||||
command_print(CMD, "invalid backend argument to cmsis-dap backend <backend>");
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("expected exactly one argument to cmsis_dap_backend <backend>");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(cmsis_dap_handle_quirk_command)
|
||||
{
|
||||
if (CMD_ARGC > 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
if (CMD_ARGC == 1)
|
||||
COMMAND_PARSE_ENABLE(CMD_ARGV[0], cmsis_dap_handle->quirk_mode);
|
||||
|
||||
command_print(CMD, "CMSIS-DAP quirk workarounds %s",
|
||||
cmsis_dap_handle->quirk_mode ? "enabled" : "disabled");
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static const struct command_registration cmsis_dap_subcommand_handlers[] = {
|
||||
{
|
||||
.name = "info",
|
||||
|
@ -2172,6 +2251,36 @@ static const struct command_registration cmsis_dap_subcommand_handlers[] = {
|
|||
.usage = "",
|
||||
.help = "issue cmsis-dap command",
|
||||
},
|
||||
{
|
||||
.name = "vid_pid",
|
||||
.handler = &cmsis_dap_handle_vid_pid_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "the vendor ID and product ID of the CMSIS-DAP device",
|
||||
.usage = "(vid pid)*",
|
||||
},
|
||||
{
|
||||
.name = "backend",
|
||||
.handler = &cmsis_dap_handle_backend_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "set the communication backend to use (USB bulk or HID).",
|
||||
.usage = "(auto | usb_bulk | hid)",
|
||||
},
|
||||
{
|
||||
.name = "quirk",
|
||||
.handler = &cmsis_dap_handle_quirk_command,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "allow expensive workarounds of known adapter quirks.",
|
||||
.usage = "[enable | disable]",
|
||||
},
|
||||
#if BUILD_CMSIS_DAP_USB
|
||||
{
|
||||
.name = "usb",
|
||||
.chain = cmsis_dap_usb_subcommand_handlers,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "USB bulk backend-specific commands",
|
||||
.usage = "<cmd>",
|
||||
},
|
||||
#endif
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
@ -2184,29 +2293,6 @@ static const struct command_registration cmsis_dap_command_handlers[] = {
|
|||
.usage = "<cmd>",
|
||||
.chain = cmsis_dap_subcommand_handlers,
|
||||
},
|
||||
{
|
||||
.name = "cmsis_dap_vid_pid",
|
||||
.handler = &cmsis_dap_handle_vid_pid_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "the vendor ID and product ID of the CMSIS-DAP device",
|
||||
.usage = "(vid pid)*",
|
||||
},
|
||||
{
|
||||
.name = "cmsis_dap_backend",
|
||||
.handler = &cmsis_dap_handle_backend_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "set the communication backend to use (USB bulk or HID).",
|
||||
.usage = "(auto | usb_bulk | hid)",
|
||||
},
|
||||
#if BUILD_CMSIS_DAP_USB
|
||||
{
|
||||
.name = "cmsis_dap_usb",
|
||||
.chain = cmsis_dap_usb_subcommand_handlers,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "USB bulk backend-specific commands",
|
||||
.usage = "<cmd>",
|
||||
},
|
||||
#endif
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ struct cmsis_dap {
|
|||
unsigned int pending_fifo_block_count;
|
||||
|
||||
uint16_t caps;
|
||||
uint8_t mode;
|
||||
bool quirk_mode; /* enable expensive workarounds */
|
||||
|
||||
uint32_t swo_buf_sz;
|
||||
bool trace_enabled;
|
||||
};
|
||||
|
@ -61,9 +62,12 @@ struct cmsis_dap_backend {
|
|||
const char *name;
|
||||
int (*open)(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], const char *serial);
|
||||
void (*close)(struct cmsis_dap *dap);
|
||||
int (*read)(struct cmsis_dap *dap, int timeout_ms);
|
||||
int (*read)(struct cmsis_dap *dap, int transfer_timeout_ms,
|
||||
struct timeval *wait_timeout);
|
||||
int (*write)(struct cmsis_dap *dap, int len, int timeout_ms);
|
||||
int (*packet_buffer_alloc)(struct cmsis_dap *dap, unsigned int pkt_sz);
|
||||
void (*packet_buffer_free)(struct cmsis_dap *dap);
|
||||
void (*cancel_all)(struct cmsis_dap *dap);
|
||||
};
|
||||
|
||||
extern const struct cmsis_dap_backend cmsis_dap_hid_backend;
|
||||
|
|
|
@ -28,8 +28,29 @@
|
|||
#include <libusb.h>
|
||||
#include <helper/log.h>
|
||||
#include <helper/replacements.h>
|
||||
#include <jtag/jtag.h> /* ERROR_JTAG_DEVICE_ERROR only */
|
||||
|
||||
#include "cmsis_dap.h"
|
||||
#include "libusb_helper.h"
|
||||
|
||||
#if !defined(LIBUSB_API_VERSION) || (LIBUSB_API_VERSION < 0x01000105) \
|
||||
|| defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define libusb_dev_mem_alloc(dev, sz) malloc(sz)
|
||||
#define libusb_dev_mem_free(dev, buffer, sz) free(buffer)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
CMSIS_DAP_TRANSFER_PENDING = 0, /* must be 0, used in libusb_handle_events_completed */
|
||||
CMSIS_DAP_TRANSFER_IDLE,
|
||||
CMSIS_DAP_TRANSFER_COMPLETED
|
||||
};
|
||||
|
||||
struct cmsis_dap_bulk_transfer {
|
||||
struct libusb_transfer *transfer;
|
||||
uint8_t *buffer;
|
||||
int status; /* either CMSIS_DAP_TRANSFER_ enum or error code */
|
||||
int transferred;
|
||||
};
|
||||
|
||||
struct cmsis_dap_backend_data {
|
||||
struct libusb_context *usb_ctx;
|
||||
|
@ -37,12 +58,16 @@ struct cmsis_dap_backend_data {
|
|||
unsigned int ep_out;
|
||||
unsigned int ep_in;
|
||||
int interface;
|
||||
|
||||
struct cmsis_dap_bulk_transfer command_transfers[MAX_PENDING_REQUESTS];
|
||||
struct cmsis_dap_bulk_transfer response_transfers[MAX_PENDING_REQUESTS];
|
||||
};
|
||||
|
||||
static int cmsis_dap_usb_interface = -1;
|
||||
|
||||
static void cmsis_dap_usb_close(struct cmsis_dap *dap);
|
||||
static int cmsis_dap_usb_alloc(struct cmsis_dap *dap, unsigned int pkt_sz);
|
||||
static void cmsis_dap_usb_free(struct cmsis_dap *dap);
|
||||
|
||||
static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], const char *serial)
|
||||
{
|
||||
|
@ -358,6 +383,24 @@ static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t p
|
|||
dap->bdata->ep_in = ep_in;
|
||||
dap->bdata->interface = interface_num;
|
||||
|
||||
for (unsigned int idx = 0; idx < MAX_PENDING_REQUESTS; idx++) {
|
||||
dap->bdata->command_transfers[idx].status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
dap->bdata->command_transfers[idx].transfer = libusb_alloc_transfer(0);
|
||||
if (!dap->bdata->command_transfers[idx].transfer) {
|
||||
LOG_ERROR("unable to allocate USB transfer");
|
||||
cmsis_dap_usb_close(dap);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dap->bdata->response_transfers[idx].status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
dap->bdata->response_transfers[idx].transfer = libusb_alloc_transfer(0);
|
||||
if (!dap->bdata->response_transfers[idx].transfer) {
|
||||
LOG_ERROR("unable to allocate USB transfer");
|
||||
cmsis_dap_usb_close(dap);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
err = cmsis_dap_usb_alloc(dap, packet_size);
|
||||
if (err != ERROR_OK)
|
||||
cmsis_dap_usb_close(dap);
|
||||
|
@ -376,65 +419,178 @@ static int cmsis_dap_usb_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t p
|
|||
|
||||
static void cmsis_dap_usb_close(struct cmsis_dap *dap)
|
||||
{
|
||||
for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) {
|
||||
libusb_free_transfer(dap->bdata->command_transfers[i].transfer);
|
||||
libusb_free_transfer(dap->bdata->response_transfers[i].transfer);
|
||||
}
|
||||
cmsis_dap_usb_free(dap);
|
||||
libusb_release_interface(dap->bdata->dev_handle, dap->bdata->interface);
|
||||
libusb_close(dap->bdata->dev_handle);
|
||||
libusb_exit(dap->bdata->usb_ctx);
|
||||
free(dap->bdata);
|
||||
dap->bdata = NULL;
|
||||
free(dap->packet_buffer);
|
||||
dap->packet_buffer = NULL;
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_read(struct cmsis_dap *dap, int timeout_ms)
|
||||
static void LIBUSB_CALL cmsis_dap_usb_callback(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct cmsis_dap_bulk_transfer *tr;
|
||||
|
||||
tr = (struct cmsis_dap_bulk_transfer *)transfer->user_data;
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
tr->status = CMSIS_DAP_TRANSFER_COMPLETED;
|
||||
tr->transferred = transfer->actual_length;
|
||||
} else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
|
||||
tr->status = ERROR_TIMEOUT_REACHED;
|
||||
} else {
|
||||
tr->status = ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_read(struct cmsis_dap *dap, int transfer_timeout_ms,
|
||||
struct timeval *wait_timeout)
|
||||
{
|
||||
int transferred = 0;
|
||||
int err;
|
||||
struct cmsis_dap_bulk_transfer *tr;
|
||||
tr = &dap->bdata->response_transfers[dap->pending_fifo_get_idx];
|
||||
|
||||
err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_in,
|
||||
dap->packet_buffer, dap->packet_size, &transferred, timeout_ms);
|
||||
if (err) {
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
return ERROR_TIMEOUT_REACHED;
|
||||
} else {
|
||||
LOG_ERROR("error reading data: %s", libusb_strerror(err));
|
||||
if (tr->status == CMSIS_DAP_TRANSFER_IDLE) {
|
||||
libusb_fill_bulk_transfer(tr->transfer,
|
||||
dap->bdata->dev_handle, dap->bdata->ep_in,
|
||||
tr->buffer, dap->packet_size,
|
||||
&cmsis_dap_usb_callback, tr,
|
||||
transfer_timeout_ms);
|
||||
LOG_DEBUG_IO("submit read @ %u", dap->pending_fifo_get_idx);
|
||||
tr->status = CMSIS_DAP_TRANSFER_PENDING;
|
||||
err = libusb_submit_transfer(tr->transfer);
|
||||
if (err) {
|
||||
tr->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
LOG_ERROR("error submitting USB read: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&dap->packet_buffer[transferred], 0, dap->packet_buffer_size - transferred);
|
||||
struct timeval tv = {
|
||||
.tv_sec = transfer_timeout_ms / 1000,
|
||||
.tv_usec = transfer_timeout_ms % 1000 * 1000
|
||||
};
|
||||
|
||||
while (tr->status == CMSIS_DAP_TRANSFER_PENDING) {
|
||||
err = libusb_handle_events_timeout_completed(dap->bdata->usb_ctx,
|
||||
wait_timeout ? wait_timeout : &tv,
|
||||
&tr->status);
|
||||
if (err) {
|
||||
LOG_ERROR("error handling USB events: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (wait_timeout)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tr->status < 0 || tr->status == CMSIS_DAP_TRANSFER_COMPLETED) {
|
||||
/* Check related command request for an error */
|
||||
struct cmsis_dap_bulk_transfer *tr_cmd;
|
||||
tr_cmd = &dap->bdata->command_transfers[dap->pending_fifo_get_idx];
|
||||
if (tr_cmd->status < 0) {
|
||||
err = tr_cmd->status;
|
||||
tr_cmd->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
if (err != ERROR_TIMEOUT_REACHED)
|
||||
LOG_ERROR("error writing USB data");
|
||||
else
|
||||
LOG_DEBUG("command write USB timeout @ %u", dap->pending_fifo_get_idx);
|
||||
|
||||
return err;
|
||||
}
|
||||
if (tr_cmd->status == CMSIS_DAP_TRANSFER_COMPLETED)
|
||||
tr_cmd->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
}
|
||||
|
||||
if (tr->status < 0) {
|
||||
err = tr->status;
|
||||
tr->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
if (err != ERROR_TIMEOUT_REACHED)
|
||||
LOG_ERROR("error reading USB data");
|
||||
else
|
||||
LOG_DEBUG("USB timeout @ %u", dap->pending_fifo_get_idx);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
if (tr->status == CMSIS_DAP_TRANSFER_COMPLETED) {
|
||||
transferred = tr->transferred;
|
||||
LOG_DEBUG_IO("completed read @ %u, transferred %i",
|
||||
dap->pending_fifo_get_idx, transferred);
|
||||
memcpy(dap->packet_buffer, tr->buffer, transferred);
|
||||
memset(&dap->packet_buffer[transferred], 0, dap->packet_buffer_size - transferred);
|
||||
tr->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
}
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_write(struct cmsis_dap *dap, int txlen, int timeout_ms)
|
||||
{
|
||||
int transferred = 0;
|
||||
int err;
|
||||
struct cmsis_dap_bulk_transfer *tr;
|
||||
tr = &dap->bdata->command_transfers[dap->pending_fifo_put_idx];
|
||||
|
||||
/* skip the first byte that is only used by the HID backend */
|
||||
err = libusb_bulk_transfer(dap->bdata->dev_handle, dap->bdata->ep_out,
|
||||
dap->packet_buffer, txlen, &transferred, timeout_ms);
|
||||
if (err) {
|
||||
if (err == LIBUSB_ERROR_TIMEOUT) {
|
||||
return ERROR_TIMEOUT_REACHED;
|
||||
} else {
|
||||
LOG_ERROR("error writing data: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (tr->status == CMSIS_DAP_TRANSFER_PENDING) {
|
||||
LOG_ERROR("busy command USB transfer at %u", dap->pending_fifo_put_idx);
|
||||
struct timeval tv = {
|
||||
.tv_sec = timeout_ms / 1000,
|
||||
.tv_usec = timeout_ms % 1000 * 1000
|
||||
};
|
||||
libusb_handle_events_timeout_completed(dap->bdata->usb_ctx, &tv, &tr->status);
|
||||
}
|
||||
if (tr->status < 0) {
|
||||
if (tr->status != ERROR_TIMEOUT_REACHED)
|
||||
LOG_ERROR("error writing USB data, late detect");
|
||||
else
|
||||
LOG_DEBUG("USB write timeout @ %u, late detect", dap->pending_fifo_get_idx);
|
||||
tr->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
}
|
||||
if (tr->status == CMSIS_DAP_TRANSFER_COMPLETED) {
|
||||
LOG_ERROR("USB write: late transfer competed");
|
||||
tr->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
}
|
||||
if (tr->status != CMSIS_DAP_TRANSFER_IDLE) {
|
||||
libusb_cancel_transfer(tr->transfer);
|
||||
/* TODO: switch to less verbose errors and wait for USB working again */
|
||||
return ERROR_JTAG_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return transferred;
|
||||
memcpy(tr->buffer, dap->packet_buffer, txlen);
|
||||
|
||||
libusb_fill_bulk_transfer(tr->transfer,
|
||||
dap->bdata->dev_handle, dap->bdata->ep_out,
|
||||
tr->buffer, txlen,
|
||||
&cmsis_dap_usb_callback, tr,
|
||||
timeout_ms);
|
||||
|
||||
LOG_DEBUG_IO("submit write @ %u", dap->pending_fifo_put_idx);
|
||||
tr->status = CMSIS_DAP_TRANSFER_PENDING;
|
||||
err = libusb_submit_transfer(tr->transfer);
|
||||
if (err) {
|
||||
if (err == LIBUSB_ERROR_BUSY)
|
||||
libusb_cancel_transfer(tr->transfer);
|
||||
else
|
||||
tr->status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
|
||||
LOG_ERROR("error submitting USB write: %s", libusb_strerror(err));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int cmsis_dap_usb_alloc(struct cmsis_dap *dap, unsigned int pkt_sz)
|
||||
{
|
||||
uint8_t *buf = malloc(pkt_sz);
|
||||
if (!buf) {
|
||||
dap->packet_buffer = malloc(pkt_sz);
|
||||
if (!dap->packet_buffer) {
|
||||
LOG_ERROR("unable to allocate CMSIS-DAP packet buffer");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
dap->packet_buffer = buf;
|
||||
dap->packet_size = pkt_sz;
|
||||
dap->packet_buffer_size = pkt_sz;
|
||||
/* Prevent sending zero size USB packets */
|
||||
|
@ -443,9 +599,54 @@ static int cmsis_dap_usb_alloc(struct cmsis_dap *dap, unsigned int pkt_sz)
|
|||
dap->command = dap->packet_buffer;
|
||||
dap->response = dap->packet_buffer;
|
||||
|
||||
for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) {
|
||||
dap->bdata->command_transfers[i].buffer =
|
||||
libusb_dev_mem_alloc(dap->bdata->dev_handle, pkt_sz);
|
||||
if (!dap->bdata->command_transfers[i].buffer) {
|
||||
LOG_ERROR("unable to allocate CMSIS-DAP packet buffer");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
dap->bdata->response_transfers[i].buffer =
|
||||
libusb_dev_mem_alloc(dap->bdata->dev_handle, pkt_sz);
|
||||
if (!dap->bdata->response_transfers[i].buffer) {
|
||||
LOG_ERROR("unable to allocate CMSIS-DAP packet buffer");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void cmsis_dap_usb_free(struct cmsis_dap *dap)
|
||||
{
|
||||
for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) {
|
||||
libusb_dev_mem_free(dap->bdata->dev_handle,
|
||||
dap->bdata->command_transfers[i].buffer, dap->packet_size);
|
||||
dap->bdata->command_transfers[i].buffer = NULL;
|
||||
libusb_dev_mem_free(dap->bdata->dev_handle,
|
||||
dap->bdata->response_transfers[i].buffer, dap->packet_size);
|
||||
dap->bdata->response_transfers[i].buffer = NULL;
|
||||
}
|
||||
|
||||
free(dap->packet_buffer);
|
||||
dap->packet_buffer = NULL;
|
||||
dap->command = NULL;
|
||||
dap->response = NULL;
|
||||
}
|
||||
|
||||
static void cmsis_dap_usb_cancel_all(struct cmsis_dap *dap)
|
||||
{
|
||||
for (unsigned int i = 0; i < MAX_PENDING_REQUESTS; i++) {
|
||||
if (dap->bdata->command_transfers[i].status == CMSIS_DAP_TRANSFER_PENDING)
|
||||
libusb_cancel_transfer(dap->bdata->command_transfers[i].transfer);
|
||||
if (dap->bdata->response_transfers[i].status == CMSIS_DAP_TRANSFER_PENDING)
|
||||
libusb_cancel_transfer(dap->bdata->response_transfers[i].transfer);
|
||||
|
||||
dap->bdata->command_transfers[i].status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
dap->bdata->response_transfers[i].status = CMSIS_DAP_TRANSFER_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(cmsis_dap_handle_usb_interface_command)
|
||||
{
|
||||
if (CMD_ARGC == 1)
|
||||
|
@ -474,4 +675,6 @@ const struct cmsis_dap_backend cmsis_dap_usb_backend = {
|
|||
.read = cmsis_dap_usb_read,
|
||||
.write = cmsis_dap_usb_write,
|
||||
.packet_buffer_alloc = cmsis_dap_usb_alloc,
|
||||
.packet_buffer_free = cmsis_dap_usb_free,
|
||||
.cancel_all = cmsis_dap_usb_cancel_all,
|
||||
};
|
||||
|
|
|
@ -34,8 +34,39 @@ struct cmsis_dap_backend_data {
|
|||
hid_device *dev_handle;
|
||||
};
|
||||
|
||||
struct cmsis_dap_report_size {
|
||||
unsigned short vid;
|
||||
unsigned short pid;
|
||||
unsigned int report_size;
|
||||
};
|
||||
|
||||
static const struct cmsis_dap_report_size report_size_quirks[] = {
|
||||
/* Third gen Atmel tools use a report size of 512 */
|
||||
/* This list of PIDs comes from toolinfo.py in Microchip's pyedbglib. */
|
||||
// Atmel JTAG-ICE 3
|
||||
{ .vid = 0x03eb, .pid = 0x2140, .report_size = 512 },
|
||||
// Atmel-ICE
|
||||
{ .vid = 0x03eb, .pid = 0x2141, .report_size = 512 },
|
||||
// Atmel Power Debugger
|
||||
{ .vid = 0x03eb, .pid = 0x2144, .report_size = 512 },
|
||||
// EDBG (found on Xplained Pro boards)
|
||||
{ .vid = 0x03eb, .pid = 0x2111, .report_size = 512 },
|
||||
// Zero (???)
|
||||
{ .vid = 0x03eb, .pid = 0x2157, .report_size = 512 },
|
||||
// EDBG with Mass Storage (found on Xplained Pro boards)
|
||||
{ .vid = 0x03eb, .pid = 0x2169, .report_size = 512 },
|
||||
// Commercially available EDBG (for third-party use)
|
||||
{ .vid = 0x03eb, .pid = 0x216a, .report_size = 512 },
|
||||
// Kraken (???)
|
||||
{ .vid = 0x03eb, .pid = 0x2170, .report_size = 512 },
|
||||
|
||||
{ .vid = 0, .pid = 0, .report_size = 0 }
|
||||
};
|
||||
|
||||
|
||||
static void cmsis_dap_hid_close(struct cmsis_dap *dap);
|
||||
static int cmsis_dap_hid_alloc(struct cmsis_dap *dap, unsigned int pkt_sz);
|
||||
static void cmsis_dap_hid_free(struct cmsis_dap *dap);
|
||||
|
||||
static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t pids[], const char *serial)
|
||||
{
|
||||
|
@ -138,13 +169,15 @@ static int cmsis_dap_hid_open(struct cmsis_dap *dap, uint16_t vids[], uint16_t p
|
|||
|
||||
unsigned int packet_size = 64;
|
||||
|
||||
/* atmel cmsis-dap uses 512 byte reports */
|
||||
/* except when it doesn't e.g. with mEDBG on SAMD10 Xplained
|
||||
* board */
|
||||
/* Check for adapters that are known to have unusual report lengths. */
|
||||
for (i = 0; report_size_quirks[i].vid != 0; i++) {
|
||||
if (report_size_quirks[i].vid == target_vid &&
|
||||
report_size_quirks[i].pid == target_pid) {
|
||||
packet_size = report_size_quirks[i].report_size;
|
||||
}
|
||||
}
|
||||
/* TODO: HID report descriptor should be parsed instead of
|
||||
* hardcoding a match by VID */
|
||||
if (target_vid == 0x03eb && target_pid != 0x2145 && target_pid != 0x2175)
|
||||
packet_size = 512;
|
||||
* hardcoding a match by VID/PID */
|
||||
|
||||
dap->bdata->dev_handle = dev;
|
||||
|
||||
|
@ -165,14 +198,21 @@ static void cmsis_dap_hid_close(struct cmsis_dap *dap)
|
|||
hid_exit();
|
||||
free(dap->bdata);
|
||||
dap->bdata = NULL;
|
||||
free(dap->packet_buffer);
|
||||
dap->packet_buffer = NULL;
|
||||
cmsis_dap_hid_free(dap);
|
||||
}
|
||||
|
||||
static int cmsis_dap_hid_read(struct cmsis_dap *dap, int timeout_ms)
|
||||
static int cmsis_dap_hid_read(struct cmsis_dap *dap, int transfer_timeout_ms,
|
||||
struct timeval *wait_timeout)
|
||||
{
|
||||
int retval = hid_read_timeout(dap->bdata->dev_handle, dap->packet_buffer, dap->packet_buffer_size, timeout_ms);
|
||||
int timeout_ms;
|
||||
if (wait_timeout)
|
||||
timeout_ms = wait_timeout->tv_usec / 1000 + wait_timeout->tv_sec * 1000;
|
||||
else
|
||||
timeout_ms = transfer_timeout_ms;
|
||||
|
||||
int retval = hid_read_timeout(dap->bdata->dev_handle,
|
||||
dap->packet_buffer, dap->packet_buffer_size,
|
||||
timeout_ms);
|
||||
if (retval == 0) {
|
||||
return ERROR_TIMEOUT_REACHED;
|
||||
} else if (retval == -1) {
|
||||
|
@ -222,6 +262,16 @@ static int cmsis_dap_hid_alloc(struct cmsis_dap *dap, unsigned int pkt_sz)
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static void cmsis_dap_hid_free(struct cmsis_dap *dap)
|
||||
{
|
||||
free(dap->packet_buffer);
|
||||
dap->packet_buffer = NULL;
|
||||
}
|
||||
|
||||
static void cmsis_dap_hid_cancel_all(struct cmsis_dap *dap)
|
||||
{
|
||||
}
|
||||
|
||||
const struct cmsis_dap_backend cmsis_dap_hid_backend = {
|
||||
.name = "hid",
|
||||
.open = cmsis_dap_hid_open,
|
||||
|
@ -229,4 +279,6 @@ const struct cmsis_dap_backend cmsis_dap_hid_backend = {
|
|||
.read = cmsis_dap_hid_read,
|
||||
.write = cmsis_dap_hid_write,
|
||||
.packet_buffer_alloc = cmsis_dap_hid_alloc,
|
||||
.packet_buffer_free = cmsis_dap_hid_free,
|
||||
.cancel_all = cmsis_dap_hid_cancel_all,
|
||||
};
|
||||
|
|
|
@ -76,13 +76,13 @@ int interface_jtag_add_ir_scan(struct jtag_tap *active,
|
|||
|
||||
if (tap == active) {
|
||||
/* if TAP is listed in input fields, copy the value */
|
||||
tap->bypass = 0;
|
||||
tap->bypass = false;
|
||||
|
||||
jtag_scan_field_clone(field, in_fields);
|
||||
} else {
|
||||
/* if a TAP isn't listed in input fields, set it to BYPASS */
|
||||
|
||||
tap->bypass = 1;
|
||||
tap->bypass = true;
|
||||
|
||||
field->num_bits = tap->ir_length;
|
||||
field->out_value = buf_set_ones(cmd_queue_alloc(DIV_ROUND_UP(tap->ir_length, 8)), tap->ir_length);
|
||||
|
|
|
@ -1213,7 +1213,7 @@ COMMAND_HANDLER(ftdi_handle_set_signal_command)
|
|||
/* fallthrough */
|
||||
default:
|
||||
LOG_ERROR("unknown signal level '%s', use 0, 1 or z", CMD_ARGV[1]);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
return mpsse_flush(mpsse_ctx);
|
||||
|
|
|
@ -966,19 +966,17 @@ COMMAND_HANDLER(jlink_usb_command)
|
|||
{
|
||||
int tmp;
|
||||
|
||||
if (CMD_ARGC != 1) {
|
||||
command_print(CMD, "Need exactly one argument for jlink usb");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (sscanf(CMD_ARGV[0], "%i", &tmp) != 1) {
|
||||
command_print(CMD, "Invalid USB address: %s", CMD_ARGV[0]);
|
||||
return ERROR_FAIL;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
if (tmp < JAYLINK_USB_ADDRESS_0 || tmp > JAYLINK_USB_ADDRESS_3) {
|
||||
command_print(CMD, "Invalid USB address: %s", CMD_ARGV[0]);
|
||||
return ERROR_FAIL;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
usb_address = tmp;
|
||||
|
@ -1059,7 +1057,7 @@ COMMAND_HANDLER(jlink_handle_jlink_jtag_command)
|
|||
} else if (CMD_ARGC == 1) {
|
||||
if (sscanf(CMD_ARGV[0], "%i", &tmp) != 1) {
|
||||
command_print(CMD, "Invalid argument: %s", CMD_ARGV[0]);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
switch (tmp) {
|
||||
|
@ -1071,10 +1069,9 @@ COMMAND_HANDLER(jlink_handle_jlink_jtag_command)
|
|||
break;
|
||||
default:
|
||||
command_print(CMD, "Invalid argument: %s", CMD_ARGV[0]);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
} else {
|
||||
command_print(CMD, "Need exactly one argument for jlink jtag");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
|
@ -1086,10 +1083,8 @@ COMMAND_HANDLER(jlink_handle_target_power_command)
|
|||
int ret;
|
||||
int enable;
|
||||
|
||||
if (CMD_ARGC != 1) {
|
||||
command_print(CMD, "Need exactly one argument for jlink targetpower");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SET_TARGET_POWER)) {
|
||||
command_print(CMD, "Target power supply is not supported by the "
|
||||
|
@ -1428,17 +1423,16 @@ COMMAND_HANDLER(jlink_handle_config_usb_address_command)
|
|||
} else if (CMD_ARGC == 1) {
|
||||
if (sscanf(CMD_ARGV[0], "%" SCNd8, &tmp) != 1) {
|
||||
command_print(CMD, "Invalid USB address: %s", CMD_ARGV[0]);
|
||||
return ERROR_FAIL;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
if (tmp > JAYLINK_USB_ADDRESS_3) {
|
||||
command_print(CMD, "Invalid USB address: %u", tmp);
|
||||
return ERROR_FAIL;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
tmp_config.usb_address = tmp;
|
||||
} else {
|
||||
command_print(CMD, "Need exactly one argument for jlink config usb");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
|
@ -1470,13 +1464,11 @@ COMMAND_HANDLER(jlink_handle_config_target_power_command)
|
|||
enable = false;
|
||||
} else {
|
||||
command_print(CMD, "Invalid argument: %s", CMD_ARGV[0]);
|
||||
return ERROR_FAIL;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
tmp_config.target_power = enable;
|
||||
} else {
|
||||
command_print(CMD, "Need exactly one argument for jlink config "
|
||||
"targetpower");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
|
@ -1510,7 +1502,7 @@ COMMAND_HANDLER(jlink_handle_config_mac_address_command)
|
|||
if ((strlen(str) != 17) || (str[2] != ':' || str[5] != ':' ||
|
||||
str[8] != ':' || str[11] != ':' || str[14] != ':')) {
|
||||
command_print(CMD, "Invalid MAC address format");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
for (i = 5; i >= 0; i--) {
|
||||
|
@ -1520,17 +1512,16 @@ COMMAND_HANDLER(jlink_handle_config_mac_address_command)
|
|||
|
||||
if (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) {
|
||||
command_print(CMD, "Invalid MAC address: zero address");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
if (!(0x01 & addr[0])) {
|
||||
command_print(CMD, "Invalid MAC address: multicast address");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
memcpy(tmp_config.mac_address, addr, sizeof(addr));
|
||||
} else {
|
||||
command_print(CMD, "Need exactly one argument for jlink config mac");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
|
@ -1592,20 +1583,26 @@ COMMAND_HANDLER(jlink_handle_config_ip_address_command)
|
|||
if (!CMD_ARGC) {
|
||||
show_config_ip_address(CMD);
|
||||
} else {
|
||||
if (!string_to_ip(CMD_ARGV[0], ip_address, &i))
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
if (!string_to_ip(CMD_ARGV[0], ip_address, &i)) {
|
||||
command_print(CMD, "invalid IPv4 address");
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
len = strlen(CMD_ARGV[0]);
|
||||
|
||||
/* Check for format A.B.C.D/E. */
|
||||
if (i < len) {
|
||||
if (CMD_ARGV[0][i] != '/')
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
if (CMD_ARGV[0][i] != '/') {
|
||||
command_print(CMD, "missing network mask");
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0] + i + 1, subnet_bits);
|
||||
} else if (CMD_ARGC > 1) {
|
||||
if (!string_to_ip(CMD_ARGV[1], (uint8_t *)&subnet_mask, &i))
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
if (!string_to_ip(CMD_ARGV[1], (uint8_t *)&subnet_mask, &i)) {
|
||||
command_print(CMD, "invalid subnet mask");
|
||||
return ERROR_COMMAND_ARGUMENT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (!subnet_mask)
|
||||
|
|
|
@ -593,10 +593,8 @@ static int jtag_vpi_quit(void)
|
|||
|
||||
COMMAND_HANDLER(jtag_vpi_set_port)
|
||||
{
|
||||
if (CMD_ARGC == 0) {
|
||||
LOG_ERROR("Command \"jtag_vpi set_port\" expects 1 argument (TCP port number)");
|
||||
if (CMD_ARGC == 0)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port);
|
||||
LOG_INFO("jtag_vpi: server port set to %u", server_port);
|
||||
|
@ -607,10 +605,8 @@ COMMAND_HANDLER(jtag_vpi_set_port)
|
|||
COMMAND_HANDLER(jtag_vpi_set_address)
|
||||
{
|
||||
|
||||
if (CMD_ARGC == 0) {
|
||||
LOG_ERROR("Command \"jtag_vpi set_address\" expects 1 argument (IP address)");
|
||||
if (CMD_ARGC == 0)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
free(server_address);
|
||||
server_address = strdup(CMD_ARGV[0]);
|
||||
|
@ -621,10 +617,8 @@ COMMAND_HANDLER(jtag_vpi_set_address)
|
|||
|
||||
COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
LOG_ERROR("Command \"jtag_vpi stop_sim_on_exit\" expects 1 argument (on|off)");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], stop_sim_on_exit);
|
||||
return ERROR_OK;
|
||||
|
|
|
@ -879,6 +879,13 @@ static const struct command_registration kitprog_subcommand_handlers[] = {
|
|||
.usage = "",
|
||||
.help = "try to acquire a PSoC",
|
||||
},
|
||||
{
|
||||
.name = "init_acquire_psoc",
|
||||
.handler = &kitprog_handle_init_acquire_psoc_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "try to acquire a PSoC during init",
|
||||
.usage = "",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
@ -890,13 +897,6 @@ static const struct command_registration kitprog_command_handlers[] = {
|
|||
.usage = "<cmd>",
|
||||
.chain = kitprog_subcommand_handlers,
|
||||
},
|
||||
{
|
||||
.name = "kitprog_init_acquire_psoc",
|
||||
.handler = &kitprog_handle_init_acquire_psoc_command,
|
||||
.mode = COMMAND_CONFIG,
|
||||
.help = "try to acquire a PSoC during init",
|
||||
.usage = "",
|
||||
},
|
||||
COMMAND_REGISTRATION_DONE
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2011 by Richard Uhler *
|
||||
* ruhler@mit.edu *
|
||||
* *
|
||||
* Copyright (C) 2021 by Manuel Wick <manuel@matronix.de> *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
@ -220,11 +222,35 @@ static int remote_bitbang_blink(int on)
|
|||
return remote_bitbang_queue(c, FLUSH_SEND_BUF);
|
||||
}
|
||||
|
||||
static void remote_bitbang_swdio_drive(bool is_output)
|
||||
{
|
||||
char c = is_output ? 'O' : 'o';
|
||||
if (remote_bitbang_queue(c, FLUSH_SEND_BUF) == ERROR_FAIL)
|
||||
LOG_ERROR("Error setting direction for swdio");
|
||||
}
|
||||
|
||||
static int remote_bitbang_swdio_read(void)
|
||||
{
|
||||
if (remote_bitbang_queue('c', FLUSH_SEND_BUF) != ERROR_FAIL)
|
||||
return remote_bitbang_read_sample();
|
||||
else
|
||||
return BB_ERROR;
|
||||
}
|
||||
|
||||
static int remote_bitbang_swd_write(int swclk, int swdio)
|
||||
{
|
||||
char c = 'd' + ((swclk ? 0x2 : 0x0) | (swdio ? 0x1 : 0x0));
|
||||
return remote_bitbang_queue(c, NO_FLUSH);
|
||||
}
|
||||
|
||||
static struct bitbang_interface remote_bitbang_bitbang = {
|
||||
.buf_size = sizeof(remote_bitbang_recv_buf) - 1,
|
||||
.sample = &remote_bitbang_sample,
|
||||
.read_sample = &remote_bitbang_read_sample,
|
||||
.write = &remote_bitbang_write,
|
||||
.swdio_read = &remote_bitbang_swdio_read,
|
||||
.swdio_drive = &remote_bitbang_swdio_drive,
|
||||
.swd_write = &remote_bitbang_swd_write,
|
||||
.blink = &remote_bitbang_blink,
|
||||
};
|
||||
|
||||
|
@ -349,6 +375,8 @@ COMMAND_HANDLER(remote_bitbang_handle_remote_bitbang_host_command)
|
|||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
static const char * const remote_bitbang_transports[] = { "jtag", "swd", NULL };
|
||||
|
||||
static const struct command_registration remote_bitbang_subcommand_handlers[] = {
|
||||
{
|
||||
.name = "port",
|
||||
|
@ -401,7 +429,7 @@ static struct jtag_interface remote_bitbang_interface = {
|
|||
|
||||
struct adapter_driver remote_bitbang_adapter_driver = {
|
||||
.name = "remote_bitbang",
|
||||
.transports = jtag_only,
|
||||
.transports = remote_bitbang_transports,
|
||||
.commands = remote_bitbang_command_handlers,
|
||||
|
||||
.init = &remote_bitbang_init,
|
||||
|
@ -409,4 +437,5 @@ struct adapter_driver remote_bitbang_adapter_driver = {
|
|||
.reset = &remote_bitbang_reset,
|
||||
|
||||
.jtag_ops = &remote_bitbang_interface,
|
||||
.swd_ops = &bitbang_swd,
|
||||
};
|
||||
|
|
|
@ -434,10 +434,8 @@ static void rshim_disconnect(struct adiv5_dap *dap)
|
|||
|
||||
COMMAND_HANDLER(rshim_dap_device_command)
|
||||
{
|
||||
if (CMD_ARGC != 1) {
|
||||
command_print(CMD, "Too many arguments");
|
||||
if (CMD_ARGC != 1)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
free(rshim_dev_path);
|
||||
rshim_dev_path = strdup(CMD_ARGV[0]);
|
||||
|
|
|
@ -100,7 +100,7 @@ int hl_interface_init_target(struct target *t)
|
|||
}
|
||||
|
||||
t->tap->priv = &hl_if;
|
||||
t->tap->hasidcode = 1;
|
||||
t->tap->has_idcode = true;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ struct jtag_tap {
|
|||
uint32_t idcode; /**< device identification code */
|
||||
/** not all devices have idcode,
|
||||
* we'll discover this during chain examination */
|
||||
bool hasidcode;
|
||||
bool has_idcode;
|
||||
|
||||
/** Array of expected identification codes */
|
||||
uint32_t *expected_ids;
|
||||
|
@ -131,7 +131,7 @@ struct jtag_tap {
|
|||
/** current instruction */
|
||||
uint8_t *cur_instr;
|
||||
/** Bypass register selected */
|
||||
int bypass;
|
||||
bool bypass;
|
||||
|
||||
struct jtag_tap_event_action *event_action;
|
||||
|
||||
|
|
|
@ -1108,6 +1108,30 @@ proc "am335xgpio led_on_state" {state} {
|
|||
}
|
||||
}
|
||||
|
||||
lappend _telnet_autocomplete_skip "cmsis_dap_backend"
|
||||
proc "cmsis_dap_backend" {backend} {
|
||||
echo "DEPRECATED! use 'cmsis-dap backend', not 'cmsis_dap_backend'"
|
||||
eval cmsis-dap backend $backend
|
||||
}
|
||||
|
||||
lappend _telnet_autocomplete_skip "cmsis_dap_vid_pid"
|
||||
proc "cmsis_dap_vid_pid" {args} {
|
||||
echo "DEPRECATED! use 'cmsis-dap vid_pid', not 'cmsis_dap_vid_pid'"
|
||||
eval cmsis-dap vid_pid $args
|
||||
}
|
||||
|
||||
lappend _telnet_autocomplete_skip "cmsis_dap_usb"
|
||||
proc "cmsis_dap_usb" {args} {
|
||||
echo "DEPRECATED! use 'cmsis-dap usb', not 'cmsis_dap_usb'"
|
||||
eval cmsis-dap usb $args
|
||||
}
|
||||
|
||||
lappend _telnet_autocomplete_skip "kitprog_init_acquire_psoc"
|
||||
proc "kitprog_init_acquire_psoc" {} {
|
||||
echo "DEPRECATED! use 'kitprog init_acquire_psoc', not 'kitprog_init_acquire_psoc'"
|
||||
eval kitprog init_acquire_psoc
|
||||
}
|
||||
|
||||
lappend _telnet_autocomplete_skip "pld device"
|
||||
proc "pld device" {driver tap_name {opt 0}} {
|
||||
echo "DEPRECATED! use 'pld create ...', not 'pld device ...'"
|
||||
|
|
|
@ -157,7 +157,7 @@ static int intel_check_for_unique_id(struct intel_pld_device *intel_info)
|
|||
|
||||
static int intel_check_config(struct intel_pld_device *intel_info)
|
||||
{
|
||||
if (!intel_info->tap->hasidcode) {
|
||||
if (!intel_info->tap->has_idcode) {
|
||||
LOG_ERROR("no IDCODE");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ static int lattice_check_device_family(struct lattice_pld_device *lattice_device
|
|||
if (lattice_device->family != LATTICE_UNKNOWN && lattice_device->preload_length != 0)
|
||||
return ERROR_OK;
|
||||
|
||||
if (!lattice_device->tap || !lattice_device->tap->hasidcode)
|
||||
if (!lattice_device->tap || !lattice_device->tap->has_idcode)
|
||||
return ERROR_FAIL;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(lattice_devices); ++i) {
|
||||
|
@ -280,7 +280,7 @@ static int lattice_load_command(struct pld_device *pld_device, const char *filen
|
|||
return ERROR_FAIL;
|
||||
struct jtag_tap *tap = lattice_device->tap;
|
||||
|
||||
if (!tap || !tap->hasidcode)
|
||||
if (!tap || !tap->has_idcode)
|
||||
return ERROR_FAIL;
|
||||
|
||||
int retval = lattice_check_device_family(lattice_device);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
COMMAND_HANDLER(handle_rtt_setup_command)
|
||||
{
|
||||
struct rtt_source source;
|
||||
struct rtt_source source;
|
||||
|
||||
if (CMD_ARGC != 3)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
struct rtt_service {
|
||||
unsigned int channel;
|
||||
char *hello_message;
|
||||
};
|
||||
|
||||
static int read_callback(unsigned int channel, const uint8_t *buffer,
|
||||
|
@ -65,6 +66,9 @@ static int rtt_new_connection(struct connection *connection)
|
|||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
if (service->hello_message)
|
||||
connection_write(connection, service->hello_message, strlen(service->hello_message));
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -117,16 +121,30 @@ COMMAND_HANDLER(handle_rtt_start_command)
|
|||
int ret;
|
||||
struct rtt_service *service;
|
||||
|
||||
if (CMD_ARGC != 2)
|
||||
if (CMD_ARGC < 2 || CMD_ARGC > 3)
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
service = malloc(sizeof(struct rtt_service));
|
||||
service = calloc(1, sizeof(struct rtt_service));
|
||||
|
||||
if (!service)
|
||||
return ERROR_FAIL;
|
||||
|
||||
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], service->channel);
|
||||
|
||||
if (CMD_ARGC >= 3) {
|
||||
const char *hello_message = CMD_ARGV[2];
|
||||
size_t hello_length = strlen(hello_message);
|
||||
|
||||
service->hello_message = malloc(hello_length + 2);
|
||||
if (!service->hello_message) {
|
||||
LOG_ERROR("Out of memory");
|
||||
free(service);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
strcpy(service->hello_message, hello_message);
|
||||
service->hello_message[hello_length] = '\n';
|
||||
service->hello_message[hello_length + 1] = '\0';
|
||||
}
|
||||
ret = add_service(&rtt_service_driver, CMD_ARGV[0], CONNECTION_LIMIT_UNLIMITED, service);
|
||||
|
||||
if (ret != ERROR_OK) {
|
||||
|
@ -153,7 +171,7 @@ static const struct command_registration rtt_server_subcommand_handlers[] = {
|
|||
.handler = handle_rtt_start_command,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "Start a RTT server",
|
||||
.usage = "<port> <channel>"
|
||||
.usage = "<port> <channel> [message]"
|
||||
},
|
||||
{
|
||||
.name = "stop",
|
||||
|
|
|
@ -105,7 +105,7 @@ static int aarch64_restore_system_control_reg(struct target *target)
|
|||
if (target_mode != ARM_MODE_ANY)
|
||||
armv8_dpm_modeswitch(&armv8->dpm, target_mode);
|
||||
|
||||
retval = armv8->dpm.instr_write_data_r0(&armv8->dpm, instr, aarch64->system_control_reg);
|
||||
retval = armv8->dpm.instr_write_data_r0_64(&armv8->dpm, instr, aarch64->system_control_reg);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
|
@ -182,7 +182,7 @@ static int aarch64_mmu_modify(struct target *target, int enable)
|
|||
if (target_mode != ARM_MODE_ANY)
|
||||
armv8_dpm_modeswitch(&armv8->dpm, target_mode);
|
||||
|
||||
retval = armv8->dpm.instr_write_data_r0(&armv8->dpm, instr,
|
||||
retval = armv8->dpm.instr_write_data_r0_64(&armv8->dpm, instr,
|
||||
aarch64->system_control_reg_curr);
|
||||
|
||||
if (target_mode != ARM_MODE_ANY)
|
||||
|
@ -1055,14 +1055,14 @@ static int aarch64_post_debug_entry(struct target *target)
|
|||
if (target_mode != ARM_MODE_ANY)
|
||||
armv8_dpm_modeswitch(&armv8->dpm, target_mode);
|
||||
|
||||
retval = armv8->dpm.instr_read_data_r0(&armv8->dpm, instr, &aarch64->system_control_reg);
|
||||
retval = armv8->dpm.instr_read_data_r0_64(&armv8->dpm, instr, &aarch64->system_control_reg);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
if (target_mode != ARM_MODE_ANY)
|
||||
armv8_dpm_modeswitch(&armv8->dpm, ARM_MODE_ANY);
|
||||
|
||||
LOG_DEBUG("System_register: %8.8" PRIx32, aarch64->system_control_reg);
|
||||
LOG_DEBUG("System_register: %8.8" PRIx64, aarch64->system_control_reg);
|
||||
aarch64->system_control_reg_curr = aarch64->system_control_reg;
|
||||
|
||||
if (armv8->armv8_mmu.armv8_cache.info == -1) {
|
||||
|
|
|
@ -43,8 +43,8 @@ struct aarch64_common {
|
|||
struct armv8_common armv8_common;
|
||||
|
||||
/* Context information */
|
||||
uint32_t system_control_reg;
|
||||
uint32_t system_control_reg_curr;
|
||||
uint64_t system_control_reg;
|
||||
uint64_t system_control_reg_curr;
|
||||
|
||||
/* Breakpoint register pairs */
|
||||
int brp_num_context;
|
||||
|
|
|
@ -243,7 +243,7 @@ static int armv8_flush_all_data(struct target *target)
|
|||
foreach_smp_target(head, target->smp_targets) {
|
||||
struct target *curr = head->target;
|
||||
if (curr->state == TARGET_HALTED) {
|
||||
LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid);
|
||||
LOG_TARGET_INFO(curr, "Wait flushing data l1.");
|
||||
retval = _armv8_flush_all_data(curr);
|
||||
}
|
||||
}
|
||||
|
@ -286,8 +286,9 @@ static struct armv8_cachesize decode_cache_reg(uint32_t cache_reg)
|
|||
size.index = (cache_reg >> 13) & 0x7fff;
|
||||
size.way = ((cache_reg >> 3) & 0x3ff);
|
||||
|
||||
while (((size.way << i) & 0x80000000) == 0)
|
||||
i++;
|
||||
if (size.way != 0)
|
||||
while (((size.way << i) & 0x80000000) == 0)
|
||||
i++;
|
||||
size.way_shift = i;
|
||||
|
||||
return size;
|
||||
|
|
|
@ -53,7 +53,7 @@ static int breakpoint_add_internal(struct target *target,
|
|||
* breakpoint" ... check all the parameters before
|
||||
* succeeding.
|
||||
*/
|
||||
LOG_ERROR("Duplicate Breakpoint address: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
|
||||
LOG_TARGET_ERROR(target, "Duplicate Breakpoint address: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
|
||||
address, breakpoint->unique_id);
|
||||
return ERROR_TARGET_DUPLICATE_BREAKPOINT;
|
||||
}
|
||||
|
@ -84,16 +84,15 @@ static int breakpoint_add_internal(struct target *target,
|
|||
default:
|
||||
reason = "unknown reason";
|
||||
fail:
|
||||
LOG_ERROR("can't add breakpoint: %s", reason);
|
||||
LOG_TARGET_ERROR(target, "can't add breakpoint: %s", reason);
|
||||
free((*breakpoint_p)->orig_instr);
|
||||
free(*breakpoint_p);
|
||||
*breakpoint_p = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOG_DEBUG("[%d] added %s breakpoint at " TARGET_ADDR_FMT
|
||||
LOG_TARGET_DEBUG(target, "added %s breakpoint at " TARGET_ADDR_FMT
|
||||
" of length 0x%8.8x, (BPID: %" PRIu32 ")",
|
||||
target->coreid,
|
||||
breakpoint_type_strings[(*breakpoint_p)->type],
|
||||
(*breakpoint_p)->address, (*breakpoint_p)->length,
|
||||
(*breakpoint_p)->unique_id);
|
||||
|
@ -135,14 +134,14 @@ static int context_breakpoint_add_internal(struct target *target,
|
|||
(*breakpoint_p)->unique_id = bpwp_unique_id++;
|
||||
retval = target_add_context_breakpoint(target, *breakpoint_p);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("could not add breakpoint");
|
||||
LOG_TARGET_ERROR(target, "could not add breakpoint");
|
||||
free((*breakpoint_p)->orig_instr);
|
||||
free(*breakpoint_p);
|
||||
*breakpoint_p = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
LOG_DEBUG("added %s Context breakpoint at 0x%8.8" PRIx32 " of length 0x%8.8x, (BPID: %" PRIu32 ")",
|
||||
LOG_TARGET_DEBUG(target, "added %s Context breakpoint at 0x%8.8" PRIx32 " of length 0x%8.8x, (BPID: %" PRIu32 ")",
|
||||
breakpoint_type_strings[(*breakpoint_p)->type],
|
||||
(*breakpoint_p)->asid, (*breakpoint_p)->length,
|
||||
(*breakpoint_p)->unique_id);
|
||||
|
@ -166,11 +165,11 @@ static int hybrid_breakpoint_add_internal(struct target *target,
|
|||
* breakpoint" ... check all the parameters before
|
||||
* succeeding.
|
||||
*/
|
||||
LOG_ERROR("Duplicate Hybrid Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")",
|
||||
LOG_TARGET_ERROR(target, "Duplicate Hybrid Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")",
|
||||
asid, breakpoint->unique_id);
|
||||
return ERROR_TARGET_DUPLICATE_BREAKPOINT;
|
||||
} else if ((breakpoint->address == address) && (breakpoint->asid == 0)) {
|
||||
LOG_ERROR("Duplicate Breakpoint IVA: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
|
||||
LOG_TARGET_ERROR(target, "Duplicate Breakpoint IVA: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
|
||||
address, breakpoint->unique_id);
|
||||
return ERROR_TARGET_DUPLICATE_BREAKPOINT;
|
||||
|
||||
|
@ -191,13 +190,13 @@ static int hybrid_breakpoint_add_internal(struct target *target,
|
|||
|
||||
retval = target_add_hybrid_breakpoint(target, *breakpoint_p);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("could not add breakpoint");
|
||||
LOG_TARGET_ERROR(target, "could not add breakpoint");
|
||||
free((*breakpoint_p)->orig_instr);
|
||||
free(*breakpoint_p);
|
||||
*breakpoint_p = NULL;
|
||||
return retval;
|
||||
}
|
||||
LOG_DEBUG(
|
||||
LOG_TARGET_DEBUG(target,
|
||||
"added %s Hybrid breakpoint at address " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")",
|
||||
breakpoint_type_strings[(*breakpoint_p)->type],
|
||||
(*breakpoint_p)->address,
|
||||
|
@ -307,7 +306,7 @@ static int breakpoint_free(struct target *data_target, struct target *breakpoint
|
|||
return retval;
|
||||
}
|
||||
|
||||
LOG_DEBUG("free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval);
|
||||
LOG_TARGET_DEBUG(target, "free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval);
|
||||
(*breakpoint_p) = breakpoint->next;
|
||||
free(breakpoint->orig_instr);
|
||||
free(breakpoint);
|
||||
|
@ -445,7 +444,7 @@ static int watchpoint_free(struct target *target, struct watchpoint *watchpoint_
|
|||
return retval;
|
||||
}
|
||||
|
||||
LOG_DEBUG("free WPID: %d --> %d", watchpoint->unique_id, retval);
|
||||
LOG_TARGET_DEBUG(target, "free WPID: %d --> %d", watchpoint->unique_id, retval);
|
||||
(*watchpoint_p) = watchpoint->next;
|
||||
free(watchpoint);
|
||||
|
||||
|
@ -556,7 +555,7 @@ static int watchpoint_add_internal(struct target *target, target_addr_t address,
|
|||
|| watchpoint->value != value
|
||||
|| watchpoint->mask != mask
|
||||
|| watchpoint->rw != rw) {
|
||||
LOG_ERROR("address " TARGET_ADDR_FMT
|
||||
LOG_TARGET_ERROR(target, "address " TARGET_ADDR_FMT
|
||||
" already has watchpoint %d",
|
||||
address, watchpoint->unique_id);
|
||||
return ERROR_FAIL;
|
||||
|
@ -590,7 +589,7 @@ static int watchpoint_add_internal(struct target *target, target_addr_t address,
|
|||
default:
|
||||
reason = "unrecognized error";
|
||||
bye:
|
||||
LOG_ERROR("can't add %s watchpoint at " TARGET_ADDR_FMT ", %s",
|
||||
LOG_TARGET_ERROR(target, "can't add %s watchpoint at " TARGET_ADDR_FMT ", %s",
|
||||
watchpoint_rw_strings[(*watchpoint_p)->rw],
|
||||
address, reason);
|
||||
free(*watchpoint_p);
|
||||
|
@ -598,9 +597,8 @@ bye:
|
|||
return retval;
|
||||
}
|
||||
|
||||
LOG_DEBUG("[%d] added %s watchpoint at " TARGET_ADDR_FMT
|
||||
LOG_TARGET_DEBUG(target, "added %s watchpoint at " TARGET_ADDR_FMT
|
||||
" of length 0x%8.8" PRIx32 " (WPID: %d)",
|
||||
target->coreid,
|
||||
watchpoint_rw_strings[(*watchpoint_p)->rw],
|
||||
(*watchpoint_p)->address,
|
||||
(*watchpoint_p)->length,
|
||||
|
@ -718,7 +716,7 @@ int watchpoint_hit(struct target *target, enum watchpoint_rw *rw,
|
|||
*rw = hit_watchpoint->rw;
|
||||
*address = hit_watchpoint->address;
|
||||
|
||||
LOG_DEBUG("Found hit watchpoint at " TARGET_ADDR_FMT " (WPID: %d)",
|
||||
LOG_TARGET_DEBUG(target, "Found hit watchpoint at " TARGET_ADDR_FMT " (WPID: %d)",
|
||||
hit_watchpoint->address,
|
||||
hit_watchpoint->unique_id);
|
||||
|
||||
|
|
|
@ -2989,29 +2989,29 @@ static int cortex_a_examine_first(struct target *target)
|
|||
armv7a->debug_base + CPUDBG_PRSR, &dbg_osreg);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
LOG_DEBUG("target->coreid %" PRId32 " DBGPRSR 0x%" PRIx32, target->coreid, dbg_osreg);
|
||||
LOG_TARGET_DEBUG(target, "DBGPRSR 0x%" PRIx32, dbg_osreg);
|
||||
|
||||
if ((dbg_osreg & PRSR_POWERUP_STATUS) == 0) {
|
||||
LOG_ERROR("target->coreid %" PRId32 " powered down!", target->coreid);
|
||||
LOG_TARGET_ERROR(target, "powered down!");
|
||||
target->state = TARGET_UNKNOWN; /* TARGET_NO_POWER? */
|
||||
return ERROR_TARGET_INIT_FAILED;
|
||||
}
|
||||
|
||||
if (dbg_osreg & PRSR_STICKY_RESET_STATUS)
|
||||
LOG_DEBUG("target->coreid %" PRId32 " was reset!", target->coreid);
|
||||
LOG_TARGET_DEBUG(target, "was reset!");
|
||||
|
||||
/* Read DBGOSLSR and check if OSLK is implemented */
|
||||
retval = mem_ap_read_atomic_u32(armv7a->debug_ap,
|
||||
armv7a->debug_base + CPUDBG_OSLSR, &dbg_osreg);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
LOG_DEBUG("target->coreid %" PRId32 " DBGOSLSR 0x%" PRIx32, target->coreid, dbg_osreg);
|
||||
LOG_TARGET_DEBUG(target, "DBGOSLSR 0x%" PRIx32, dbg_osreg);
|
||||
|
||||
/* check if OS Lock is implemented */
|
||||
if ((dbg_osreg & OSLSR_OSLM) == OSLSR_OSLM0 || (dbg_osreg & OSLSR_OSLM) == OSLSR_OSLM1) {
|
||||
/* check if OS Lock is set */
|
||||
if (dbg_osreg & OSLSR_OSLK) {
|
||||
LOG_DEBUG("target->coreid %" PRId32 " OSLock set! Trying to unlock", target->coreid);
|
||||
LOG_TARGET_DEBUG(target, "OSLock set! Trying to unlock");
|
||||
|
||||
retval = mem_ap_write_atomic_u32(armv7a->debug_ap,
|
||||
armv7a->debug_base + CPUDBG_OSLAR,
|
||||
|
@ -3022,8 +3022,7 @@ static int cortex_a_examine_first(struct target *target)
|
|||
|
||||
/* if we fail to access the register or cannot reset the OSLK bit, bail out */
|
||||
if (retval != ERROR_OK || (dbg_osreg & OSLSR_OSLK) != 0) {
|
||||
LOG_ERROR("target->coreid %" PRId32 " OSLock sticky, core not powered?",
|
||||
target->coreid);
|
||||
LOG_TARGET_ERROR(target, "OSLock sticky, core not powered?");
|
||||
target->state = TARGET_UNKNOWN; /* TARGET_NO_POWER? */
|
||||
return ERROR_TARGET_INIT_FAILED;
|
||||
}
|
||||
|
@ -3036,13 +3035,11 @@ static int cortex_a_examine_first(struct target *target)
|
|||
return retval;
|
||||
|
||||
if (dbg_idpfr1 & 0x000000f0) {
|
||||
LOG_DEBUG("target->coreid %" PRId32 " has security extensions",
|
||||
target->coreid);
|
||||
LOG_TARGET_DEBUG(target, "has security extensions");
|
||||
armv7a->arm.core_type = ARM_CORE_TYPE_SEC_EXT;
|
||||
}
|
||||
if (dbg_idpfr1 & 0x0000f000) {
|
||||
LOG_DEBUG("target->coreid %" PRId32 " has virtualization extensions",
|
||||
target->coreid);
|
||||
LOG_TARGET_DEBUG(target, "has virtualization extensions");
|
||||
/*
|
||||
* overwrite and simplify the checks.
|
||||
* virtualization extensions require implementation of security extension
|
||||
|
|
|
@ -912,7 +912,7 @@ static int dsp563xx_examine(struct target *target)
|
|||
{
|
||||
uint32_t chip;
|
||||
|
||||
if (target->tap->hasidcode == false) {
|
||||
if (!target->tap->has_idcode) {
|
||||
LOG_ERROR("no IDCODE present on device");
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ noinst_LTLIBRARIES += %D%/libespressif.la
|
|||
%D%/esp_xtensa_semihosting.h \
|
||||
%D%/esp_xtensa_apptrace.c \
|
||||
%D%/esp_xtensa_apptrace.h \
|
||||
%D%/esp_xtensa_algorithm.c \
|
||||
%D%/esp_xtensa_algorithm.h \
|
||||
%D%/esp32_apptrace.c \
|
||||
%D%/esp32_apptrace.h \
|
||||
%D%/esp32.c \
|
||||
|
@ -21,4 +23,6 @@ noinst_LTLIBRARIES += %D%/libespressif.la
|
|||
%D%/esp32_sysview.h \
|
||||
%D%/segger_sysview.h \
|
||||
%D%/esp_semihosting.c \
|
||||
%D%/esp_semihosting.h
|
||||
%D%/esp_semihosting.h \
|
||||
%D%/esp_algorithm.c \
|
||||
%D%/esp_algorithm.h
|
||||
|
|
|
@ -14,6 +14,16 @@
|
|||
#include "target/target.h"
|
||||
#include "esp.h"
|
||||
|
||||
int esp_common_init(struct esp_common *esp, const struct esp_algorithm_hw *algo_hw)
|
||||
{
|
||||
if (!esp)
|
||||
return ERROR_FAIL;
|
||||
|
||||
esp->algo_hw = algo_hw;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_dbgstubs_table_read(struct target *target, struct esp_dbg_stubs *dbg_stubs)
|
||||
{
|
||||
uint32_t table_size, table_start_id, desc_entry_id, gcov_entry_id;
|
||||
|
|
|
@ -77,9 +77,11 @@ struct esp_dbg_stubs {
|
|||
};
|
||||
|
||||
struct esp_common {
|
||||
const struct esp_algorithm_hw *algo_hw;
|
||||
struct esp_dbg_stubs dbg_stubs;
|
||||
};
|
||||
|
||||
int esp_common_init(struct esp_common *esp, const struct esp_algorithm_hw *algo_hw);
|
||||
int esp_dbgstubs_table_read(struct target *target, struct esp_dbg_stubs *dbg_stubs);
|
||||
|
||||
#endif /* OPENOCD_TARGET_ESP_H */
|
||||
|
|
|
@ -484,6 +484,10 @@ struct target_type esp32_target = {
|
|||
.get_gdb_arch = xtensa_get_gdb_arch,
|
||||
.get_gdb_reg_list = xtensa_get_gdb_reg_list,
|
||||
|
||||
.run_algorithm = xtensa_run_algorithm,
|
||||
.start_algorithm = xtensa_start_algorithm,
|
||||
.wait_algorithm = xtensa_wait_algorithm,
|
||||
|
||||
.add_breakpoint = esp_xtensa_breakpoint_add,
|
||||
.remove_breakpoint = esp_xtensa_breakpoint_remove,
|
||||
|
||||
|
|
|
@ -521,6 +521,10 @@ struct target_type esp32s2_target = {
|
|||
.get_gdb_arch = xtensa_get_gdb_arch,
|
||||
.get_gdb_reg_list = xtensa_get_gdb_reg_list,
|
||||
|
||||
.run_algorithm = xtensa_run_algorithm,
|
||||
.start_algorithm = xtensa_start_algorithm,
|
||||
.wait_algorithm = xtensa_wait_algorithm,
|
||||
|
||||
.add_breakpoint = esp_xtensa_breakpoint_add,
|
||||
.remove_breakpoint = esp_xtensa_breakpoint_remove,
|
||||
|
||||
|
|
|
@ -405,6 +405,10 @@ struct target_type esp32s3_target = {
|
|||
.get_gdb_arch = xtensa_get_gdb_arch,
|
||||
.get_gdb_reg_list = xtensa_get_gdb_reg_list,
|
||||
|
||||
.run_algorithm = xtensa_run_algorithm,
|
||||
.start_algorithm = xtensa_start_algorithm,
|
||||
.wait_algorithm = xtensa_wait_algorithm,
|
||||
|
||||
.add_breakpoint = esp_xtensa_breakpoint_add,
|
||||
.remove_breakpoint = esp_xtensa_breakpoint_remove,
|
||||
|
||||
|
|
|
@ -0,0 +1,595 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Espressif chips common algorithm API for OpenOCD *
|
||||
* Copyright (C) 2022 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <helper/align.h>
|
||||
#include <target/algorithm.h>
|
||||
#include <target/target.h>
|
||||
#include "esp_algorithm.h"
|
||||
|
||||
#define DEFAULT_ALGORITHM_TIMEOUT_MS 40000 /* ms */
|
||||
|
||||
static int esp_algorithm_read_stub_logs(struct target *target, struct esp_algorithm_stub *stub)
|
||||
{
|
||||
if (!stub || stub->log_buff_addr == 0 || stub->log_buff_size == 0)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint32_t len = 0;
|
||||
int retval = target_read_u32(target, stub->log_buff_addr, &len);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* sanity check. log_buff_size = sizeof(len) + sizeof(log_buff) */
|
||||
if (len == 0 || len > stub->log_buff_size - 4)
|
||||
return ERROR_FAIL;
|
||||
|
||||
uint8_t *log_buff = calloc(1, len);
|
||||
if (!log_buff) {
|
||||
LOG_ERROR("Failed to allocate memory for the stub log!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
retval = target_read_memory(target, stub->log_buff_addr + 4, 1, len, log_buff);
|
||||
if (retval == ERROR_OK)
|
||||
LOG_OUTPUT("%*.*s", len, len, log_buff);
|
||||
free(log_buff);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int esp_algorithm_run_image(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap)
|
||||
{
|
||||
struct working_area **mem_handles = NULL;
|
||||
|
||||
if (!run || !run->hw)
|
||||
return ERROR_FAIL;
|
||||
|
||||
int retval = run->hw->algo_init(target, run, num_args, ap);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* allocate memory arguments and fill respective reg params */
|
||||
if (run->mem_args.count > 0) {
|
||||
mem_handles = calloc(run->mem_args.count, sizeof(*mem_handles));
|
||||
if (!mem_handles) {
|
||||
LOG_ERROR("Failed to alloc target mem handles!");
|
||||
retval = ERROR_FAIL;
|
||||
goto _cleanup;
|
||||
}
|
||||
/* alloc memory args target buffers */
|
||||
for (uint32_t i = 0; i < run->mem_args.count; i++) {
|
||||
/* small hack: if we need to update some reg param this field holds
|
||||
* appropriate user argument number, */
|
||||
/* otherwise should hold UINT_MAX */
|
||||
uint32_t usr_param_num = run->mem_args.params[i].address;
|
||||
static struct working_area *area;
|
||||
retval = target_alloc_working_area(target, run->mem_args.params[i].size, &area);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to alloc target buffer!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _cleanup;
|
||||
}
|
||||
mem_handles[i] = area;
|
||||
run->mem_args.params[i].address = area->address;
|
||||
if (usr_param_num != UINT_MAX) /* if we need update some register param with mem param value */
|
||||
esp_algorithm_user_arg_set_uint(run, usr_param_num, run->mem_args.params[i].address);
|
||||
}
|
||||
}
|
||||
|
||||
if (run->usr_func_init) {
|
||||
retval = run->usr_func_init(target, run, run->usr_func_arg);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to prepare algorithm host side args stub (%d)!", retval);
|
||||
goto _cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("Algorithm start @ " TARGET_ADDR_FMT ", stack %d bytes @ " TARGET_ADDR_FMT,
|
||||
run->stub.tramp_mapped_addr, run->stack_size, run->stub.stack_addr);
|
||||
retval = target_start_algorithm(target,
|
||||
run->mem_args.count, run->mem_args.params,
|
||||
run->reg_args.count, run->reg_args.params,
|
||||
run->stub.tramp_mapped_addr, 0,
|
||||
run->stub.ainfo);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to start algorithm (%d)!", retval);
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
if (run->usr_func) {
|
||||
/* give target algorithm stub time to init itself, then user func can communicate to it safely */
|
||||
alive_sleep(100);
|
||||
retval = run->usr_func(target, run->usr_func_arg);
|
||||
if (retval != ERROR_OK)
|
||||
LOG_ERROR("Failed to exec algorithm user func (%d)!", retval);
|
||||
}
|
||||
uint32_t timeout_ms = 0; /* do not wait if 'usr_func' returned error */
|
||||
if (retval == ERROR_OK)
|
||||
timeout_ms = run->timeout_ms ? run->timeout_ms : DEFAULT_ALGORITHM_TIMEOUT_MS;
|
||||
LOG_DEBUG("Wait algorithm completion");
|
||||
retval = target_wait_algorithm(target,
|
||||
run->mem_args.count, run->mem_args.params,
|
||||
run->reg_args.count, run->reg_args.params,
|
||||
0, timeout_ms,
|
||||
run->stub.ainfo);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to wait algorithm (%d)!", retval);
|
||||
/* target has been forced to stop in target_wait_algorithm() */
|
||||
}
|
||||
esp_algorithm_read_stub_logs(target, &run->stub);
|
||||
|
||||
if (run->usr_func_done)
|
||||
run->usr_func_done(target, run, run->usr_func_arg);
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Algorithm run failed (%d)!", retval);
|
||||
} else {
|
||||
run->ret_code = esp_algorithm_user_arg_get_uint(run, 0);
|
||||
LOG_DEBUG("Got algorithm RC 0x%" PRIx32, run->ret_code);
|
||||
}
|
||||
|
||||
_cleanup:
|
||||
/* free memory arguments */
|
||||
if (mem_handles) {
|
||||
for (uint32_t i = 0; i < run->mem_args.count; i++) {
|
||||
if (mem_handles[i])
|
||||
target_free_working_area(target, mem_handles[i]);
|
||||
}
|
||||
free(mem_handles);
|
||||
}
|
||||
run->hw->algo_cleanup(target, run);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int esp_algorithm_run_debug_stub(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap)
|
||||
{
|
||||
if (!run || !run->hw)
|
||||
return ERROR_FAIL;
|
||||
|
||||
int retval = run->hw->algo_init(target, run, num_args, ap);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
LOG_DEBUG("Algorithm start @ " TARGET_ADDR_FMT ", stack %d bytes @ " TARGET_ADDR_FMT,
|
||||
run->stub.tramp_mapped_addr, run->stack_size, run->stub.stack_addr);
|
||||
retval = target_start_algorithm(target,
|
||||
run->mem_args.count, run->mem_args.params,
|
||||
run->reg_args.count, run->reg_args.params,
|
||||
run->stub.tramp_mapped_addr, 0,
|
||||
run->stub.ainfo);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to start algorithm (%d)!", retval);
|
||||
goto _cleanup;
|
||||
}
|
||||
|
||||
uint32_t timeout_ms = 0; /* do not wait if 'usr_func' returned error */
|
||||
if (retval == ERROR_OK)
|
||||
timeout_ms = run->timeout_ms ? run->timeout_ms : DEFAULT_ALGORITHM_TIMEOUT_MS;
|
||||
LOG_DEBUG("Wait algorithm completion");
|
||||
retval = target_wait_algorithm(target,
|
||||
run->mem_args.count, run->mem_args.params,
|
||||
run->reg_args.count, run->reg_args.params,
|
||||
0, timeout_ms,
|
||||
run->stub.ainfo);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to wait algorithm (%d)!", retval);
|
||||
/* target has been forced to stop in target_wait_algorithm() */
|
||||
}
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Algorithm run failed (%d)!", retval);
|
||||
} else {
|
||||
run->ret_code = esp_algorithm_user_arg_get_uint(run, 0);
|
||||
LOG_DEBUG("Got algorithm RC 0x%" PRIx32, run->ret_code);
|
||||
}
|
||||
|
||||
_cleanup:
|
||||
run->hw->algo_cleanup(target, run);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void reverse_binary(const uint8_t *src, uint8_t *dest, size_t length)
|
||||
{
|
||||
size_t remaining = length % 4;
|
||||
size_t offset = 0;
|
||||
size_t aligned_len = ALIGN_UP(length, 4);
|
||||
|
||||
if (remaining > 0) {
|
||||
/* Put extra bytes to the beginning with padding */
|
||||
memset(dest + remaining, 0xFF, 4 - remaining);
|
||||
for (size_t i = 0; i < remaining; i++)
|
||||
dest[i] = src[length - remaining + i];
|
||||
length -= remaining; /* reverse the others */
|
||||
offset = 4;
|
||||
}
|
||||
|
||||
for (size_t i = offset; i < aligned_len; i += 4) {
|
||||
dest[i + 0] = src[length - i + offset - 4];
|
||||
dest[i + 1] = src[length - i + offset - 3];
|
||||
dest[i + 2] = src[length - i + offset - 2];
|
||||
dest[i + 3] = src[length - i + offset - 1];
|
||||
}
|
||||
}
|
||||
|
||||
static int load_section_from_image(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
int section_num,
|
||||
bool reverse)
|
||||
{
|
||||
if (!run)
|
||||
return ERROR_FAIL;
|
||||
|
||||
struct imagesection *section = &run->image.image.sections[section_num];
|
||||
uint32_t sec_wr = 0;
|
||||
uint8_t buf[1024];
|
||||
|
||||
assert(sizeof(buf) % 4 == 0);
|
||||
|
||||
while (sec_wr < section->size) {
|
||||
uint32_t nb = section->size - sec_wr > sizeof(buf) ? sizeof(buf) : section->size - sec_wr;
|
||||
size_t size_read = 0;
|
||||
int retval = image_read_section(&run->image.image, section_num, sec_wr, nb, buf, &size_read);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to read stub section (%d)!", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (reverse) {
|
||||
size_t aligned_len = ALIGN_UP(size_read, 4);
|
||||
uint8_t reversed_buf[aligned_len];
|
||||
|
||||
/* Send original size to allow padding */
|
||||
reverse_binary(buf, reversed_buf, size_read);
|
||||
|
||||
/*
|
||||
The address range accessed via the instruction bus is in reverse order (word-wise) compared to access
|
||||
via the data bus. That is to say, address
|
||||
0x3FFE_0000 and 0x400B_FFFC access the same word
|
||||
0x3FFE_0004 and 0x400B_FFF8 access the same word
|
||||
0x3FFE_0008 and 0x400B_FFF4 access the same word
|
||||
...
|
||||
The data bus and instruction bus of the CPU are still both little-endian,
|
||||
so the byte order of individual words is not reversed between address spaces.
|
||||
For example, address
|
||||
0x3FFE_0000 accesses the least significant byte in the word accessed by 0x400B_FFFC.
|
||||
0x3FFE_0001 accesses the second least significant byte in the word accessed by 0x400B_FFFC.
|
||||
0x3FFE_0002 accesses the second most significant byte in the word accessed by 0x400B_FFFC.
|
||||
For more details, please refer to ESP32 TRM, Internal SRAM1 section.
|
||||
*/
|
||||
retval = target_write_buffer(target, run->image.dram_org - sec_wr - aligned_len, aligned_len, reversed_buf);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to write stub section!");
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
retval = target_write_buffer(target, section->base_address + sec_wr, size_read, buf);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to write stub section!");
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
sec_wr += size_read;
|
||||
}
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration:
|
||||
* ----------------------------
|
||||
* The linker scripts defines the memory layout for the stub code.
|
||||
* The OpenOCD script specifies the workarea address and it's size
|
||||
* Sections defined in the linker are organized to share the same addresses with the workarea.
|
||||
* code and data sections are located in Internal SRAM1 and OpenOCD fills these sections using the data bus.
|
||||
*/
|
||||
int esp_algorithm_load_func_image(struct target *target, struct esp_algorithm_run_data *run)
|
||||
{
|
||||
int retval;
|
||||
size_t tramp_sz = 0;
|
||||
const uint8_t *tramp = NULL;
|
||||
struct duration algo_time;
|
||||
bool alloc_code_working_area = true;
|
||||
|
||||
if (!run || !run->hw)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (duration_start(&algo_time) != 0) {
|
||||
LOG_ERROR("Failed to start algo time measurement!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (run->hw->stub_tramp_get) {
|
||||
tramp = run->hw->stub_tramp_get(target, &tramp_sz);
|
||||
if (!tramp)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
LOG_DEBUG("stub: base 0x%x, start 0x%" PRIx32 ", %d sections",
|
||||
run->image.image.base_address_set ? (unsigned int)run->image.image.base_address : 0,
|
||||
run->image.image.start_address,
|
||||
run->image.image.num_sections);
|
||||
run->stub.entry = run->image.image.start_address;
|
||||
|
||||
/* [code + trampoline] + <padding> + [data] */
|
||||
|
||||
/* ESP32 has reversed memory region. It will use the last part of DRAM, the others will use the first part.
|
||||
* To avoid complexity for the backup/restore process, we will allocate a workarea for all IRAM region from
|
||||
* the beginning. In that case no need to have a padding area.
|
||||
*/
|
||||
if (run->image.reverse) {
|
||||
if (target_alloc_working_area(target, run->image.iram_len, &run->stub.code) != ERROR_OK) {
|
||||
LOG_ERROR("no working area available, can't alloc space for stub code!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _on_error;
|
||||
}
|
||||
alloc_code_working_area = false;
|
||||
}
|
||||
|
||||
uint32_t code_size = 0;
|
||||
|
||||
/* Load code section */
|
||||
for (unsigned int i = 0; i < run->image.image.num_sections; i++) {
|
||||
struct imagesection *section = &run->image.image.sections[i];
|
||||
|
||||
if (section->size == 0)
|
||||
continue;
|
||||
|
||||
if (section->flags & ESP_IMAGE_ELF_PHF_EXEC) {
|
||||
LOG_DEBUG("addr " TARGET_ADDR_FMT ", sz %d, flags %" PRIx64,
|
||||
section->base_address, section->size, section->flags);
|
||||
|
||||
if (alloc_code_working_area) {
|
||||
retval = target_alloc_working_area(target, section->size, &run->stub.code);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("no working area available, can't alloc space for stub code!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _on_error;
|
||||
}
|
||||
}
|
||||
|
||||
if (section->base_address == 0) {
|
||||
section->base_address = run->stub.code->address;
|
||||
/* sanity check, stub is compiled to be run from working area */
|
||||
} else if (run->stub.code->address != section->base_address) {
|
||||
LOG_ERROR("working area " TARGET_ADDR_FMT " and stub code section " TARGET_ADDR_FMT
|
||||
" address mismatch!",
|
||||
section->base_address,
|
||||
run->stub.code->address);
|
||||
retval = ERROR_FAIL;
|
||||
goto _on_error;
|
||||
}
|
||||
|
||||
retval = load_section_from_image(target, run, i, run->image.reverse);
|
||||
if (retval != ERROR_OK)
|
||||
goto _on_error;
|
||||
|
||||
code_size += ALIGN_UP(section->size, 4);
|
||||
break; /* Stub has one executable text section */
|
||||
}
|
||||
}
|
||||
|
||||
/* If exists, load trampoline to the code area */
|
||||
if (tramp) {
|
||||
if (run->stub.tramp_addr == 0) {
|
||||
if (alloc_code_working_area) {
|
||||
/* alloc trampoline in code working area */
|
||||
if (target_alloc_working_area(target, tramp_sz, &run->stub.tramp) != ERROR_OK) {
|
||||
LOG_ERROR("no working area available, can't alloc space for stub jumper!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _on_error;
|
||||
}
|
||||
run->stub.tramp_addr = run->stub.tramp->address;
|
||||
}
|
||||
}
|
||||
|
||||
size_t al_tramp_size = ALIGN_UP(tramp_sz, 4);
|
||||
|
||||
if (run->image.reverse) {
|
||||
target_addr_t reversed_tramp_addr = run->image.dram_org - code_size;
|
||||
uint8_t reversed_tramp[al_tramp_size];
|
||||
|
||||
/* Send original size to allow padding */
|
||||
reverse_binary(tramp, reversed_tramp, tramp_sz);
|
||||
run->stub.tramp_addr = reversed_tramp_addr - al_tramp_size;
|
||||
LOG_DEBUG("Write reversed tramp to addr " TARGET_ADDR_FMT ", sz %zu", run->stub.tramp_addr, al_tramp_size);
|
||||
retval = target_write_buffer(target, run->stub.tramp_addr, al_tramp_size, reversed_tramp);
|
||||
} else {
|
||||
LOG_DEBUG("Write tramp to addr " TARGET_ADDR_FMT ", sz %zu", run->stub.tramp_addr, tramp_sz);
|
||||
retval = target_write_buffer(target, run->stub.tramp_addr, tramp_sz, tramp);
|
||||
}
|
||||
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_ERROR("Failed to write stub jumper!");
|
||||
goto _on_error;
|
||||
}
|
||||
|
||||
run->stub.tramp_mapped_addr = run->image.iram_org + code_size;
|
||||
code_size += al_tramp_size;
|
||||
LOG_DEBUG("Tramp mapped to addr " TARGET_ADDR_FMT, run->stub.tramp_mapped_addr);
|
||||
}
|
||||
|
||||
/* allocate dummy space until the data address */
|
||||
if (alloc_code_working_area) {
|
||||
/* we dont need to restore padding area. */
|
||||
uint32_t backup_working_area_prev = target->backup_working_area;
|
||||
target->backup_working_area = 0;
|
||||
if (target_alloc_working_area(target, run->image.iram_len - code_size, &run->stub.padding) != ERROR_OK) {
|
||||
LOG_ERROR("no working area available, can't alloc space for stub code!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _on_error;
|
||||
}
|
||||
target->backup_working_area = backup_working_area_prev;
|
||||
}
|
||||
|
||||
/* Load the data section */
|
||||
for (unsigned int i = 0; i < run->image.image.num_sections; i++) {
|
||||
struct imagesection *section = &run->image.image.sections[i];
|
||||
|
||||
if (section->size == 0)
|
||||
continue;
|
||||
|
||||
if (!(section->flags & ESP_IMAGE_ELF_PHF_EXEC)) {
|
||||
LOG_DEBUG("addr " TARGET_ADDR_FMT ", sz %d, flags %" PRIx64, section->base_address, section->size,
|
||||
section->flags);
|
||||
/* target_alloc_working_area() aligns the whole working area size to 4-byte boundary.
|
||||
We alloc one area for both DATA and BSS, so align each of them ourselves. */
|
||||
uint32_t data_sec_sz = ALIGN_UP(section->size, 4);
|
||||
LOG_DEBUG("DATA sec size %" PRIu32 " -> %" PRIu32, section->size, data_sec_sz);
|
||||
uint32_t bss_sec_sz = ALIGN_UP(run->image.bss_size, 4);
|
||||
LOG_DEBUG("BSS sec size %" PRIu32 " -> %" PRIu32, run->image.bss_size, bss_sec_sz);
|
||||
if (target_alloc_working_area(target, data_sec_sz + bss_sec_sz, &run->stub.data) != ERROR_OK) {
|
||||
LOG_ERROR("no working area available, can't alloc space for stub data!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _on_error;
|
||||
}
|
||||
if (section->base_address == 0) {
|
||||
section->base_address = run->stub.data->address;
|
||||
/* sanity check, stub is compiled to be run from working area */
|
||||
} else if (run->stub.data->address != section->base_address) {
|
||||
LOG_ERROR("working area " TARGET_ADDR_FMT
|
||||
" and stub data section " TARGET_ADDR_FMT
|
||||
" address mismatch!",
|
||||
section->base_address,
|
||||
run->stub.data->address);
|
||||
retval = ERROR_FAIL;
|
||||
goto _on_error;
|
||||
}
|
||||
|
||||
retval = load_section_from_image(target, run, i, false);
|
||||
if (retval != ERROR_OK)
|
||||
goto _on_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* stack */
|
||||
if (run->stub.stack_addr == 0 && run->stack_size > 0) {
|
||||
/* allocate stack in data working area */
|
||||
if (target_alloc_working_area(target, run->stack_size, &run->stub.stack) != ERROR_OK) {
|
||||
LOG_ERROR("no working area available, can't alloc stub stack!");
|
||||
retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
goto _on_error;
|
||||
}
|
||||
run->stub.stack_addr = run->stub.stack->address + run->stack_size;
|
||||
}
|
||||
|
||||
if (duration_measure(&algo_time) != 0) {
|
||||
LOG_ERROR("Failed to stop algo run measurement!");
|
||||
retval = ERROR_FAIL;
|
||||
goto _on_error;
|
||||
}
|
||||
LOG_DEBUG("Stub loaded in %g ms", duration_elapsed(&algo_time) * 1000);
|
||||
return ERROR_OK;
|
||||
|
||||
_on_error:
|
||||
esp_algorithm_unload_func_image(target, run);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int esp_algorithm_unload_func_image(struct target *target, struct esp_algorithm_run_data *run)
|
||||
{
|
||||
if (!run)
|
||||
return ERROR_FAIL;
|
||||
|
||||
target_free_all_working_areas(target);
|
||||
|
||||
run->stub.tramp = NULL;
|
||||
run->stub.stack = NULL;
|
||||
run->stub.code = NULL;
|
||||
run->stub.data = NULL;
|
||||
run->stub.padding = NULL;
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_algorithm_exec_func_image_va(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap)
|
||||
{
|
||||
if (!run || !run->image.image.start_address_set || run->image.image.start_address == 0)
|
||||
return ERROR_FAIL;
|
||||
|
||||
return esp_algorithm_run_image(target, run, num_args, ap);
|
||||
}
|
||||
|
||||
int esp_algorithm_load_onboard_func(struct target *target, target_addr_t func_addr, struct esp_algorithm_run_data *run)
|
||||
{
|
||||
int res;
|
||||
const uint8_t *tramp = NULL;
|
||||
size_t tramp_sz = 0;
|
||||
struct duration algo_time;
|
||||
|
||||
if (!run || !run->hw)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (duration_start(&algo_time) != 0) {
|
||||
LOG_ERROR("Failed to start algo time measurement!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (run->hw->stub_tramp_get) {
|
||||
tramp = run->hw->stub_tramp_get(target, &tramp_sz);
|
||||
if (!tramp)
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (tramp_sz > run->on_board.code_buf_size) {
|
||||
LOG_ERROR("Stub tramp size %zu bytes exceeds target buf size %d bytes!",
|
||||
tramp_sz, run->on_board.code_buf_size);
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (run->stack_size > run->on_board.min_stack_size) {
|
||||
LOG_ERROR("Algorithm stack size not fit into the allocated target stack!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
run->stub.stack_addr = run->on_board.min_stack_addr + run->stack_size;
|
||||
run->stub.tramp_addr = run->on_board.code_buf_addr;
|
||||
run->stub.tramp_mapped_addr = run->stub.tramp_addr;
|
||||
run->stub.entry = func_addr;
|
||||
|
||||
if (tramp) {
|
||||
res = target_write_buffer(target, run->stub.tramp_addr, tramp_sz, tramp);
|
||||
if (res != ERROR_OK) {
|
||||
LOG_ERROR("Failed to write stub jumper!");
|
||||
esp_algorithm_unload_onboard_func(target, run);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (duration_measure(&algo_time) != 0) {
|
||||
LOG_ERROR("Failed to stop algo run measurement!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
LOG_DEBUG("Stub loaded in %g ms", duration_elapsed(&algo_time) * 1000);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_algorithm_unload_onboard_func(struct target *target, struct esp_algorithm_run_data *run)
|
||||
{
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_algorithm_exec_onboard_func_va(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap)
|
||||
{
|
||||
return esp_algorithm_run_debug_stub(target, run, num_args, ap);
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/***************************************************************************
|
||||
* Espressif chips common algorithm API for OpenOCD *
|
||||
* Copyright (C) 2022 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_TARGET_ESP_ALGORITHM_H
|
||||
#define OPENOCD_TARGET_ESP_ALGORITHM_H
|
||||
|
||||
#include "helper/log.h"
|
||||
#include "helper/binarybuffer.h"
|
||||
#include <helper/time_support.h>
|
||||
#include <target/algorithm.h>
|
||||
#include <target/image.h>
|
||||
|
||||
/**
|
||||
* API defined below allows executing pieces of code on target without breaking the execution of the running program.
|
||||
* This functionality can be useful for various debugging and maintenance procedures.
|
||||
* @note ESP flashing code to load flasher stub on target and write/read/erase flash.
|
||||
* Also ESP GCOV command uses some of these functions to run onboard routines to dump coverage info.
|
||||
* Stub entry function can take up to 5 arguments and should be of the following form:
|
||||
*
|
||||
* int stub_entry([uint32_t a1 [, uint32_t a2 [, uint32_t a3 [, uint32_t a4 [, uint32_t a5]]]]]);
|
||||
*
|
||||
* The general scheme of stub code execution is shown below.
|
||||
*
|
||||
* ------- ----------- (initial frame) ----
|
||||
* | | -------(registers, stub entry, stub args)------> |trampoline | ---(stub args)---> | |
|
||||
* | | | | | |
|
||||
* |OpenOCD| <----------(stub-specific communications)---------------------------------------> |stub|
|
||||
* | | | | | |
|
||||
* | | <---------(target halted event, ret code)------- |tramp break| <---(ret code)---- | |
|
||||
* ------- ----------- ----
|
||||
*
|
||||
* Procedure of executing stub on target includes:
|
||||
* 1) User prepares struct esp_algorithm_run_data and calls one of algorithm_run_xxx() functions.
|
||||
* 2) Routine allocates all necessary stub code and data sections.
|
||||
* 3) If a user specifies an initializer func esp_algorithm_usr_func_init_t it is called just before the stub starts.
|
||||
* 4) If user specifies stub communication func esp_algorithm_usr_func_t (@see esp_flash_write/read in ESP flash driver)
|
||||
* it is called just after the stub starts. When communication with stub is finished this function must return.
|
||||
* 5) OpenOCD waits for the stub to finish (hit exit breakpoint).
|
||||
* 6) If the user specified arguments cleanup func esp_algorithm_usr_func_done_t,
|
||||
* it is called just after the stub finishes.
|
||||
*
|
||||
* There are two options to run code on target under OpenOCD control:
|
||||
* - Run externally compiled stub code.
|
||||
* - Run onboard pre-compiled code. @note For ESP chips debug stubs must be enabled in target code @see ESP IDF docs.
|
||||
* The main difference between the execution of external stub code and target built-in functions is that
|
||||
* in the latter case working areas can not be used to allocate target memory for code and data because they can overlap
|
||||
* with code and data involved in onboard function execution. For example, if memory allocated in the working area
|
||||
* for the stub stack will overlap with some on-board data used by the stub the stack will get overwritten.
|
||||
* The same stands for allocations in target code space.
|
||||
*
|
||||
* External Code Execution
|
||||
* -----------------------
|
||||
* To run external code on the target user should use esp_algorithm_run_func_image().
|
||||
* In this case all necessary memory (code/data) is allocated in working areas that have fixed configuration
|
||||
* defined in target TCL file. Stub code is actually a standalone program, so all its segments must have known
|
||||
* addresses due to position-dependent code nature. So stub must be linked in such a way that its code segment
|
||||
* starts at the beginning of the working area for code space defined in TCL. The same restriction must be applied
|
||||
* to stub's data segment and base addresses of working area for data space. @see ESP stub flasher LD scripts.
|
||||
* Also in order to simplify memory allocation BSS section must follow the DATA section in the stub image.
|
||||
* The size of the BSS section must be specified in the bss_size field of struct algorithm_image.
|
||||
* Sample stub memory map is shown below.
|
||||
* ___________________________________________
|
||||
* | data space working area start |
|
||||
* | |
|
||||
* | <stub .data segment> |
|
||||
* |___________________________________________|
|
||||
* | stub .bss start |
|
||||
* | |
|
||||
* | <stub .bss segment of size 'bss_size'> |
|
||||
* |___________________________________________|
|
||||
* | stub stack base |
|
||||
* | |
|
||||
* | <stub stack> |
|
||||
* |___________________________________________|
|
||||
* | |
|
||||
* | <stub mem arg1> |
|
||||
* |___________________________________________|
|
||||
* | |
|
||||
* | <stub mem arg2> |
|
||||
* |___________________________________________|
|
||||
* ___________________________________________
|
||||
* | code space working area start |
|
||||
* | |
|
||||
* | <stub .text segment> |
|
||||
* |___________________________________________|
|
||||
* | |
|
||||
* | <stub trampoline with exit breakpoint> |
|
||||
* |___________________________________________|
|
||||
*
|
||||
* For example on how to execute external code with memory arguments @see esp_algo_flash_blank_check in
|
||||
* ESP flash driver.
|
||||
*
|
||||
* On-Board Code Execution
|
||||
* -----------------------
|
||||
* To run on-board code on the target user should use esp_algorithm_run_onboard_func().
|
||||
* On-board code execution process does not need to allocate target memory for stub code and data,
|
||||
* Because the stub is pre-compiled to the code running on the target.
|
||||
* But it still needs memory for stub trampoline, stack, and memory arguments.
|
||||
* Working areas can not be used due to possible memory layout conflicts with on-board stub code and data.
|
||||
* Debug stubs functionality provided by ESP IDF allows OpenOCD to overcome the above problem.
|
||||
* It provides a special descriptor which provides info necessary to safely allocate memory on target.
|
||||
* @see struct esp_dbg_stubs_desc.
|
||||
* That info is also used to locate memory for stub trampoline code.
|
||||
* User can execute target function at any address, but @see ESP IDF debug stubs also provide a way to pass to the host
|
||||
* an entry address of pre-defined registered stub functions.
|
||||
* For example of an on-board code execution @see esp32_cmd_gcov() in ESP32 apptrace module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Algorithm image data.
|
||||
* Helper struct to work with algorithms consisting of code and data segments.
|
||||
*/
|
||||
struct esp_algorithm_image {
|
||||
/** Image. */
|
||||
struct image image;
|
||||
/** BSS section size. */
|
||||
uint32_t bss_size;
|
||||
/** IRAM start address in the linker script */
|
||||
uint32_t iram_org;
|
||||
/** Total reserved IRAM size */
|
||||
uint32_t iram_len;
|
||||
/** DRAM start address in the linker script */
|
||||
uint32_t dram_org;
|
||||
/** Total reserved DRAM size */
|
||||
uint32_t dram_len;
|
||||
/** IRAM DRAM address range reversed or not */
|
||||
bool reverse;
|
||||
};
|
||||
|
||||
#define ESP_IMAGE_ELF_PHF_EXEC 0x1
|
||||
|
||||
/**
|
||||
* Algorithm stub data.
|
||||
*/
|
||||
struct esp_algorithm_stub {
|
||||
/** Entry addr. */
|
||||
target_addr_t entry;
|
||||
/** Working area for code segment. */
|
||||
struct working_area *code;
|
||||
/** Working area for data segment. */
|
||||
struct working_area *data;
|
||||
/** Working area for trampoline. */
|
||||
struct working_area *tramp;
|
||||
/** Working area for padding between code and data area. */
|
||||
struct working_area *padding;
|
||||
/** Address of the target buffer for stub trampoline. If zero tramp->address will be used. */
|
||||
target_addr_t tramp_addr;
|
||||
/** Tramp code area will be filled from dbus.
|
||||
* We need to map it to the ibus to be able to initialize PC register to start algorithm execution from.
|
||||
*/
|
||||
target_addr_t tramp_mapped_addr;
|
||||
/** Working area for stack. */
|
||||
struct working_area *stack;
|
||||
/** Address of the target buffer for stack. If zero tramp->address will be used. */
|
||||
target_addr_t stack_addr;
|
||||
/** Address of the log buffer */
|
||||
target_addr_t log_buff_addr;
|
||||
/** Size of the log buffer */
|
||||
uint32_t log_buff_size;
|
||||
/** Algorithm's arch-specific info. */
|
||||
void *ainfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* Algorithm stub in-memory arguments.
|
||||
*/
|
||||
struct esp_algorithm_mem_args {
|
||||
/** Memory params. */
|
||||
struct mem_param *params;
|
||||
/** Number of memory params. */
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Algorithm stub register arguments.
|
||||
*/
|
||||
struct esp_algorithm_reg_args {
|
||||
/** Algorithm register params. User args start from user_first_reg_param */
|
||||
struct reg_param *params;
|
||||
/** Number of register params. */
|
||||
uint32_t count;
|
||||
/** The first several reg_params can be used by stub itself (e.g. for trampoline).
|
||||
* This is the index of the first reg_param available for user to pass args to algorithm stub. */
|
||||
uint32_t first_user_param;
|
||||
};
|
||||
|
||||
struct esp_algorithm_run_data;
|
||||
|
||||
/**
|
||||
* @brief Algorithm run function.
|
||||
*
|
||||
* @param target Pointer to target.
|
||||
* @param run Pointer to algo run data.
|
||||
* @param arg Function specific argument.
|
||||
*
|
||||
* @return ERROR_OK on success, otherwise ERROR_XXX.
|
||||
*/
|
||||
typedef int (*esp_algorithm_func_t)(struct target *target, struct esp_algorithm_run_data *run, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Host part of algorithm.
|
||||
* This function will be called while stub is running on target.
|
||||
* It can be used for communication with stub.
|
||||
*
|
||||
* @param target Pointer to target.
|
||||
* @param usr_arg Function specific argument.
|
||||
*
|
||||
* @return ERROR_OK on success, otherwise ERROR_XXX.
|
||||
*/
|
||||
typedef int (*esp_algorithm_usr_func_t)(struct target *target, void *usr_arg);
|
||||
|
||||
/**
|
||||
* @brief Algorithm's arguments setup function.
|
||||
* This function will be called just before stub start.
|
||||
* It must return when all operations with running stub are completed.
|
||||
* It can be used to prepare stub memory parameters.
|
||||
*
|
||||
* @param target Pointer to target.
|
||||
* @param run Pointer to algo run data.
|
||||
* @param usr_arg Function specific argument. The same as for esp_algorithm_usr_func_t.
|
||||
*
|
||||
* @return ERROR_OK on success, otherwise ERROR_XXX.
|
||||
*/
|
||||
typedef int (*esp_algorithm_usr_func_init_t)(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
void *usr_arg);
|
||||
|
||||
/**
|
||||
* @brief Algorithm's arguments cleanup function.
|
||||
* This function will be called just after stub exit.
|
||||
* It can be used to cleanup stub memory parameters.
|
||||
*
|
||||
* @param target Pointer to target.
|
||||
* @param run Pointer to algo run data.
|
||||
* @param usr_arg Function specific argument. The same as for esp_algorithm_usr_func_t.
|
||||
*
|
||||
* @return ERROR_OK on success, otherwise ERROR_XXX.
|
||||
*/
|
||||
typedef void (*esp_algorithm_usr_func_done_t)(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
void *usr_arg);
|
||||
|
||||
struct esp_algorithm_hw {
|
||||
int (*algo_init)(struct target *target, struct esp_algorithm_run_data *run, uint32_t num_args, va_list ap);
|
||||
int (*algo_cleanup)(struct target *target, struct esp_algorithm_run_data *run);
|
||||
const uint8_t *(*stub_tramp_get)(struct target *target, size_t *size);
|
||||
};
|
||||
|
||||
/**
|
||||
* Algorithm run data.
|
||||
*/
|
||||
struct esp_algorithm_run_data {
|
||||
/** Algorithm completion timeout in ms. If 0, default value will be used */
|
||||
uint32_t timeout_ms;
|
||||
/** Algorithm stack size. */
|
||||
uint32_t stack_size;
|
||||
/** Algorithm register arguments. */
|
||||
struct esp_algorithm_reg_args reg_args;
|
||||
/** Algorithm memory arguments. */
|
||||
struct esp_algorithm_mem_args mem_args;
|
||||
/** Algorithm arch-specific info. For Xtensa this should point to struct xtensa_algorithm. */
|
||||
void *arch_info;
|
||||
/** Algorithm return code. */
|
||||
int32_t ret_code;
|
||||
/** Stub. */
|
||||
struct esp_algorithm_stub stub;
|
||||
union {
|
||||
struct {
|
||||
/** Size of the pre-alocated on-board buffer for stub's code. */
|
||||
uint32_t code_buf_size;
|
||||
/** Address of pre-compiled target buffer for stub trampoline. */
|
||||
target_addr_t code_buf_addr;
|
||||
/** Size of the pre-alocated on-board buffer for stub's stack. */
|
||||
uint32_t min_stack_size;
|
||||
/** Pre-compiled target buffer's addr for stack. */
|
||||
target_addr_t min_stack_addr;
|
||||
} on_board;
|
||||
struct esp_algorithm_image image;
|
||||
};
|
||||
/** Host side algorithm function argument. */
|
||||
void *usr_func_arg;
|
||||
/** Host side algorithm function. */
|
||||
esp_algorithm_usr_func_t usr_func;
|
||||
/** Host side algorithm function setup routine. */
|
||||
esp_algorithm_usr_func_init_t usr_func_init;
|
||||
/** Host side algorithm function cleanup routine. */
|
||||
esp_algorithm_usr_func_done_t usr_func_done;
|
||||
/** Algorithm run function: see algorithm_run_xxx for example. */
|
||||
esp_algorithm_func_t algo_func;
|
||||
/** HW specific API */
|
||||
const struct esp_algorithm_hw *hw;
|
||||
};
|
||||
|
||||
int esp_algorithm_load_func_image(struct target *target, struct esp_algorithm_run_data *run);
|
||||
int esp_algorithm_unload_func_image(struct target *target, struct esp_algorithm_run_data *run);
|
||||
|
||||
int esp_algorithm_exec_func_image_va(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap);
|
||||
|
||||
/**
|
||||
* @brief Loads and runs stub from specified image.
|
||||
* This function should be used to run external stub code on target.
|
||||
*
|
||||
* @param target Pointer to target.
|
||||
* @param run Pointer to algo run data.
|
||||
* @param num_args Number of stub arguments that follow.
|
||||
*
|
||||
* @return ERROR_OK on success, otherwise ERROR_XXX. Stub return code is in run->ret_code.
|
||||
*/
|
||||
static inline int esp_algorithm_run_func_image_va(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap)
|
||||
{
|
||||
int ret = esp_algorithm_load_func_image(target, run);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
ret = esp_algorithm_exec_func_image_va(target, run, num_args, ap);
|
||||
int rc = esp_algorithm_unload_func_image(target, run);
|
||||
return ret != ERROR_OK ? ret : rc;
|
||||
}
|
||||
|
||||
static inline int esp_algorithm_run_func_image(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, num_args);
|
||||
int retval = esp_algorithm_run_func_image_va(target, run, num_args, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int esp_algorithm_load_onboard_func(struct target *target,
|
||||
target_addr_t func_addr,
|
||||
struct esp_algorithm_run_data *run);
|
||||
int esp_algorithm_unload_onboard_func(struct target *target, struct esp_algorithm_run_data *run);
|
||||
int esp_algorithm_exec_onboard_func_va(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args,
|
||||
va_list ap);
|
||||
|
||||
/**
|
||||
* @brief Runs pre-compiled on-board function.
|
||||
* This function should be used to run on-board stub code.
|
||||
*
|
||||
* @param target Pointer to target.
|
||||
* @param run Pointer to algo run data.
|
||||
* @param func_entry Address of the function to run.
|
||||
* @param num_args Number of function arguments that follow.
|
||||
*
|
||||
* @return ERROR_OK on success, otherwise ERROR_XXX. Stub return code is in run->ret_code.
|
||||
*/
|
||||
static inline int esp_algorithm_run_onboard_func_va(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
target_addr_t func_addr,
|
||||
uint32_t num_args,
|
||||
va_list ap)
|
||||
{
|
||||
int ret = esp_algorithm_load_onboard_func(target, func_addr, run);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
ret = esp_algorithm_exec_onboard_func_va(target, run, num_args, ap);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
return esp_algorithm_unload_onboard_func(target, run);
|
||||
}
|
||||
|
||||
static inline int esp_algorithm_run_onboard_func(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
target_addr_t func_addr,
|
||||
uint32_t num_args,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, num_args);
|
||||
int retval = esp_algorithm_run_onboard_func_va(target, run, func_addr, num_args, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the value of an argument passed via registers to the stub main function.
|
||||
*/
|
||||
static inline void esp_algorithm_user_arg_set_uint(struct esp_algorithm_run_data *run,
|
||||
int arg_num,
|
||||
uint64_t val)
|
||||
{
|
||||
struct reg_param *param = &run->reg_args.params[run->reg_args.first_user_param + arg_num];
|
||||
|
||||
assert(param->size <= 64);
|
||||
|
||||
if (param->size <= 32)
|
||||
buf_set_u32(param->value, 0, param->size, val);
|
||||
else
|
||||
buf_set_u64(param->value, 0, param->size, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value of an argument passed via registers from the stub main function.
|
||||
*/
|
||||
static inline uint64_t esp_algorithm_user_arg_get_uint(struct esp_algorithm_run_data *run, int arg_num)
|
||||
{
|
||||
struct reg_param *param = &run->reg_args.params[run->reg_args.first_user_param + arg_num];
|
||||
|
||||
assert(param->size <= 64);
|
||||
|
||||
if (param->size <= 32)
|
||||
return buf_get_u32(param->value, 0, param->size);
|
||||
return buf_get_u64(param->value, 0, param->size);
|
||||
}
|
||||
|
||||
#endif /* OPENOCD_TARGET_ESP_ALGORITHM_H */
|
|
@ -12,10 +12,12 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <target/smp.h>
|
||||
#include "esp_xtensa_apptrace.h"
|
||||
#include <target/register.h>
|
||||
#include "esp.h"
|
||||
#include "esp_xtensa.h"
|
||||
#include "esp_xtensa_apptrace.h"
|
||||
#include "esp_semihosting.h"
|
||||
#include "esp_xtensa_algorithm.h"
|
||||
|
||||
#define ESP_XTENSA_DBGSTUBS_UPDATE_DATA_ENTRY(_e_) \
|
||||
do { \
|
||||
|
@ -68,6 +70,10 @@ int esp_xtensa_init_arch_info(struct target *target,
|
|||
int ret = xtensa_init_arch_info(target, &esp_xtensa->xtensa, dm_cfg);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
ret = esp_common_init(&esp_xtensa->esp, &xtensa_algo_hw);
|
||||
if (ret != ERROR_OK)
|
||||
return ret;
|
||||
|
||||
esp_xtensa->semihost.ops = (struct esp_semihost_ops *)semihost_ops;
|
||||
esp_xtensa->apptrace.hw = &esp_xtensa_apptrace_hw;
|
||||
return ERROR_OK;
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
/***************************************************************************
|
||||
* Module to run arbitrary code on Xtensa using OpenOCD *
|
||||
* Copyright (C) 2019 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <target/xtensa/xtensa.h>
|
||||
#include "esp_xtensa_algorithm.h"
|
||||
|
||||
static int esp_xtensa_algo_init(struct target *target, struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args, va_list ap);
|
||||
static int esp_xtensa_algo_cleanup(struct target *target, struct esp_algorithm_run_data *run);
|
||||
static const uint8_t *esp_xtensa_stub_tramp_get(struct target *target, size_t *size);
|
||||
|
||||
const struct esp_algorithm_hw xtensa_algo_hw = {
|
||||
.algo_init = esp_xtensa_algo_init,
|
||||
.algo_cleanup = esp_xtensa_algo_cleanup,
|
||||
.stub_tramp_get = esp_xtensa_stub_tramp_get,
|
||||
};
|
||||
|
||||
/* Generated from contrib/loaders/trampoline/espressif/xtensa/esp_xtensa_stub_tramp_win.S */
|
||||
static const uint8_t esp_xtensa_stub_tramp_win[] = {
|
||||
#include "../../../contrib/loaders/trampoline/espressif/xtensa/esp_xtensa_stub_tramp_win.inc"
|
||||
};
|
||||
|
||||
static const uint8_t *esp_xtensa_stub_tramp_get(struct target *target, size_t *size)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
||||
if (!xtensa->core_config->windowed) {
|
||||
LOG_ERROR("Running stubs is not supported for cores without windowed registers option!");
|
||||
return NULL;
|
||||
}
|
||||
*size = sizeof(esp_xtensa_stub_tramp_win);
|
||||
return esp_xtensa_stub_tramp_win;
|
||||
}
|
||||
|
||||
static int esp_xtensa_algo_regs_init_start(struct target *target, struct esp_algorithm_run_data *run)
|
||||
{
|
||||
uint32_t stack_addr = run->stub.stack_addr;
|
||||
|
||||
LOG_TARGET_DEBUG(target, "Check stack addr 0x%x", stack_addr);
|
||||
if (stack_addr & 0xFUL) {
|
||||
stack_addr &= ~0xFUL;
|
||||
LOG_TARGET_DEBUG(target, "Adjust stack addr to 0x%x", stack_addr);
|
||||
}
|
||||
stack_addr -= 16;
|
||||
struct reg_param *params = run->reg_args.params;
|
||||
init_reg_param(¶ms[0], "a0", 32, PARAM_OUT); /*TODO: move to tramp */
|
||||
init_reg_param(¶ms[1], "a1", 32, PARAM_OUT);
|
||||
init_reg_param(¶ms[2], "a8", 32, PARAM_OUT);
|
||||
init_reg_param(¶ms[3], "windowbase", 32, PARAM_OUT); /*TODO: move to tramp */
|
||||
init_reg_param(¶ms[4], "windowstart", 32, PARAM_OUT); /*TODO: move to tramp */
|
||||
init_reg_param(¶ms[5], "ps", 32, PARAM_OUT);
|
||||
buf_set_u32(params[0].value, 0, 32, 0); /* a0 TODO: move to tramp */
|
||||
buf_set_u32(params[1].value, 0, 32, stack_addr); /* a1 */
|
||||
buf_set_u32(params[2].value, 0, 32, run->stub.entry); /* a8 */
|
||||
buf_set_u32(params[3].value, 0, 32, 0x0); /* initial window base TODO: move to tramp */
|
||||
buf_set_u32(params[4].value, 0, 32, 0x1); /* initial window start TODO: move to tramp */
|
||||
buf_set_u32(params[5].value, 0, 32, 0x60025); /* enable WOE, UM and debug interrupts level (6) */
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_algo_init(struct target *target, struct esp_algorithm_run_data *run,
|
||||
uint32_t num_args, va_list ap)
|
||||
{
|
||||
enum xtensa_mode core_mode = XT_MODE_ANY;
|
||||
static const char *const arg_regs[] = { "a2", "a3", "a4", "a5", "a6" };
|
||||
|
||||
if (!run)
|
||||
return ERROR_FAIL;
|
||||
|
||||
if (num_args > ARRAY_SIZE(arg_regs)) {
|
||||
LOG_ERROR("Too many algo user args %u! Max %zu args are supported.", num_args, ARRAY_SIZE(arg_regs));
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
struct xtensa_algorithm *ainfo = calloc(1, sizeof(struct xtensa_algorithm));
|
||||
if (!ainfo) {
|
||||
LOG_ERROR("Unable to allocate memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
if (run->arch_info) {
|
||||
struct xtensa_algorithm *xtensa_algo = run->arch_info;
|
||||
core_mode = xtensa_algo->core_mode;
|
||||
}
|
||||
|
||||
run->reg_args.first_user_param = ESP_XTENSA_STUB_ARGS_FUNC_START;
|
||||
run->reg_args.count = run->reg_args.first_user_param + num_args;
|
||||
if (num_args == 0)
|
||||
run->reg_args.count++; /* a2 reg is used as the 1st arg and return code */
|
||||
LOG_DEBUG("reg params count %d (%d/%d).",
|
||||
run->reg_args.count,
|
||||
run->reg_args.first_user_param,
|
||||
num_args);
|
||||
run->reg_args.params = calloc(run->reg_args.count, sizeof(struct reg_param));
|
||||
if (!run->reg_args.params) {
|
||||
free(ainfo);
|
||||
LOG_ERROR("Unable to allocate memory");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
esp_xtensa_algo_regs_init_start(target, run);
|
||||
|
||||
init_reg_param(&run->reg_args.params[run->reg_args.first_user_param + 0], "a2", 32, PARAM_IN_OUT);
|
||||
|
||||
if (num_args > 0) {
|
||||
uint32_t arg = va_arg(ap, uint32_t);
|
||||
esp_algorithm_user_arg_set_uint(run, 0, arg);
|
||||
LOG_DEBUG("Set arg[0] = %d (%s)", arg, run->reg_args.params[run->reg_args.first_user_param + 0].reg_name);
|
||||
} else {
|
||||
esp_algorithm_user_arg_set_uint(run, 0, 0);
|
||||
}
|
||||
|
||||
for (unsigned int i = 1; i < num_args; i++) {
|
||||
uint32_t arg = va_arg(ap, uint32_t);
|
||||
init_reg_param(&run->reg_args.params[run->reg_args.first_user_param + i], (char *)arg_regs[i], 32, PARAM_OUT);
|
||||
esp_algorithm_user_arg_set_uint(run, i, arg);
|
||||
LOG_DEBUG("Set arg[%d] = %d (%s)", i, arg, run->reg_args.params[run->reg_args.first_user_param + i].reg_name);
|
||||
}
|
||||
|
||||
ainfo->core_mode = core_mode;
|
||||
run->stub.ainfo = ainfo;
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
static int esp_xtensa_algo_cleanup(struct target *target, struct esp_algorithm_run_data *run)
|
||||
{
|
||||
free(run->stub.ainfo);
|
||||
for (uint32_t i = 0; i < run->reg_args.count; i++)
|
||||
destroy_reg_param(&run->reg_args.params[i]);
|
||||
free(run->reg_args.params);
|
||||
return ERROR_OK;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/***************************************************************************
|
||||
* Module to run arbitrary code on Xtensa using OpenOCD *
|
||||
* Copyright (C) 2019 Espressif Systems Ltd. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef OPENOCD_TARGET_ESP_XTENSA_ALGO_H
|
||||
#define OPENOCD_TARGET_ESP_XTENSA_ALGO_H
|
||||
|
||||
#include <target/xtensa/xtensa.h>
|
||||
#include <target/espressif/esp_algorithm.h>
|
||||
|
||||
/** Index of the first user-defined algo arg. @see algorithm_stub */
|
||||
#define ESP_XTENSA_STUB_ARGS_FUNC_START 6
|
||||
|
||||
extern const struct esp_algorithm_hw xtensa_algo_hw;
|
||||
|
||||
#endif /* OPENOCD_TARGET_XTENSA_ALGO_H */
|
|
@ -16,6 +16,7 @@
|
|||
#include <target/semihosting_common.h>
|
||||
#include "esp_xtensa_smp.h"
|
||||
#include "esp_xtensa_semihosting.h"
|
||||
#include "esp_algorithm.h"
|
||||
|
||||
/*
|
||||
Multiprocessor stuff common:
|
||||
|
@ -495,6 +496,83 @@ int esp_xtensa_smp_watchpoint_remove(struct target *target, struct watchpoint *w
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int esp_xtensa_smp_run_func_image(struct target *target, struct esp_algorithm_run_data *run, uint32_t num_args, ...)
|
||||
{
|
||||
struct target *run_target = target;
|
||||
struct target_list *head;
|
||||
va_list ap;
|
||||
uint32_t smp_break = 0;
|
||||
int res;
|
||||
|
||||
if (target->smp) {
|
||||
/* find first HALTED and examined core */
|
||||
foreach_smp_target(head, target->smp_targets) {
|
||||
run_target = head->target;
|
||||
if (target_was_examined(run_target) && run_target->state == TARGET_HALTED)
|
||||
break;
|
||||
}
|
||||
if (!head) {
|
||||
LOG_ERROR("Failed to find HALTED core!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
res = esp_xtensa_smp_smpbreak_disable(run_target, &smp_break);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
va_start(ap, num_args);
|
||||
int algo_res = esp_algorithm_run_func_image_va(run_target, run, num_args, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (target->smp) {
|
||||
res = esp_xtensa_smp_smpbreak_restore(run_target, smp_break);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
return algo_res;
|
||||
}
|
||||
|
||||
int esp_xtensa_smp_run_onboard_func(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t func_addr,
|
||||
uint32_t num_args,
|
||||
...)
|
||||
{
|
||||
struct target *run_target = target;
|
||||
struct target_list *head;
|
||||
va_list ap;
|
||||
uint32_t smp_break = 0;
|
||||
int res;
|
||||
|
||||
if (target->smp) {
|
||||
/* find first HALTED and examined core */
|
||||
foreach_smp_target(head, target->smp_targets) {
|
||||
run_target = head->target;
|
||||
if (target_was_examined(run_target) && run_target->state == TARGET_HALTED)
|
||||
break;
|
||||
}
|
||||
if (!head) {
|
||||
LOG_ERROR("Failed to find HALTED core!");
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
res = esp_xtensa_smp_smpbreak_disable(run_target, &smp_break);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
va_start(ap, num_args);
|
||||
int algo_res = esp_algorithm_run_onboard_func_va(run_target, run, func_addr, num_args, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (target->smp) {
|
||||
res = esp_xtensa_smp_smpbreak_restore(run_target, smp_break);
|
||||
if (res != ERROR_OK)
|
||||
return res;
|
||||
}
|
||||
return algo_res;
|
||||
}
|
||||
|
||||
int esp_xtensa_smp_init_arch_info(struct target *target,
|
||||
struct esp_xtensa_smp_common *esp_xtensa_smp,
|
||||
struct xtensa_debug_module_config *dm_cfg,
|
||||
|
@ -746,7 +824,7 @@ COMMAND_HANDLER(esp_xtensa_smp_cmd_perfmon_dump)
|
|||
struct target *curr;
|
||||
foreach_smp_target(head, target->smp_targets) {
|
||||
curr = head->target;
|
||||
LOG_INFO("CPU%d:", curr->coreid);
|
||||
LOG_TARGET_INFO(curr, ":");
|
||||
int ret = CALL_COMMAND_HANDLER(xtensa_cmd_perfmon_dump_do,
|
||||
target_to_xtensa(curr));
|
||||
if (ret != ERROR_OK)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#define OPENOCD_TARGET_XTENSA_ESP_SMP_H
|
||||
|
||||
#include "esp_xtensa.h"
|
||||
#include "esp_algorithm.h"
|
||||
|
||||
struct esp_xtensa_smp_chip_ops {
|
||||
int (*poll)(struct target *target);
|
||||
|
@ -47,7 +48,12 @@ int esp_xtensa_smp_init_arch_info(struct target *target,
|
|||
struct xtensa_debug_module_config *dm_cfg,
|
||||
const struct esp_xtensa_smp_chip_ops *chip_ops,
|
||||
const struct esp_semihost_ops *semihost_ops);
|
||||
|
||||
int esp_xtensa_smp_run_func_image(struct target *target, struct esp_algorithm_run_data *run, uint32_t num_args, ...);
|
||||
int esp_xtensa_smp_run_onboard_func(struct target *target,
|
||||
struct esp_algorithm_run_data *run,
|
||||
uint32_t func_addr,
|
||||
uint32_t num_args,
|
||||
...);
|
||||
extern const struct command_registration esp_xtensa_smp_command_handlers[];
|
||||
extern const struct command_registration esp_xtensa_smp_xtensa_command_handlers[];
|
||||
extern const struct command_registration esp_xtensa_smp_esp_command_handlers[];
|
||||
|
|
|
@ -380,7 +380,8 @@ struct mips32_algorithm {
|
|||
#define MIPS32_OP_XORI 0x0Eu
|
||||
#define MIPS32_OP_XOR 0x26u
|
||||
#define MIPS32_OP_SLTU 0x2Bu
|
||||
#define MIPS32_OP_SRL 0x03u
|
||||
#define MIPS32_OP_SRL 0x02u
|
||||
#define MIPS32_OP_SRA 0x03u
|
||||
#define MIPS32_OP_SYNCI 0x1Fu
|
||||
#define MIPS32_OP_SLL 0x00u
|
||||
#define MIPS32_OP_SLTI 0x0Au
|
||||
|
@ -439,7 +440,8 @@ struct mips32_algorithm {
|
|||
#define MIPS32_ISA_SLL(dst, src, sa) MIPS32_R_INST(MIPS32_OP_SPECIAL, 0, src, dst, sa, MIPS32_OP_SLL)
|
||||
#define MIPS32_ISA_SLTI(tar, src, val) MIPS32_I_INST(MIPS32_OP_SLTI, src, tar, val)
|
||||
#define MIPS32_ISA_SLTU(dst, src, tar) MIPS32_R_INST(MIPS32_OP_SPECIAL, src, tar, dst, 0, MIPS32_OP_SLTU)
|
||||
#define MIPS32_ISA_SRL(reg, src, off) MIPS32_R_INST(0, 0, src, reg, off, MIPS32_OP_SRL)
|
||||
#define MIPS32_ISA_SRA(reg, src, off) MIPS32_R_INST(MIPS32_OP_SPECIAL, 0, src, reg, off, MIPS32_OP_SRA)
|
||||
#define MIPS32_ISA_SRL(reg, src, off) MIPS32_R_INST(MIPS32_OP_SPECIAL, 0, src, reg, off, MIPS32_OP_SRL)
|
||||
#define MIPS32_ISA_SYNC 0xFu
|
||||
#define MIPS32_ISA_SYNCI(off, base) MIPS32_I_INST(MIPS32_OP_REGIMM, base, MIPS32_OP_SYNCI, off)
|
||||
|
||||
|
|
|
@ -842,12 +842,12 @@ int mips32_pracc_write_regs(struct mips32_common *mips32)
|
|||
};
|
||||
|
||||
uint32_t cp0_write_data[] = {
|
||||
/* status */
|
||||
c0rs[0],
|
||||
/* lo */
|
||||
gprs[32],
|
||||
/* hi */
|
||||
gprs[33],
|
||||
/* status */
|
||||
c0rs[0],
|
||||
/* badvaddr */
|
||||
c0rs[1],
|
||||
/* cause */
|
||||
|
@ -856,6 +856,9 @@ int mips32_pracc_write_regs(struct mips32_common *mips32)
|
|||
c0rs[3],
|
||||
};
|
||||
|
||||
/* Write CP0 Status Register first, changes on EXL or ERL bits
|
||||
* may lead to different behaviour on writing to other CP0 registers.
|
||||
*/
|
||||
for (size_t i = 0; i < ARRAY_SIZE(cp0_write_code); i++) {
|
||||
/* load CP0 value in $1 */
|
||||
pracc_add_li32(&ctx, 1, cp0_write_data[i], 0);
|
||||
|
|
|
@ -142,7 +142,7 @@ static int mips_m4k_halt_smp(struct target *target)
|
|||
ret = mips_m4k_halt(curr);
|
||||
|
||||
if (ret != ERROR_OK) {
|
||||
LOG_ERROR("halt failed target->coreid: %" PRId32, curr->coreid);
|
||||
LOG_TARGET_ERROR(curr, "halt failed.");
|
||||
retval = ret;
|
||||
}
|
||||
}
|
||||
|
@ -412,8 +412,8 @@ static int mips_m4k_restore_smp(struct target *target, uint32_t address, int han
|
|||
handle_breakpoints, 0);
|
||||
|
||||
if (ret != ERROR_OK) {
|
||||
LOG_ERROR("target->coreid :%" PRId32 " failed to resume at address :0x%" PRIx32,
|
||||
curr->coreid, address);
|
||||
LOG_TARGET_ERROR(curr, "failed to resume at address: 0x%" PRIx32,
|
||||
address);
|
||||
retval = ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,6 +132,9 @@ COMMAND_HANDLER(handle_smp_gdb_command)
|
|||
{
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
int retval = ERROR_OK;
|
||||
|
||||
LOG_WARNING(DEPRECATED_MSG);
|
||||
|
||||
if (!list_empty(target->smp_targets)) {
|
||||
if (CMD_ARGC == 1) {
|
||||
int coreid = 0;
|
||||
|
|
|
@ -299,23 +299,6 @@ const char *target_reset_mode_name(enum target_reset_mode reset_mode)
|
|||
return cp;
|
||||
}
|
||||
|
||||
/* determine the number of the new target */
|
||||
static int new_target_number(void)
|
||||
{
|
||||
struct target *t;
|
||||
int x;
|
||||
|
||||
/* number is 0 based */
|
||||
x = -1;
|
||||
t = all_targets;
|
||||
while (t) {
|
||||
if (x < t->target_number)
|
||||
x = t->target_number;
|
||||
t = t->next;
|
||||
}
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
static void append_to_list_all_targets(struct target *target)
|
||||
{
|
||||
struct target **t = &all_targets;
|
||||
|
@ -451,7 +434,7 @@ void target_buffer_set_u16_array(struct target *target, uint8_t *buffer, uint32_
|
|||
target_buffer_set_u16(target, &buffer[i * 2], srcbuf[i]);
|
||||
}
|
||||
|
||||
/* return a pointer to a configured target; id is name or number */
|
||||
/* return a pointer to a configured target; id is name or index in all_targets */
|
||||
struct target *get_target(const char *id)
|
||||
{
|
||||
struct target *target;
|
||||
|
@ -464,36 +447,17 @@ struct target *get_target(const char *id)
|
|||
return target;
|
||||
}
|
||||
|
||||
/* It's OK to remove this fallback sometime after August 2010 or so */
|
||||
|
||||
/* no match, try as number */
|
||||
unsigned num;
|
||||
if (parse_uint(id, &num) != ERROR_OK)
|
||||
/* try as index */
|
||||
unsigned int index, counter;
|
||||
if (parse_uint(id, &index) != ERROR_OK)
|
||||
return NULL;
|
||||
|
||||
for (target = all_targets; target; target = target->next) {
|
||||
if (target->target_number == (int)num) {
|
||||
LOG_WARNING("use '%s' as target identifier, not '%u'",
|
||||
target_name(target), num);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
for (target = all_targets, counter = index;
|
||||
target && counter;
|
||||
target = target->next, --counter)
|
||||
;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* returns a pointer to the n-th configured target */
|
||||
struct target *get_target_by_num(int num)
|
||||
{
|
||||
struct target *target = all_targets;
|
||||
|
||||
while (target) {
|
||||
if (target->target_number == num)
|
||||
return target;
|
||||
target = target->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return target;
|
||||
}
|
||||
|
||||
struct target *get_current_target(struct command_context *cmd_ctx)
|
||||
|
@ -712,10 +676,14 @@ static int default_check_reset(struct target *target)
|
|||
* Keep in sync */
|
||||
int target_examine_one(struct target *target)
|
||||
{
|
||||
LOG_TARGET_DEBUG(target, "Examination started");
|
||||
|
||||
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_START);
|
||||
|
||||
int retval = target->type->examine(target);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_TARGET_ERROR(target, "Examination failed");
|
||||
LOG_TARGET_DEBUG(target, "examine() returned error code %d", retval);
|
||||
target_reset_examined(target);
|
||||
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_FAIL);
|
||||
return retval;
|
||||
|
@ -725,6 +693,7 @@ int target_examine_one(struct target *target)
|
|||
target_set_examined(target);
|
||||
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
|
||||
|
||||
LOG_TARGET_INFO(target, "Examination succeed");
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
||||
|
@ -2222,6 +2191,9 @@ uint32_t target_get_working_area_avail(struct target *target)
|
|||
|
||||
static void target_destroy(struct target *target)
|
||||
{
|
||||
breakpoint_remove_all(target);
|
||||
watchpoint_remove_all(target);
|
||||
|
||||
if (target->type->deinit_target)
|
||||
target->type->deinit_target(target);
|
||||
|
||||
|
@ -2850,10 +2822,10 @@ COMMAND_HANDLER(handle_targets_command)
|
|||
}
|
||||
}
|
||||
|
||||
struct target *target = all_targets;
|
||||
unsigned int index = 0;
|
||||
command_print(CMD, " TargetName Type Endian TapName State ");
|
||||
command_print(CMD, "-- ------------------ ---------- ------ ------------------ ------------");
|
||||
while (target) {
|
||||
for (struct target *target = all_targets; target; target = target->next, ++index) {
|
||||
const char *state;
|
||||
char marker = ' ';
|
||||
|
||||
|
@ -2868,7 +2840,7 @@ COMMAND_HANDLER(handle_targets_command)
|
|||
/* keep columns lined up to match the headers above */
|
||||
command_print(CMD,
|
||||
"%2d%c %-18s %-10s %-6s %-18s %s",
|
||||
target->target_number,
|
||||
index,
|
||||
marker,
|
||||
target_name(target),
|
||||
target_type_name(target),
|
||||
|
@ -2876,7 +2848,6 @@ COMMAND_HANDLER(handle_targets_command)
|
|||
target->endianness)->name,
|
||||
target->tap->dotted_name,
|
||||
state);
|
||||
target = target->next;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -3973,7 +3944,7 @@ static int handle_bp_command_set(struct command_invocation *cmd,
|
|||
|
||||
} else if (addr == 0) {
|
||||
if (!target->type->add_context_breakpoint) {
|
||||
LOG_ERROR("Context breakpoint not available");
|
||||
LOG_TARGET_ERROR(target, "Context breakpoint not available");
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
retval = context_breakpoint_add(target, asid, length, hw);
|
||||
|
@ -3983,7 +3954,7 @@ static int handle_bp_command_set(struct command_invocation *cmd,
|
|||
|
||||
} else {
|
||||
if (!target->type->add_hybrid_breakpoint) {
|
||||
LOG_ERROR("Hybrid breakpoint not available");
|
||||
LOG_TARGET_ERROR(target, "Hybrid breakpoint not available");
|
||||
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
|
||||
}
|
||||
retval = hybrid_breakpoint_add(target, addr, asid, length, hw);
|
||||
|
@ -4120,7 +4091,7 @@ COMMAND_HANDLER(handle_wp_command)
|
|||
type = WPT_ACCESS;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("invalid watchpoint mode ('%c')", CMD_ARGV[2][0]);
|
||||
LOG_TARGET_ERROR(target, "invalid watchpoint mode ('%c')", CMD_ARGV[2][0]);
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
/* fall through */
|
||||
|
@ -4136,7 +4107,7 @@ COMMAND_HANDLER(handle_wp_command)
|
|||
int retval = watchpoint_add(target, addr, length, type,
|
||||
data_value, data_mask);
|
||||
if (retval != ERROR_OK)
|
||||
LOG_ERROR("Failure setting watchpoints");
|
||||
LOG_TARGET_ERROR(target, "Failure setting watchpoints");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -4329,7 +4300,7 @@ COMMAND_HANDLER(handle_profile_command)
|
|||
if ((CMD_ARGC != 2) && (CMD_ARGC != 4))
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
|
||||
const uint32_t MAX_PROFILE_SAMPLE_NUM = 10000;
|
||||
const uint32_t MAX_PROFILE_SAMPLE_NUM = 1000000;
|
||||
uint32_t offset;
|
||||
uint32_t num_of_samples;
|
||||
int retval = ERROR_OK;
|
||||
|
@ -5062,8 +5033,7 @@ void target_handle_event(struct target *target, enum target_event e)
|
|||
|
||||
for (teap = target->event_action; teap; teap = teap->next) {
|
||||
if (teap->event == e) {
|
||||
LOG_DEBUG("target(%d): %s (%s) event: %d (%s) action: %s",
|
||||
target->target_number,
|
||||
LOG_DEBUG("target: %s (%s) event: %d (%s) action: %s",
|
||||
target_name(target),
|
||||
target_type_name(target),
|
||||
e,
|
||||
|
@ -5482,13 +5452,13 @@ no_params:
|
|||
e = jim_getopt_wide(goi, &w);
|
||||
if (e != JIM_OK)
|
||||
return e;
|
||||
/* make this exactly 1 or 0 */
|
||||
target->backup_working_area = (!!w);
|
||||
/* make this boolean */
|
||||
target->backup_working_area = (w != 0);
|
||||
} else {
|
||||
if (goi->argc != 0)
|
||||
goto no_params;
|
||||
}
|
||||
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->backup_working_area));
|
||||
Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->backup_working_area ? 1 : 0));
|
||||
/* loop for more e*/
|
||||
break;
|
||||
|
||||
|
@ -5848,8 +5818,7 @@ COMMAND_HANDLER(handle_target_event_list)
|
|||
struct target *target = get_current_target(CMD_CTX);
|
||||
struct target_event_action *teap = target->event_action;
|
||||
|
||||
command_print(CMD, "Event actions for target (%d) %s\n",
|
||||
target->target_number,
|
||||
command_print(CMD, "Event actions for target %s\n",
|
||||
target_name(target));
|
||||
command_print(CMD, "%-25s | Body", "Event");
|
||||
command_print(CMD, "------------------------- | "
|
||||
|
@ -5883,7 +5852,17 @@ COMMAND_HANDLER(handle_target_debug_reason)
|
|||
|
||||
struct target *target = get_current_target(CMD_CTX);
|
||||
|
||||
command_print(CMD, "%s", debug_reason_name(target));
|
||||
|
||||
const char *debug_reason = nvp_value2name(nvp_target_debug_reason,
|
||||
target->debug_reason)->name;
|
||||
|
||||
if (!debug_reason) {
|
||||
command_print(CMD, "bug: invalid debug reason (%d)",
|
||||
target->debug_reason);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
command_print(CMD, "%s", debug_reason);
|
||||
|
||||
return ERROR_OK;
|
||||
}
|
||||
|
@ -6188,9 +6167,6 @@ static int target_create(struct jim_getopt_info *goi)
|
|||
/* set empty smp cluster */
|
||||
target->smp_targets = &empty_smp_targets;
|
||||
|
||||
/* set target number */
|
||||
target->target_number = new_target_number();
|
||||
|
||||
/* allocate memory for each unique target type */
|
||||
target->type = malloc(sizeof(struct target_type));
|
||||
if (!target->type) {
|
||||
|
@ -6207,7 +6183,7 @@ static int target_create(struct jim_getopt_info *goi)
|
|||
target->working_area = 0x0;
|
||||
target->working_area_size = 0x0;
|
||||
target->working_areas = NULL;
|
||||
target->backup_working_area = 0;
|
||||
target->backup_working_area = false;
|
||||
|
||||
target->state = TARGET_UNKNOWN;
|
||||
target->debug_reason = DBG_REASON_UNDEFINED;
|
||||
|
@ -7095,7 +7071,7 @@ static const struct command_registration target_exec_command_handlers[] = {
|
|||
.handler = handle_wp_command,
|
||||
.mode = COMMAND_EXEC,
|
||||
.help = "list (no params) or create watchpoints",
|
||||
.usage = "[address length [('r'|'w'|'a') value [mask]]]",
|
||||
.usage = "[address length [('r'|'w'|'a') [value [mask]]]]",
|
||||
},
|
||||
{
|
||||
.name = "rwp",
|
||||
|
|
|
@ -118,7 +118,6 @@ enum target_register_class {
|
|||
struct target {
|
||||
struct target_type *type; /* target type definition (name, access functions) */
|
||||
char *cmd_name; /* tcl Name of target */
|
||||
int target_number; /* DO NOT USE! field to be removed in 2010 */
|
||||
struct jtag_tap *tap; /* where on the jtag chain is this */
|
||||
int32_t coreid; /* which device on the TAP? */
|
||||
|
||||
|
@ -152,7 +151,7 @@ struct target {
|
|||
bool working_area_phys_spec; /* physical address specified? */
|
||||
target_addr_t working_area_phys; /* physical address */
|
||||
uint32_t working_area_size; /* size in bytes */
|
||||
uint32_t backup_working_area; /* whether the content of the working area has to be preserved */
|
||||
bool backup_working_area; /* whether the content of the working area has to be preserved */
|
||||
struct working_area *working_areas;/* list of allocated working areas */
|
||||
enum target_debug_reason debug_reason;/* reason why the target entered debug state */
|
||||
enum target_endianness endianness; /* target endianness */
|
||||
|
@ -418,7 +417,6 @@ int target_call_timer_callbacks_now(void);
|
|||
*/
|
||||
int64_t target_timer_next_event(void);
|
||||
|
||||
struct target *get_target_by_num(int num);
|
||||
struct target *get_current_target(struct command_context *cmd_ctx);
|
||||
struct target *get_current_target_or_null(struct command_context *cmd_ctx);
|
||||
struct target *get_target(const char *id);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <helper/time_support.h>
|
||||
#include <helper/align.h>
|
||||
#include <target/register.h>
|
||||
#include <target/algorithm.h>
|
||||
|
||||
#include "xtensa_chip.h"
|
||||
#include "xtensa.h"
|
||||
|
@ -822,7 +823,7 @@ int xtensa_examine(struct target *target)
|
|||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
unsigned int cmd = PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) | PWRCTL_COREWAKEUP(xtensa);
|
||||
|
||||
LOG_DEBUG("coreid = %d", target->coreid);
|
||||
LOG_TARGET_DEBUG(target, "");
|
||||
|
||||
if (xtensa->core_config->core_type == XT_UNDEF) {
|
||||
LOG_ERROR("XTensa core not configured; is xtensa-core-openocd.cfg missing?");
|
||||
|
@ -1096,7 +1097,7 @@ int xtensa_assert_reset(struct target *target)
|
|||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
||||
LOG_TARGET_DEBUG(target, "target_number=%i, begin", target->target_number);
|
||||
LOG_TARGET_DEBUG(target, " begin");
|
||||
xtensa_queue_pwr_reg_write(xtensa,
|
||||
XDMREG_PWRCTL,
|
||||
PWRCTL_JTAGDEBUGUSE(xtensa) | PWRCTL_DEBUGWAKEUP(xtensa) | PWRCTL_MEMWAKEUP(xtensa) |
|
||||
|
@ -2635,6 +2636,214 @@ int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoin
|
|||
return ERROR_OK;
|
||||
}
|
||||
|
||||
int xtensa_start_algorithm(struct target *target,
|
||||
int num_mem_params, struct mem_param *mem_params,
|
||||
int num_reg_params, struct reg_param *reg_params,
|
||||
target_addr_t entry_point, target_addr_t exit_point,
|
||||
void *arch_info)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
struct xtensa_algorithm *algorithm_info = arch_info;
|
||||
int retval = ERROR_OK;
|
||||
bool usr_ps = false;
|
||||
|
||||
/* NOTE: xtensa_run_algorithm requires that each algorithm uses a software breakpoint
|
||||
* at the exit point */
|
||||
|
||||
if (target->state != TARGET_HALTED) {
|
||||
LOG_WARNING("Target not halted!");
|
||||
return ERROR_TARGET_NOT_HALTED;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < xtensa->core_cache->num_regs; i++) {
|
||||
struct reg *reg = &xtensa->core_cache->reg_list[i];
|
||||
buf_cpy(reg->value, xtensa->algo_context_backup[i], reg->size);
|
||||
}
|
||||
/* save debug reason, it will be changed */
|
||||
algorithm_info->ctx_debug_reason = target->debug_reason;
|
||||
/* write mem params */
|
||||
for (int i = 0; i < num_mem_params; i++) {
|
||||
if (mem_params[i].direction != PARAM_IN) {
|
||||
retval = target_write_buffer(target, mem_params[i].address,
|
||||
mem_params[i].size,
|
||||
mem_params[i].value);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
/* write reg params */
|
||||
for (int i = 0; i < num_reg_params; i++) {
|
||||
if (reg_params[i].size > 32) {
|
||||
LOG_ERROR("BUG: not supported register size (%d)", reg_params[i].size);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
struct reg *reg = register_get_by_name(xtensa->core_cache, reg_params[i].reg_name, 0);
|
||||
if (!reg) {
|
||||
LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (reg->size != reg_params[i].size) {
|
||||
LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (memcmp(reg_params[i].reg_name, "ps", 3)) {
|
||||
usr_ps = true;
|
||||
} else {
|
||||
unsigned int reg_id = xtensa->eps_dbglevel_idx;
|
||||
assert(reg_id < xtensa->core_cache->num_regs && "Attempt to access non-existing reg!");
|
||||
reg = &xtensa->core_cache->reg_list[reg_id];
|
||||
}
|
||||
xtensa_reg_set_value(reg, buf_get_u32(reg_params[i].value, 0, reg->size));
|
||||
reg->valid = 1;
|
||||
}
|
||||
/* ignore custom core mode if custom PS value is specified */
|
||||
if (!usr_ps) {
|
||||
unsigned int eps_reg_idx = xtensa->eps_dbglevel_idx;
|
||||
xtensa_reg_val_t ps = xtensa_reg_get(target, eps_reg_idx);
|
||||
enum xtensa_mode core_mode = XT_PS_RING_GET(ps);
|
||||
if (algorithm_info->core_mode != XT_MODE_ANY && algorithm_info->core_mode != core_mode) {
|
||||
LOG_DEBUG("setting core_mode: 0x%x", algorithm_info->core_mode);
|
||||
xtensa_reg_val_t new_ps = (ps & ~XT_PS_RING_MSK) | XT_PS_RING(algorithm_info->core_mode);
|
||||
/* save previous core mode */
|
||||
/* TODO: core_mode is not restored for now. Can be added to the end of wait_algorithm */
|
||||
algorithm_info->core_mode = core_mode;
|
||||
xtensa_reg_set(target, eps_reg_idx, new_ps);
|
||||
xtensa->core_cache->reg_list[eps_reg_idx].valid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return xtensa_resume(target, 0, entry_point, 1, 1);
|
||||
}
|
||||
|
||||
/** Waits for an algorithm in the target. */
|
||||
int xtensa_wait_algorithm(struct target *target,
|
||||
int num_mem_params, struct mem_param *mem_params,
|
||||
int num_reg_params, struct reg_param *reg_params,
|
||||
target_addr_t exit_point, unsigned int timeout_ms,
|
||||
void *arch_info)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
struct xtensa_algorithm *algorithm_info = arch_info;
|
||||
int retval = ERROR_OK;
|
||||
xtensa_reg_val_t pc;
|
||||
|
||||
/* NOTE: xtensa_run_algorithm requires that each algorithm uses a software breakpoint
|
||||
* at the exit point */
|
||||
|
||||
retval = target_wait_state(target, TARGET_HALTED, timeout_ms);
|
||||
/* If the target fails to halt due to the breakpoint, force a halt */
|
||||
if (retval != ERROR_OK || target->state != TARGET_HALTED) {
|
||||
retval = target_halt(target);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
retval = target_wait_state(target, TARGET_HALTED, 500);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
LOG_TARGET_ERROR(target, "not halted %d, pc 0x%" PRIx32 ", ps 0x%" PRIx32, retval,
|
||||
xtensa_reg_get(target, XT_REG_IDX_PC),
|
||||
xtensa_reg_get(target, xtensa->eps_dbglevel_idx));
|
||||
return ERROR_TARGET_TIMEOUT;
|
||||
}
|
||||
pc = xtensa_reg_get(target, XT_REG_IDX_PC);
|
||||
if (exit_point && pc != exit_point) {
|
||||
LOG_ERROR("failed algorithm halted at 0x%" PRIx32 ", expected " TARGET_ADDR_FMT, pc, exit_point);
|
||||
return ERROR_TARGET_TIMEOUT;
|
||||
}
|
||||
/* Copy core register values to reg_params[] */
|
||||
for (int i = 0; i < num_reg_params; i++) {
|
||||
if (reg_params[i].direction != PARAM_OUT) {
|
||||
struct reg *reg = register_get_by_name(xtensa->core_cache, reg_params[i].reg_name, 0);
|
||||
if (!reg) {
|
||||
LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
if (reg->size != reg_params[i].size) {
|
||||
LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name);
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
buf_set_u32(reg_params[i].value, 0, 32, xtensa_reg_get_value(reg));
|
||||
}
|
||||
}
|
||||
/* Read memory values to mem_params */
|
||||
LOG_DEBUG("Read mem params");
|
||||
for (int i = 0; i < num_mem_params; i++) {
|
||||
LOG_DEBUG("Check mem param @ " TARGET_ADDR_FMT, mem_params[i].address);
|
||||
if (mem_params[i].direction != PARAM_OUT) {
|
||||
LOG_DEBUG("Read mem param @ " TARGET_ADDR_FMT, mem_params[i].address);
|
||||
retval = target_read_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value);
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/* avoid gdb keep_alive warning */
|
||||
keep_alive();
|
||||
|
||||
for (int i = xtensa->core_cache->num_regs - 1; i >= 0; i--) {
|
||||
struct reg *reg = &xtensa->core_cache->reg_list[i];
|
||||
if (i == XT_REG_IDX_PS) {
|
||||
continue; /* restore mapped reg number of PS depends on NDEBUGLEVEL */
|
||||
} else if (i == XT_REG_IDX_DEBUGCAUSE) {
|
||||
/*FIXME: restoring DEBUGCAUSE causes exception when executing corresponding
|
||||
* instruction in DIR */
|
||||
LOG_DEBUG("Skip restoring register %s: 0x%8.8" PRIx32 " -> 0x%8.8" PRIx32,
|
||||
xtensa->core_cache->reg_list[i].name,
|
||||
buf_get_u32(reg->value, 0, 32),
|
||||
buf_get_u32(xtensa->algo_context_backup[i], 0, 32));
|
||||
buf_cpy(xtensa->algo_context_backup[i], reg->value, reg->size);
|
||||
xtensa->core_cache->reg_list[i].dirty = 0;
|
||||
xtensa->core_cache->reg_list[i].valid = 0;
|
||||
} else if (memcmp(xtensa->algo_context_backup[i], reg->value, reg->size / 8)) {
|
||||
if (reg->size <= 32) {
|
||||
LOG_DEBUG("restoring register %s: 0x%8.8" PRIx32 " -> 0x%8.8" PRIx32,
|
||||
xtensa->core_cache->reg_list[i].name,
|
||||
buf_get_u32(reg->value, 0, reg->size),
|
||||
buf_get_u32(xtensa->algo_context_backup[i], 0, reg->size));
|
||||
} else if (reg->size <= 64) {
|
||||
LOG_DEBUG("restoring register %s: 0x%8.8" PRIx64 " -> 0x%8.8" PRIx64,
|
||||
xtensa->core_cache->reg_list[i].name,
|
||||
buf_get_u64(reg->value, 0, reg->size),
|
||||
buf_get_u64(xtensa->algo_context_backup[i], 0, reg->size));
|
||||
} else {
|
||||
LOG_DEBUG("restoring register %s %u-bits", xtensa->core_cache->reg_list[i].name, reg->size);
|
||||
}
|
||||
buf_cpy(xtensa->algo_context_backup[i], reg->value, reg->size);
|
||||
xtensa->core_cache->reg_list[i].dirty = 1;
|
||||
xtensa->core_cache->reg_list[i].valid = 1;
|
||||
}
|
||||
}
|
||||
target->debug_reason = algorithm_info->ctx_debug_reason;
|
||||
|
||||
retval = xtensa_write_dirty_registers(target);
|
||||
if (retval != ERROR_OK)
|
||||
LOG_ERROR("Failed to write dirty regs (%d)!", retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int xtensa_run_algorithm(struct target *target,
|
||||
int num_mem_params, struct mem_param *mem_params,
|
||||
int num_reg_params, struct reg_param *reg_params,
|
||||
target_addr_t entry_point, target_addr_t exit_point,
|
||||
unsigned int timeout_ms, void *arch_info)
|
||||
{
|
||||
int retval = xtensa_start_algorithm(target,
|
||||
num_mem_params, mem_params,
|
||||
num_reg_params, reg_params,
|
||||
entry_point, exit_point,
|
||||
arch_info);
|
||||
|
||||
if (retval == ERROR_OK) {
|
||||
retval = xtensa_wait_algorithm(target,
|
||||
num_mem_params, mem_params,
|
||||
num_reg_params, reg_params,
|
||||
exit_point, timeout_ms,
|
||||
arch_info);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int xtensa_build_reg_cache(struct target *target)
|
||||
{
|
||||
struct xtensa *xtensa = target_to_xtensa(target);
|
||||
|
@ -3978,6 +4187,38 @@ COMMAND_HANDLER(xtensa_cmd_smpbreak)
|
|||
get_current_target(CMD_CTX));
|
||||
}
|
||||
|
||||
COMMAND_HELPER(xtensa_cmd_dm_rw_do, struct xtensa *xtensa)
|
||||
{
|
||||
if (CMD_ARGC == 1) {
|
||||
// read: xtensa dm addr
|
||||
uint32_t addr = strtoul(CMD_ARGV[0], NULL, 0);
|
||||
uint32_t val;
|
||||
int res = xtensa_dm_read(&xtensa->dbg_mod, addr, &val);
|
||||
if (res == ERROR_OK)
|
||||
command_print(CMD, "xtensa DM(0x%08" PRIx32 ") -> 0x%08" PRIx32, addr, val);
|
||||
else
|
||||
command_print(CMD, "xtensa DM(0x%08" PRIx32 ") : read ERROR %" PRId32, addr, res);
|
||||
return res;
|
||||
} else if (CMD_ARGC == 2) {
|
||||
// write: xtensa dm addr value
|
||||
uint32_t addr = strtoul(CMD_ARGV[0], NULL, 0);
|
||||
uint32_t val = strtoul(CMD_ARGV[1], NULL, 0);
|
||||
int res = xtensa_dm_write(&xtensa->dbg_mod, addr, val);
|
||||
if (res == ERROR_OK)
|
||||
command_print(CMD, "xtensa DM(0x%08" PRIx32 ") <- 0x%08" PRIx32, addr, val);
|
||||
else
|
||||
command_print(CMD, "xtensa DM(0x%08" PRIx32 ") : write ERROR %" PRId32, addr, res);
|
||||
return res;
|
||||
}
|
||||
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
COMMAND_HANDLER(xtensa_cmd_dm_rw)
|
||||
{
|
||||
return CALL_COMMAND_HANDLER(xtensa_cmd_dm_rw_do,
|
||||
target_to_xtensa(get_current_target(CMD_CTX)));
|
||||
}
|
||||
|
||||
COMMAND_HELPER(xtensa_cmd_tracestart_do, struct xtensa *xtensa)
|
||||
{
|
||||
struct xtensa_trace_status trace_status;
|
||||
|
@ -4234,6 +4475,13 @@ static const struct command_registration xtensa_any_command_handlers[] = {
|
|||
.help = "Set the way the CPU chains OCD breaks",
|
||||
.usage = "[none|breakinout|runstall] | [BreakIn] [BreakOut] [RunStallIn] [DebugModeOut]",
|
||||
},
|
||||
{
|
||||
.name = "dm",
|
||||
.handler = xtensa_cmd_dm_rw,
|
||||
.mode = COMMAND_ANY,
|
||||
.help = "Xtensa DM read/write",
|
||||
.usage = "addr [value]"
|
||||
},
|
||||
{
|
||||
.name = "perfmon_enable",
|
||||
.handler = xtensa_cmd_perfmon_enable,
|
||||
|
|
|
@ -222,6 +222,16 @@ struct xtensa_sw_breakpoint {
|
|||
uint8_t insn_sz; /* 2 or 3 bytes */
|
||||
};
|
||||
|
||||
/**
|
||||
* Xtensa algorithm data.
|
||||
*/
|
||||
struct xtensa_algorithm {
|
||||
/** User can set this to specify which core mode algorithm should be run in. */
|
||||
enum xtensa_mode core_mode;
|
||||
/** Used internally to backup and restore debug_reason. */
|
||||
enum target_debug_reason ctx_debug_reason;
|
||||
};
|
||||
|
||||
#define XTENSA_COMMON_MAGIC 0x54E4E555U
|
||||
|
||||
/**
|
||||
|
@ -395,6 +405,21 @@ int xtensa_breakpoint_add(struct target *target, struct breakpoint *breakpoint);
|
|||
int xtensa_breakpoint_remove(struct target *target, struct breakpoint *breakpoint);
|
||||
int xtensa_watchpoint_add(struct target *target, struct watchpoint *watchpoint);
|
||||
int xtensa_watchpoint_remove(struct target *target, struct watchpoint *watchpoint);
|
||||
int xtensa_start_algorithm(struct target *target,
|
||||
int num_mem_params, struct mem_param *mem_params,
|
||||
int num_reg_params, struct reg_param *reg_params,
|
||||
target_addr_t entry_point, target_addr_t exit_point,
|
||||
void *arch_info);
|
||||
int xtensa_wait_algorithm(struct target *target,
|
||||
int num_mem_params, struct mem_param *mem_params,
|
||||
int num_reg_params, struct reg_param *reg_params,
|
||||
target_addr_t exit_point, unsigned int timeout_ms,
|
||||
void *arch_info);
|
||||
int xtensa_run_algorithm(struct target *target,
|
||||
int num_mem_params, struct mem_param *mem_params,
|
||||
int num_reg_params, struct reg_param *reg_params,
|
||||
target_addr_t entry_point, target_addr_t exit_point,
|
||||
unsigned int timeout_ms, void *arch_info);
|
||||
void xtensa_set_permissive_mode(struct target *target, bool state);
|
||||
const char *xtensa_get_gdb_arch(struct target *target);
|
||||
int xtensa_gdb_query_custom(struct target *target, const char *packet, char **response_p);
|
||||
|
|
|
@ -34,6 +34,16 @@ static const struct xtensa_dm_pwr_reg_offsets xdm_pwr_regs[XDMREG_PWRNUM] =
|
|||
static const struct xtensa_dm_reg_offsets xdm_regs[XDMREG_NUM] =
|
||||
XTENSA_DM_REG_OFFSETS;
|
||||
|
||||
static enum xtensa_dm_reg xtensa_dm_regaddr_to_id(uint32_t addr)
|
||||
{
|
||||
enum xtensa_dm_reg id;
|
||||
uint32_t addr_masked = (addr & (XTENSA_DM_APB_ALIGN - 1));
|
||||
for (id = XDMREG_TRAXID; id < XDMREG_NUM; id++)
|
||||
if (xdm_regs[id].apb == addr_masked)
|
||||
break;
|
||||
return id;
|
||||
}
|
||||
|
||||
static void xtensa_dm_add_set_ir(struct xtensa_debug_module *dm, uint8_t value)
|
||||
{
|
||||
struct scan_field field;
|
||||
|
@ -285,6 +295,34 @@ int xtensa_dm_core_status_clear(struct xtensa_debug_module *dm, xtensa_dsr_t bit
|
|||
return xtensa_dm_queue_execute(dm);
|
||||
}
|
||||
|
||||
int xtensa_dm_read(struct xtensa_debug_module *dm, uint32_t addr, uint32_t *val)
|
||||
{
|
||||
enum xtensa_dm_reg reg = xtensa_dm_regaddr_to_id(addr);
|
||||
uint8_t buf[sizeof(uint32_t)];
|
||||
if (reg < XDMREG_NUM) {
|
||||
xtensa_dm_queue_enable(dm);
|
||||
dm->dbg_ops->queue_reg_read(dm, reg, buf);
|
||||
xtensa_dm_queue_tdi_idle(dm);
|
||||
int res = xtensa_dm_queue_execute(dm);
|
||||
if (res == ERROR_OK && val)
|
||||
*val = buf_get_u32(buf, 0, 32);
|
||||
return res;
|
||||
}
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int xtensa_dm_write(struct xtensa_debug_module *dm, uint32_t addr, uint32_t val)
|
||||
{
|
||||
enum xtensa_dm_reg reg = xtensa_dm_regaddr_to_id(addr);
|
||||
if (reg < XDMREG_NUM) {
|
||||
xtensa_dm_queue_enable(dm);
|
||||
dm->dbg_ops->queue_reg_write(dm, reg, val);
|
||||
xtensa_dm_queue_tdi_idle(dm);
|
||||
return xtensa_dm_queue_execute(dm);
|
||||
}
|
||||
return ERROR_FAIL;
|
||||
}
|
||||
|
||||
int xtensa_dm_trace_start(struct xtensa_debug_module *dm, struct xtensa_trace_start_config *cfg)
|
||||
{
|
||||
/*Turn off trace unit so we can start a new trace. */
|
||||
|
|
|
@ -75,6 +75,22 @@ enum xtensa_dm_reg {
|
|||
XDMREG_DELAYCNT,
|
||||
XDMREG_MEMADDRSTART,
|
||||
XDMREG_MEMADDREND,
|
||||
XDMREG_EXTTIMELO,
|
||||
XDMREG_EXTTIMEHI,
|
||||
XDMREG_TRAXRSVD48,
|
||||
XDMREG_TRAXRSVD4C,
|
||||
XDMREG_TRAXRSVD50,
|
||||
XDMREG_TRAXRSVD54,
|
||||
XDMREG_TRAXRSVD58,
|
||||
XDMREG_TRAXRSVD5C,
|
||||
XDMREG_TRAXRSVD60,
|
||||
XDMREG_TRAXRSVD64,
|
||||
XDMREG_TRAXRSVD68,
|
||||
XDMREG_TRAXRSVD6C,
|
||||
XDMREG_TRAXRSVD70,
|
||||
XDMREG_TRAXRSVD74,
|
||||
XDMREG_CONFIGID0,
|
||||
XDMREG_CONFIGID1,
|
||||
|
||||
/* Performance Monitor Registers */
|
||||
XDMREG_PMG,
|
||||
|
@ -168,6 +184,22 @@ struct xtensa_dm_reg_offsets {
|
|||
{ .nar = 0x07, .apb = 0x001c }, /* XDMREG_DELAYCNT */ \
|
||||
{ .nar = 0x08, .apb = 0x0020 }, /* XDMREG_MEMADDRSTART */ \
|
||||
{ .nar = 0x09, .apb = 0x0024 }, /* XDMREG_MEMADDREND */ \
|
||||
{ .nar = 0x10, .apb = 0x0040 }, /* XDMREG_EXTTIMELO */ \
|
||||
{ .nar = 0x11, .apb = 0x0044 }, /* XDMREG_EXTTIMEHI */ \
|
||||
{ .nar = 0x12, .apb = 0x0048 }, /* XDMREG_TRAXRSVD48 */ \
|
||||
{ .nar = 0x13, .apb = 0x004c }, /* XDMREG_TRAXRSVD4C */ \
|
||||
{ .nar = 0x14, .apb = 0x0050 }, /* XDMREG_TRAXRSVD50 */ \
|
||||
{ .nar = 0x15, .apb = 0x0054 }, /* XDMREG_TRAXRSVD54 */ \
|
||||
{ .nar = 0x16, .apb = 0x0058 }, /* XDMREG_TRAXRSVD58 */ \
|
||||
{ .nar = 0x17, .apb = 0x005c }, /* XDMREG_TRAXRSVD5C */ \
|
||||
{ .nar = 0x18, .apb = 0x0060 }, /* XDMREG_TRAXRSVD60 */ \
|
||||
{ .nar = 0x19, .apb = 0x0064 }, /* XDMREG_TRAXRSVD64 */ \
|
||||
{ .nar = 0x1a, .apb = 0x0068 }, /* XDMREG_TRAXRSVD68 */ \
|
||||
{ .nar = 0x1b, .apb = 0x006c }, /* XDMREG_TRAXRSVD6C */ \
|
||||
{ .nar = 0x1c, .apb = 0x0070 }, /* XDMREG_TRAXRSVD70 */ \
|
||||
{ .nar = 0x1d, .apb = 0x0074 }, /* XDMREG_TRAXRSVD74 */ \
|
||||
{ .nar = 0x1e, .apb = 0x0078 }, /* XDMREG_CONFIGID0 */ \
|
||||
{ .nar = 0x1f, .apb = 0x007c }, /* XDMREG_CONFIGID1 */ \
|
||||
\
|
||||
/* Performance Monitor Registers */ \
|
||||
{ .nar = 0x20, .apb = 0x1000 }, /* XDMREG_PMG */ \
|
||||
|
@ -297,6 +329,11 @@ struct xtensa_dm_reg_offsets {
|
|||
#define DEBUGCAUSE_DI BIT(5) /* Debug Interrupt */
|
||||
#define DEBUGCAUSE_VALID BIT(31) /* Pseudo-value to trigger reread (NX only) */
|
||||
|
||||
/* TRAXID */
|
||||
#define TRAXID_PRODNO_TRAX 0 /* TRAXID.PRODNO value for TRAX module */
|
||||
#define TRAXID_PRODNO_SHIFT 28
|
||||
#define TRAXID_PRODNO_MASK 0xf
|
||||
|
||||
#define TRAXCTRL_TREN BIT(0) /* Trace enable. Tracing starts on 0->1 */
|
||||
#define TRAXCTRL_TRSTP BIT(1) /* Trace Stop. Make 1 to stop trace. */
|
||||
#define TRAXCTRL_PCMEN BIT(2) /* PC match enable */
|
||||
|
@ -512,6 +549,9 @@ static inline xtensa_dsr_t xtensa_dm_core_status_get(struct xtensa_debug_module
|
|||
return dm->core_status.dsr;
|
||||
}
|
||||
|
||||
int xtensa_dm_read(struct xtensa_debug_module *dm, uint32_t addr, uint32_t *val);
|
||||
int xtensa_dm_write(struct xtensa_debug_module *dm, uint32_t addr, uint32_t val);
|
||||
|
||||
int xtensa_dm_device_id_read(struct xtensa_debug_module *dm);
|
||||
static inline xtensa_ocdid_t xtensa_dm_device_id_get(struct xtensa_debug_module *dm)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# SAMA5D2 devices support both JTAG and SWD transports.
|
||||
#
|
||||
# The JTAG connection is disabled at reset, and during the ROM Code execution.
|
||||
# It is re-enabled when the ROM code jumps in the boot file copied from an
|
||||
# external Flash memory into the internalSRAM, or when the ROM code launches
|
||||
|
@ -12,14 +14,28 @@
|
|||
# - if enabled, boundary Scan mode is activated. JTAG ID Code value is 0x05B3F03F.
|
||||
# - if disabled, ICE mode is activated. Debug Port JTAG IDCODE value is 0x5BA00477
|
||||
#
|
||||
|
||||
source [find target/swj-dp.tcl]
|
||||
|
||||
#jtag scan chain
|
||||
if { [info exists CPUTAPID] } {
|
||||
set _CPUTAPID $CPUTAPID
|
||||
} else {
|
||||
if { [using_jtag] } {
|
||||
set _CPUTAPID 0x5ba00477
|
||||
} else {
|
||||
# SWD IDCODE (single drop, arm)
|
||||
set _CPUTAPID 0x5ba02477
|
||||
}
|
||||
}
|
||||
|
||||
if { [info exists CHIPNAME] } {
|
||||
set _CHIPNAME $CHIPNAME
|
||||
} else {
|
||||
set _CHIPNAME at91sama5d2
|
||||
}
|
||||
|
||||
jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x01 -irmask 0x0f \
|
||||
-expected-id 0x5ba00477
|
||||
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
|
||||
|
||||
# Cortex-A5 target
|
||||
set _TARGETNAME $_CHIPNAME.cpu_a5
|
||||
|
|
|
@ -13,6 +13,13 @@ transport select jtag
|
|||
|
||||
reset_config srst_nogate
|
||||
|
||||
# The smallest RAM size 6kB (GD32VF103C4/T4/R4)
|
||||
if { [info exists WORKAREASIZE] } {
|
||||
set _WORKAREASIZE $WORKAREASIZE
|
||||
} else {
|
||||
set _WORKAREASIZE 0x1800
|
||||
}
|
||||
|
||||
set _CHIPNAME gd32vf103
|
||||
# The vendor's configuration expects an ID of 0x1e200a6d, but this one is what
|
||||
# I have on my board (Sipeed Longan Nano, GD32VF103CBT6).
|
||||
|
@ -29,7 +36,7 @@ proc default_mem_access {} {
|
|||
|
||||
default_mem_access
|
||||
|
||||
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x1000 -work-area-backup 1
|
||||
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
|
||||
|
||||
set _FLASHNAME $_CHIPNAME.flash
|
||||
flash bank $_FLASHNAME stm32f1x 0x08000000 0 0 0 $_TARGETNAME
|
||||
|
@ -107,3 +114,74 @@ proc init_reset { mode } {
|
|||
jtag arp_init-reset
|
||||
}
|
||||
}
|
||||
|
||||
# On this chip, ndmreset (the debug module bit that triggers a software reset)
|
||||
# doesn't work. So for JTAG connections without an SRST, we need to trigger a
|
||||
# reset manually. This is an undocumented reset sequence that's used by the
|
||||
# JTAG flashing script in the vendor-supplied GD32VF103 PlatformIO plugin:
|
||||
#
|
||||
# https://github.com/sipeed/platform-gd32v/commit/f9cbb44819bc05dd2010cc815c32be0486800cc2
|
||||
#
|
||||
$_TARGETNAME configure -event reset-assert {
|
||||
set dmcontrol 0x10
|
||||
set dmcontrol_dmactive [expr {1 << 0}]
|
||||
set dmcontrol_ackhavereset [expr {1 << 28}]
|
||||
set dmcontrol_haltreq [expr {1 << 31}]
|
||||
|
||||
global _RESETMODE
|
||||
|
||||
# If hardware NRST signal is connected and configured (reset_config srst_only)
|
||||
# the device has been recently reset in 'jtag arp_init-reset', therefore
|
||||
# DM_DMSTATUS_ANYHAVERESET reads 1.
|
||||
# The following 'halt' command checks this status bit
|
||||
# and shows 'Hart 0 unexpectedly reset!' if set.
|
||||
# Prevent this message by sending an acknowledge first.
|
||||
set val [expr {$dmcontrol_dmactive | $dmcontrol_ackhavereset}]
|
||||
riscv dmi_write $dmcontrol $val
|
||||
|
||||
# Halt the core so that we can write to memory. We do this first so
|
||||
# that it doesn't clobber our dmcontrol configuration.
|
||||
halt
|
||||
|
||||
# Set haltreq appropriately for the type of reset we're doing. This
|
||||
# replicates what the generic RISC-V reset_assert() function would
|
||||
# do if we weren't overriding it. The $_RESETMODE hack sucks, but
|
||||
# it's the least invasive way to determine whether we need to halt.
|
||||
#
|
||||
# If we didn't override the generic handler, we'd actually still have
|
||||
# to do this: the default handler sets ndmreset, which prevents memory
|
||||
# access even though it doesn't actually trigger a reset on this chip.
|
||||
# So we'd need to unset it here, which involves a write to dmcontrol,
|
||||
# Since haltreq is write-only and there's no way to leave it unchanged,
|
||||
# we'd have to figure out its proper value anyway.
|
||||
set val $dmcontrol_dmactive
|
||||
if {$_RESETMODE ne "run"} {
|
||||
set val [expr {$val | $dmcontrol_haltreq}]
|
||||
}
|
||||
riscv dmi_write $dmcontrol $val
|
||||
|
||||
# Unlock 0xe0042008 so that the next write triggers a reset
|
||||
mww 0xe004200c 0x4b5a6978
|
||||
|
||||
# We need to trigger the reset using abstract memory access, since
|
||||
# progbuf access tries to read a status code out of a core register
|
||||
# after the write happens, which fails when the core is in reset.
|
||||
riscv set_mem_access abstract
|
||||
|
||||
# Go!
|
||||
mww 0xe0042008 0x1
|
||||
|
||||
# Put the memory access mode back to what it was.
|
||||
default_mem_access
|
||||
}
|
||||
|
||||
# Capture the mode of a given reset so that we can use it later in the
|
||||
# reset-assert handler.
|
||||
proc init_reset { mode } {
|
||||
global _RESETMODE
|
||||
set _RESETMODE $mode
|
||||
|
||||
if {[using_jtag]} {
|
||||
jtag arp_init-reset
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue