From: Paul Fertser Date: Sun, 26 Oct 2014 07:51:02 +0000 (+0300) Subject: drivers/cmsis-dap: port to common SWD framework X-Git-Tag: v0.9.0-rc1~74 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=ef02b69b14d133b061217a91add5a028a77e86bc drivers/cmsis-dap: port to common SWD framework Valgrind-tested. Comparison of flashing performance on an FRDM-KL25Z board running mbed CMSIS-DAP variant, 5MHz clock, old driver: wrote 28096 bytes from file demo.elf in 26.833590s (1.023 KiB/s) verified 27264 bytes in 1.754972s (15.171 KiB/s) this implementation: wrote 28096 bytes from file demo.elf in 3.691939s (7.432 KiB/s) verified 27264 bytes in 0.598987s (44.450 KiB/s) Also tested "Keil ULINK-ME CMSIS-DAP" with an STM32F100 target, 5MHz clock, results reading from flash, old driver: dumped 131072 bytes in 98.445305s (1.300 KiB/s) this implementation: dumped 131072 bytes in 8.242686s (15.529 KiB/s) Change-Id: Ic64d3124b1d6cd9dd1016445bb627c71e189ae95 Signed-off-by: Paul Fertser Reviewed-on: http://openocd.zylin.com/2356 Tested-by: jenkins Reviewed-by: Spencer Oliver --- diff --git a/src/jtag/core.c b/src/jtag/core.c index cb3e9265cc..1b1106f121 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -1801,8 +1801,6 @@ void adapter_assert_reset(void) jtag_add_reset(0, 1); } else if (transport_is_swd()) swd_add_reset(1); - else if (transport_is_cmsis_dap()) - swd_add_reset(1); /* FIXME */ else if (get_current_transport() != NULL) LOG_ERROR("reset is not supported on %s", get_current_transport()->name); @@ -1816,8 +1814,6 @@ void adapter_deassert_reset(void) jtag_add_reset(0, 0); else if (transport_is_swd()) swd_add_reset(0); - else if (transport_is_cmsis_dap()) - swd_add_reset(0); /* FIXME */ else if (get_current_transport() != NULL) LOG_ERROR("reset is not supported on %s", get_current_transport()->name); diff --git a/src/jtag/drivers/cmsis_dap_usb.c b/src/jtag/drivers/cmsis_dap_usb.c index d607b7f59e..6a7ca2595d 100644 --- a/src/jtag/drivers/cmsis_dap_usb.c +++ b/src/jtag/drivers/cmsis_dap_usb.c @@ -1,4 +1,7 @@ /*************************************************************************** + * Copyright (C) 2014 by Paul Fertser * + * fercerpav@gmail.com * + * * * Copyright (C) 2013 by mike brown * * mike@theshedworks.org.uk * * * @@ -33,12 +36,6 @@ #include -#ifdef _DEBUG_JTAG_IO_ - #define DEBUG_IO(expr...) LOG_DEBUG(expr) -#else - #define DEBUG_IO(expr...) do {} while (0) -#endif - /* * See CMSIS-DAP documentation: * Version 0.01 - Beta. @@ -155,6 +152,17 @@ struct cmsis_dap { uint8_t mode; }; +struct pending_transfer_result { + uint8_t cmd; + uint32_t data; + void *buffer; +}; + +static int pending_transfer_count, pending_queue_len; +static struct pending_transfer_result *pending_transfers; + +static int queued_retval; + static struct cmsis_dap *cmsis_dap_handle; static int cmsis_dap_usb_open(void) @@ -263,6 +271,8 @@ static int cmsis_dap_usb_open(void) int packet_size = PACKET_SIZE; /* atmel cmsis-dap uses 512 byte reports */ + /* TODO: HID report descriptor should be parsed instead of + * hardcoding a match by VID */ if (target_vid == 0x03eb) packet_size = 512 + 1; @@ -282,18 +292,13 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap) hid_close(dap->dev_handle); hid_exit(); - if (cmsis_dap_handle->packet_buffer) - free(cmsis_dap_handle->packet_buffer); - - if (cmsis_dap_handle) { - free(cmsis_dap_handle); - cmsis_dap_handle = NULL; - } - - if (cmsis_dap_serial) { - free(cmsis_dap_serial); - cmsis_dap_serial = NULL; - } + free(cmsis_dap_handle->packet_buffer); + free(cmsis_dap_handle); + cmsis_dap_handle = NULL; + free(cmsis_dap_serial); + cmsis_dap_serial = NULL; + free(pending_transfers); + pending_transfers = NULL; return; } @@ -302,7 +307,7 @@ static void cmsis_dap_usb_close(struct cmsis_dap *dap) static int cmsis_dap_usb_xfer(struct cmsis_dap *dap, int txlen) { /* Pad the rest of the TX buffer with 0's */ - memset(dap->packet_buffer + txlen, 0, dap->packet_size - 1 - txlen); + memset(dap->packet_buffer + txlen, 0, dap->packet_size - txlen); /* write data to device */ int retval = hid_write(dap->dev_handle, dap->packet_buffer, dap->packet_size); @@ -449,7 +454,7 @@ static int cmsis_dap_cmd_DAP_Disconnect(void) return ERROR_OK; } -static int cmsis_dap_cmd_DAP_TFER_Configure(uint8_t idle, uint16_t delay, uint16_t retry) +static int cmsis_dap_cmd_DAP_TFER_Configure(uint8_t idle, uint16_t retry_count, uint16_t match_retry) { int retval; uint8_t *buffer = cmsis_dap_handle->packet_buffer; @@ -457,10 +462,10 @@ static int cmsis_dap_cmd_DAP_TFER_Configure(uint8_t idle, uint16_t delay, uint16 buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_TFER_CONFIGURE; buffer[2] = idle; - buffer[3] = delay & 0xff; - buffer[4] = (delay >> 8) & 0xff; - buffer[5] = retry & 0xff; - buffer[6] = (retry >> 8) & 0xff; + buffer[3] = retry_count & 0xff; + buffer[4] = (retry_count >> 8) & 0xff; + buffer[5] = match_retry & 0xff; + buffer[6] = (match_retry >> 8) & 0xff; retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 7); if (retval != ERROR_OK || buffer[1] != DAP_OK) { @@ -510,75 +515,139 @@ static int cmsis_dap_cmd_DAP_Delay(uint16_t delay_us) } #endif -static int queued_retval; - -static void cmsis_dap_swd_read_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t *value) +static int cmsis_dap_swd_run_queue(struct adiv5_dap *dap) { - if (queued_retval != ERROR_OK) - return; - uint8_t *buffer = cmsis_dap_handle->packet_buffer; - int retval; - uint32_t val; - DEBUG_IO("CMSIS-DAP: Read Reg 0x%02" PRIx8, cmd); + LOG_DEBUG("Executing %d queued transactions", pending_transfer_count); - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_TFER; - buffer[2] = 0x00; - buffer[3] = 0x01; - buffer[4] = cmd; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 5); - - /* TODO - need better response checking */ - if (retval != ERROR_OK || buffer[1] != 0x01) { - LOG_ERROR("CMSIS-DAP: Read Error (0x%02" PRIx8 ")", buffer[2]); - queued_retval = buffer[2]; - return; + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skipping due to previous errors: %d", queued_retval); + goto skip; + } + + if (!pending_transfer_count) + goto skip; + + size_t idx = 0; + buffer[idx++] = 0; /* report number */ + buffer[idx++] = CMD_DAP_TFER; + buffer[idx++] = 0x00; /* DAP Index */ + buffer[idx++] = pending_transfer_count; + + for (int i = 0; i < pending_transfer_count; i++) { + uint8_t cmd = pending_transfers[i].cmd; + uint32_t data = pending_transfers[i].data; + + LOG_DEBUG("%s %s reg %x %"PRIx32, + cmd & SWD_CMD_APnDP ? "AP" : "DP", + cmd & SWD_CMD_RnW ? "read" : "write", + (cmd & SWD_CMD_A32) >> 1, data); + + /* When proper WAIT handling is implemented in the + * common SWD framework, this kludge can be + * removed. However, this might lead to minor + * performance degradation as the adapter wouldn't be + * able to automatically retry anything (because ARM + * has forgotten to implement sticky error flags + * clearing). See also comments regarding + * cmsis_dap_cmd_DAP_TFER_Configure() and + * cmsis_dap_cmd_DAP_SWD_Configure() in + * cmsis_dap_init(). + */ + if (!(cmd & SWD_CMD_RnW) && + !(cmd & SWD_CMD_APnDP) && + (cmd & SWD_CMD_A32) >> 1 == DP_CTRL_STAT && + (data & CORUNDETECT)) { + LOG_DEBUG("refusing to enable sticky overrun detection"); + data &= ~CORUNDETECT; + } + + buffer[idx++] = (cmd >> 1) & 0x0f; + if (!(cmd & SWD_CMD_RnW)) { + buffer[idx++] = (data) & 0xff; + buffer[idx++] = (data >> 8) & 0xff; + buffer[idx++] = (data >> 16) & 0xff; + buffer[idx++] = (data >> 24) & 0xff; + } + } + + queued_retval = cmsis_dap_usb_xfer(cmsis_dap_handle, idx); + if (queued_retval != ERROR_OK) + goto skip; + + idx = 2; + uint8_t ack = buffer[idx] & 0x07; + if (ack != SWD_ACK_OK || (buffer[idx] & 0x08)) { + LOG_DEBUG("SWD ack not OK: %d %s", buffer[idx-1], + ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK"); + queued_retval = ack == SWD_ACK_WAIT ? ERROR_WAIT : ERROR_FAIL; + goto skip; } + idx++; + + if (pending_transfer_count != buffer[1]) + LOG_ERROR("CMSIS-DAP transfer count mismatch: expected %d, got %d", + pending_transfer_count, buffer[1]); + + for (int i = 0; i < buffer[1]; i++) { + if (pending_transfers[i].cmd & SWD_CMD_RnW) { + static uint32_t last_read; + uint32_t data = le_to_h_u32(&buffer[idx]); + uint32_t tmp = data; + idx += 4; + + LOG_DEBUG("Read result: %"PRIx32, data); + + /* Imitate posted AP reads */ + if ((pending_transfers[i].cmd & SWD_CMD_APnDP) || + ((pending_transfers[i].cmd & SWD_CMD_A32) >> 1 == DP_RDBUFF)) { + tmp = last_read; + last_read = data; + } - val = le_to_h_u32(&buffer[3]); - DEBUG_IO("0x%08" PRIx32, val); + if (pending_transfers[i].buffer) + *(uint32_t *)pending_transfers[i].buffer = tmp; + } + } - if (value) - *value = val; +skip: + pending_transfer_count = 0; + int retval = queued_retval; + queued_retval = ERROR_OK; - queued_retval = retval; + return retval; } -static void cmsis_dap_swd_write_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t value) +static void cmsis_dap_swd_queue_cmd(struct adiv5_dap *dap, uint8_t cmd, uint32_t *dst, uint32_t data) { + if (pending_transfer_count == pending_queue_len) { + /* Not enough room in the queue. Run the queue. */ + queued_retval = cmsis_dap_swd_run_queue(dap); + } + if (queued_retval != ERROR_OK) return; - uint8_t *buffer = cmsis_dap_handle->packet_buffer; - - DEBUG_IO("CMSIS-DAP: Write Reg 0x%02" PRIx8 " 0x%08" PRIx32, cmd, value); - - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_TFER; - buffer[2] = 0x00; - buffer[3] = 0x01; - buffer[4] = cmd; - buffer[5] = (value) & 0xff; - buffer[6] = (value >> 8) & 0xff; - buffer[7] = (value >> 16) & 0xff; - buffer[8] = (value >> 24) & 0xff; - int retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 9); - - if (buffer[1] != 0x01) { - LOG_ERROR("CMSIS-DAP: Write Error (0x%02" PRIx8 ")", buffer[2]); - retval = buffer[2]; + pending_transfers[pending_transfer_count].data = data; + pending_transfers[pending_transfer_count].cmd = cmd; + if (cmd & SWD_CMD_RnW) { + /* Queue a read transaction */ + pending_transfers[pending_transfer_count].buffer = dst; } + pending_transfer_count++; +} - queued_retval = retval; +static void cmsis_dap_swd_write_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t value) +{ + assert(!(cmd & SWD_CMD_RnW)); + cmsis_dap_swd_queue_cmd(dap, cmd, NULL, value); } -static int cmsis_dap_swd_run(struct adiv5_dap *dap) +static void cmsis_dap_swd_read_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t *value) { - int retval = queued_retval; - queued_retval = ERROR_OK; - return retval; + assert(cmd & SWD_CMD_RnW); + cmsis_dap_swd_queue_cmd(dap, cmd, value, 0); } static int cmsis_dap_get_version_info(void) @@ -638,125 +707,59 @@ static int cmsis_dap_get_status(void) return retval; } -static int cmsis_dap_reset_link(void) +static int cmsis_dap_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq) { uint8_t *buffer = cmsis_dap_handle->packet_buffer; + const uint8_t *s; + unsigned int s_len; + int retval; - LOG_DEBUG("CMSIS-DAP: cmsis_dap_reset_link"); - LOG_INFO("DAP_SWJ Sequence (reset: 50+ '1' followed by 0)"); - - /* reset line with SWDIO high for >50 cycles */ - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_SWJ_SEQ; - buffer[2] = 7 * 8; - buffer[3] = 0xff; - buffer[4] = 0xff; - buffer[5] = 0xff; - buffer[6] = 0xff; - buffer[7] = 0xff; - buffer[8] = 0xff; - buffer[9] = 0xff; - int retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 10); - - if (retval != ERROR_OK || buffer[1] != DAP_OK) - return ERROR_FAIL; - - /* 16bit JTAG-SWD sequence */ - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_SWJ_SEQ; - buffer[2] = 2 * 8; - buffer[3] = 0x9e; - buffer[4] = 0xe7; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 5); + /* When we are reconnecting, DAP_Connect needs to be rerun, at + * least on Keil ULINK-ME */ + retval = cmsis_dap_cmd_DAP_Connect(seq == LINE_RESET || seq == JTAG_TO_SWD ? + CONNECT_SWD : CONNECT_JTAG); + if (retval != ERROR_OK) + return retval; - if (retval != ERROR_OK || buffer[1] != DAP_OK) + switch (seq) { + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + s = swd_seq_line_reset; + s_len = swd_seq_line_reset_len; + break; + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + s = swd_seq_jtag_to_swd; + s_len = swd_seq_jtag_to_swd_len; + break; + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + s = swd_seq_swd_to_jtag; + s_len = swd_seq_swd_to_jtag_len; + break; + default: + LOG_ERROR("Sequence %d not supported", seq); return ERROR_FAIL; + } - /* another reset just incase */ buffer[0] = 0; /* report number */ buffer[1] = CMD_DAP_SWJ_SEQ; - buffer[2] = 7 * 8; - buffer[3] = 0xff; - buffer[4] = 0xff; - buffer[5] = 0xff; - buffer[6] = 0xff; - buffer[7] = 0xff; - buffer[8] = 0xff; - buffer[9] = 0xff; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 10); - - if (retval != ERROR_OK || buffer[1] != DAP_OK) - return ERROR_FAIL; + buffer[2] = s_len; + bit_copy(&buffer[3], 0, s, 0, s_len); - /* 16 cycle idle period */ - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_SWJ_SEQ; - buffer[2] = 2 * 8; - buffer[3] = 0x00; - buffer[4] = 0x00; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 5); + retval = cmsis_dap_usb_xfer(cmsis_dap_handle, DIV_ROUND_UP(s_len, 8) + 3); if (retval != ERROR_OK || buffer[1] != DAP_OK) return ERROR_FAIL; - DEBUG_IO("DAP Read IDCODE"); - - /* read the id code is always the next sequence */ - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_TFER; - buffer[2] = 0x00; - buffer[3] = 0x01; - buffer[4] = 0x02; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 5); - - if (retval != ERROR_OK) - return retval; - - if (buffer[1] == 0) { - LOG_DEBUG("Result 0x%02" PRIx8 " 0x%02" PRIx8, buffer[1], buffer[2]); - - LOG_DEBUG("DAP Reset Target"); - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_RESET_TARGET; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 2); - LOG_DEBUG("Result 0x%02" PRIx8 " 0x%02" PRIx8, buffer[1], buffer[2]); - - LOG_DEBUG("DAP Write Abort"); - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_WRITE_ABORT; - buffer[2] = 0x00; - buffer[3] = 0x1e/*0x1f*/; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 7); - LOG_DEBUG("Result 0x%02" PRIx8, buffer[1]); - - return 0x80 + buffer[1]; - } - - LOG_DEBUG("DAP Write Abort"); - buffer[0] = 0; /* report number */ - buffer[1] = CMD_DAP_WRITE_ABORT; - buffer[2] = 0x00; - buffer[3] = 0x1e; - buffer[4] = 0x00; - buffer[5] = 0x00; - buffer[6] = 0x00; - retval = cmsis_dap_usb_xfer(cmsis_dap_handle, 7); - LOG_DEBUG("Result 0x%02" PRIx8, buffer[1]); - - return retval; + return ERROR_OK; } static int cmsis_dap_swd_open(void) { int retval; - DEBUG_IO("CMSIS-DAP: cmsis_dap_swd_open"); - if (cmsis_dap_handle == NULL) { - /* SWD init */ retval = cmsis_dap_usb_open(); if (retval != ERROR_OK) @@ -829,6 +832,16 @@ static int cmsis_dap_init(void) if (data[0] == 2) { /* short */ uint16_t pkt_sz = data[1] + (data[2] << 8); + /* 4 bytes of command header + 5 bytes per register + * write. For bulk read sequences just 4 bytes are + * needed per transfer, so this is suboptimal. */ + pending_queue_len = (pkt_sz - 4) / 5; + pending_transfers = malloc(pending_queue_len * sizeof(*pending_transfers)); + if (!pending_transfers) { + LOG_ERROR("Unable to allocate memory for CMSIS-DAP queue"); + return ERROR_FAIL; + } + if (cmsis_dap_handle->packet_size != pkt_sz + 1) { /* reallocate buffer */ cmsis_dap_handle->packet_size = pkt_sz + 1; @@ -860,14 +873,19 @@ static int cmsis_dap_init(void) /* Now try to connect to the target * TODO: This is all SWD only @ present */ - retval = cmsis_dap_cmd_DAP_SWJ_Clock(100); /* 100kHz */ + retval = cmsis_dap_cmd_DAP_SWJ_Clock(jtag_get_speed_khz()); if (retval != ERROR_OK) return ERROR_FAIL; + /* Ask CMSIS-DAP to automatically retry on receiving WAIT for + * up to 64 times. This must be changed to 0 if sticky + * overrun detection is enabled. */ retval = cmsis_dap_cmd_DAP_TFER_Configure(0, 64, 0); if (retval != ERROR_OK) return ERROR_FAIL; - retval = cmsis_dap_cmd_DAP_SWD_Configure(0x00); + /* Data Phase (bit 2) must be set to 1 if sticky overrun + * detection is enabled */ + retval = cmsis_dap_cmd_DAP_SWD_Configure(0); /* 1 TRN, no Data Phase */ if (retval != ERROR_OK) return ERROR_FAIL; @@ -887,11 +905,7 @@ static int cmsis_dap_init(void) } } - retval = cmsis_dap_reset_link(); - if (retval != ERROR_OK) - return ERROR_FAIL; - - cmsis_dap_cmd_DAP_LED(0x00); /* Both LEDs off */ + cmsis_dap_cmd_DAP_LED(0x00); /* Both LEDs off */ LOG_INFO("CMSIS-DAP: Interface ready"); @@ -985,6 +999,14 @@ static int cmsis_dap_khz(int khz, int *jtag_speed) return ERROR_OK; } +static int_least32_t cmsis_dap_swd_frequency(struct adiv5_dap *dap, int_least32_t hz) +{ + if (hz > 0) + cmsis_dap_speed(hz / 1000); + + return hz; +} + COMMAND_HANDLER(cmsis_dap_handle_info_command) { if (cmsis_dap_get_version_info() == ERROR_OK) @@ -1082,12 +1104,14 @@ static const struct command_registration cmsis_dap_command_handlers[] = { static const struct swd_driver cmsis_dap_swd_driver = { .init = cmsis_dap_swd_init, + .frequency = cmsis_dap_swd_frequency, + .switch_seq = cmsis_dap_swd_switch_seq, .read_reg = cmsis_dap_swd_read_reg, .write_reg = cmsis_dap_swd_write_reg, - .run = cmsis_dap_swd_run, + .run = cmsis_dap_swd_run_queue, }; -const char *cmsis_dap_transport[] = {"cmsis-dap", NULL}; +static const char * const cmsis_dap_transport[] = { "swd", NULL }; struct jtag_interface cmsis_dap_interface = { .name = "cmsis-dap", diff --git a/src/jtag/swd.h b/src/jtag/swd.h index d6746dd32e..41baec175d 100644 --- a/src/jtag/swd.h +++ b/src/jtag/swd.h @@ -215,6 +215,5 @@ int swd_init_reset(struct command_context *cmd_ctx); void swd_add_reset(int req_srst); bool transport_is_swd(void); -bool transport_is_cmsis_dap(void); #endif /* SWD_H */ diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c index 11687b9c1a..7f08b00fb1 100644 --- a/src/jtag/tcl.c +++ b/src/jtag/tcl.c @@ -554,8 +554,7 @@ static int jim_newtap_cmd(Jim_GetOptInfo *goi) pTap->chip, pTap->tapname, pTap->dotted_name, goi->argc); if (!transport_is_jtag()) { - /* SWD or CMSIS-DAP (which is currently SWD-only) doesn't - require any JTAG tap parameters */ + /* SWD doesn't require any JTAG tap parameters */ pTap->enabled = true; jtag_tap_init(pTap); return JIM_OK; diff --git a/src/target/Makefile.am b/src/target/Makefile.am index c5911036f1..2cec491e9b 100644 --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -93,7 +93,6 @@ ARM_DEBUG_SRC = \ arm_adi_v5.c \ adi_v5_jtag.c \ adi_v5_swd.c \ - adi_v5_cmsis_dap.c \ embeddedice.c \ trace.c \ etb.c \ diff --git a/src/target/adi_v5_cmsis_dap.c b/src/target/adi_v5_cmsis_dap.c deleted file mode 100644 index 9cdf08abef..0000000000 --- a/src/target/adi_v5_cmsis_dap.c +++ /dev/null @@ -1,325 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2013 by mike brown * - * mike@theshedworks.org.uk * - * * - * Copyright (C) 2013 by Spencer Oliver * - * spen@spen-soft.co.uk * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - ***************************************************************************/ - -/** - * @file - * Utilities to support ARM "CMSIS-DAP", The CoreSight Debug Access Port. - * This is coupled to recent versions of ARM's "CoreSight" debug framework. - * This specific code is a transport level interface, with - * "target/arm_adi_v5.[hc]" code understanding operation semantics, - * shared with the SWD & JTAG transports. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "arm.h" -#include "arm_adi_v5.h" -#include - -#include -#include - -#include - -#define CMSIS_CMD_DP (0 << 0) /* set only for AP access */ -#define CMSIS_CMD_AP (1 << 0) /* set only for AP access */ -#define CMSIS_CMD_READ (1 << 1) /* set only for read access */ -#define CMSIS_CMD_WRITE (0 << 1) /* set only for read access */ -#define CMSIS_CMD_A32(n) ((n)&0x0C) /* bits A[3:2] of register addr */ -#define CMSIS_CMD_VAL_MATCH (1 << 4) /* Value Match */ -#define CMSIS_CMD_MATCH_MSK (1 << 5) /* Match Mask */ - -/* YUK! - but this is currently a global.... */ -extern struct jtag_interface *jtag_interface; - -static int cmsis_dap_clear_sticky_errors(struct adiv5_dap *dap) -{ - LOG_DEBUG(" "); - - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - - swd->write_reg(dap, (CMSIS_CMD_DP | CMSIS_CMD_WRITE | CMSIS_CMD_A32(DP_ABORT)), - STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR); - return ERROR_OK; -} - -static int cmsis_dap_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack) -{ - LOG_DEBUG(" "); - - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - - swd->write_reg(dap, (CMSIS_CMD_DP | CMSIS_CMD_WRITE | CMSIS_CMD_A32(DP_ABORT)), - DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR); - return ERROR_OK; -} - -static int cmsis_dap_queue_dp_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data) -{ - LOG_DEBUG("reg = %d", reg); - - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - - swd->read_reg(dap, (CMSIS_CMD_DP | CMSIS_CMD_READ | CMSIS_CMD_A32(reg)), data); - return ERROR_OK; -} - -static int (cmsis_dap_queue_dp_write)(struct adiv5_dap *dap, unsigned reg, uint32_t data) -{ - LOG_DEBUG("reg = %d, data = 0x%08" PRIx32, reg, data); - - /* setting the ORUNDETECT bit causes issues for some targets, - * disable until we find out why */ - if (reg == DP_CTRL_STAT) { - LOG_DEBUG("disabling overrun detection"); - data &= ~CORUNDETECT; - } - - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - - swd->write_reg(dap, (CMSIS_CMD_DP | CMSIS_CMD_WRITE | CMSIS_CMD_A32(reg)), data); - return ERROR_OK; -} - -/** Select the AP register bank matching bits 7:4 of reg. */ -static int cmsis_dap_ap_q_bankselect(struct adiv5_dap *dap, unsigned reg) -{ - uint32_t select_ap_bank = reg & 0x000000F0; - - if (select_ap_bank == dap->ap_bank_value) - return ERROR_OK; - - dap->ap_bank_value = select_ap_bank; - select_ap_bank |= dap->ap_current; - - cmsis_dap_queue_dp_write(dap, DP_SELECT, select_ap_bank); - return ERROR_OK; -} - -static int (cmsis_dap_queue_ap_read)(struct adiv5_dap *dap, unsigned reg, uint32_t *data) -{ - cmsis_dap_ap_q_bankselect(dap, reg); - - LOG_DEBUG("reg = %d", reg); - - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - - swd->read_reg(dap, (CMSIS_CMD_AP | CMSIS_CMD_READ | CMSIS_CMD_A32(reg)), data); - - return ERROR_OK; -} - -static int (cmsis_dap_queue_ap_write)(struct adiv5_dap *dap, unsigned reg, uint32_t data) -{ - /* TODO: CSW_DBGSWENABLE (bit31) causes issues for some targets - * disable until we find out why */ - if (reg == AP_REG_CSW) - data &= ~CSW_DBGSWENABLE; - - cmsis_dap_ap_q_bankselect(dap, reg); - - LOG_DEBUG("reg = %d, data = 0x%08" PRIx32, reg, data); - - const struct swd_driver *swd = jtag_interface->swd; - assert(swd); - - swd->write_reg(dap, (CMSIS_CMD_AP | CMSIS_CMD_WRITE | CMSIS_CMD_A32(reg)), data); - - return ERROR_OK; -} - -/** Executes all queued DAP operations. */ -static int cmsis_dap_run(struct adiv5_dap *dap) -{ - LOG_DEBUG(" "); - /* - Some debug dongles do more than asked for(e.g. EDBG from - Atmel) behind the scene and issuing an AP write - may result in more than just APACC SWD transaction, which in - turn can possibly set sticky error bit in CTRL/STAT register - of the DP(an example would be writing SYSRESETREQ to AIRCR). - Such adapters may interpret CMSIS-DAP secification - differently and not guarantee to be report those failures - via status byte of the return USB packet from CMSIS-DAP, so - we need to check CTRL/STAT and if that happens to clear it. - Note that once the CMSIS-DAP SWD implementation starts queueing - transfers this will cause loss of the transfers after the - failed one. At least a warning is printed. - */ - uint32_t ctrlstat; - cmsis_dap_queue_dp_read(dap, DP_CTRL_STAT, &ctrlstat); - - int retval = jtag_interface->swd->run(dap); - - if (retval == ERROR_OK && (ctrlstat & SSTICKYERR)) - LOG_WARNING("Adapter returned success despite SSTICKYERR being set."); - - if (retval != ERROR_OK || (ctrlstat & SSTICKYERR)) - cmsis_dap_clear_sticky_errors(dap); - - return retval; -} - -const struct dap_ops cmsis_dap_ops = { - .is_swd = true, - .queue_dp_read = cmsis_dap_queue_dp_read, - .queue_dp_write = cmsis_dap_queue_dp_write, - .queue_ap_read = cmsis_dap_queue_ap_read, - .queue_ap_write = cmsis_dap_queue_ap_write, - .queue_ap_abort = cmsis_dap_queue_ap_abort, - .run = cmsis_dap_run, -}; - -static const struct command_registration cmsis_dap_commands[] = { - { - /* - * Set up SWD and JTAG targets identically, unless/until - * infrastructure improves ... meanwhile, ignore all - * JTAG-specific stuff like IR length for SWD. - * - * REVISIT can we verify "just one SWD DAP" here/early? - */ - .name = "newdap", - .jim_handler = jim_jtag_newtap, - .mode = COMMAND_CONFIG, - .help = "declare a new CMSIS-DAP" - }, - COMMAND_REGISTRATION_DONE -}; - -static const struct command_registration cmsis_dap_handlers[] = { - { - .name = "cmsis-dap", - .mode = COMMAND_ANY, - .help = "cmsis_dap command group", - .chain = cmsis_dap_commands, - }, - COMMAND_REGISTRATION_DONE -}; - -static int cmsis_dap_select(struct command_context *ctx) -{ - LOG_DEBUG("CMSIS-ADI: cmsis_dap_select"); - - int retval = register_commands(ctx, NULL, cmsis_dap_handlers); - - if (retval != ERROR_OK) - return retval; - - /* FIXME: This needs a real overhaul!! FIXME - * be sure driver is in SWD mode; start - * with hardware default TRN (1), it can be changed later - * we use a bogus 'swd' driver to implement cmsis-dap as it is quite similar */ - - const struct swd_driver *swd = jtag_interface->swd; - if (!swd || !swd->read_reg || !swd->write_reg || !swd->init) { - LOG_ERROR("no SWD driver?"); - return ERROR_FAIL; - } - - retval = swd->init(); - if (retval != ERROR_OK) { - LOG_ERROR("unable to init CMSIS-DAP driver"); - return retval; - } - - return retval; -} - -static int cmsis_dap_init(struct command_context *ctx) -{ - struct target *target = get_current_target(ctx); - struct arm *arm = target_to_arm(target); - struct adiv5_dap *dap = arm->dap; - uint32_t idcode; - int status; - - LOG_DEBUG("CMSIS-ADI init"); - - /* Force the DAP's ops vector for CMSIS-DAP mode. - * messy - is there a better way? */ - arm->dap->ops = &cmsis_dap_ops; - - /* FIXME validate transport config ... is the - * configured DAP present (check IDCODE)? - * Is *only* one DAP configured? - * - * MUST READ IDCODE - */ - - /* Note, debugport_init() does setup too */ - -#if 0 - const struct swd_driver *swd = jtag_interface->swd; - if (!swd || !swd->read_reg || !swd->write_reg || !swd->init) { - LOG_ERROR("no SWD driver?"); - return ERROR_FAIL; - } - - int retval = swd->init(1); - if (retval != ERROR_OK) { - LOG_ERROR("unable to init CMSIS-DAP driver"); - return retval; - } -#endif - - status = cmsis_dap_queue_dp_read(dap, DP_IDCODE, &idcode); - - if (status == ERROR_OK) - LOG_INFO("IDCODE 0x%08" PRIx32, idcode); - - /* force clear all sticky faults */ - cmsis_dap_clear_sticky_errors(dap); - - /* this is a workaround to get polling working */ - jtag_add_reset(0, 0); - - return status; -} - -static struct transport cmsis_dap_transport = { - .name = "cmsis-dap", - .select = cmsis_dap_select, - .init = cmsis_dap_init, -}; - -static void cmsis_dap_constructor(void) __attribute__((constructor)); -static void cmsis_dap_constructor(void) -{ - transport_register(&cmsis_dap_transport); -} - -/** Returns true if the current debug session - * is using CMSIS-DAP as its transport. - */ -bool transport_is_cmsis_dap(void) -{ - return get_current_transport() == &cmsis_dap_transport; -} diff --git a/tcl/target/swj-dp.tcl b/tcl/target/swj-dp.tcl index 4f2b49692b..f759e7c8df 100644 --- a/tcl/target/swj-dp.tcl +++ b/tcl/target/swj-dp.tcl @@ -30,7 +30,5 @@ proc swj_newdap {chip tag args} { eval jtag newtap $chip $tag $args } elseif [using_swd] { eval swd newdap $chip $tag $args - } elseif [string equal [transport select] "cmsis-dap"] { - eval cmsis-dap newdap $chip $tag $args } }