diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/jtag/drivers/Makefile.am | 3 | ||||
-rw-r--r-- | src/jtag/drivers/usb_blaster.c | 589 | ||||
-rw-r--r-- | src/jtag/interfaces.c | 6 |
4 files changed, 602 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index f60feac3..a566b4d4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,12 +60,16 @@ endif if FT2232_LIBFTDI FTDI2232LIB = -lftdi -lusb else +if USB_BLASTER_LIBFTDI +FTDI2232LIB = -lftdi -lusb +else if PRESTO_LIBFTDI FTDI2232LIB = -lftdi -lusb else FTDI2232LIB = endif endif +endif if USBPROG LIBUSB = -lusb diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index 8ee5ac59..d6113c60 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -28,6 +28,9 @@ endif if FT2232_DRIVER DRIVERFILES += ft2232.c endif +if USB_BLASTER_DRIVER +DRIVERFILES += usb_blaster.c +endif if AMTJTAGACCEL DRIVERFILES += amt_jtagaccel.c endif diff --git a/src/jtag/drivers/usb_blaster.c b/src/jtag/drivers/usb_blaster.c new file mode 100644 index 00000000..3703323b --- /dev/null +++ b/src/jtag/drivers/usb_blaster.c @@ -0,0 +1,589 @@ +/*************************************************************************** + * Driver for USB-JTAG, Altera USB-Blaster and compatibles * + * Original code from Kolja Waschk's USB-JTAG project * + * (http://www.ixo.de/info/usb_jtag/). * + * Some updates by Anthony Liu (2006). * + * Minor updates and cleanup by Catalin Patulea (2009). * + * * + * Copyright (C) 2009 Catalin Patulea * + * cat@vv.carleton.ca * + * * + * Copyright (C) 2006 Kolja Waschk * + * usbjtag@ixo.de * + * * + * Based on ft2232.c and bitbang.c, * + * Copyright (C) 2004,2006 by Dominic Rath * + * * + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* + * The following information is originally from Kolja Waschk's USB-JTAG, + * where it was obtained by reverse engineering an Altera USB-Blaster. + * See http://www.ixo.de/info/usb_jtag/ for USB-Blaster block diagram and + * usb_jtag-20080705-1200.zip#usb_jtag/host/openocd for protocol. + * + * The same information is also on the UrJTAG mediawiki, with some additional + * notes on bits marked as "unknown" by usb_jtag. + * (http://sourceforge.net/apps/mediawiki/urjtag/index.php? + * title=Cable_Altera_USB-Blaster) + * + * USB-JTAG, Altera USB-Blaster and compatibles are typically implemented as + * an FTDIChip FT245 followed by a CPLD which handles a two-mode protocol: + * + * _________ + * | | + * | AT93C46 | + * |_________| + * __|__________ _________ + * | | | | + * USB__| FTDI 245BM |__| EPM7064 |__JTAG (B_TDO,B_TDI,B_TMS,B_TCK) + * |_____________| |_________| + * __|__________ _|___________ + * | | | | + * | 6 MHz XTAL | | 24 MHz Osc. | + * |_____________| |_____________| + * + * Protocol details are given in the code below. + * + * It is also possible to emulate this configuration using a single-chip USB + * controller like the Cypress FX2 (again, see usb_jtag for details). + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if IS_CYGWIN == 1 +#include "windows.h" +#undef LOG_ERROR +#endif + +/* project specific includes */ +#include <jtag/interface.h> +#include <jtag/commands.h> +#include <helper/time_support.h> + +/* system includes */ +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "bitbang.h" + +#if (BUILD_USB_BLASTER_FTD2XX == 1 && BUILD_USB_BLASTER_LIBFTDI == 1) +#error "BUILD_USB_BLASTER_FTD2XX && BUILD_USB_BLASTER_LIBFTDI " + "are mutually exclusive" +#elif (BUILD_USB_BLASTER_FTD2XX != 1 && BUILD_USB_BLASTER_LIBFTDI != 1) +#error "BUILD_USB_BLASTER_FTD2XX || BUILD_USB_BLASTER_LIBFTDI must be chosen" +#endif + +/* USB_BLASTER access library includes */ +#if BUILD_USB_BLASTER_FTD2XX == 1 +#include <ftd2xx.h> +#elif BUILD_USB_BLASTER_LIBFTDI == 1 +#include <ftdi.h> +#endif + +#include <sys/time.h> +#include <time.h> + +static char *usb_blaster_device_desc; +static uint16_t usb_blaster_vid = 0x09fb; /* Altera */ +static uint16_t usb_blaster_pid = 0x6001; /* USB-Blaster */ + +/* last output byte in simple bit banging mode */ +static uint8_t out_value; + +#if BUILD_USB_BLASTER_FTD2XX == 1 +static FT_HANDLE ftdih; +#elif BUILD_USB_BLASTER_LIBFTDI == 1 +static struct ftdi_context ftdic; +#endif + +static int usb_blaster_buf_write( + uint8_t *buf, int size, uint32_t *bytes_written) +{ +#if BUILD_USB_BLASTER_FTD2XX == 1 + FT_STATUS status; + DWORD dw_bytes_written; + +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("usb_blaster_buf_write %02X (%d)\n", buf[0], size); +#endif + status = FT_Write(ftdih, buf, size, &dw_bytes_written); + if (status != FT_OK) + { + *bytes_written = dw_bytes_written; + LOG_ERROR("FT_Write returned: %lu", status); + return ERROR_JTAG_DEVICE_ERROR; + } + *bytes_written = dw_bytes_written; + return ERROR_OK; +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + int retval; +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("usb_blaster_buf_write %02X (%d)\n", buf[0], size); +#endif + retval = ftdi_write_data(&ftdic, buf, size); + if (retval < 0) + { + *bytes_written = 0; + LOG_ERROR("ftdi_write_data: %s", ftdi_get_error_string(&ftdic)); + return ERROR_JTAG_DEVICE_ERROR; + } + *bytes_written = retval; + return ERROR_OK; +#endif +} + +static int +usb_blaster_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read) +{ +#if BUILD_USB_BLASTER_FTD2XX == 1 + DWORD dw_bytes_read; + FT_STATUS status; + + status = FT_Read(ftdih, buf, size, &dw_bytes_read); + if (status != FT_OK) + { + *bytes_read = dw_bytes_read; + LOG_ERROR("FT_Read returned: %lu", status); + return ERROR_JTAG_DEVICE_ERROR; + } +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("usb_blaster_buf_read %02X (%lu)\n", buf[0], dw_bytes_read); +#endif + *bytes_read = dw_bytes_read; + return ERROR_OK; + +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + int retval; + int timeout = 100; + + *bytes_read = 0; + while ((*bytes_read < size) && timeout--) + { + retval = ftdi_read_data(&ftdic, buf + *bytes_read, + size - *bytes_read); + if (retval < 0) + { + *bytes_read = 0; + LOG_ERROR("ftdi_read_data: %s", + ftdi_get_error_string(&ftdic)); + return ERROR_JTAG_DEVICE_ERROR; + } + *bytes_read += retval; + } +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("usb_blaster_buf_read %02X (%d)\n", buf[0], *bytes_read); +#endif + return ERROR_OK; +#endif +} + +/* The following code doesn't fully utilize the possibilities of the + * USB-Blaster. It writes one byte per JTAG pin state change at a time; it + * doesn't even try to buffer data up to the maximum packet size of 64 bytes. + * + * Actually, the USB-Blaster offers a byte-shift mode to transmit up to 504 data + * bits (bidirectional) in a single USB packet. A header byte has to be sent as + * the first byte in a packet with the following meaning: + * + * Bit 7 (0x80): Must be set to indicate byte-shift mode. + * Bit 6 (0x40): If set, the USB-Blaster will also read data, not just write. + * Bit 5..0: Define the number N of following bytes + * + * All N following bytes will then be clocked out serially on TDI. If Bit 6 was + * set, it will afterwards return N bytes with TDO data read while clocking out + * the TDI data. LSB of the first byte after the header byte will appear first + * on TDI. + */ + +/* Simple bit banging mode: + * + * Bit 7 (0x80): Must be zero (see byte-shift mode above) + * Bit 6 (0x40): If set, you will receive a byte indicating the state of TDO + * in return. + * Bit 5 (0x20): Output Enable/LED. + * Bit 4 (0x10): TDI Output. + * Bit 3 (0x08): nCS Output (not used in JTAG mode). + * Bit 2 (0x04): nCE Output (not used in JTAG mode). + * Bit 1 (0x02): TMS Output. + * Bit 0 (0x01): TCK Output. + * + * For transmitting a single data bit, you need to write two bytes. Up to 64 + * bytes can be combined in a single USB packet (but this is not done in the + * code below). It isn't possible to read a data without transmitting data. + */ + +#define TCK (1 << 0) +#define TMS (1 << 1) +#define NCE (1 << 2) +#define NCS (1 << 3) +#define TDI (1 << 4) +#define LED (1 << 5) +#define READ (1 << 6) +#define SHMODE (1 << 7) +#define OTHERS ((1 << 2) | (1 << 3) | (1 << 5)) + +#define READ_TDO (1 << 0) + +static void usb_blaster_write_data(void) +{ + uint32_t bytes_written; + usb_blaster_buf_write(&out_value, 1, &bytes_written); +} + +static int usb_blaster_read_data(void) +{ + int status; + uint8_t buf[1]; + uint32_t bytes_read; + + out_value |= READ; + usb_blaster_write_data(); + out_value &= ~READ; + + status = usb_blaster_buf_read(buf, 1, &bytes_read); + if (status < 0) + return 0; + + return !!(buf[0] & READ_TDO); +} + +static void usb_blaster_write(int tck, int tms, int tdi) +{ +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("---- usb_blaster_write(%d,%d,%d)\n", tck, tms, tdi); +#endif + out_value &= ~(TCK | TMS | TDI); + if (tck) + out_value |= TCK; + if (tms) + out_value |= TMS; + if (tdi) + out_value |= TDI; + + usb_blaster_write_data(); +} + +static int usb_blaster_speed(int speed) +{ +#if BUILD_USB_BLASTER_FTD2XX == 1 + LOG_DEBUG("TODO: usb_blaster_speed() isn't implemented for libftd2xx!"); +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + LOG_DEBUG("TODO: usb_blaster_speed() isn't optimally implemented!"); + + /* TODO: libftdi's ftdi_set_baudrate chokes on high rates, use lowlevel + * usb function instead! And additionally allow user to throttle. + */ + if (ftdi_set_baudrate(&ftdic, 3000000 / 4) < 0) + { + LOG_ERROR("Can't set baud rate to max: %s", + ftdi_get_error_string(&ftdic)); + return ERROR_JTAG_DEVICE_ERROR; + }; +#endif + + return ERROR_OK; +} + +static void usb_blaster_reset(int trst, int srst) +{ + LOG_DEBUG("TODO: usb_blaster_reset(%d,%d) isn't implemented!", + trst, srst); +} + +static struct bitbang_interface usb_blaster_bitbang = { + .read = usb_blaster_read_data, + .write = usb_blaster_write, + .reset = usb_blaster_reset, +}; + +static int usb_blaster_init(void) +{ + uint8_t latency_timer; + +#if BUILD_USB_BLASTER_FTD2XX == 1 + FT_STATUS status; +#endif + +#if BUILD_USB_BLASTER_FTD2XX == 1 + LOG_DEBUG("'usb_blaster' interface using FTD2XX"); +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + LOG_DEBUG("'usb_blaster' interface using libftdi"); +#endif + +#if BUILD_USB_BLASTER_FTD2XX == 1 + /* Open by device description */ + if (usb_blaster_device_desc == NULL) + { + LOG_WARNING("no usb_blaster device description specified, " + "using default 'USB-Blaster'"); + usb_blaster_device_desc = "USB-Blaster"; + } + +#if IS_WIN32 == 0 + /* Add non-standard Vid/Pid to the linux driver */ + status = FT_SetVIDPID(usb_blaster_vid, usb_blaster_pid); + if (status != FT_OK) + { + LOG_WARNING("couldn't add %4.4x:%4.4x", + usb_blaster_vid, usb_blaster_pid); + } +#endif + + status = FT_OpenEx(usb_blaster_device_desc, FT_OPEN_BY_DESCRIPTION, + &ftdih); + if (status != FT_OK) + { + DWORD num_devices; + + LOG_ERROR("unable to open ftdi device: %lu", status); + status = FT_ListDevices(&num_devices, NULL, + FT_LIST_NUMBER_ONLY); + if (status == FT_OK) + { + char **desc_array = malloc(sizeof(char *) + * (num_devices + 1)); + unsigned int i; + + for (i = 0; i < num_devices; i++) + desc_array[i] = malloc(64); + desc_array[num_devices] = NULL; + + status = FT_ListDevices(desc_array, &num_devices, + FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION); + + if (status == FT_OK) + { + LOG_ERROR("ListDevices: %lu\n", num_devices); + for (i = 0; i < num_devices; i++) + LOG_ERROR("%i: %s", i, desc_array[i]); + } + + for (i = 0; i < num_devices; i++) + free(desc_array[i]); + free(desc_array); + } + else + { + printf("ListDevices: NONE\n"); + } + return ERROR_JTAG_INIT_FAILED; + } + + status = FT_SetLatencyTimer(ftdih, 2); + if (status != FT_OK) + { + LOG_ERROR("unable to set latency timer: %lu", status); + return ERROR_JTAG_INIT_FAILED; + } + + status = FT_GetLatencyTimer(ftdih, &latency_timer); + if (status != FT_OK) + { + LOG_ERROR("unable to get latency timer: %lu", status); + return ERROR_JTAG_INIT_FAILED; + } + LOG_DEBUG("current latency timer: %i", latency_timer); + + status = FT_SetBitMode(ftdih, 0x00, 0); + if (status != FT_OK) + { + LOG_ERROR("unable to disable bit i/o mode: %lu", status); + return ERROR_JTAG_INIT_FAILED; + } +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + if (ftdi_init(&ftdic) < 0) + return ERROR_JTAG_INIT_FAILED; + + /* context, vendor id, product id */ + if (ftdi_usb_open(&ftdic, usb_blaster_vid, usb_blaster_pid) < 0) + { + LOG_ERROR("unable to open ftdi device: %s", ftdic.error_str); + return ERROR_JTAG_INIT_FAILED; + } + + if (ftdi_usb_reset(&ftdic) < 0) + { + LOG_ERROR("unable to reset ftdi device"); + return ERROR_JTAG_INIT_FAILED; + } + + if (ftdi_set_latency_timer(&ftdic, 2) < 0) + { + LOG_ERROR("unable to set latency timer"); + return ERROR_JTAG_INIT_FAILED; + } + + if (ftdi_get_latency_timer(&ftdic, &latency_timer) < 0) + { + LOG_ERROR("unable to get latency timer"); + return ERROR_JTAG_INIT_FAILED; + } + LOG_DEBUG("current latency timer: %u", latency_timer); + + ftdi_disable_bitbang(&ftdic); +#endif + + bitbang_interface = &usb_blaster_bitbang; + + usb_blaster_speed(jtag_get_speed()); + +#if 0 +#if BUILD_USB_BLASTER_FTD2XX == 1 + if ((status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX)) != FT_OK) + { + LOG_ERROR("error purging ftd2xx device: %i", status); + return ERROR_JTAG_INIT_FAILED; + } +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + if (ftdi_usb_purge_buffers(&ftdic) < 0) + { + LOG_ERROR("ftdi_purge_buffers: %s", ftdic.error_str); + return ERROR_JTAG_INIT_FAILED; + } +#endif +#endif + + return ERROR_OK; +} + +static int usb_blaster_quit(void) +{ +#if BUILD_USB_BLASTER_FTD2XX == 1 + FT_STATUS status; + + status = FT_Close(ftdih); +#elif BUILD_USB_BLASTER_LIBFTDI == 1 + ftdi_usb_close(&ftdic); + ftdi_deinit(&ftdic); +#endif + + return ERROR_OK; +} + +COMMAND_HANDLER(usb_blaster_handle_device_desc_command) +{ + if (CMD_ARGC == 1) + usb_blaster_device_desc = strdup(CMD_ARGV[0]); + else + LOG_ERROR("require exactly one argument to " + "usb_blaster_device_desc <description>"); + + return ERROR_OK; +} + +COMMAND_HANDLER(usb_blaster_handle_vid_pid_command) +{ + if (CMD_ARGC > 2) + { + LOG_WARNING("ignoring extra IDs in ft2232_vid_pid " + "(maximum is 1 pair)"); + CMD_ARGC = 2; + } + if (CMD_ARGC == 2) + { + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], usb_blaster_vid); + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], usb_blaster_pid); + } + else + LOG_WARNING("incomplete usb_blaster_vid_pid configuration"); + + return ERROR_OK; +} + +COMMAND_HANDLER(usb_blaster_handle_pin_command) +{ + if (CMD_ARGC == 2) + { + const char * const pin_name = CMD_ARGV[0]; + uint8_t mask; + unsigned int state; + + if (!strcmp(pin_name, "pin6")) + mask = NCE; + else if (!strcmp(pin_name, "pin8")) + mask = NCS; + else + { + LOG_ERROR("%s: pin name must be \"pin6\" or \"pin8\"", + CMD_NAME); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], state); + if (state == 0) + { + out_value &= ~mask; + usb_blaster_write_data(); + } + else if (state == 1) + { + out_value |= mask; + usb_blaster_write_data(); + } + else + { + LOG_ERROR("%s: pin state must be 0 or 1", CMD_NAME); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + return ERROR_OK; + } + else + { + LOG_ERROR("%s takes exactly two arguments", CMD_NAME); + return ERROR_COMMAND_SYNTAX_ERROR; + } +} + +static const struct command_registration usb_blaster_command_handlers[] = { + { + .name = "usb_blaster_device_desc", + .handler = usb_blaster_handle_device_desc_command, + .mode = COMMAND_CONFIG, + .help = "set the USB device description of the USB-Blaster", + .usage = "description-string", + }, + { + .name = "usb_blaster_vid_pid", + .handler = usb_blaster_handle_vid_pid_command, + .mode = COMMAND_CONFIG, + .help = "the vendor ID and product ID of the USB-Blaster", + .usage = "vid pid", + }, + { + .name = "usb_blaster", + .handler = usb_blaster_handle_pin_command, + .mode = COMMAND_ANY, + .help = "set pin state for the unused GPIO pins", + .usage = "(pin6|pin8) (0|1)", + }, + COMMAND_REGISTRATION_DONE +}; + +struct jtag_interface usb_blaster_interface = { + .name = "usb_blaster", + .commands = usb_blaster_command_handlers, + + .execute_queue = bitbang_execute_queue, + + .speed = usb_blaster_speed, + .init = usb_blaster_init, + .quit = usb_blaster_quit, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 643e1118..f6d82195 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -58,6 +58,9 @@ extern struct jtag_interface ft2232_interface; #if BUILD_FT2232_LIBFTDI == 1 extern struct jtag_interface ft2232_interface; #endif +#if BUILD_USB_BLASTER_LIBFTDI == 1 || BUILD_USB_BLASTER_FTD2XX == 1 +extern struct jtag_interface usb_blaster_interface; +#endif #if BUILD_AMTJTAGACCEL == 1 extern struct jtag_interface amt_jtagaccel_interface; #endif @@ -115,6 +118,9 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_FT2232_LIBFTDI == 1 &ft2232_interface, #endif +#if BUILD_USB_BLASTER_LIBFTDI == 1 || BUILD_USB_BLASTER_FTD2XX == 1 + &usb_blaster_interface, +#endif #if BUILD_AMTJTAGACCEL == 1 &amt_jtagaccel_interface, #endif |