Hello, Kolja Waschk's USB-JTAG (http://www.ixo.de/info/usb_jtag/) is one of many devices protocol-compatible with the Altera USB Blaster. He hosts an outdated patch to openocd which adds support for these devices. The attached patch is an updated work-in-progress version of his patch.
Note that the USB Blaster protocol is different from the MPSSE protocol implemented by ft2232.c Testing: Correctly auto-probes a single-tap chain hanging off a USB Blaster clone. (Windows XP with FT2DXX). Any testing in other environments would be very much appreciated :-) Bugs: - usb_blaster_reset is unimplemented - should use the more USB packet-efficient mode of the protocol (currently bitbangs through bitbang.c) - there is some potential code sharing between this and ft2232.c (abstraction of libftdi and ft2dxx) This is a work-in-progress, I'm just putting it out there so I can get some early feedback. Catalin
diff --git a/configure.in b/configure.in index 12270eb..2b13a79 100644 --- a/configure.in +++ b/configure.in @@ -392,6 +392,14 @@ AC_ARG_ENABLE(ft2232_ftd2xx, AS_HELP_STRING([--enable-ft2232_ftd2xx], [Enable building support for FT2232 based devices using the FTD2XX driver from ftdichip.com]), [build_ft2232_ftd2xx=$enableval], [build_ft2232_ftd2xx=no]) +AC_ARG_ENABLE(usb_blaster_libftdi, + AS_HELP_STRING([--enable-usb_blaster_libftdi], [Enable building support for the Altera USB-Blaster using the libftdi driver, opensource alternate of FTD2XX]), + [build_usb_blaster_libftdi=$enableval], [build_usb_blaster_libftdi=no]) + +AC_ARG_ENABLE(usb_blaster_ftd2xx, + AS_HELP_STRING([--enable-usb_blaster_ftd2xx], [Enable building support for the Altera USB-Blaster using the FTD2XX driver from ftdichip.com]), + [build_usb_blaster_ftd2xx=$enableval], [build_usb_blaster_ftd2xx=no]) + AC_ARG_ENABLE(amtjtagaccel, AS_HELP_STRING([--enable-amtjtagaccel], [Enable building the Amontec JTAG-Accelerator driver]), [build_amtjtagaccel=$enableval], [build_amtjtagaccel=no]) @@ -651,6 +659,20 @@ else AC_DEFINE(BUILD_FT2232_FTD2XX, 0, [0 if you don't want ftd2xx ft2232.]) fi +if test $build_usb_blaster_libftdi = yes; then + build_bitbang=yes + AC_DEFINE(BUILD_USB_BLASTER_LIBFTDI, 1, [1 if you want libftdi usb_blaster.]) +else + AC_DEFINE(BUILD_USB_BLASTER_LIBFTDI, 0, [0 if you don't want libftdi usb_blaster.]) +fi + +if test $build_usb_blaster_ftd2xx = yes; then + build_bitbang=yes + AC_DEFINE(BUILD_USB_BLASTER_FTD2XX, 1, [1 if you want ftd2xx usb_blaster.]) +else + AC_DEFINE(BUILD_USB_BLASTER_FTD2XX, 0, [0 if you don't want ftd2xx usb_blaster.]) +fi + if test $build_amtjtagaccel = yes; then AC_DEFINE(BUILD_AMTJTAGACCEL, 1, [1 if you want the Amontec JTAG-Accelerator driver.]) else @@ -727,7 +749,7 @@ then AC_MSG_ERROR([The option: with_ftd2xx_linux_tardir is for LINUX only.]) fi -if test $build_ft2232_ftd2xx = yes -o $build_presto_ftd2xx = yes ; then +if test $build_ft2232_ftd2xx = yes -o $build_presto_ftd2xx = yes -o $build_usb_blaster_ftd2xx = yes ; then AC_MSG_CHECKING([for ftd2xx.lib exists (win32)]) # if we are given a zipdir... @@ -1001,6 +1023,8 @@ AM_CONDITIONAL(AT91RM9200, test $build_at91rm9200 = yes) AM_CONDITIONAL(BITBANG, test $build_bitbang = yes) AM_CONDITIONAL(FT2232_LIBFTDI, test $build_ft2232_libftdi = yes) AM_CONDITIONAL(FT2232_DRIVER, test $build_ft2232_ftd2xx = yes -o $build_ft2232_libftdi = yes) +AM_CONDITIONAL(USB_BLASTER_LIBFTDI, test $build_usb_blaster_libftdi = yes) +AM_CONDITIONAL(USB_BLASTER_DRIVER, test $build_usb_blaster_ftd2xx = yes -o $build_usb_blaster_libftdi = yes) AM_CONDITIONAL(AMTJTAGACCEL, test $build_amtjtagaccel = yes) AM_CONDITIONAL(GW16012, test $build_gw16012 = yes) AM_CONDITIONAL(PRESTO_LIBFTDI, test $build_presto_libftdi = yes) diff --git a/src/Makefile.am b/src/Makefile.am index f60feac..a566b4d 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 8ee5ac5..d6113c6 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 0000000..af05c51 --- /dev/null +++ b/src/jtag/drivers/usb_blaster.c @@ -0,0 +1,484 @@ +/*************************************************************************** + * * + * Copyright (C) 2006 Kolja Waschk * + * [email protected] * + * * + * 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. * + ***************************************************************************/ +#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> +#include "bitbang.h" + +/* system includes */ +#include <string.h> +#include <stdlib.h> +#include <unistd.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> + +int usb_blaster_execute_queue(void); + +void usb_blaster_blink(void); +int usb_blaster_speed(int speed); +int usb_blaster_init(void); +int usb_blaster_quit(void); + +char *usb_blaster_device_desc = NULL; +char *usb_blaster_layout = NULL; +uint16_t usb_blaster_vid = 0x09fb; // Altera +uint16_t usb_blaster_pid = 0x6001; // USB-Blaster + +static int last_tck; +static int last_tms; +static int last_tdi; + +#if BUILD_USB_BLASTER_FTD2XX == 1 +static FT_HANDLE ftdih = NULL; +#elif BUILD_USB_BLASTER_LIBFTDI == 1 +static struct ftdi_context ftdic; +#endif + +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 + if ((status = FT_Write(ftdih, buf, size, &dw_bytes_written)) != FT_OK) + { + *bytes_written = dw_bytes_written; + LOG_ERROR("FT_Write returned: %lu", status); + return ERROR_JTAG_DEVICE_ERROR; + } + else + { + *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 + if ((retval = ftdi_write_data(&ftdic, buf, size)) < 0) + { + *bytes_written = 0; + LOG_ERROR("ftdi_write_data: %s", ftdi_get_error_string(&ftdic)); + return ERROR_JTAG_DEVICE_ERROR; + } + else + { + *bytes_written = retval; + return ERROR_OK; + } +#endif +} + +int usb_blaster_buf_read(uint8_t* buf, int size, uint32_t* bytes_read) +{ +#if BUILD_USB_BLASTER_FTD2XX == 1 + DWORD dw_bytes_read; + FT_STATUS status; + if ((status = FT_Read(ftdih, buf, size, &dw_bytes_read)) != 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--) + { + if ((retval = ftdi_read_data(&ftdic, buf + *bytes_read, size - *bytes_read)) < 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): Unknown; for now, set to one. + * Bit 4 (0x10): TDI Output. + * Bit 3 (0x08): Unknown; for now, set to one. + * Bit 2 (0x04): Unknown; for now, set to one. + * 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 0 +#define TMS 1 +#define TDI 4 +#define READ 6 +#define SHMODE 7 +#define OTHERS ((1<<2)|(1<<3)|(1<<5)) + +void usb_blaster_write(int tck, int tms, int tdi) +{ + uint8_t buf[1]; + uint32_t count; +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("---- usb_blaster_write(%d,%d,%d)\n", tck,tms,tdi); +#endif + buf[0] = OTHERS | (tck?(1<<TCK):0) | (tms?(1<<TMS):0) | (tdi?(1<<TDI):0); + usb_blaster_buf_write(buf, 1, &count); + + last_tck = tck; + last_tms = tms; + last_tdi = tdi; +} + +int usb_blaster_read(void) +{ + uint8_t buf[1]; + uint32_t count; +#ifdef _DEBUG_JTAG_IO_ + LOG_DEBUG("++++ usb_blaster_write_read(%d,%d,%d)\n", last_tck,last_tms,last_tdi); +#endif + buf[0] = OTHERS | (last_tck?(1<<TCK):0) | (last_tms?(1<<TMS):0) | (last_tdi?(1<<TDI):0) | (1<<READ); + usb_blaster_buf_write(buf, 1, &count); + usb_blaster_buf_read(buf, 1, &count); + return (buf[0]&1); +} + +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; +} + +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, + .write = usb_blaster_write, + .reset = usb_blaster_reset, +}; + +int usb_blaster_init(void) +{ + uint8_t latency_timer; + uint8_t buf[1]; + int retval; + uint32_t bytes_written; + +#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 */ + if ((status = FT_SetVIDPID(usb_blaster_vid, usb_blaster_pid)) != FT_OK) + { + LOG_WARNING("couldn't add %4.4x:%4.4x", usb_blaster_vid, usb_blaster_pid); + } +#endif + + if ((status = FT_OpenEx(usb_blaster_device_desc, FT_OPEN_BY_DESCRIPTION, &ftdih)) != 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)); + 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: %d\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; + } + + if ((status = FT_SetLatencyTimer(ftdih, 2)) != FT_OK) + { + LOG_ERROR("unable to set latency timer: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + + if ((status = FT_GetLatencyTimer(ftdih, &latency_timer)) != FT_OK) + { + LOG_ERROR("unable to get latency timer: %i", status); + return ERROR_JTAG_INIT_FAILED; + } + else + { + LOG_DEBUG("current latency timer: %i", latency_timer); + } + + if ((status = FT_SetBitMode(ftdih, 0x00, 0)) != FT_OK) + { + LOG_ERROR("unable to disable bit i/o mode: %i", 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; + } + else + { + LOG_DEBUG("current latency timer: %i", 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; +} + +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("expected 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 directive"); + } + + return ERROR_OK; +} + +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 Altera USB-Blaster device", + .usage = "<description>", + }, + { + .name = "usb_blaster_vid_pid", + .handler = usb_blaster_handle_vid_pid_command, + .mode = COMMAND_CONFIG, + .help = "the vendor ID and product ID of the Altera USB-Blaster device", + .usage = "<vid> <pid>", + }, + COMMAND_REGISTRATION_DONE +}; + +struct jtag_interface usb_blaster_interface = +{ + .name = "usb_blaster", + .commands = usb_blaster_command_handlers, + + .execute_queue = bitbang_execute_queue, + +// .support_pathmove = 1, + + .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 643e111..4226b3f 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -58,6 +58,12 @@ extern struct jtag_interface ft2232_interface; #if BUILD_FT2232_LIBFTDI == 1 extern struct jtag_interface ft2232_interface; #endif +#if BUILD_USB_BLASTER_FTD2XX == 1 +extern struct jtag_interface usb_blaster_interface; +#endif +#if BUILD_USB_BLASTER_LIBFTDI == 1 +extern struct jtag_interface usb_blaster_interface; +#endif #if BUILD_AMTJTAGACCEL == 1 extern struct jtag_interface amt_jtagaccel_interface; #endif @@ -115,6 +121,12 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_FT2232_LIBFTDI == 1 &ft2232_interface, #endif +#if BUILD_USB_BLASTER_FTD2XX == 1 + &usb_blaster_interface, +#endif +#if BUILD_USB_BLASTER_LIBFTDI == 1 + &usb_blaster_interface, +#endif #if BUILD_AMTJTAGACCEL == 1 &amt_jtagaccel_interface, #endif
_______________________________________________ Openocd-development mailing list [email protected] https://lists.berlios.de/mailman/listinfo/openocd-development
