// SPDX-License-Identifier: GPL-2.0-or-later /*************************************************************************** * Copyright (C) 2017 by Texas Instruments, Inc. * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include /* XDS110 stand-alone probe voltage supply limits */ #define XDS110_MIN_VOLTAGE 1800 #define XDS110_MAX_VOLTAGE 3600 /* XDS110 stand-alone probe hardware ID */ #define XDS110_STAND_ALONE_ID 0x21 /* Firmware version that introduced OpenOCD support via block accesses */ #define OCD_FIRMWARE_VERSION 0x02030011 #define OCD_FIRMWARE_UPGRADE \ "XDS110: upgrade to version 2.3.0.11+ for improved support" /* Firmware version that introduced improved TCK performance */ #define FAST_TCK_FIRMWARE_VERSION 0x03000000 /* Firmware version that introduced 10 MHz and 12 MHz TCK support */ #define FAST_TCK_PLUS_FIRMWARE_VERSION 0x03000003 /*************************************************************************** * USB Connection Buffer Definitions * ***************************************************************************/ /* Max USB packet size for up to USB 3.0 */ #define MAX_PACKET 1024 /* * Maximum data payload that can be handled in a single call * Limitation is the size of the buffers in the XDS110 firmware */ #define MAX_DATA_BLOCK 4096 #ifndef USB_PAYLOAD_SIZE /* Largest data block plus parameters */ #define USB_PAYLOAD_SIZE (MAX_DATA_BLOCK + 60) #endif #define MAX_RESULT_QUEUE (MAX_DATA_BLOCK / 4) /*************************************************************************** * XDS110 Firmware API Definitions * ***************************************************************************/ /* * Default values controlling how the host communicates commands * with XDS110 firmware (automatic retry count and wait timeout) */ #define DEFAULT_ATTEMPTS (1) #define DEFAULT_TIMEOUT (4000) /* XDS110 API error codes */ #define SC_ERR_NONE 0 #define SC_ERR_XDS110_FAIL -261 #define SC_ERR_SWD_WAIT -613 #define SC_ERR_SWD_FAULT -614 #define SC_ERR_SWD_PROTOCOL -615 #define SC_ERR_SWD_PARITY -616 #define SC_ERR_SWD_DEVICE_ID -617 /* TCK frequency limits */ #define XDS110_MIN_TCK_SPEED 100 /* kHz */ #define XDS110_MAX_SLOW_TCK_SPEED 2500 /* kHz */ #define XDS110_MAX_FAST_TCK_SPEED 14000 /* kHz */ #define XDS110_DEFAULT_TCK_SPEED 2500 /* kHz */ /* Fixed TCK delay values for "Fast" TCK frequencies */ #define FAST_TCK_DELAY_14000_KHZ 0 #define FAST_TCK_DELAY_10000_KHZ 0xfffffffd #define FAST_TCK_DELAY_12000_KHZ 0xfffffffe #define FAST_TCK_DELAY_8500_KHZ 1 #define FAST_TCK_DELAY_5500_KHZ 2 /* For TCK frequencies below 5500 kHz, use calculated delay */ /* Scan mode on connect */ #define MODE_JTAG 1 /* XDS110 API JTAG state definitions */ #define XDS_JTAG_STATE_RESET 1 #define XDS_JTAG_STATE_IDLE 2 #define XDS_JTAG_STATE_SHIFT_DR 3 #define XDS_JTAG_STATE_SHIFT_IR 4 #define XDS_JTAG_STATE_PAUSE_DR 5 #define XDS_JTAG_STATE_PAUSE_IR 6 #define XDS_JTAG_STATE_EXIT1_DR 8 #define XDS_JTAG_STATE_EXIT1_IR 9 #define XDS_JTAG_STATE_EXIT2_DR 10 #define XDS_JTAG_STATE_EXIT2_IR 11 #define XDS_JTAG_STATE_SELECT_DR 12 #define XDS_JTAG_STATE_SELECT_IR 13 #define XDS_JTAG_STATE_UPDATE_DR 14 #define XDS_JTAG_STATE_UPDATE_IR 15 #define XDS_JTAG_STATE_CAPTURE_DR 16 #define XDS_JTAG_STATE_CAPTURE_IR 17 /* XDS110 API JTAG transit definitions */ #define XDS_JTAG_TRANSIT_QUICKEST 1 #define XDS_JTAG_TRANSIT_VIA_CAPTURE 2 #define XDS_JTAG_TRANSIT_VIA_IDLE 3 /* DAP register definitions as used by XDS110 APIs */ #define DAP_AP 0 /* DAP AP register type */ #define DAP_DP 1 /* DAP DP register type */ #define DAP_DP_IDCODE 0x0 /* DAP DP IDCODE register (read only) */ #define DAP_DP_ABORT 0x0 /* DAP DP ABORT register (write only) */ #define DAP_DP_STAT 0x4 /* DAP DP STAT register (for read only) */ #define DAP_DP_CTRL 0x4 /* DAP DP CTRL register (for write only) */ #define DAP_DP_ADDR 0x8 /* DAP DP SELECT register (legacy name) */ #define DAP_DP_RESEND 0x8 /* DAP DP RESEND register (read only) */ #define DAP_DP_SELECT 0x8 /* DAP DP SELECT register (write only) */ #define DAP_DP_RDBUFF 0xc /* DAP DP RDBUFF Read Buffer register */ #define DAP_AP_CSW 0x00 /* DAP AP Control Status Word */ #define DAP_AP_TAR 0x04 /* DAP AP Transfer Address */ #define DAP_AP_DRW 0x0C /* DAP AP Data Read/Write */ #define DAP_AP_BD0 0x10 /* DAP AP Banked Data 0 */ #define DAP_AP_BD1 0x14 /* DAP AP Banked Data 1 */ #define DAP_AP_BD2 0x18 /* DAP AP Banked Data 2 */ #define DAP_AP_BD3 0x1C /* DAP AP Banked Data 3 */ #define DAP_AP_RTBL 0xF8 /* DAP AP Debug ROM Table */ #define DAP_AP_IDR 0xFC /* DAP AP Identification Register */ /* Command packet definitions */ #define XDS_OUT_LEN 1 /* command (byte) */ #define XDS_IN_LEN 4 /* error code (int) */ /* XDS API Commands */ #define XDS_CONNECT 0x01 /* Connect JTAG connection */ #define XDS_DISCONNECT 0x02 /* Disconnect JTAG connection */ #define XDS_VERSION 0x03 /* Get firmware version and hardware ID */ #define XDS_SET_TCK 0x04 /* Set TCK delay (to set TCK frequency) */ #define XDS_SET_TRST 0x05 /* Assert or deassert nTRST signal */ #define XDS_CYCLE_TCK 0x07 /* Toggle TCK for a number of cycles */ #define XDS_GOTO_STATE 0x09 /* Go to requested JTAG state */ #define XDS_JTAG_SCAN 0x0c /* Send and receive JTAG scan */ #define XDS_SET_SRST 0x0e /* Assert or deassert nSRST signal */ #define CMAPI_CONNECT 0x0f /* CMAPI connect */ #define CMAPI_DISCONNECT 0x10 /* CMAPI disconnect */ #define CMAPI_ACQUIRE 0x11 /* CMAPI acquire */ #define CMAPI_RELEASE 0x12 /* CMAPI release */ #define CMAPI_REG_READ 0x15 /* CMAPI DAP register read */ #define CMAPI_REG_WRITE 0x16 /* CMAPI DAP register write */ #define SWD_CONNECT 0x17 /* Switch from JTAG to SWD connection */ #define SWD_DISCONNECT 0x18 /* Switch from SWD to JTAG connection */ #define CJTAG_CONNECT 0x2b /* Switch from JTAG to cJTAG connection */ #define CJTAG_DISCONNECT 0x2c /* Switch from cJTAG to JTAG connection */ #define XDS_SET_SUPPLY 0x32 /* Set up stand-alone probe upply voltage */ #define OCD_DAP_REQUEST 0x3a /* Handle block of DAP requests */ #define OCD_SCAN_REQUEST 0x3b /* Handle block of JTAG scan requests */ #define OCD_PATHMOVE 0x3c /* Handle PATHMOVE to navigate JTAG states */ #define CMD_IR_SCAN 1 #define CMD_DR_SCAN 2 #define CMD_RUNTEST 3 #define CMD_STABLECLOCKS 4 /* Array to convert from OpenOCD tap_state_t to XDS JTAG state */ static const uint32_t xds_jtag_state[] = { XDS_JTAG_STATE_EXIT2_DR, /* TAP_DREXIT2 = 0x0 */ XDS_JTAG_STATE_EXIT1_DR, /* TAP_DREXIT1 = 0x1 */ XDS_JTAG_STATE_SHIFT_DR, /* TAP_DRSHIFT = 0x2 */ XDS_JTAG_STATE_PAUSE_DR, /* TAP_DRPAUSE = 0x3 */ XDS_JTAG_STATE_SELECT_IR, /* TAP_IRSELECT = 0x4 */ XDS_JTAG_STATE_UPDATE_DR, /* TAP_DRUPDATE = 0x5 */ XDS_JTAG_STATE_CAPTURE_DR, /* TAP_DRCAPTURE = 0x6 */ XDS_JTAG_STATE_SELECT_DR, /* TAP_DRSELECT = 0x7 */ XDS_JTAG_STATE_EXIT2_IR, /* TAP_IREXIT2 = 0x8 */ XDS_JTAG_STATE_EXIT1_IR, /* TAP_IREXIT1 = 0x9 */ XDS_JTAG_STATE_SHIFT_IR, /* TAP_IRSHIFT = 0xa */ XDS_JTAG_STATE_PAUSE_IR, /* TAP_IRPAUSE = 0xb */ XDS_JTAG_STATE_IDLE, /* TAP_IDLE = 0xc */ XDS_JTAG_STATE_UPDATE_IR, /* TAP_IRUPDATE = 0xd */ XDS_JTAG_STATE_CAPTURE_IR, /* TAP_IRCAPTURE = 0xe */ XDS_JTAG_STATE_RESET, /* TAP_RESET = 0xf */ }; struct scan_result { bool first; uint8_t *buffer; uint32_t num_bits; }; struct xds110_info { /* USB connection handles and data buffers */ struct libusb_context *ctx; struct libusb_device_handle *dev; unsigned char read_payload[USB_PAYLOAD_SIZE]; unsigned char write_packet[3]; unsigned char write_payload[USB_PAYLOAD_SIZE]; /* Device vid/pid */ uint16_t vid; uint16_t pid; /* Debug interface */ uint8_t interface; uint8_t endpoint_in; uint8_t endpoint_out; /* Status flags */ bool is_connected; bool is_cmapi_connected; bool is_cmapi_acquired; bool is_swd_mode; bool is_ap_dirty; /* DAP register caches */ uint32_t select; uint32_t rdbuff; bool use_rdbuff; /* TCK speed and delay count*/ uint32_t speed; uint32_t delay_count; /* XDS110 voltage supply setting */ uint32_t voltage; /* XDS110 firmware and hardware version */ uint32_t firmware; uint16_t hardware; /* Transaction queues */ unsigned char txn_requests[MAX_DATA_BLOCK]; uint32_t *txn_dap_results[MAX_DATA_BLOCK / 4]; struct scan_result txn_scan_results[MAX_DATA_BLOCK / 4]; uint32_t txn_request_size; uint32_t txn_result_size; uint32_t txn_result_count; }; static struct xds110_info xds110 = { .ctx = NULL, .dev = NULL, .vid = 0, .pid = 0, .interface = 0, .endpoint_in = 0, .endpoint_out = 0, .is_connected = false, .is_cmapi_connected = false, .is_cmapi_acquired = false, .is_swd_mode = false, .is_ap_dirty = false, .speed = XDS110_DEFAULT_TCK_SPEED, .delay_count = 0, .voltage = 0, .firmware = 0, .hardware = 0, .txn_request_size = 0, .txn_result_size = 0, .txn_result_count = 0 }; static inline void xds110_set_u32(uint8_t *buffer, uint32_t value) { buffer[3] = (value >> 24) & 0xff; buffer[2] = (value >> 16) & 0xff; buffer[1] = (value >> 8) & 0xff; buffer[0] = (value >> 0) & 0xff; } static inline void xds110_set_u16(uint8_t *buffer, uint16_t value) { buffer[1] = (value >> 8) & 0xff; buffer[0] = (value >> 0) & 0xff; } static inline uint32_t xds110_get_u32(uint8_t *buffer) { uint32_t value = (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) | (((uint32_t)buffer[1]) << 8) | (((uint32_t)buffer[0]) << 0); return value; } static inline uint16_t xds110_get_u16(uint8_t *buffer) { uint16_t value = (((uint32_t)buffer[1]) << 8) | (((uint32_t)buffer[0]) << 0); return value; } /*************************************************************************** * usb connection routines * * * * The following functions handle connecting, reading, and writing to * * the XDS110 over USB using the libusb library. * ***************************************************************************/ static bool usb_connect(void) { struct libusb_context *ctx = NULL; struct libusb_device **list = NULL; struct libusb_device_handle *dev = NULL; struct libusb_device_descriptor desc; /* The vid/pids of possible XDS110 configurations */ uint16_t vids[] = { 0x0451, 0x0451, 0x1cbe }; uint16_t pids[] = { 0xbef3, 0xbef4, 0x02a5 }; /* Corresponding interface and endpoint numbers for configurations */ uint8_t interfaces[] = { 2, 2, 0 }; uint8_t endpoints_in[] = { 3, 3, 1 }; uint8_t endpoints_out[] = { 2, 2, 1 }; ssize_t count = 0; ssize_t i = 0; int result = 0; bool found = false; uint32_t device = 0; bool match = false; /* Initialize libusb context */ result = libusb_init(&ctx); if (result == 0) { /* Get list of USB devices attached to system */ count = libusb_get_device_list(ctx, &list); if (count <= 0) { result = -1; list = NULL; } } if (result == 0) { /* Scan through list of devices for any XDS110s */ for (i = 0; i < count; i++) { /* Check for device vid/pid match */ libusb_get_device_descriptor(list[i], &desc); match = false; for (device = 0; device < ARRAY_SIZE(vids); device++) { if (desc.idVendor == vids[device] && desc.idProduct == pids[device]) { match = true; break; } } if (match) { result = libusb_open(list[i], &dev); if (result == 0) { const int max_data = 256; unsigned char data[max_data + 1]; *data = '\0'; /* May be the requested device if serial number matches */ if (!adapter_get_required_serial()) { /* No serial number given; match first XDS110 found */ found = true; break; } else { /* Get the device's serial number string */ result = libusb_get_string_descriptor_ascii(dev, desc.iSerialNumber, data, max_data); if (result > 0 && strcmp((char *)data, adapter_get_required_serial()) == 0) { found = true; break; } } /* If we fall though to here, we don't want this device */ libusb_close(dev); dev = NULL; } } } } /* * We can fall through the for() loop with two possible exit conditions: * 1) found the right XDS110, and that device is open * 2) didn't find the XDS110, and no devices are currently open */ if (list) { /* Free the device list, we're done with it */ libusb_free_device_list(list, 1); } if (found) { /* Save the vid/pid of the device we're using */ xds110.vid = vids[device]; xds110.pid = pids[device]; /* Save the debug interface and endpoints for the device */ xds110.interface = interfaces[device]; xds110.endpoint_in = endpoints_in[device] | LIBUSB_ENDPOINT_IN; xds110.endpoint_out = endpoints_out[device] | LIBUSB_ENDPOINT_OUT; /* Save the context and device handles */ xds110.ctx = ctx; xds110.dev = dev; /* Set libusb to auto detach kernel */ (void)libusb_set_auto_detach_kernel_driver(dev, 1); /* Claim the debug interface on the XDS110 */ result = libusb_claim_interface(dev, xds110.interface); } else { /* Couldn't find an XDS110, flag the error */ result = -1; } /* On an error, clean up what we can */ if (result != 0) { if (dev) { /* Release the debug and data interface on the XDS110 */ (void)libusb_release_interface(dev, xds110.interface); libusb_close(dev); } if (ctx) libusb_exit(ctx); xds110.ctx = NULL; xds110.dev = NULL; } /* Log the results */ if (result == 0) LOG_INFO("XDS110: connected"); else LOG_ERROR("XDS110: failed to connect"); return (result == 0) ? true : false; } static void usb_disconnect(void) { if (xds110.dev) { /* Release the debug and data interface on the XDS110 */ (void)libusb_release_interface(xds110.dev, xds110.interface); libusb_close(xds110.dev); xds110.dev = NULL; } if (xds110.ctx) { libusb_exit(xds110.ctx); xds110.ctx = NULL; } LOG_INFO("XDS110: disconnected"); } static bool usb_read(unsigned char *buffer, int size, int *bytes_read, int timeout) { int result; if (!xds110.dev || !buffer || !bytes_read) return false; /* Force a non-zero timeout to prevent blocking */ if (timeout == 0) timeout = DEFAULT_TIMEOUT; result = libusb_bulk_transfer(xds110.dev, xds110.endpoint_in, buffer, size, bytes_read, timeout); return (result == 0) ? true : false; } static bool usb_write(unsigned char *buffer, int size, int *written) { int bytes_written = 0; int result = LIBUSB_SUCCESS; int retries = 0; if (!xds110.dev || !buffer) return false; result = libusb_bulk_transfer(xds110.dev, xds110.endpoint_out, buffer, size, &bytes_written, 0); while (result == LIBUSB_ERROR_PIPE && retries < 3) { /* Try clearing the pipe stall and retry transfer */ libusb_clear_halt(xds110.dev, xds110.endpoint_out); result = libusb_bulk_transfer(xds110.dev, xds110.endpoint_out, buffer, size, &bytes_written, 0); retries++; } if (written) *written = bytes_written; return (result == 0 && size == bytes_written) ? true : false; } static bool usb_get_response(uint32_t *total_bytes_read, uint32_t timeout) { static unsigned char buffer[MAX_PACKET]; int bytes_read; uint16_t size; uint16_t count; bool success; size = 0; success = true; while (success) { success = usb_read(buffer, sizeof(buffer), &bytes_read, timeout); if (success) { /* * Validate that this appears to be a good response packet * First check it contains enough data for header and error * code, plus the first character is the start character */ if (bytes_read >= 7 && '*' == buffer[0]) { /* Extract the payload size */ size = xds110_get_u16(&buffer[1]); /* Sanity test on payload size */ if (USB_PAYLOAD_SIZE >= size && 4 <= size) { /* Check we didn't get more data than expected */ if ((bytes_read - 3) <= size) { /* Packet appears to be valid, move on */ break; } } } } /* * Somehow received an invalid packet, retry till we * time out or a valid response packet is received */ } /* Abort now if we didn't receive a valid response */ if (!success) { if (total_bytes_read) *total_bytes_read = 0; return false; } /* Build the return payload into xds110.read_payload */ /* Copy over payload data from received buffer (skipping header) */ count = 0; bytes_read -= 3; memcpy((void *)&xds110.read_payload[count], (void *)&buffer[3], bytes_read); count += bytes_read; /* * Drop timeout to just 1/2 second. Once the XDS110 starts sending * a response, the remaining packets should arrive in short order */ if (timeout > 500) timeout = 500; /* ms */ /* If there's more data to retrieve, get it now */ while ((count < size) && success) { success = usb_read(buffer, sizeof(buffer), &bytes_read, timeout); if (success) { if ((count + bytes_read) > size) { /* Read too much data, not a valid packet, abort */ success = false; } else { /* Copy this data over to xds110.read_payload */ memcpy((void *)&xds110.read_payload[count], (void *)buffer, bytes_read); count += bytes_read; } } } if (!success) count = 0; if (total_bytes_read) *total_bytes_read = count; return success; } static bool usb_send_command(uint16_t size) { /* Check the packet length */ if (size > USB_PAYLOAD_SIZE) return false; /* Place the start character into the packet buffer */ xds110.write_packet[0] = '*'; /* Place the payload size into the packet buffer */ xds110_set_u16(&xds110.write_packet[1], size); /* Adjust size to include header */ size += 3; /* Send the data via the USB connection */ return usb_write(xds110.write_packet, (int)size, NULL); } /*************************************************************************** * XDS110 firmware API routines * * * * The following functions handle calling into the XDS110 firmware to * * perform requested debug actions. * ***************************************************************************/ static bool xds_execute(uint32_t out_length, uint32_t in_length, uint32_t attempts, uint32_t timeout) { bool done = false; bool success = true; int error = 0; uint32_t bytes_read = 0; if (!xds110.dev) return false; while (!done && attempts > 0) { attempts--; /* Send command to XDS110 */ success = usb_send_command(out_length); if (success) { /* Get response from XDS110 */ success = usb_get_response(&bytes_read, timeout); } if (success) { /* Check for valid response from XDS code handling */ if (bytes_read != in_length) { /* Unexpected amount of data returned */ success = false; LOG_DEBUG("XDS110: command 0x%02x return %" PRIu32 " bytes, expected %" PRIu32, xds110.write_payload[0], bytes_read, in_length); } else { /* Extract error code from return packet */ error = (int)xds110_get_u32(&xds110.read_payload[0]); done = true; if (error != SC_ERR_NONE) LOG_DEBUG("XDS110: command 0x%02x returned error %d", xds110.write_payload[0], error); } } } if (!success) error = SC_ERR_XDS110_FAIL; if (error != 0) success = false; return success; } static bool xds_connect(void) { bool success; xds110.write_payload[0] = XDS_CONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_disconnect(void) { bool success; xds110.write_payload[0] = XDS_DISCONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_version(uint32_t *firmware_id, uint16_t *hardware_id) { uint8_t *fw_id_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */ uint8_t *hw_id_pntr = &xds110.read_payload[XDS_IN_LEN + 4]; /* 16-bits */ bool success; xds110.write_payload[0] = XDS_VERSION; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN + 6, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); if (success) { if (firmware_id) *firmware_id = xds110_get_u32(fw_id_pntr); if (hardware_id) *hardware_id = xds110_get_u16(hw_id_pntr); } return success; } static bool xds_set_tck_delay(uint32_t delay) { uint8_t *delay_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ bool success; xds110.write_payload[0] = XDS_SET_TCK; xds110_set_u32(delay_pntr, delay); success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_set_trst(uint8_t trst) { uint8_t *trst_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ bool success; xds110.write_payload[0] = XDS_SET_TRST; *trst_pntr = trst; success = xds_execute(XDS_OUT_LEN + 1, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_cycle_tck(uint32_t count) { uint8_t *count_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ bool success; xds110.write_payload[0] = XDS_CYCLE_TCK; xds110_set_u32(count_pntr, count); success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_goto_state(uint32_t state) { uint8_t *state_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ uint8_t *transit_pntr = &xds110.write_payload[XDS_OUT_LEN+4]; /* 32-bits */ bool success; xds110.write_payload[0] = XDS_GOTO_STATE; xds110_set_u32(state_pntr, state); xds110_set_u32(transit_pntr, XDS_JTAG_TRANSIT_QUICKEST); success = xds_execute(XDS_OUT_LEN+8, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_jtag_scan(uint32_t shift_state, uint16_t shift_bits, uint32_t end_state, uint8_t *data_out, uint8_t *data_in) { uint8_t *bits_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 16-bits */ uint8_t *path_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */ uint8_t *trans1_pntr = &xds110.write_payload[XDS_OUT_LEN + 3]; /* 8-bits */ uint8_t *end_pntr = &xds110.write_payload[XDS_OUT_LEN + 4]; /* 8-bits */ uint8_t *trans2_pntr = &xds110.write_payload[XDS_OUT_LEN + 5]; /* 8-bits */ uint8_t *pre_pntr = &xds110.write_payload[XDS_OUT_LEN + 6]; /* 16-bits */ uint8_t *pos_pntr = &xds110.write_payload[XDS_OUT_LEN + 8]; /* 16-bits */ uint8_t *delay_pntr = &xds110.write_payload[XDS_OUT_LEN + 10]; /* 16-bits */ uint8_t *rep_pntr = &xds110.write_payload[XDS_OUT_LEN + 12]; /* 16-bits */ uint8_t *out_pntr = &xds110.write_payload[XDS_OUT_LEN + 14]; /* 16-bits */ uint8_t *in_pntr = &xds110.write_payload[XDS_OUT_LEN + 16]; /* 16-bits */ uint8_t *data_out_pntr = &xds110.write_payload[XDS_OUT_LEN + 18]; uint8_t *data_in_pntr = &xds110.read_payload[XDS_IN_LEN+0]; uint16_t total_bytes = DIV_ROUND_UP(shift_bits, 8); bool success; xds110.write_payload[0] = XDS_JTAG_SCAN; xds110_set_u16(bits_pntr, shift_bits); /* bits to scan */ *path_pntr = (uint8_t)(shift_state & 0xff); /* IR vs DR path */ *trans1_pntr = (uint8_t)XDS_JTAG_TRANSIT_QUICKEST; /* start state route */ *end_pntr = (uint8_t)(end_state & 0xff); /* JTAG state after scan */ *trans2_pntr = (uint8_t)XDS_JTAG_TRANSIT_QUICKEST; /* end state route */ xds110_set_u16(pre_pntr, 0); /* number of preamble bits */ xds110_set_u16(pos_pntr, 0); /* number of postamble bits */ xds110_set_u16(delay_pntr, 0); /* number of extra TCKs after scan */ xds110_set_u16(rep_pntr, 1); /* number of repetitions */ xds110_set_u16(out_pntr, total_bytes); /* out buffer offset (if repeats) */ xds110_set_u16(in_pntr, total_bytes); /* in buffer offset (if repeats) */ memcpy((void *)data_out_pntr, (void *)data_out, total_bytes); success = xds_execute(XDS_OUT_LEN + 18 + total_bytes, XDS_IN_LEN + total_bytes, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); if (success) memcpy((void *)data_in, (void *)data_in_pntr, total_bytes); return success; } static bool xds_set_srst(uint8_t srst) { uint8_t *srst_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ bool success; xds110.write_payload[0] = XDS_SET_SRST; *srst_pntr = srst; success = xds_execute(XDS_OUT_LEN + 1, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool cmapi_connect(uint32_t *idcode) { uint8_t *idcode_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */ bool success; xds110.write_payload[0] = CMAPI_CONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN+4, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); if (success) { if (idcode) *idcode = xds110_get_u32(idcode_pntr); } return success; } static bool cmapi_disconnect(void) { bool success; xds110.write_payload[0] = CMAPI_DISCONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool cmapi_acquire(void) { bool success; xds110.write_payload[0] = CMAPI_ACQUIRE; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool cmapi_release(void) { bool success; xds110.write_payload[0] = CMAPI_RELEASE; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool cmapi_read_dap_reg(uint32_t type, uint32_t ap_num, uint32_t address, uint32_t *value) { uint8_t *type_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ uint8_t *ap_num_pntr = &xds110.write_payload[XDS_OUT_LEN + 1]; /* 8-bits */ uint8_t *address_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */ uint8_t *value_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; /* 32-bits */ bool success; xds110.write_payload[0] = CMAPI_REG_READ; *type_pntr = (uint8_t)(type & 0xff); *ap_num_pntr = (uint8_t)(ap_num & 0xff); *address_pntr = (uint8_t)(address & 0xff); success = xds_execute(XDS_OUT_LEN + 3, XDS_IN_LEN + 4, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); if (success) { if (value) *value = xds110_get_u32(value_pntr); } return success; } static bool cmapi_write_dap_reg(uint32_t type, uint32_t ap_num, uint32_t address, uint32_t *value) { uint8_t *type_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 8-bits */ uint8_t *ap_num_pntr = &xds110.write_payload[XDS_OUT_LEN + 1]; /* 8-bits */ uint8_t *address_pntr = &xds110.write_payload[XDS_OUT_LEN + 2]; /* 8-bits */ uint8_t *value_pntr = &xds110.write_payload[XDS_OUT_LEN + 3]; /* 32-bits */ bool success; if (!value) return false; xds110.write_payload[0] = CMAPI_REG_WRITE; *type_pntr = (uint8_t)(type & 0xff); *ap_num_pntr = (uint8_t)(ap_num & 0xff); *address_pntr = (uint8_t)(address & 0xff); xds110_set_u32(value_pntr, *value); success = xds_execute(XDS_OUT_LEN + 7, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool swd_connect(void) { bool success; xds110.write_payload[0] = SWD_CONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool swd_disconnect(void) { bool success; xds110.write_payload[0] = SWD_DISCONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool cjtag_connect(uint32_t format) { uint8_t *format_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ bool success; xds110.write_payload[0] = CJTAG_CONNECT; xds110_set_u32(format_pntr, format); success = xds_execute(XDS_OUT_LEN + 4, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool cjtag_disconnect(void) { bool success; xds110.write_payload[0] = CJTAG_DISCONNECT; success = xds_execute(XDS_OUT_LEN, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool xds_set_supply(uint32_t voltage) { uint8_t *volts_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ uint8_t *source_pntr = &xds110.write_payload[XDS_OUT_LEN + 4]; /* 8-bits */ bool success; xds110.write_payload[0] = XDS_SET_SUPPLY; xds110_set_u32(volts_pntr, voltage); *source_pntr = (uint8_t)(voltage != 0 ? 1 : 0); success = xds_execute(XDS_OUT_LEN + 5, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } static bool ocd_dap_request(uint8_t *dap_requests, uint32_t request_size, uint32_t *dap_results, uint32_t result_count) { uint8_t *request_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; uint8_t *result_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; bool success; if (!dap_requests || !dap_results) return false; xds110.write_payload[0] = OCD_DAP_REQUEST; memcpy((void *)request_pntr, (void *)dap_requests, request_size); success = xds_execute(XDS_OUT_LEN + request_size, XDS_IN_LEN + (result_count * 4), DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); if (success && (result_count > 0)) memcpy((void *)dap_results, (void *)result_pntr, result_count * 4); return success; } static bool ocd_scan_request(uint8_t *scan_requests, uint32_t request_size, uint8_t *scan_results, uint32_t result_size) { uint8_t *request_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; uint8_t *result_pntr = &xds110.read_payload[XDS_IN_LEN + 0]; bool success; if (!scan_requests || !scan_results) return false; xds110.write_payload[0] = OCD_SCAN_REQUEST; memcpy((void *)request_pntr, (void *)scan_requests, request_size); success = xds_execute(XDS_OUT_LEN + request_size, XDS_IN_LEN + result_size, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); if (success && (result_size > 0)) memcpy((void *)scan_results, (void *)result_pntr, result_size); return success; } static bool ocd_pathmove(uint32_t num_states, uint8_t *path) { uint8_t *num_pntr = &xds110.write_payload[XDS_OUT_LEN + 0]; /* 32-bits */ uint8_t *path_pntr = &xds110.write_payload[XDS_OUT_LEN + 4]; bool success; if (!path) return false; xds110.write_payload[0] = OCD_PATHMOVE; xds110_set_u32(num_pntr, num_states); memcpy((void *)path_pntr, (void *)path, num_states); success = xds_execute(XDS_OUT_LEN + 4 + num_states, XDS_IN_LEN, DEFAULT_ATTEMPTS, DEFAULT_TIMEOUT); return success; } /*************************************************************************** * swd driver interface * * * * The following functions provide SWD support to OpenOCD. * ***************************************************************************/ static int xds110_swd_init(void) { xds110.is_swd_mode = true; return ERROR_OK; } static int xds110_swd_switch_seq(enum swd_special_seq seq) { uint32_t idcode; bool success; switch (seq) { case LINE_RESET: LOG_ERROR("Sequence SWD line reset (%d) not supported", seq); return ERROR_FAIL; case JTAG_TO_SWD: LOG_DEBUG("JTAG-to-SWD"); xds110.is_swd_mode = false; xds110.is_cmapi_connected = false; xds110.is_cmapi_acquired = false; /* Run sequence to put target in SWD mode */ success = swd_connect(); /* Re-initialize CMAPI API for DAP access */ if (success) { xds110.is_swd_mode = true; success = cmapi_connect(&idcode); if (success) { xds110.is_cmapi_connected = true; success = cmapi_acquire(); } } break; case SWD_TO_JTAG: LOG_DEBUG("SWD-to-JTAG"); xds110.is_swd_mode = false; xds110.is_cmapi_connected = false; xds110.is_cmapi_acquired = false; /* Run sequence to put target in JTAG mode */ success = swd_disconnect(); if (success) { /* Re-initialize JTAG interface */ success = cjtag_connect(MODE_JTAG); } break; default: LOG_ERROR("Sequence %d not supported", seq); return ERROR_FAIL; } if (success) return ERROR_OK; else return ERROR_FAIL; } static bool xds110_legacy_read_reg(uint8_t cmd, uint32_t *value) { /* Make sure this is a read request */ bool is_read_request = (0 != (SWD_CMD_RNW & cmd)); /* Determine whether this is a DP or AP register access */ uint32_t type = (0 != (SWD_CMD_APNDP & cmd)) ? DAP_AP : DAP_DP; /* Determine the AP number from cached SELECT value */ uint32_t ap_num = (xds110.select & 0xff000000) >> 24; /* Extract register address from command */ uint32_t address = ((cmd & SWD_CMD_A32) >> 1); /* Extract bank address from cached SELECT value */ uint32_t bank = (xds110.select & 0x000000f0); uint32_t reg_value = 0; uint32_t temp_value = 0; bool success; if (!is_read_request) return false; if (type == DAP_AP) { /* Add bank address to register address for CMAPI call */ address |= bank; } if (DAP_DP == type && DAP_DP_RDBUFF == address && xds110.use_rdbuff) { /* If RDBUFF is cached and this is a DP RDBUFF read, use the cache */ reg_value = xds110.rdbuff; success = true; } else if (DAP_AP == type && DAP_AP_DRW == address && xds110.use_rdbuff) { /* If RDBUFF is cached and this is an AP DRW read, use the cache, */ /* but still call into the firmware to get the next read. */ reg_value = xds110.rdbuff; success = cmapi_read_dap_reg(type, ap_num, address, &temp_value); } else { success = cmapi_read_dap_reg(type, ap_num, address, &temp_value); if (success) reg_value = temp_value; } /* Mark that we have consumed or invalidated the RDBUFF cache */ xds110.use_rdbuff = false; /* Handle result of read attempt */ if (!success) LOG_ERROR("XDS110: failed to read DAP register"); else if (value) *value = reg_value; if (success && DAP_AP == type) { /* * On a successful DAP AP read, we actually have the value from RDBUFF, * the firmware will have run the AP request and made the RDBUFF read */ xds110.use_rdbuff = true; xds110.rdbuff = temp_value; } return success; } static bool xds110_legacy_write_reg(uint8_t cmd, uint32_t value) { /* Make sure this isn't a read request */ bool is_read_request = (0 != (SWD_CMD_RNW & cmd)); /* Determine whether this is a DP or AP register access */ uint32_t type = (0 != (SWD_CMD_APNDP & cmd)) ? DAP_AP : DAP_DP; /* Determine the AP number from cached SELECT value */ uint32_t ap_num = (xds110.select & 0xff000000) >> 24; /* Extract register address from command */ uint32_t address = ((cmd & SWD_CMD_A32) >> 1); /* Extract bank address from cached SELECT value */ uint32_t bank = (xds110.select & 0x000000f0); bool success; if (is_read_request) return false; /* Invalidate the RDBUFF cache */ xds110.use_rdbuff = false; if (type == DAP_AP) { /* Add bank address to register address for CMAPI call */ address |= bank; /* Any write to an AP register invalidates the firmware's cache */ xds110.is_ap_dirty = true; } else if (address == DAP_DP_SELECT) { /* Any write to the SELECT register invalidates the firmware's cache */ xds110.is_ap_dirty = true; } success = cmapi_write_dap_reg(type, ap_num, address, &value); if (!success) { LOG_ERROR("XDS110: failed to write DAP register"); } else { /* * If the debugger wrote to SELECT, cache the value * to use to build the apNum and address values above */ if ((type == DAP_DP) && (address == DAP_DP_SELECT)) xds110.select = value; } return success; } static int xds110_swd_run_queue(void) { static uint32_t dap_results[MAX_RESULT_QUEUE]; uint8_t cmd; uint32_t request; uint32_t result; uint32_t value; bool success = true; if (xds110.txn_request_size == 0) return ERROR_OK; /* Terminate request queue */ xds110.txn_requests[xds110.txn_request_size++] = 0; if (xds110.firmware >= OCD_FIRMWARE_VERSION) { /* XDS110 firmware has the API to directly handle the queue */ success = ocd_dap_request(xds110.txn_requests, xds110.txn_request_size, dap_results, xds110.txn_result_count); } else { /* Legacy firmware needs to handle queue via discrete DAP calls */ request = 0; result = 0; while (xds110.txn_requests[request] != 0) { cmd = xds110.txn_requests[request++]; if (0 == (SWD_CMD_RNW & cmd)) { /* DAP register write command */ value = (uint32_t)(xds110.txn_requests[request++]) << 0; value |= (uint32_t)(xds110.txn_requests[request++]) << 8; value |= (uint32_t)(xds110.txn_requests[request++]) << 16; value |= (uint32_t)(xds110.txn_requests[request++]) << 24; if (success) success = xds110_legacy_write_reg(cmd, value); } else { /* DAP register read command */ value = 0; if (success) success = xds110_legacy_read_reg(cmd, &value); dap_results[result++] = value; } } } /* Transfer results into caller's buffers */ for (result = 0; result < xds110.txn_result_count; result++) if (xds110.txn_dap_results[result]) *xds110.txn_dap_results[result] = dap_results[result]; xds110.txn_request_size = 0; xds110.txn_result_size = 0; xds110.txn_result_count = 0; return (success) ? ERROR_OK : ERROR_FAIL; } static void xds110_swd_queue_cmd(uint8_t cmd, uint32_t *value) { /* Check if this is a read or write request */ bool is_read_request = (0 != (SWD_CMD_RNW & cmd)); /* Determine whether this is a DP or AP register access */ uint32_t type = (0 != (SWD_CMD_APNDP & cmd)) ? DAP_AP : DAP_DP; /* Extract register address from command */ uint32_t address = ((cmd & SWD_CMD_A32) >> 1); uint32_t request_size = (is_read_request) ? 1 : 5; /* Check if new request would be too large to fit */ if (((xds110.txn_request_size + request_size + 1) > MAX_DATA_BLOCK) || ((xds110.txn_result_count + 1) > MAX_RESULT_QUEUE)) xds110_swd_run_queue(); /* Set the START bit in cmd to ensure cmd is not zero */ /* (a value of zero is used to terminate the buffer) */ cmd |= SWD_CMD_START; /* Add request to queue; queue is built marshalled for XDS110 call */ if (is_read_request) { /* Queue read request, save pointer to pass back result */ xds110.txn_requests[xds110.txn_request_size++] = cmd; xds110.txn_dap_results[xds110.txn_result_count++] = value; xds110.txn_result_size += 4; } else { /* Check for and prevent sticky overrun detection */ if (DAP_DP == type && DAP_DP_CTRL == address && (*value & CORUNDETECT)) { LOG_DEBUG("XDS110: refusing to enable sticky overrun detection"); *value &= ~CORUNDETECT; } /* Queue write request, add value directly to queue buffer */ xds110.txn_requests[xds110.txn_request_size++] = cmd; xds110.txn_requests[xds110.txn_request_size++] = (*value >> 0) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (*value >> 8) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (*value >> 16) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (*value >> 24) & 0xff; } } static void xds110_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk) { assert(cmd & SWD_CMD_RNW); xds110_swd_queue_cmd(cmd, value); } static void xds110_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) { assert(!(cmd & SWD_CMD_RNW)); xds110_swd_queue_cmd(cmd, &value); } /*************************************************************************** * jtag interface * * * * The following functions provide XDS110 interface to OpenOCD. * ***************************************************************************/ static void xds110_show_info(void) { uint32_t firmware = xds110.firmware; LOG_INFO("XDS110: vid/pid = %04x/%04x", xds110.vid, xds110.pid); LOG_INFO("XDS110: firmware version = %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32, (((firmware >> 28) & 0xf) * 10) + ((firmware >> 24) & 0xf), (((firmware >> 20) & 0xf) * 10) + ((firmware >> 16) & 0xf), (((firmware >> 12) & 0xf) * 10) + ((firmware >> 8) & 0xf), (((firmware >> 4) & 0xf) * 10) + ((firmware >> 0) & 0xf)); LOG_INFO("XDS110: hardware version = 0x%04x", xds110.hardware); if (adapter_get_required_serial()) LOG_INFO("XDS110: serial number = %s", adapter_get_required_serial()); if (xds110.is_swd_mode) { LOG_INFO("XDS110: connected to target via SWD"); LOG_INFO("XDS110: SWCLK set to %" PRIu32 " kHz", xds110.speed); } else { LOG_INFO("XDS110: connected to target via JTAG"); LOG_INFO("XDS110: TCK set to %" PRIu32 " kHz", xds110.speed); } /* Alert user that there's a better firmware to use */ if (firmware < OCD_FIRMWARE_VERSION) { LOG_WARNING("XDS110: the firmware is not optimized for OpenOCD"); LOG_WARNING(OCD_FIRMWARE_UPGRADE); } } static int xds110_quit(void) { if (xds110.is_cmapi_acquired) { (void)cmapi_release(); xds110.is_cmapi_acquired = false; } if (xds110.is_cmapi_connected) { (void)cmapi_disconnect(); xds110.is_cmapi_connected = false; } if (xds110.is_connected) { if (xds110.is_swd_mode) { /* Switch out of SWD mode */ (void)swd_disconnect(); } else { /* Switch out of cJTAG mode */ (void)cjtag_disconnect(); } /* Tell firmware we're disconnecting */ (void)xds_disconnect(); xds110.is_connected = false; } /* Close down the USB connection to the XDS110 debug probe */ usb_disconnect(); return ERROR_OK; } static int xds110_init(void) { bool success; /* Establish USB connection to the XDS110 debug probe */ success = usb_connect(); if (success) { /* Send connect message to XDS110 firmware */ success = xds_connect(); if (success) xds110.is_connected = true; } if (success) { uint32_t firmware; uint16_t hardware; /* Retrieve version IDs from firmware */ /* Version numbers are stored in BCD format */ success = xds_version(&firmware, &hardware); if (success) { /* Save the firmware and hardware version */ xds110.firmware = firmware; xds110.hardware = hardware; } } if (success) { /* Set supply voltage for stand-alone probes */ if (xds110.hardware == XDS110_STAND_ALONE_ID) { success = xds_set_supply(xds110.voltage); /* Allow time for target device to power up */ /* (CC32xx takes up to 1300 ms before debug is enabled) */ alive_sleep(1500); } else if (xds110.voltage != 0) { /* Voltage supply not a feature of embedded probes */ LOG_WARNING( "XDS110: ignoring supply voltage, not supported on this probe"); } } if (success) { success = xds_set_trst(0); if (success) success = xds_cycle_tck(50); if (success) success = xds_set_trst(1); if (success) success = xds_cycle_tck(50); } if (success) { if (xds110.is_swd_mode) { /* Switch to SWD if needed */ success = swd_connect(); } else { success = cjtag_connect(MODE_JTAG); } } if (success && xds110.is_swd_mode) { uint32_t idcode; /* Connect to CMAPI interface in XDS110 */ success = cmapi_connect(&idcode); /* Acquire exclusive access to CMAPI interface */ if (success) { xds110.is_cmapi_connected = true; success = cmapi_acquire(); if (success) xds110.is_cmapi_acquired = true; } } if (!success) xds110_quit(); if (success) xds110_show_info(); return (success) ? ERROR_OK : ERROR_FAIL; } static void xds110_legacy_scan(uint32_t shift_state, uint32_t total_bits, uint32_t end_state, uint8_t *data_out, uint8_t *data_in) { (void)xds_jtag_scan(shift_state, total_bits, end_state, data_out, data_in); } static void xds110_legacy_runtest(uint32_t clocks, uint32_t end_state) { xds_goto_state(XDS_JTAG_STATE_IDLE); xds_cycle_tck(clocks); xds_goto_state(end_state); } static void xds110_legacy_stableclocks(uint32_t clocks) { xds_cycle_tck(clocks); } static void xds110_flush(void) { uint8_t command; uint32_t clocks; uint32_t shift_state; uint32_t end_state; uint32_t bits; uint32_t bytes; uint32_t request; uint32_t result; uint8_t *data_out; uint8_t data_in[MAX_DATA_BLOCK]; uint8_t *data_pntr; if (xds110.txn_request_size == 0) return; /* Terminate request queue */ xds110.txn_requests[xds110.txn_request_size++] = 0; if (xds110.firmware >= OCD_FIRMWARE_VERSION) { /* Updated firmware has the API to directly handle the queue */ (void)ocd_scan_request(xds110.txn_requests, xds110.txn_request_size, data_in, xds110.txn_result_size); } else { /* Legacy firmware needs to handle queue via discrete JTAG calls */ request = 0; result = 0; while (xds110.txn_requests[request] != 0) { command = xds110.txn_requests[request++]; switch (command) { case CMD_IR_SCAN: case CMD_DR_SCAN: if (command == CMD_IR_SCAN) shift_state = XDS_JTAG_STATE_SHIFT_IR; else shift_state = XDS_JTAG_STATE_SHIFT_DR; end_state = (uint32_t)(xds110.txn_requests[request++]); bits = (uint32_t)(xds110.txn_requests[request++]) << 0; bits |= (uint32_t)(xds110.txn_requests[request++]) << 8; data_out = &xds110.txn_requests[request]; bytes = DIV_ROUND_UP(bits, 8); xds110_legacy_scan(shift_state, bits, end_state, data_out, &data_in[result]); result += bytes; request += bytes; break; case CMD_RUNTEST: clocks = (uint32_t)(xds110.txn_requests[request++]) << 0; clocks |= (uint32_t)(xds110.txn_requests[request++]) << 8; clocks |= (uint32_t)(xds110.txn_requests[request++]) << 16; clocks |= (uint32_t)(xds110.txn_requests[request++]) << 24; end_state = (uint32_t)xds110.txn_requests[request++]; xds110_legacy_runtest(clocks, end_state); break; case CMD_STABLECLOCKS: clocks = (uint32_t)(xds110.txn_requests[request++]) << 0; clocks |= (uint32_t)(xds110.txn_requests[request++]) << 8; clocks |= (uint32_t)(xds110.txn_requests[request++]) << 16; clocks |= (uint32_t)(xds110.txn_requests[request++]) << 24; xds110_legacy_stableclocks(clocks); break; default: LOG_ERROR("BUG: unknown JTAG command type 0x%x encountered", command); exit(-1); break; } } } /* Transfer results into caller's buffers from data_in buffer */ bits = 0; /* Bit offset into current scan result */ data_pntr = data_in; for (result = 0; result < xds110.txn_result_count; result++) { if (xds110.txn_scan_results[result].first) { if (bits != 0) { bytes = DIV_ROUND_UP(bits, 8); data_pntr += bytes; } bits = 0; } if (xds110.txn_scan_results[result].buffer) bit_copy(xds110.txn_scan_results[result].buffer, 0, data_pntr, bits, xds110.txn_scan_results[result].num_bits); bits += xds110.txn_scan_results[result].num_bits; } xds110.txn_request_size = 0; xds110.txn_result_size = 0; xds110.txn_result_count = 0; } static int xds110_reset(int trst, int srst) { uint8_t value; bool success; int retval = ERROR_OK; if (trst != -1) { if (trst == 0) { /* Deassert nTRST (active low) */ value = 1; } else { /* Assert nTRST (active low) */ value = 0; } success = xds_set_trst(value); if (!success) retval = ERROR_FAIL; } if (srst != -1) { if (srst == 0) { /* Deassert nSRST (active low) */ value = 1; } else { /* Assert nSRST (active low) */ value = 0; } success = xds_set_srst(value); if (!success) retval = ERROR_FAIL; /* Toggle TCK to trigger HIB on CC13x/CC26x devices */ if (success && !xds110.is_swd_mode) { /* Toggle TCK for about 50 ms */ success = xds_cycle_tck(xds110.speed * 50); } if (!success) retval = ERROR_FAIL; } return retval; } static void xds110_execute_sleep(struct jtag_command *cmd) { jtag_sleep(cmd->cmd.sleep->us); } static void xds110_execute_tlr_reset(struct jtag_command *cmd) { (void)xds_goto_state(XDS_JTAG_STATE_RESET); } static void xds110_execute_pathmove(struct jtag_command *cmd) { uint32_t i; uint32_t num_states; uint8_t *path; num_states = (uint32_t)cmd->cmd.pathmove->num_states; if (num_states == 0) return; path = malloc(num_states * sizeof(uint8_t)); if (!path) { LOG_ERROR("XDS110: unable to allocate memory"); return; } /* Convert requested path states into XDS API states */ for (i = 0; i < num_states; i++) path[i] = (uint8_t)xds_jtag_state[cmd->cmd.pathmove->path[i]]; if (xds110.firmware >= OCD_FIRMWARE_VERSION) { /* Updated firmware fully supports pathmove */ (void)ocd_pathmove(num_states, path); } else { /* Notify user that legacy firmware simply cannot handle pathmove */ LOG_ERROR("XDS110: the firmware does not support pathmove command"); LOG_ERROR(OCD_FIRMWARE_UPGRADE); /* If pathmove is required, then debug is not possible */ exit(-1); } free((void *)path); } static void xds110_queue_scan(struct jtag_command *cmd) { int i; uint32_t offset; uint32_t total_fields; uint32_t total_bits; uint32_t total_bytes; uint8_t end_state; uint8_t *buffer; /* Calculate the total number of bits to scan */ total_bits = 0; total_fields = 0; for (i = 0; i < cmd->cmd.scan->num_fields; i++) { total_fields++; total_bits += (uint32_t)cmd->cmd.scan->fields[i].num_bits; } if (total_bits == 0) return; total_bytes = DIV_ROUND_UP(total_bits, 8); /* Check if new request would be too large to fit */ if (((xds110.txn_request_size + 1 + total_bytes + sizeof(end_state) + 1) > MAX_DATA_BLOCK) || ((xds110.txn_result_count + total_fields) > MAX_RESULT_QUEUE)) xds110_flush(); /* Check if this single request is too large to fit */ if ((1 + total_bytes + sizeof(end_state) + 1) > MAX_DATA_BLOCK) { LOG_ERROR("BUG: JTAG scan request is too large to handle (%" PRIu32 " bits)", total_bits); /* Failing to run this scan mucks up debug on this target */ exit(-1); } if (cmd->cmd.scan->ir_scan) xds110.txn_requests[xds110.txn_request_size++] = CMD_IR_SCAN; else xds110.txn_requests[xds110.txn_request_size++] = CMD_DR_SCAN; end_state = (uint8_t)xds_jtag_state[cmd->cmd.scan->end_state]; xds110.txn_requests[xds110.txn_request_size++] = end_state; xds110.txn_requests[xds110.txn_request_size++] = (total_bits >> 0) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (total_bits >> 8) & 0xff; /* Build request data by flattening fields into single buffer */ /* also populate the results array to return the results when run */ offset = 0; buffer = &xds110.txn_requests[xds110.txn_request_size]; /* Clear data out buffer to default value of all zeros */ memset((void *)buffer, 0x00, total_bytes); for (i = 0; i < cmd->cmd.scan->num_fields; i++) { if (cmd->cmd.scan->fields[i].out_value) { /* Copy over data to scan out into request buffer */ bit_copy(buffer, offset, cmd->cmd.scan->fields[i].out_value, 0, cmd->cmd.scan->fields[i].num_bits); } offset += cmd->cmd.scan->fields[i].num_bits; xds110.txn_scan_results[xds110.txn_result_count].first = (i == 0); xds110.txn_scan_results[xds110.txn_result_count].num_bits = cmd->cmd.scan->fields[i].num_bits; xds110.txn_scan_results[xds110.txn_result_count++].buffer = cmd->cmd.scan->fields[i].in_value; } xds110.txn_request_size += total_bytes; xds110.txn_result_size += total_bytes; } static void xds110_queue_runtest(struct jtag_command *cmd) { uint32_t clocks = (uint32_t)cmd->cmd.stableclocks->num_cycles; uint8_t end_state = (uint8_t)xds_jtag_state[cmd->cmd.runtest->end_state]; /* Check if new request would be too large to fit */ if ((xds110.txn_request_size + 1 + sizeof(clocks) + sizeof(end_state) + 1) > MAX_DATA_BLOCK) xds110_flush(); /* Queue request and cycle count directly to queue buffer */ xds110.txn_requests[xds110.txn_request_size++] = CMD_RUNTEST; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 0) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 8) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 16) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 24) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = end_state; } static void xds110_queue_stableclocks(struct jtag_command *cmd) { uint32_t clocks = (uint32_t)cmd->cmd.stableclocks->num_cycles; /* Check if new request would be too large to fit */ if ((xds110.txn_request_size + 1 + sizeof(clocks) + 1) > MAX_DATA_BLOCK) xds110_flush(); /* Queue request and cycle count directly to queue buffer */ xds110.txn_requests[xds110.txn_request_size++] = CMD_STABLECLOCKS; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 0) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 8) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 16) & 0xff; xds110.txn_requests[xds110.txn_request_size++] = (clocks >> 24) & 0xff; } static void xds110_execute_command(struct jtag_command *cmd) { switch (cmd->type) { case JTAG_SLEEP: xds110_flush(); xds110_execute_sleep(cmd); break; case JTAG_TLR_RESET: xds110_flush(); xds110_execute_tlr_reset(cmd); break; case JTAG_PATHMOVE: xds110_flush(); xds110_execute_pathmove(cmd); break; case JTAG_SCAN: xds110_queue_scan(cmd); break; case JTAG_RUNTEST: xds110_queue_runtest(cmd); break; case JTAG_STABLECLOCKS: xds110_queue_stableclocks(cmd); break; case JTAG_TMS: default: LOG_ERROR("BUG: unknown JTAG command type 0x%x encountered", cmd->type); exit(-1); } } static int xds110_execute_queue(struct jtag_command *cmd_queue) { struct jtag_command *cmd = cmd_queue; while (cmd) { xds110_execute_command(cmd); cmd = cmd->next; } xds110_flush(); return ERROR_OK; } static int xds110_speed(int speed) { double freq_to_use; uint32_t delay_count; bool success; if (speed == 0) { LOG_INFO("XDS110: RTCK not supported"); return ERROR_JTAG_NOT_IMPLEMENTED; } if (speed < XDS110_MIN_TCK_SPEED) { LOG_INFO("XDS110: increase speed request: %d kHz to %d kHz minimum", speed, XDS110_MIN_TCK_SPEED); speed = XDS110_MIN_TCK_SPEED; } /* Older XDS110 firmware had inefficient scan routines and could only */ /* achieve a peak TCK frequency of about 2500 kHz */ if (xds110.firmware < FAST_TCK_FIRMWARE_VERSION) { /* Check for request for top speed or higher */ if (speed >= XDS110_MAX_SLOW_TCK_SPEED) { /* Inform user that speed was adjusted down to max possible */ if (speed > XDS110_MAX_SLOW_TCK_SPEED) { LOG_INFO( "XDS110: reduce speed request: %d kHz to %d kHz maximum", speed, XDS110_MAX_SLOW_TCK_SPEED); speed = XDS110_MAX_SLOW_TCK_SPEED; } delay_count = 0; } else { const double XDS110_TCK_PULSE_INCREMENT = 66.0; freq_to_use = speed * 1000; /* Hz */ delay_count = 0; /* Calculate the delay count value */ double one_giga = 1000000000; /* Get the pulse duration for the max frequency supported in ns */ double max_freq_pulse_duration = one_giga / (XDS110_MAX_SLOW_TCK_SPEED * 1000); /* Convert frequency to pulse duration */ double freq_to_pulse_width_in_ns = one_giga / freq_to_use; /* * Start with the pulse duration for the maximum frequency. Keep * decrementing time added by each count value till the requested * frequency pulse is less than the calculated value. */ double current_value = max_freq_pulse_duration; while (current_value < freq_to_pulse_width_in_ns) { current_value += XDS110_TCK_PULSE_INCREMENT; ++delay_count; } /* * Determine which delay count yields the best match. * The one obtained above or one less. */ if (delay_count) { double diff_freq_1 = freq_to_use - (one_giga / (max_freq_pulse_duration + (XDS110_TCK_PULSE_INCREMENT * delay_count))); double diff_freq_2 = (one_giga / (max_freq_pulse_duration + (XDS110_TCK_PULSE_INCREMENT * (delay_count - 1)))) - freq_to_use; /* One less count value yields a better match */ if (diff_freq_1 > diff_freq_2) --delay_count; } } /* Newer firmware has reworked TCK routines that are much more efficient */ /* and can now achieve a peak TCK frequency of 14000 kHz */ } else { if (speed >= XDS110_MAX_FAST_TCK_SPEED) { if (speed > XDS110_MAX_FAST_TCK_SPEED) { LOG_INFO( "XDS110: reduce speed request: %d kHz to %d kHz maximum", speed, XDS110_MAX_FAST_TCK_SPEED); speed = XDS110_MAX_FAST_TCK_SPEED; } delay_count = 0; } else if (speed >= 12000 && xds110.firmware >= FAST_TCK_PLUS_FIRMWARE_VERSION) { delay_count = FAST_TCK_DELAY_12000_KHZ; } else if (speed >= 10000 && xds110.firmware >= FAST_TCK_PLUS_FIRMWARE_VERSION) { delay_count = FAST_TCK_DELAY_10000_KHZ; } else if (speed >= 8500) { delay_count = FAST_TCK_DELAY_8500_KHZ; } else if (speed >= 5500) { delay_count = FAST_TCK_DELAY_5500_KHZ; } else { /* Calculate the delay count to set the frequency */ /* Formula determined by measuring the waveform on Saeleae logic */ /* analyzer using known values for delay count */ const double m = 17100000.0; /* slope */ const double b = -1.02; /* y-intercept */ freq_to_use = speed * 1000; /* Hz */ double period = 1.0/freq_to_use; double delay = m * period + b; if (delay < 1.0) delay_count = 1; else delay_count = (uint32_t)delay; } } /* Send the delay count to the XDS110 firmware */ success = xds_set_tck_delay(delay_count); if (success) { xds110.delay_count = delay_count; xds110.speed = speed; } return (success) ? ERROR_OK : ERROR_FAIL; } static int xds110_speed_div(int speed, int *khz) { *khz = speed; return ERROR_OK; } static int xds110_khz(int khz, int *jtag_speed) { *jtag_speed = khz; return ERROR_OK; } COMMAND_HANDLER(xds110_handle_info_command) { xds110_show_info(); return ERROR_OK; } COMMAND_HANDLER(xds110_handle_supply_voltage_command) { uint32_t voltage = 0; if (CMD_ARGC == 1) { COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], voltage); if (voltage == 0 || (voltage >= XDS110_MIN_VOLTAGE && voltage <= XDS110_MAX_VOLTAGE)) { /* Requested voltage is in range */ xds110.voltage = voltage; } else { LOG_ERROR("XDS110: voltage must be 0 or between %d and %d " "millivolts", XDS110_MIN_VOLTAGE, XDS110_MAX_VOLTAGE); return ERROR_FAIL; } xds110.voltage = voltage; } else return ERROR_COMMAND_SYNTAX_ERROR; return ERROR_OK; } static const struct command_registration xds110_subcommand_handlers[] = { { .name = "info", .handler = &xds110_handle_info_command, .mode = COMMAND_EXEC, .help = "show XDS110 info", .usage = "", }, { .name = "supply", .handler = &xds110_handle_supply_voltage_command, .mode = COMMAND_CONFIG, .help = "set the XDS110 probe supply voltage", .usage = "voltage_in_millivolts", }, COMMAND_REGISTRATION_DONE }; static const struct command_registration xds110_command_handlers[] = { { .name = "xds110", .mode = COMMAND_ANY, .help = "perform XDS110 management", .usage = "", .chain = xds110_subcommand_handlers, }, COMMAND_REGISTRATION_DONE }; static const struct swd_driver xds110_swd_driver = { .init = xds110_swd_init, .switch_seq = xds110_swd_switch_seq, .read_reg = xds110_swd_read_reg, .write_reg = xds110_swd_write_reg, .run = xds110_swd_run_queue, }; static const char * const xds110_transport[] = { "swd", "jtag", NULL }; static struct jtag_interface xds110_interface = { .execute_queue = xds110_execute_queue, }; struct adapter_driver xds110_adapter_driver = { .name = "xds110", .transports = xds110_transport, .commands = xds110_command_handlers, .init = xds110_init, .quit = xds110_quit, .reset = xds110_reset, .speed = xds110_speed, .khz = xds110_khz, .speed_div = xds110_speed_div, .jtag_ops = &xds110_interface, .swd_ops = &xds110_swd_driver, };