/*************************************************************************** * Copyright (C) 2005 by Dominic Rath * * Dominic.Rath@gmx.de * * * * Copyright (C) 2007,2008 Øyvind Harboe * * oyvind.harboe@zylin.com * * * * Copyright (C) 2008 Rob Brown, Lou Deluxe * * rob@cobbleware.com, lou.openocd012@fixit.nospammail.net * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* project specific includes */ #include #include #include "rlink.h" #include "rlink_st7.h" #include "rlink_ep1_cmd.h" #include "rlink_dtc_cmd.h" #include "usb_common.h" /* This feature is made useless by running the DTC all the time. When automatic, the LED is on *whenever the DTC is running. Otherwise, USB messages are sent to turn it on and off. */ #undef AUTOMATIC_BUSY_LED /* This feature may require derating the speed due to reduced hold time. */ #undef USE_HARDWARE_SHIFTER_FOR_TMS #define INTERFACE_NAME "RLink" #define USB_IDVENDOR (0x138e) #define USB_IDPRODUCT (0x9000) #define USB_EP1OUT_ADDR (0x01) #define USB_EP1OUT_SIZE (16) #define USB_EP1IN_ADDR (USB_EP1OUT_ADDR | 0x80) #define USB_EP1IN_SIZE (USB_EP1OUT_SIZE) #define USB_EP2OUT_ADDR (0x02) #define USB_EP2OUT_SIZE (64) #define USB_EP2IN_ADDR (USB_EP2OUT_ADDR | 0x80) #define USB_EP2IN_SIZE (USB_EP2OUT_SIZE) #define USB_EP2BANK_SIZE (512) #define USB_TIMEOUT_MS (3 * 1000) #define DTC_STATUS_POLL_BYTE (ST7_USB_BUF_EP0OUT + 0xff) #define ST7_PD_NBUSY_LED ST7_PD0 #define ST7_PD_NRUN_LED ST7_PD1 /* low enables VPP at adapter header, high connects it to GND instead */ #define ST7_PD_VPP_SEL ST7_PD6 /* low: VPP = 12v, high: VPP <= 5v */ #define ST7_PD_VPP_SHDN ST7_PD7 /* These pins are connected together */ #define ST7_PE_ADAPTER_SENSE_IN ST7_PE3 #define ST7_PE_ADAPTER_SENSE_OUT ST7_PE4 /* Symbolic mapping between port pins and numbered IO lines */ #define ST7_PA_IO1 ST7_PA1 #define ST7_PA_IO2 ST7_PA2 #define ST7_PA_IO4 ST7_PA4 #define ST7_PA_IO8 ST7_PA6 #define ST7_PA_IO10 ST7_PA7 #define ST7_PB_IO5 ST7_PB5 #define ST7_PC_IO9 ST7_PC1 #define ST7_PC_IO3 ST7_PC2 #define ST7_PC_IO7 ST7_PC3 #define ST7_PE_IO6 ST7_PE5 /* Symbolic mapping between numbered IO lines and adapter signals */ #define ST7_PA_RTCK ST7_PA_IO0 #define ST7_PA_NTRST ST7_PA_IO1 #define ST7_PC_TDI ST7_PC_IO3 #define ST7_PA_DBGRQ ST7_PA_IO4 #define ST7_PB_NSRST ST7_PB_IO5 #define ST7_PE_TMS ST7_PE_IO6 #define ST7_PC_TCK ST7_PC_IO7 #define ST7_PC_TDO ST7_PC_IO9 #define ST7_PA_DBGACK ST7_PA_IO10 static usb_dev_handle *pHDev; /* * ep1 commands are up to USB_EP1OUT_SIZE bytes in length. * This function takes care of zeroing the unused bytes before sending the packet. * Any reply packet is not handled by this function. */ static int ep1_generic_commandl(usb_dev_handle *pHDev_param, size_t length, ...) { uint8_t usb_buffer[USB_EP1OUT_SIZE]; uint8_t *usb_buffer_p; va_list ap; int usb_ret; if (length > sizeof(usb_buffer)) length = sizeof(usb_buffer); usb_buffer_p = usb_buffer; va_start(ap, length); while (length > 0) { *usb_buffer_p++ = va_arg(ap, int); length--; } memset( usb_buffer_p, 0, sizeof(usb_buffer) - (usb_buffer_p - usb_buffer) ); usb_ret = usb_bulk_write( pHDev_param, USB_EP1OUT_ADDR, (char *)usb_buffer, sizeof(usb_buffer), USB_TIMEOUT_MS ); return usb_ret; } #if 0 static ssize_t ep1_memory_read( usb_dev_handle *pHDev, uint16_t addr, size_t length, uint8_t *buffer) { uint8_t usb_buffer[USB_EP1OUT_SIZE]; int usb_ret; size_t remain; ssize_t count; usb_buffer[0] = EP1_CMD_MEMORY_READ; memset( usb_buffer + 4, 0, sizeof(usb_buffer) - 4 ); remain = length; count = 0; while (remain) { if (remain > sizeof(usb_buffer)) length = sizeof(usb_buffer); else length = remain; usb_buffer[1] = addr >> 8; usb_buffer[2] = addr; usb_buffer[3] = length; usb_ret = usb_bulk_write( pHDev, USB_EP1OUT_ADDR, usb_buffer, sizeof(usb_buffer), USB_TIMEOUT_MS ); if (usb_ret < sizeof(usb_buffer)) break; usb_ret = usb_bulk_read( pHDev, USB_EP1IN_ADDR, buffer, length, USB_TIMEOUT_MS ); if (usb_ret < length) break; addr += length; buffer += length; count += length; remain -= length; } return count; } #endif static ssize_t ep1_memory_write(usb_dev_handle *pHDev_param, uint16_t addr, size_t length, uint8_t const *buffer) { uint8_t usb_buffer[USB_EP1OUT_SIZE]; int usb_ret; size_t remain; ssize_t count; usb_buffer[0] = EP1_CMD_MEMORY_WRITE; remain = length; count = 0; while (remain) { if (remain > (sizeof(usb_buffer) - 4)) length = (sizeof(usb_buffer) - 4); else length = remain; usb_buffer[1] = addr >> 8; usb_buffer[2] = addr; usb_buffer[3] = length; memcpy( usb_buffer + 4, buffer, length ); memset( usb_buffer + 4 + length, 0, sizeof(usb_buffer) - 4 - length ); usb_ret = usb_bulk_write( pHDev_param, USB_EP1OUT_ADDR, (char *)usb_buffer, sizeof(usb_buffer), USB_TIMEOUT_MS ); if ((size_t)usb_ret < sizeof(usb_buffer)) break; addr += length; buffer += length; count += length; remain -= length; } return count; } #if 0 static ssize_t ep1_memory_writel(usb_dev_handle *pHDev, uint16_t addr, size_t length, ...) { uint8_t buffer[USB_EP1OUT_SIZE - 4]; uint8_t *buffer_p; va_list ap; size_t remain; if (length > sizeof(buffer)) length = sizeof(buffer); remain = length; buffer_p = buffer; va_start(ap, length); while (remain > 0) { *buffer_p++ = va_arg(ap, int); remain--; } return ep1_memory_write(pHDev, addr, length, buffer); } #endif #define DTCLOAD_COMMENT (0) #define DTCLOAD_ENTRY (1) #define DTCLOAD_LOAD (2) #define DTCLOAD_RUN (3) #define DTCLOAD_LUT_START (4) #define DTCLOAD_LUT (5) #define DTC_LOAD_BUFFER ST7_USB_BUF_EP2UIDO /* This gets set by the DTC loader */ static uint8_t dtc_entry_download; /* The buffer is specially formatted to represent a valid image to load into the DTC. */ static int dtc_load_from_buffer(usb_dev_handle *pHDev_param, const uint8_t *buffer, size_t length) { struct header_s { uint8_t type; uint8_t length; }; int usb_err; struct header_s *header; uint8_t lut_start = 0xc0; dtc_entry_download = 0; /* Stop the DTC before loading anything. */ usb_err = ep1_generic_commandl( pHDev_param, 1, EP1_CMD_DTC_STOP ); if (usb_err < 0) return usb_err; while (length) { if (length < sizeof(*header)) { LOG_ERROR("Malformed DTC image"); exit(1); } header = (struct header_s *)buffer; buffer += sizeof(*header); length -= sizeof(*header); if (length < (size_t)header->length + 1) { LOG_ERROR("Malformed DTC image"); exit(1); } switch (header->type) { case DTCLOAD_COMMENT: break; case DTCLOAD_ENTRY: /* store entry addresses somewhere */ if (!strncmp("download", (char *)buffer + 1, 8)) dtc_entry_download = buffer[0]; break; case DTCLOAD_LOAD: /* Send the DTC program to ST7 RAM. */ usb_err = ep1_memory_write( pHDev_param, DTC_LOAD_BUFFER, header->length + 1, buffer ); if (usb_err < 0) return usb_err; /* Load it into the DTC. */ usb_err = ep1_generic_commandl( pHDev_param, 3, EP1_CMD_DTC_LOAD, (DTC_LOAD_BUFFER >> 8), DTC_LOAD_BUFFER ); if (usb_err < 0) return usb_err; break; case DTCLOAD_RUN: usb_err = ep1_generic_commandl( pHDev_param, 3, EP1_CMD_DTC_CALL, buffer[0], EP1_CMD_DTC_WAIT ); if (usb_err < 0) return usb_err; break; case DTCLOAD_LUT_START: lut_start = buffer[0]; break; case DTCLOAD_LUT: usb_err = ep1_memory_write( pHDev_param, ST7_USB_BUF_EP0OUT + lut_start, header->length + 1, buffer ); if (usb_err < 0) return usb_err; break; default: LOG_ERROR("Invalid DTC image record type: 0x%02x", header->type); exit(1); break; } buffer += (header->length + 1); length -= (header->length + 1); } return 0; } /* * Start the DTC running in download mode (waiting for 512 byte command packets on ep2). */ static int dtc_start_download(void) { int usb_err; uint8_t ep2txr; /* set up for download mode and make sure EP2 is set up to transmit */ usb_err = ep1_generic_commandl( pHDev, 7, EP1_CMD_DTC_STOP, EP1_CMD_SET_UPLOAD, EP1_CMD_SET_DOWNLOAD, EP1_CMD_MEMORY_READ, /* read EP2TXR for its data toggle */ ST7_EP2TXR >> 8, ST7_EP2TXR, 1 ); if (usb_err < 0) return usb_err; /* read back ep2txr */ usb_err = usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)&ep2txr, 1, USB_TIMEOUT_MS ); if (usb_err < 0) return usb_err; usb_err = ep1_generic_commandl( pHDev, 13, EP1_CMD_MEMORY_WRITE, /* preinitialize poll byte */ DTC_STATUS_POLL_BYTE >> 8, DTC_STATUS_POLL_BYTE, 1, 0x00, EP1_CMD_MEMORY_WRITE, /* set EP2IN to return data */ ST7_EP2TXR >> 8, ST7_EP2TXR, 1, (ep2txr & ST7_EP2TXR_DTOG_TX) | ST7_EP2TXR_STAT_VALID, EP1_CMD_DTC_CALL, /* start running the DTC */ dtc_entry_download, EP1_CMD_DTC_GET_CACHED_STATUS ); if (usb_err < 0) return usb_err; /* wait for completion */ usb_err = usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)&ep2txr, 1, USB_TIMEOUT_MS ); return usb_err; } static int dtc_run_download( usb_dev_handle *pHDev_param, uint8_t *command_buffer, int command_buffer_size, uint8_t *reply_buffer, int reply_buffer_size ) { char dtc_status; int usb_err; int i; LOG_DEBUG("%d/%d", command_buffer_size, reply_buffer_size); usb_err = usb_bulk_write( pHDev_param, USB_EP2OUT_ADDR, (char *)command_buffer, USB_EP2BANK_SIZE, USB_TIMEOUT_MS ); if (usb_err < 0) return usb_err; /* Wait for DTC to finish running command buffer */ for (i = 50;; ) { usb_err = ep1_generic_commandl( pHDev_param, 4, EP1_CMD_MEMORY_READ, DTC_STATUS_POLL_BYTE >> 8, DTC_STATUS_POLL_BYTE, 1 ); if (usb_err < 0) return usb_err; usb_err = usb_bulk_read( pHDev_param, USB_EP1IN_ADDR, &dtc_status, 1, USB_TIMEOUT_MS ); if (usb_err < 0) return usb_err; if (dtc_status & 0x01) break; if (!--i) { LOG_ERROR("too many retries waiting for DTC status"); return -ETIMEDOUT; } } if (reply_buffer && reply_buffer_size) { usb_err = usb_bulk_read( pHDev_param, USB_EP2IN_ADDR, (char *)reply_buffer, reply_buffer_size, USB_TIMEOUT_MS ); if (usb_err < reply_buffer_size) { LOG_ERROR("Read of endpoint 2 returned %d, expected %d", usb_err, reply_buffer_size ); return usb_err; } } return usb_err; } /* * The dtc reply queue is a singly linked list that describes what to do * with the reply packet that comes from the DTC. Only SCAN_IN and SCAN_IO generate * these entries. */ struct dtc_reply_queue_entry { struct dtc_reply_queue_entry *next; struct jtag_command *cmd; /* the command that resulted in this entry */ struct { uint8_t *buffer; /* the scan buffer */ int size; /* size of the scan buffer in bits */ int offset; /* how many bits were already done before this? */ int length; /* how many bits are processed in this operation? */ enum scan_type type; /* SCAN_IN/SCAN_OUT/SCAN_IO */ } scan; }; /* * The dtc_queue consists of a buffer of pending commands and a reply queue. * rlink_scan and tap_state_run add to the command buffer and maybe to the reply queue. */ static struct { struct dtc_reply_queue_entry *rq_head; struct dtc_reply_queue_entry *rq_tail; uint32_t cmd_index; uint32_t reply_index; uint8_t cmd_buffer[USB_EP2BANK_SIZE]; } dtc_queue; /* * The tap state queue is for accumulating TAP state changes wiithout needlessly * flushing the dtc_queue. When it fills or is run, it adds the accumulated bytes to * the dtc_queue. */ static struct { uint32_t length; uint32_t buffer; } tap_state_queue; static int dtc_queue_init(void) { dtc_queue.rq_head = NULL; dtc_queue.rq_tail = NULL; dtc_queue.cmd_index = 0; dtc_queue.reply_index = 0; return 0; } static inline struct dtc_reply_queue_entry *dtc_queue_enqueue_reply( enum scan_type type, uint8_t *buffer, int size, int offset, int length, struct jtag_command *cmd) { struct dtc_reply_queue_entry *rq_entry; rq_entry = malloc(sizeof(struct dtc_reply_queue_entry)); if (rq_entry != NULL) { rq_entry->scan.type = type; rq_entry->scan.buffer = buffer; rq_entry->scan.size = size; rq_entry->scan.offset = offset; rq_entry->scan.length = length; rq_entry->cmd = cmd; rq_entry->next = NULL; if (dtc_queue.rq_head == NULL) dtc_queue.rq_head = rq_entry; else dtc_queue.rq_tail->next = rq_entry; dtc_queue.rq_tail = rq_entry; } return rq_entry; } /* * Running the queue means that any pending command buffer is run * and any reply data dealt with. The command buffer is then cleared for subsequent processing. * The queue is automatically run by append when it is necessary to get space for the append. */ static int dtc_queue_run(void) { struct dtc_reply_queue_entry *rq_p, *rq_next; int retval; int usb_err; int bit_cnt; int x; uint8_t *dtc_p, *tdo_p; uint8_t dtc_mask, tdo_mask; uint8_t reply_buffer[USB_EP2IN_SIZE]; assert((dtc_queue.rq_head != 0) == (dtc_queue.reply_index > 0)); assert(dtc_queue.cmd_index < USB_EP2BANK_SIZE); assert(dtc_queue.reply_index <= USB_EP2IN_SIZE); retval = ERROR_OK; if (dtc_queue.cmd_index < 1) return retval; dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_STOP; usb_err = dtc_run_download(pHDev, dtc_queue.cmd_buffer, dtc_queue.cmd_index, reply_buffer, sizeof(reply_buffer) ); if (usb_err < 0) { LOG_ERROR("dtc_run_download: %s", usb_strerror()); exit(1); } if (dtc_queue.rq_head != NULL) { /* process the reply, which empties the reply queue and frees its entries */ dtc_p = reply_buffer; /* The rigamarole with the masks and doing it bit-by-bit is due to the fact that the *scan buffer is LSb-first and the DTC code is MSb-first for hardware reasons. It *was that or craft a function to do the reversal, and that wouldn't work with *bit-stuffing (supplying extra bits to use mostly byte operations), or any other *scheme which would throw the byte alignment off. */ for ( rq_p = dtc_queue.rq_head; rq_p != NULL; rq_p = rq_next ) { tdo_p = rq_p->scan.buffer + (rq_p->scan.offset / 8); tdo_mask = 1 << (rq_p->scan.offset % 8); bit_cnt = rq_p->scan.length; if (bit_cnt >= 8) { /* bytes */ dtc_mask = 1 << (8 - 1); for ( ; bit_cnt; bit_cnt-- ) { if (*dtc_p & dtc_mask) *tdo_p |= tdo_mask; else *tdo_p &= ~tdo_mask; dtc_mask >>= 1; if (dtc_mask == 0) { dtc_p++; dtc_mask = 1 << (8 - 1); } tdo_mask <<= 1; if (tdo_mask == 0) { tdo_p++; tdo_mask = 1; } } } else { /* extra bits or last bit */ x = *dtc_p++; if ((rq_p->scan.type == SCAN_IN) && ( rq_p->scan.offset != rq_p->scan.size - 1 )) { /* extra bits were sent as a full byte with padding on the *end */ dtc_mask = 1 << (8 - 1); } else dtc_mask = 1 << (bit_cnt - 1); for ( ; bit_cnt; bit_cnt-- ) { if (x & dtc_mask) *tdo_p |= tdo_mask; else *tdo_p &= ~tdo_mask; dtc_mask >>= 1; tdo_mask <<= 1; if (tdo_mask == 0) { tdo_p++; tdo_mask = 1; } } } if ((rq_p->scan.offset + rq_p->scan.length) >= rq_p->scan.size) { /* feed scan buffer back into openocd and free it */ if (jtag_read_buffer(rq_p->scan.buffer, rq_p->cmd->cmd.scan) != ERROR_OK) retval = ERROR_JTAG_QUEUE_FAILED; free(rq_p->scan.buffer); } rq_next = rq_p->next; free(rq_p); } dtc_queue.rq_head = NULL; dtc_queue.rq_tail = NULL; } /* reset state for new appends */ dtc_queue.cmd_index = 0; dtc_queue.reply_index = 0; return retval; } /* runs the queue if it cannot take reserved_cmd bytes of command data * or reserved_reply bytes of reply data */ static int dtc_queue_run_if_full(int reserved_cmd, int reserved_reply) { /* reserve one additional byte for the STOP cmd appended during run */ if (dtc_queue.cmd_index + reserved_cmd + 1 > USB_EP2BANK_SIZE) return dtc_queue_run(); if (dtc_queue.reply_index + reserved_reply > USB_EP2IN_SIZE) return dtc_queue_run(); return ERROR_OK; } static int tap_state_queue_init(void) { tap_state_queue.length = 0; tap_state_queue.buffer = 0; return 0; } static int tap_state_queue_run(void) { int i; int bits; uint8_t byte_param; int retval; retval = 0; if (!tap_state_queue.length) return retval; bits = 1; byte_param = 0; for (i = tap_state_queue.length; i--; ) { byte_param <<= 1; if (tap_state_queue.buffer & 1) byte_param |= 1; if ((bits >= 8) || !i) { byte_param <<= (8 - bits); /* make sure there's room for two cmd bytes */ dtc_queue_run_if_full(2, 0); #ifdef USE_HARDWARE_SHIFTER_FOR_TMS if (bits == 8) { dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TMS_BYTES(1); } else { #endif dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TMS_BITS(bits); #ifdef USE_HARDWARE_SHIFTER_FOR_TMS } #endif dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = byte_param; byte_param = 0; bits = 1; } else bits++; tap_state_queue.buffer >>= 1; } retval = tap_state_queue_init(); return retval; } static int tap_state_queue_append(uint8_t tms) { int retval; if (tap_state_queue.length >= sizeof(tap_state_queue.buffer) * 8) { retval = tap_state_queue_run(); if (retval != 0) return retval; } if (tms) tap_state_queue.buffer |= (1 << tap_state_queue.length); tap_state_queue.length++; return 0; } static void rlink_end_state(tap_state_t state) { if (tap_is_state_stable(state)) tap_set_end_state(state); else { LOG_ERROR("BUG: %i is not a valid end state", state); exit(-1); } } static void rlink_state_move(void) { int i = 0, tms = 0; uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state()); int tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state()); for (i = 0; i < tms_count; i++) { tms = (tms_scan >> i) & 1; tap_state_queue_append(tms); } tap_set_state(tap_get_end_state()); } static void rlink_path_move(struct pathmove_command *cmd) { int num_states = cmd->num_states; int state_count; int tms = 0; state_count = 0; while (num_states) { if (tap_state_transition(tap_get_state(), false) == cmd->path[state_count]) tms = 0; else if (tap_state_transition(tap_get_state(), true) == cmd->path[state_count]) tms = 1; else { LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition", tap_state_name(tap_get_state()), tap_state_name(cmd->path[state_count])); exit(-1); } tap_state_queue_append(tms); tap_set_state(cmd->path[state_count]); state_count++; num_states--; } tap_set_end_state(tap_get_state()); } static void rlink_runtest(int num_cycles) { int i; tap_state_t saved_end_state = tap_get_end_state(); /* only do a state_move when we're not already in RTI */ if (tap_get_state() != TAP_IDLE) { rlink_end_state(TAP_IDLE); rlink_state_move(); } /* execute num_cycles */ for (i = 0; i < num_cycles; i++) tap_state_queue_append(0); /* finish in end_state */ rlink_end_state(saved_end_state); if (tap_get_state() != tap_get_end_state()) rlink_state_move(); } /* (1) assert or (0) deassert reset lines */ static void rlink_reset(int trst, int srst) { uint8_t bitmap; int usb_err; /* Read port A for bit op */ usb_err = ep1_generic_commandl( pHDev, 4, EP1_CMD_MEMORY_READ, ST7_PADR >> 8, ST7_PADR, 1 ); if (usb_err < 0) { LOG_ERROR("%s", usb_strerror()); exit(1); } usb_err = usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)&bitmap, 1, USB_TIMEOUT_MS ); if (usb_err < 1) { LOG_ERROR("%s", usb_strerror()); exit(1); } if (trst) bitmap &= ~ST7_PA_NTRST; else bitmap |= ST7_PA_NTRST; /* Write port A and read port B for bit op * port B has no OR, and we want to emulate open drain on NSRST, so we initialize DR to 0 *and assert NSRST by setting DDR to 1. */ usb_err = ep1_generic_commandl( pHDev, 9, EP1_CMD_MEMORY_WRITE, ST7_PADR >> 8, ST7_PADR, 1, bitmap, EP1_CMD_MEMORY_READ, ST7_PBDDR >> 8, ST7_PBDDR, 1 ); if (usb_err < 0) { LOG_ERROR("%s", usb_strerror()); exit(1); } usb_err = usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)&bitmap, 1, USB_TIMEOUT_MS ); if (usb_err < 1) { LOG_ERROR("%s", usb_strerror()); exit(1); } if (srst) bitmap |= ST7_PB_NSRST; else bitmap &= ~ST7_PB_NSRST; /* write port B and read dummy to ensure completion before returning */ usb_err = ep1_generic_commandl( pHDev, 6, EP1_CMD_MEMORY_WRITE, ST7_PBDDR >> 8, ST7_PBDDR, 1, bitmap, EP1_CMD_DTC_GET_CACHED_STATUS ); if (usb_err < 0) { LOG_ERROR("%s", usb_strerror()); exit(1); } usb_err = usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)&bitmap, 1, USB_TIMEOUT_MS ); if (usb_err < 1) { LOG_ERROR("%s", usb_strerror()); exit(1); } } static int rlink_scan(struct jtag_command *cmd, enum scan_type type, uint8_t *buffer, int scan_size) { bool ir_scan; tap_state_t saved_end_state; int byte_bits; int extra_bits; int chunk_bits; int chunk_bytes; int x; int tdi_bit_offset; uint8_t tdi_mask, *tdi_p; uint8_t dtc_mask; if (scan_size < 1) { LOG_ERROR("scan_size cannot be less than 1 bit"); exit(1); } ir_scan = cmd->cmd.scan->ir_scan; /* Move to the proper state before starting to shift TDI/TDO. */ if (!((!ir_scan && (tap_get_state() == TAP_DRSHIFT)) || (ir_scan && (tap_get_state() == TAP_IRSHIFT)))) { saved_end_state = tap_get_end_state(); rlink_end_state(ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT); rlink_state_move(); rlink_end_state(saved_end_state); } tap_state_queue_run(); #if 0 printf("scan_size = %d, type = 0x%x\n", scan_size, type); { int i; /* clear unused bits in scan buffer for ease of debugging * (it makes diffing output easier) */ buffer[scan_size / 8] &= ((1 << ((scan_size - 1) % 8) + 1) - 1); printf("before scan:"); for (i = 0; i < (scan_size + 7) / 8; i++) printf(" %02x", buffer[i]); printf("\n"); } #endif /* The number of bits that can be shifted as complete bytes */ byte_bits = (int)(scan_size - 1) / 8 * 8; /* The number of bits left over, not counting the last bit */ extra_bits = (scan_size - 1) - byte_bits; tdi_bit_offset = 0; tdi_p = buffer; tdi_mask = 1; if (extra_bits && (type == SCAN_OUT)) { /* Schedule any extra bits into the DTC command buffer, padding as needed * For SCAN_OUT, this comes before the full bytes so the (leading) padding bits will *fall off the end */ /* make sure there's room for two cmd bytes */ dtc_queue_run_if_full(2, 0); x = 0; dtc_mask = 1 << (extra_bits - 1); while (extra_bits--) { if (*tdi_p & tdi_mask) x |= dtc_mask; dtc_mask >>= 1; tdi_mask <<= 1; if (tdi_mask == 0) { tdi_p++; tdi_mask = 1; } } dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TDI_BYTES(1); dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = x; } /* Loop scheduling full bytes into the DTC command buffer */ while (byte_bits) { /* make sure there's room for one (for in scans) or two cmd bytes and * at least one reply byte for in or inout scans*/ dtc_queue_run_if_full(type == SCAN_IN ? 1 : 2, type != SCAN_OUT ? 1 : 0); chunk_bits = byte_bits; /* we can only use up to 16 bytes at a time */ if (chunk_bits > (16 * 8)) chunk_bits = (16 * 8); if (type != SCAN_IN) { /* how much is there room for, considering stop and byte op? */ x = (sizeof(dtc_queue.cmd_buffer) - (dtc_queue.cmd_index + 1 + 1)) * 8; if (chunk_bits > x) chunk_bits = x; } if (type != SCAN_OUT) { /* how much is there room for in the reply buffer? */ x = (USB_EP2IN_SIZE - dtc_queue.reply_index) * 8; if (chunk_bits > x) chunk_bits = x; } /* so the loop will end */ byte_bits -= chunk_bits; if (type != SCAN_OUT) { if (dtc_queue_enqueue_reply( type, buffer, scan_size, tdi_bit_offset, chunk_bits, cmd ) == NULL) { LOG_ERROR("enqueuing DTC reply entry: %s", strerror(errno)); exit(1); } dtc_queue.reply_index += (chunk_bits + 7) / 8; tdi_bit_offset += chunk_bits; } /* chunk_bits is a multiple of 8, so there are no rounding issues. */ chunk_bytes = chunk_bits / 8; switch (type) { case SCAN_IN: x = DTC_CMD_SHIFT_TDO_BYTES(chunk_bytes); break; case SCAN_OUT: x = DTC_CMD_SHIFT_TDI_BYTES(chunk_bytes); break; default: x = DTC_CMD_SHIFT_TDIO_BYTES(chunk_bytes); break; } dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = x; if (type != SCAN_IN) { x = 0; dtc_mask = 1 << (8 - 1); while (chunk_bits--) { if (*tdi_p & tdi_mask) x |= dtc_mask; dtc_mask >>= 1; if (dtc_mask == 0) { dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = x; x = 0; dtc_mask = 1 << (8 - 1); } tdi_mask <<= 1; if (tdi_mask == 0) { tdi_p++; tdi_mask = 1; } } } } if (extra_bits && (type != SCAN_OUT)) { /* Schedule any extra bits into the DTC command buffer */ /* make sure there's room for one (for in scans) or two cmd bytes * and one reply byte */ dtc_queue_run_if_full(type == SCAN_IN ? 1 : 2, 1); if (dtc_queue_enqueue_reply( type, buffer, scan_size, tdi_bit_offset, extra_bits, cmd ) == NULL) { LOG_ERROR("enqueuing DTC reply entry: %s", strerror(errno)); exit(1); } dtc_queue.reply_index++; tdi_bit_offset += extra_bits; if (type == SCAN_IN) { dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TDO_BYTES(1); } else { dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TDIO_BITS(extra_bits); x = 0; dtc_mask = 1 << (8 - 1); while (extra_bits--) { if (*tdi_p & tdi_mask) x |= dtc_mask; dtc_mask >>= 1; tdi_mask <<= 1; if (tdi_mask == 0) { tdi_p++; tdi_mask = 1; } } dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = x; } } /* Schedule the last bit into the DTC command buffer */ /* make sure there's room for one cmd byte and one reply byte * for in or inout scans*/ dtc_queue_run_if_full(1, type == SCAN_OUT ? 0 : 1); if (type == SCAN_OUT) { dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TMS_TDI_BIT_PAIR(1, (*tdi_p & tdi_mask), 0); } else { if (dtc_queue_enqueue_reply( type, buffer, scan_size, tdi_bit_offset, 1, cmd ) == NULL) { LOG_ERROR("enqueuing DTC reply entry: %s", strerror(errno)); exit(1); } dtc_queue.reply_index++; dtc_queue.cmd_buffer[dtc_queue.cmd_index++] = DTC_CMD_SHIFT_TMS_TDI_BIT_PAIR(1, (*tdi_p & tdi_mask), 1); } /* Move to pause state */ tap_state_queue_append(0); tap_set_state(ir_scan ? TAP_IRPAUSE : TAP_DRPAUSE); if (tap_get_state() != tap_get_end_state()) rlink_state_move(); return 0; } static int rlink_execute_queue(void) { struct jtag_command *cmd = jtag_command_queue; /* currently processed command */ int scan_size; enum scan_type type; uint8_t *buffer; int retval, tmp_retval; /* return ERROR_OK, unless something goes wrong */ retval = ERROR_OK; #ifndef AUTOMATIC_BUSY_LED /* turn LED on */ ep1_generic_commandl(pHDev, 2, EP1_CMD_SET_PORTD_LEDS, ~(ST7_PD_NBUSY_LED) ); #endif while (cmd) { switch (cmd->type) { case JTAG_RUNTEST: case JTAG_TLR_RESET: case JTAG_PATHMOVE: case JTAG_SCAN: break; default: /* some events, such as resets, need a queue flush to ensure *consistency */ tap_state_queue_run(); dtc_queue_run(); break; } switch (cmd->type) { case JTAG_RESET: #ifdef _DEBUG_JTAG_IO_ LOG_DEBUG("reset trst: %i srst %i", cmd->cmd.reset->trst, cmd->cmd.reset->srst); #endif if ((cmd->cmd.reset->trst == 1) || (cmd->cmd.reset->srst && (jtag_get_reset_config() & RESET_SRST_PULLS_TRST))) tap_set_state(TAP_RESET); rlink_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); break; case JTAG_RUNTEST: #ifdef _DEBUG_JTAG_IO_ LOG_DEBUG("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles, cmd->cmd.runtest->end_state); #endif if (cmd->cmd.runtest->end_state != -1) rlink_end_state(cmd->cmd.runtest->end_state); rlink_runtest(cmd->cmd.runtest->num_cycles); break; case JTAG_TLR_RESET: #ifdef _DEBUG_JTAG_IO_ LOG_DEBUG("statemove end in %i", cmd->cmd.statemove->end_state); #endif if (cmd->cmd.statemove->end_state != -1) rlink_end_state(cmd->cmd.statemove->end_state); rlink_state_move(); break; case JTAG_PATHMOVE: #ifdef _DEBUG_JTAG_IO_ LOG_DEBUG("pathmove: %i states, end in %i", cmd->cmd.pathmove->num_states, cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]); #endif rlink_path_move(cmd->cmd.pathmove); break; case JTAG_SCAN: #ifdef _DEBUG_JTAG_IO_ LOG_DEBUG("%s scan end in %i", (cmd->cmd.scan->ir_scan) ? "IR" : "DR", cmd->cmd.scan->end_state); #endif if (cmd->cmd.scan->end_state != -1) rlink_end_state(cmd->cmd.scan->end_state); scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer); type = jtag_scan_type(cmd->cmd.scan); if (rlink_scan(cmd, type, buffer, scan_size) != ERROR_OK) retval = ERROR_FAIL; break; case JTAG_SLEEP: #ifdef _DEBUG_JTAG_IO_ LOG_DEBUG("sleep %i", cmd->cmd.sleep->us); #endif jtag_sleep(cmd->cmd.sleep->us); break; default: LOG_ERROR("BUG: unknown JTAG command type encountered"); exit(-1); } cmd = cmd->next; } /* Flush the DTC queue to make sure any pending reads have been done before exiting this *function */ tap_state_queue_run(); tmp_retval = dtc_queue_run(); if (tmp_retval != ERROR_OK) retval = tmp_retval; #ifndef AUTOMATIC_BUSY_LED /* turn LED onff */ ep1_generic_commandl(pHDev, 2, EP1_CMD_SET_PORTD_LEDS, ~0 ); #endif return retval; } /* Using an unindexed table because it is infrequently accessed and it is short. The table must be *in order of ascending speed (and descending prescaler), as it is scanned in reverse. */ static int rlink_speed(int speed) { int i; if (speed == 0) { /* fastest speed */ speed = rlink_speed_table[rlink_speed_table_size - 1].prescaler; } for (i = rlink_speed_table_size; i--; ) { if (rlink_speed_table[i].prescaler == speed) { if (dtc_load_from_buffer(pHDev, rlink_speed_table[i].dtc, rlink_speed_table[i].dtc_size) != 0) { LOG_ERROR( "An error occurred while trying to load DTC code for speed \"%d\".", speed); exit(1); } if (dtc_start_download() < 0) { LOG_ERROR("starting DTC: %s", usb_strerror()); exit(1); } return ERROR_OK; } } LOG_ERROR("%d is not a supported speed", speed); return ERROR_FAIL; } static int rlink_speed_div(int speed, int *khz) { int i; for (i = rlink_speed_table_size; i--; ) { if (rlink_speed_table[i].prescaler == speed) { *khz = rlink_speed_table[i].khz; return ERROR_OK; } } LOG_ERROR("%d is not a supported speed", speed); return ERROR_FAIL; } static int rlink_khz(int khz, int *speed) { int i; if (khz == 0) { LOG_ERROR("RCLK not supported"); return ERROR_FAIL; } for (i = rlink_speed_table_size; i--; ) { if (rlink_speed_table[i].khz <= khz) { *speed = rlink_speed_table[i].prescaler; return ERROR_OK; } } LOG_WARNING("The lowest supported JTAG speed is %d KHz", rlink_speed_table[0].khz); *speed = rlink_speed_table[0].prescaler; return ERROR_OK; } static int rlink_init(void) { int i, j, retries; uint8_t reply_buffer[USB_EP1IN_SIZE]; usb_init(); const uint16_t vids[] = { USB_IDVENDOR, 0 }; const uint16_t pids[] = { USB_IDPRODUCT, 0 }; if (jtag_usb_open(vids, pids, &pHDev) != ERROR_OK) return ERROR_FAIL; struct usb_device *dev = usb_device(pHDev); if (dev->descriptor.bNumConfigurations > 1) { LOG_ERROR("Whoops! NumConfigurations is not 1, don't know what to do..."); return ERROR_FAIL; } if (dev->config->bNumInterfaces > 1) { LOG_ERROR("Whoops! NumInterfaces is not 1, don't know what to do..."); return ERROR_FAIL; } LOG_DEBUG("Opened device, pHDev = %p", pHDev); /* usb_set_configuration required under win32 */ usb_set_configuration(pHDev, dev->config[0].bConfigurationValue); retries = 3; do { i = usb_claim_interface(pHDev, 0); if (i) { LOG_ERROR("usb_claim_interface: %s", usb_strerror()); #ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP j = usb_detach_kernel_driver_np(pHDev, 0); if (j) LOG_ERROR("detach kernel driver: %s", usb_strerror()); #endif } else { LOG_DEBUG("interface claimed!"); break; } } while (--retries); if (i) { LOG_ERROR("Initialisation failed."); return ERROR_FAIL; } if (usb_set_altinterface(pHDev, 0) != 0) { LOG_ERROR("Failed to set interface."); return ERROR_FAIL; } /* The device starts out in an unknown state on open. As such, * result reads time out, and it's not even known whether the * command was accepted. So, for this first command, we issue * it repeatedly until its response doesn't time out. Also, if * sending a command is going to time out, we find that out here. * * It must be possible to open the device in such a way that * this special magic isn't needed, but, so far, it escapes us. */ for (i = 0; i < 5; i++) { j = ep1_generic_commandl( pHDev, 1, EP1_CMD_GET_FWREV ); if (j < USB_EP1OUT_SIZE) { LOG_ERROR("USB write error: %s", usb_strerror()); return ERROR_FAIL; } j = usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)reply_buffer, sizeof(reply_buffer), 200 ); if (j != -ETIMEDOUT) break; } if (j < (int)sizeof(reply_buffer)) { LOG_ERROR("USB read error: %s", usb_strerror()); return ERROR_FAIL; } LOG_DEBUG(INTERFACE_NAME " firmware version: %d.%d.%d", reply_buffer[0], reply_buffer[1], reply_buffer[2]); if ((reply_buffer[0] != 0) || (reply_buffer[1] != 0) || (reply_buffer[2] != 3)) LOG_WARNING( "The rlink device is not of the version that the developers have played with. It may or may not work."); /* Probe port E for adapter presence */ ep1_generic_commandl( pHDev, 16, EP1_CMD_MEMORY_WRITE, /* Drive sense pin with 0 */ ST7_PEDR >> 8, ST7_PEDR, 3, 0x00, /* DR */ ST7_PE_ADAPTER_SENSE_OUT, /* DDR */ ST7_PE_ADAPTER_SENSE_OUT, /* OR */ EP1_CMD_MEMORY_READ, /* Read back */ ST7_PEDR >> 8, ST7_PEDR, 1, EP1_CMD_MEMORY_WRITE, /* Drive sense pin with 1 */ ST7_PEDR >> 8, ST7_PEDR, 1, ST7_PE_ADAPTER_SENSE_OUT ); usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)reply_buffer, 1, USB_TIMEOUT_MS ); if ((reply_buffer[0] & ST7_PE_ADAPTER_SENSE_IN) != 0) LOG_WARNING("target detection problem"); ep1_generic_commandl( pHDev, 11, EP1_CMD_MEMORY_READ, /* Read back */ ST7_PEDR >> 8, ST7_PEDR, 1, EP1_CMD_MEMORY_WRITE, /* float port E */ ST7_PEDR >> 8, ST7_PEDR, 3, 0x00, /* DR */ 0x00, /* DDR */ 0x00 /* OR */ ); usb_bulk_read( pHDev, USB_EP1IN_ADDR, (char *)reply_buffer, 1, USB_TIMEOUT_MS ); if ((reply_buffer[0] & ST7_PE_ADAPTER_SENSE_IN) == 0) LOG_WARNING("target not plugged in"); /* float ports A and B */ ep1_generic_commandl( pHDev, 11, EP1_CMD_MEMORY_WRITE, ST7_PADDR >> 8, ST7_PADDR, 2, 0x00, 0x00, EP1_CMD_MEMORY_WRITE, ST7_PBDDR >> 8, ST7_PBDDR, 1, 0x00 ); /* make sure DTC is stopped, set VPP control, set up ports A and B */ ep1_generic_commandl( pHDev, 14, EP1_CMD_DTC_STOP, EP1_CMD_SET_PORTD_VPP, ~(ST7_PD_VPP_SHDN), EP1_CMD_MEMORY_WRITE, ST7_PADR >> 8, ST7_PADR, 2, ((~(0)) & (ST7_PA_NTRST)), (ST7_PA_NTRST), /* port B has no OR, and we want to emulate open drain on NSRST, so we set DR to 0 *here and later assert NSRST by setting DDR bit to 1. */ EP1_CMD_MEMORY_WRITE, ST7_PBDR >> 8, ST7_PBDR, 1, 0x00 ); /* set LED updating mode and make sure they're unlit */ ep1_generic_commandl( pHDev, 3, #ifdef AUTOMATIC_BUSY_LED EP1_CMD_LEDUE_BUSY, #else EP1_CMD_LEDUE_NONE, #endif EP1_CMD_SET_PORTD_LEDS, ~0 ); tap_state_queue_init(); dtc_queue_init(); rlink_reset(0, 0); return ERROR_OK; } static int rlink_quit(void) { /* stop DTC and make sure LEDs are off */ ep1_generic_commandl( pHDev, 6, EP1_CMD_DTC_STOP, EP1_CMD_LEDUE_NONE, EP1_CMD_SET_PORTD_LEDS, ~0, EP1_CMD_SET_PORTD_VPP, ~0 ); usb_release_interface(pHDev, 0); usb_close(pHDev); return ERROR_OK; } struct jtag_interface rlink_interface = { .name = "rlink", .init = rlink_init, .quit = rlink_quit, .speed = rlink_speed, .speed_div = rlink_speed_div, .khz = rlink_khz, .execute_queue = rlink_execute_queue, };