Browse Source

drivers/jtag: rewrite usb_blaster driver

Rewrite the Altera USB Blaster dongle driver :

 - make extensive use of byte-shift mode, to improve JTAG
   speed.
   This is the main reason of the rewrite. It improves the
   memory dumps with a factor 3 at least, and upload 100
   times, from 1 kBytes/sec to 100 kBytes/sec with a
   USB-Blaster connected to an Altera Virtual JTAG TAP +
   OpenRISC CPU.

 - split the low level API part (between FTDI and FTD2xx)
 from core driver, so that in the future, if both libftdi
 and ftd2xx can coexist, the driver will be able to switch
 dynamically from one access to the other.

Change-Id: I2ee9cedf4a5eb27501f337993ee0cdee52517e7c
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Marek Czerski <ma.czerski@gmail.com>
Tested-by: Franck Jullien <franck.jullien@gmail.com>
Reviewed-on: http://openocd.zylin.com/467
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
Reviewed-by: Alexandre Becoulet <alexandre.becoulet@free.fr>
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
tags/v0.8.0-rc1
Robert Jarzmik 11 years ago
committed by Spencer Oliver
parent
commit
8890ce3469
9 changed files with 1316 additions and 585 deletions
  1. +3
    -3
      configure.ac
  2. +0
    -4
      src/Makefile.am
  3. +4
    -1
      src/jtag/drivers/Makefile.am
  4. +0
    -577
      src/jtag/drivers/usb_blaster.c
  5. +16
    -0
      src/jtag/drivers/usb_blaster/Makefile.am
  6. +52
    -0
      src/jtag/drivers/usb_blaster/ublast_access.h
  7. +178
    -0
      src/jtag/drivers/usb_blaster/ublast_access_ftd2xx.c
  8. +136
    -0
      src/jtag/drivers/usb_blaster/ublast_access_ftdi.c
  9. +927
    -0
      src/jtag/drivers/usb_blaster/usb_blaster.c

+ 3
- 3
configure.ac View File

@@ -701,14 +701,12 @@ else
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.])
@@ -1058,7 +1056,7 @@ LDFLAGS=$LDFLAGS_SAVE
CFLAGS=$CFLAGS_SAVE
fi

if test $build_ft2232_libftdi = yes ; then
if test $build_ft2232_libftdi = yes -o $build_usb_blaster_libftdi = yes; then
# We assume: the package is preinstalled in the proper place
# these present as 2 libraries..
LIBS="$LIBS -lftdi -lusb"
@@ -1182,6 +1180,7 @@ 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([FTDI_DRIVER], [test $build_ftdi = yes])
AM_CONDITIONAL([USB_BLASTER_LIBFTDI], [test $build_usb_blaster_libftdi = yes])
AM_CONDITIONAL([USB_BLASTER_FTD2XX], [test $build_usb_blaster_ftd2xx = 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])
@@ -1312,6 +1311,7 @@ AC_CONFIG_FILES([
src/helper/Makefile
src/jtag/Makefile
src/jtag/drivers/Makefile
src/jtag/drivers/usb_blaster/Makefile
src/jtag/hla/Makefile
src/transport/Makefile
src/xsvf/Makefile


+ 0
- 4
src/Makefile.am View File

@@ -66,16 +66,12 @@ 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

LIBUSB =
if USE_LIBUSB1


+ 4
- 1
src/jtag/drivers/Makefile.am View File

@@ -1,6 +1,7 @@
include $(top_srcdir)/common.mk

noinst_LTLIBRARIES = libocdjtagdrivers.la
libocdjtagdrivers_la_LIBADD =

libocdjtagdrivers_la_SOURCES = \
$(DRIVERFILES)
@@ -13,6 +14,7 @@ ULINK_FIRMWARE = $(srcdir)/OpenULINK
EXTRA_DIST = $(ULINK_FIRMWARE)

DRIVERFILES =
SUBDIRS=

# Standard Driver: common files
DRIVERFILES += driver.c
@@ -47,7 +49,8 @@ if FTDI_DRIVER
DRIVERFILES += ftdi.c mpsse.c
endif
if USB_BLASTER_DRIVER
DRIVERFILES += usb_blaster.c
SUBDIRS += usb_blaster
libocdjtagdrivers_la_LIBADD += $(top_builddir)/src/jtag/drivers/usb_blaster/libocdusbblaster.la
endif
if AMTJTAGACCEL
DRIVERFILES += amt_jtagaccel.c


+ 0
- 577
src/jtag/drivers/usb_blaster.c View File

@@ -1,577 +0,0 @@
/***************************************************************************
* 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). *
* Speed updates by Ali Lown (2011). *
* *
* Copyright (C) 2011 Ali Lown *
* ali@lown.me.uk *
* *
* 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>
#include "ftd2xx_common.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 (legacy) mode */
static uint8_t out_value;
/* global output buffer for bit banging */
#define BUF_LEN 64 /* Size of EP1 */
static uint8_t out_buffer[BUF_LEN];
static uint16_t out_count;

#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)", 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: %s", ftd2xx_status_string(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)", 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: %s", ftd2xx_status_string(status));
return ERROR_JTAG_DEVICE_ERROR;
}
#ifdef _DEBUG_JTAG_IO_
LOG_DEBUG("usb_blaster_buf_read %02X (%" PRIu32 ")", 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)", buf[0], *bytes_read);
#endif
return ERROR_OK;
#endif
}

/* The following code doesn't fully utilize the possibilities of the
* USB-Blaster. It only buffers 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.
* 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_databuffer(uint8_t *buf, uint16_t len)
{
uint32_t bytes_written;
usb_blaster_buf_write(buf, len, &bytes_written);
out_count = 0;
#ifdef _DEBUG_JTAG_IO_
LOG_DEBUG("---- WROTE %d", bytes_written);
#endif
}

static void usb_blaster_addtowritebuffer(uint8_t value, bool forcewrite)
{
out_buffer[out_count] = value;
out_count += 1;
if (out_count == BUF_LEN || forcewrite)
usb_blaster_write_databuffer(out_buffer, out_count);
}

static int usb_blaster_read_data(void)
{
int status;
uint8_t buf[1];
uint32_t bytes_read;

if (out_count > 0)
usb_blaster_write_databuffer(out_buffer, out_count);

out_value |= READ;
usb_blaster_addtowritebuffer(out_value, true);
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)", 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_addtowritebuffer(out_value, false);
}

static void usb_blaster_reset(int trst, int srst)
{
LOG_DEBUG("TODO: usb_blaster_reset(%d,%d) isn't implemented!",
trst, srst);
}

static void usb_blaster_blink(int state)
{
out_value = 0x00;
if (state)
out_value |= LED;

usb_blaster_addtowritebuffer(out_value, true);
}

static struct bitbang_interface usb_blaster_bitbang = {
.read = usb_blaster_read_data,
.write = usb_blaster_write,
.reset = usb_blaster_reset,
.blink = usb_blaster_blink,
};

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 = strdup("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: %s",
ftd2xx_status_string(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: %" PRIu32, (uint32_t)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: %s",
ftd2xx_status_string(status));
return ERROR_JTAG_INIT_FAILED;
}

status = FT_GetLatencyTimer(ftdih, &latency_timer);
if (status != FT_OK) {
LOG_ERROR("unable to get latency timer: %s",
ftd2xx_status_string(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: %s",
ftd2xx_status_string(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;

#if 0
#if BUILD_USB_BLASTER_FTD2XX == 1
status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX);
if (status != 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 (out_count > 0)
usb_blaster_write_databuffer(out_buffer, out_count);

#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

if (usb_blaster_device_desc) {
free(usb_blaster_device_desc);
usb_blaster_device_desc = NULL;
}

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 usb_blaster_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_addtowritebuffer(out_value, true);
} else if (state == 1) {
out_value |= mask;
usb_blaster_addtowritebuffer(out_value, true);
} 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,
.supported = DEBUG_CAP_TMS_SEQ,

.execute_queue = bitbang_execute_queue,

.init = usb_blaster_init,
.quit = usb_blaster_quit,
};

+ 16
- 0
src/jtag/drivers/usb_blaster/Makefile.am View File

@@ -0,0 +1,16 @@
include $(top_srcdir)/common.mk

noinst_LTLIBRARIES = libocdusbblaster.la
libocdusbblaster_la_SOURCES = $(USB_BLASTER_SRC)

USB_BLASTER_SRC = usb_blaster.c

if USB_BLASTER_LIBFTDI
USB_BLASTER_SRC += ublast_access_ftdi.c
endif

if USB_BLASTER_FTD2XX
USB_BLASTER_SRC += ublast_access_ftd2xx.c
endif

noinst_HEADERS = ublast_access.h

+ 52
- 0
src/jtag/drivers/usb_blaster/ublast_access.h View File

@@ -0,0 +1,52 @@
/*
* Driver for USB-JTAG, Altera USB-Blaster and compatibles
*
* Inspired from original code from Kolja Waschk's USB-JTAG project
* (http://www.ixo.de/info/usb_jtag/), and from openocd project.
*
* Copyright (C) 2012 Robert Jarzmik robert.jarzmik@free.fr
* Copyright (C) 2011 Ali Lown ali@lown.me.uk
* Copyright (C) 2009 Catalin Patulea cat@vv.carleton.ca
* Copyright (C) 2006 Kolja Waschk usbjtag@ixo.de
*
* 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.
*
*/

struct ublast_lowlevel {
uint16_t ublast_vid;
uint16_t ublast_pid;
char *ublast_device_desc;

int (*write)(struct ublast_lowlevel *low, uint8_t *buf, int size,
uint32_t *bytes_written);
int (*read)(struct ublast_lowlevel *low, uint8_t *buf, unsigned size,
uint32_t *bytes_read);
int (*open)(struct ublast_lowlevel *low);
int (*close)(struct ublast_lowlevel *low);
int (*speed)(struct ublast_lowlevel *low, int speed);

void *priv;
};

/**
* ublast_register_ftdi - get a lowlevel USB Blaster driver
* ublast_register_ftd2xx - get a lowlevel USB Blaster driver
*
* Get a lowlevel USB-Blaster driver. In the current implementation, there are 2
* possible lowlevel drivers :
* - one based on libftdi from ftdichip.com
* - one based on libftdxx, the free alternative
*
* Returns the lowlevel driver structure.
*/
extern struct ublast_lowlevel *ublast_register_ftdi(void);
extern struct ublast_lowlevel *ublast_register_ftd2xx(void);

+ 178
- 0
src/jtag/drivers/usb_blaster/ublast_access_ftd2xx.c View File

@@ -0,0 +1,178 @@
/*
* Driver for USB-JTAG, Altera USB-Blaster and compatibles
*
* Inspired from original code from Kolja Waschk's USB-JTAG project
* (http://www.ixo.de/info/usb_jtag/), and from openocd project.
*
* Copyright (C) 2012 Robert Jarzmik robert.jarzmik@free.fr
* Copyright (C) 2011 Ali Lown ali@lown.me.uk
* Copyright (C) 2009 Catalin Patulea cat@vv.carleton.ca
* Copyright (C) 2006 Kolja Waschk usbjtag@ixo.de
*
* 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.
*
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <jtag/interface.h>
#include <jtag/commands.h>

#include "ublast_access.h"

#include <ftd2xx.h>
#include "jtag/drivers/ftd2xx_common.h"

static FT_HANDLE *ublast_getftdih(struct ublast_lowlevel *low)
{
return low->priv;
}

static int ublast_ftd2xx_write(struct ublast_lowlevel *low, uint8_t *buf, int size,
uint32_t *bytes_written)
{
FT_STATUS status;
DWORD dw_bytes_written;
FT_HANDLE *ftdih = ublast_getftdih(low);

status = FT_Write(*ftdih, buf, size, &dw_bytes_written);
if (status != FT_OK) {
*bytes_written = dw_bytes_written;
LOG_ERROR("FT_Write returned: %s", ftd2xx_status_string(status));
return ERROR_JTAG_DEVICE_ERROR;
}
*bytes_written = dw_bytes_written;
return ERROR_OK;
}

static int ublast_ftd2xx_read(struct ublast_lowlevel *low, uint8_t *buf,
unsigned size, uint32_t *bytes_read)
{
DWORD dw_bytes_read;
FT_STATUS status;
FT_HANDLE *ftdih = ublast_getftdih(low);

status = FT_Read(*ftdih, buf, size, &dw_bytes_read);
if (status != FT_OK) {
*bytes_read = dw_bytes_read;
LOG_ERROR("FT_Read returned: %s", ftd2xx_status_string(status));
return ERROR_JTAG_DEVICE_ERROR;
}
*bytes_read = dw_bytes_read;
return ERROR_OK;
}

static int ublast_ftd2xx_init(struct ublast_lowlevel *low)
{
FT_STATUS status;
FT_HANDLE *ftdih = ublast_getftdih(low);
uint8_t latency_timer;

LOG_INFO("usb blaster interface using FTD2XX");
/* Open by device description */
if (low->ublast_device_desc == NULL) {
LOG_WARNING("no usb blaster device description specified, "
"using default 'USB-Blaster'");
low->ublast_device_desc = "USB-Blaster";
}

#if IS_WIN32 == 0
/* Add non-standard Vid/Pid to the linux driver */
status = FT_SetVIDPID(low->ublast_vid, low->ublast_pid);
if (status != FT_OK) {
LOG_WARNING("couldn't add %4.4x:%4.4x",
low->ublast_vid, low->ublast_pid);
}
#endif
status = FT_OpenEx(low->ublast_device_desc, FT_OPEN_BY_DESCRIPTION,
ftdih);
if (status != FT_OK) {
DWORD num_devices;

LOG_ERROR("unable to open ftdi device: %s",
ftd2xx_status_string(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: %" PRIu32, (uint32_t)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: %s",
ftd2xx_status_string(status));
return ERROR_JTAG_INIT_FAILED;
}

status = FT_GetLatencyTimer(*ftdih, &latency_timer);
if (status != FT_OK) {
LOG_ERROR("unable to get latency timer: %s",
ftd2xx_status_string(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: %s",
ftd2xx_status_string(status));
return ERROR_JTAG_INIT_FAILED;
}
return ERROR_OK;
}

static int ublast_ftd2xx_quit(struct ublast_lowlevel *low)
{
FT_HANDLE *ftdih = ublast_getftdih(low);

FT_Close(*ftdih);
return ERROR_OK;
}

static struct ublast_lowlevel_priv {
FT_HANDLE ftdih;
} info;

static struct ublast_lowlevel low = {
.open = ublast_ftd2xx_init,
.close = ublast_ftd2xx_quit,
.read = ublast_ftd2xx_read,
.write = ublast_ftd2xx_write,
.priv = &info,
};

struct ublast_lowlevel *ublast_register_ftd2xx(void)
{
return &low;
}

+ 136
- 0
src/jtag/drivers/usb_blaster/ublast_access_ftdi.c View File

@@ -0,0 +1,136 @@
/*
* Driver for USB-JTAG, Altera USB-Blaster and compatibles
*
* Inspired from original code from Kolja Waschk's USB-JTAG project
* (http://www.ixo.de/info/usb_jtag/), and from openocd project.
*
* Copyright (C) 2012 Robert Jarzmik robert.jarzmik@free.fr
* Copyright (C) 2011 Ali Lown ali@lown.me.uk
* Copyright (C) 2009 Catalin Patulea cat@vv.carleton.ca
* Copyright (C) 2006 Kolja Waschk usbjtag@ixo.de
*
* 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.
*
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <jtag/interface.h>
#include <jtag/commands.h>

#include "ublast_access.h"
#include <ftdi.h>

static struct ftdi_context *ublast_getftdic(struct ublast_lowlevel *low)
{
return low->priv;
}

static int ublast_ftdi_read(struct ublast_lowlevel *low, uint8_t *buf,
unsigned size, uint32_t *bytes_read)
{
int retval;
int timeout = 100;
struct ftdi_context *ftdic = ublast_getftdic(low);

*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;
}
return ERROR_OK;
}

static int ublast_ftdi_write(struct ublast_lowlevel *low, uint8_t *buf, int size,
uint32_t *bytes_written)
{
int retval;
struct ftdi_context *ftdic = ublast_getftdic(low);

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;
}

static int ublast_ftdi_init(struct ublast_lowlevel *low)
{
uint8_t latency_timer;
struct ftdi_context *ftdic = ublast_getftdic(low);

LOG_INFO("usb blaster interface using libftdi");
if (ftdi_init(ftdic) < 0)
return ERROR_JTAG_INIT_FAILED;

/* context, vendor id, product id */
if (ftdi_usb_open(ftdic, low->ublast_vid, low->ublast_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);
return ERROR_OK;
}

static int ublast_ftdi_quit(struct ublast_lowlevel *low)
{
struct ftdi_context *ftdic = ublast_getftdic(low);

ftdi_usb_close(ftdic);
ftdi_deinit(ftdic);
return ERROR_OK;
};

static struct ublast_lowlevel_priv {
struct ftdi_context ftdic;
} info;

static struct ublast_lowlevel low = {
.open = ublast_ftdi_init,
.close = ublast_ftdi_quit,
.read = ublast_ftdi_read,
.write = ublast_ftdi_write,
.priv = &info,
};

struct ublast_lowlevel *ublast_register_ftdi(void)
{
return &low;
}

+ 927
- 0
src/jtag/drivers/usb_blaster/usb_blaster.c View File

@@ -0,0 +1,927 @@
/*
* Driver for USB-JTAG, Altera USB-Blaster and compatibles
*
* Inspired from original code from Kolja Waschk's USB-JTAG project
* (http://www.ixo.de/info/usb_jtag/), and from openocd project.
*
* Copyright (C) 2012 Robert Jarzmik robert.jarzmik@free.fr
* Copyright (C) 2011 Ali Lown ali@lown.me.uk
* Copyright (C) 2009 Catalin Patulea cat@vv.carleton.ca
* Copyright (C) 2006 Kolja Waschk usbjtag@ixo.de
*
* 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.
*
*/

/*
* 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. |
* |_____________| |_____________|
*
*/

#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 "ublast_access.h"

/* system includes */
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>

/* Size of USB endpoint max packet size, ie. 64 bytes */
#define MAX_PACKET_SIZE 64
/*
* Size of data buffer that holds bytes in byte-shift mode.
* This buffer can hold multiple USB packets aligned to
* MAX_PACKET_SIZE bytes boundaries.
* BUF_LEN must be grater than or equal MAX_PACKET_SIZE.
*/
#define BUF_LEN 4096

struct ublast_info {
int pin6;
int pin8;
int tms;
int tdi;
uint8_t buf[BUF_LEN];
int bufidx;

char *lowlevel_name;
struct ublast_lowlevel *drv;
char *ublast_device_desc;
uint16_t ublast_vid, ublast_pid;
};

/*
* Global device control
*/
static struct ublast_info info = {
.ublast_vid = 0x09fb, /* Altera */
.ublast_pid = 0x6001, /* USB-Blaster */
.lowlevel_name = NULL,
};

/*
* Available lowlevel drivers (FTDI, FTD2xx, ...)
*/
struct drvs_map {
char *name;
struct ublast_lowlevel *(*drv_register)(void);
};

static struct drvs_map lowlevel_drivers_map[] = {
#if BUILD_USB_BLASTER_LIBFTDI
{ .name = "ftdi", .drv_register = ublast_register_ftdi },
#endif
#if BUILD_USB_BLASTER_FTD2XX
{ .name = "ftd2xx", .drv_register = ublast_register_ftd2xx },
#endif
{ NULL, NULL },
};

/*
* Access functions to lowlevel driver, agnostic of libftdi/libftdxx
*/
static char *hexdump(uint8_t *buf, unsigned int size)
{
unsigned int i;
char *str = calloc(size * 2 + 1, 1);

for (i = 0; i < size; i++)
sprintf(str + 2*i, "%02x", buf[i]);
return str;
}

static int ublast_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read)
{
int ret = info.drv->read(info.drv, buf, size, bytes_read);
char *str = hexdump(buf, *bytes_read);

DEBUG_JTAG_IO("(size=%d, buf=[%s]) -> %u", size, str,
*bytes_read);
free(str);
return ret;
}

static int ublast_buf_write(uint8_t *buf, int size, uint32_t *bytes_written)
{
int ret = info.drv->write(info.drv, buf, size, bytes_written);
char *str = hexdump(buf, *bytes_written);

DEBUG_JTAG_IO("(size=%d, buf=[%s]) -> %u", size, str,
*bytes_written);
free(str);
return ret;
}

static int nb_buf_remaining(void)
{
return BUF_LEN - info.bufidx;
}

static void ublast_flush_buffer(void)
{
unsigned int retlen;
int nb = info.bufidx, ret = ERROR_OK;

while (ret == ERROR_OK && nb > 0) {
ret = ublast_buf_write(info.buf, nb, &retlen);
nb -= retlen;
}
info.bufidx = 0;
}

/*
* 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 (one for
* setting up TDI/TMS/TCK=0, and one to trigger TCK high with same TDI/TMS
* held). Up to 64 bytes can be combined in a single USB packet.
* 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 READ_TDO (1 << 0)

/**
* ublast_reset - reset the JTAG device is possible
* @trst: 1 if TRST is to be asserted
* @srst: 1 if SRST is to be asserted
*
* This is not implemented yet. If pin6 or pin8 controlls the TRST/SRST, code
* should be added so that this function makes use of it.
*/
static void ublast_reset(int trst, int srst)
{
DEBUG_JTAG_IO("TODO: ublast_reset(%d,%d) isn't implemented!",
trst, srst);
}

/**
* ublast_queue_byte - queue one 'bitbang mode' byte for USB Blaster
* @abyte: the byte to queue
*
* Queues one byte in 'bitbang mode' to the USB Blaster. The byte is not
* actually sent, but stored in a buffer. The write is performed once
* the buffer is filled, or if an explicit ublast_flush_buffer() is called.
*/
static void ublast_queue_byte(uint8_t abyte)
{
if (nb_buf_remaining() < 1)
ublast_flush_buffer();
info.buf[info.bufidx++] = abyte;
if (nb_buf_remaining() == 0)
ublast_flush_buffer();
DEBUG_JTAG_IO("(byte=0x%02x)", abyte);
}

/**
* ublast_build_out - build bitbang mode output byte
* @type: says if reading back TDO is required
*
* Returns the compute bitbang mode byte
*/
static uint8_t ublast_build_out(enum scan_type type)
{
uint8_t abyte = 0;

abyte |= info.tms ? TMS : 0;
abyte |= info.pin6 ? NCE : 0;
abyte |= info.pin8 ? NCS : 0;
abyte |= info.tdi ? TDI : 0;
abyte |= LED;
if (type == SCAN_IN || type == SCAN_IO)
abyte |= READ;
return abyte;
}

/**
* ublast_clock_tms - clock a TMS transition
* @tms: the TMS to be sent
*
* Triggers a TMS transition (ie. one JTAG TAP state move).
*/
static void ublast_clock_tms(int tms)
{
uint8_t out;

DEBUG_JTAG_IO("(tms=%d)", !!tms);
info.tms = !!tms;
info.tdi = 0;
out = ublast_build_out(SCAN_OUT);
ublast_queue_byte(out);
ublast_queue_byte(out | TCK);
}

/**
* ublast_idle_clock - put back TCK to low level
*
* See ublast_queue_tdi() comment for the usage of this function.
*/
static void ublast_idle_clock(void)
{
uint8_t out = ublast_build_out(SCAN_OUT);

DEBUG_JTAG_IO(".");
ublast_queue_byte(out);
}

/**
* ublast_clock_tdi - Output a TDI with bitbang mode
* @tdi: the TDI bit to be shifted out
* @type: scan type (ie. does a readback of TDO is required)
*
* Output a TDI bit and assert clock to push it into the JTAG device :
* - writing out TCK=0, TMS=<old_state>=0, TDI=<tdi>
* - writing out TCK=1, TMS=<new_state>, TDI=<tdi> which triggers the JTAG
* device aquiring the data.
*
* If a TDO is to be read back, the required read is requested (bitbang mode),
* and the USB Blaster will send back a byte with bit0 reprensenting the TDO.
*/
static void ublast_clock_tdi(int tdi, enum scan_type type)
{
uint8_t out;

DEBUG_JTAG_IO("(tdi=%d)", !!tdi);
info.tdi = !!tdi;

out = ublast_build_out(SCAN_OUT);
ublast_queue_byte(out);

out = ublast_build_out(type);
ublast_queue_byte(out | TCK);
}

/**
* ublast_clock_tdi_flip_tms - Output a TDI with bitbang mode, change JTAG state
* @tdi: the TDI bit to be shifted out
* @type: scan type (ie. does a readback of TDO is required)
*
* This function is the same as ublast_clock_tdi(), but it changes also the TMS
* while outputing the TDI. This should be the last TDI output of a TDI
* sequence, which will change state from :
* - IRSHIFT -> IREXIT1
* - or DRSHIFT -> DREXIT1
*/
static void ublast_clock_tdi_flip_tms(int tdi, enum scan_type type)
{
uint8_t out;

DEBUG_JTAG_IO("(tdi=%d)", !!tdi);
info.tdi = !!tdi;
info.tms = !info.tms;

out = ublast_build_out(SCAN_OUT);
ublast_queue_byte(out);

out = ublast_build_out(type);
ublast_queue_byte(out | TCK);

out = ublast_build_out(SCAN_OUT);
ublast_queue_byte(out);
}

/**
* ublast_queue_bytes - queue bytes for the USB Blaster
* @bytes: byte array
* @nb_bytes: number of bytes
*
* Queues bytes to be sent to the USB Blaster. The bytes are not
* actually sent, but stored in a buffer. The write is performed once
* the buffer is filled, or if an explicit ublast_flush_buffer() is called.
*/
static void ublast_queue_bytes(uint8_t *bytes, int nb_bytes)
{
if (info.bufidx + nb_bytes > BUF_LEN) {
LOG_ERROR("buggy code, should never queue more that %d bytes",
info.bufidx + nb_bytes);
exit(-1);
}
DEBUG_JTAG_IO("(nb_bytes=%d, bytes=[0x%02x, ...])", nb_bytes,
bytes ? bytes[0] : 0);
if (bytes)
memcpy(&info.buf[info.bufidx], bytes, nb_bytes);
else
memset(&info.buf[info.bufidx], 0, nb_bytes);
info.bufidx += nb_bytes;
if (nb_buf_remaining() == 0)
ublast_flush_buffer();
}

/**
* ublast_tms_seq - write a TMS sequence transition to JTAG
* @bits: TMS bits to be written (bit0, bit1 .. bitN)
* @nb_bits: number of TMS bits (between 1 and 8)
*
* Write a serie of TMS transitions, where each transition consists in :
* - writing out TCK=0, TMS=<new_state>, TDI=<???>
* - writing out TCK=1, TMS=<new_state>, TDI=<???> which triggers the transition
* The function ensures that at the end of the sequence, the clock (TCK) is put
* low.
*/
static void ublast_tms_seq(const uint8_t *bits, int nb_bits)
{
int i;

DEBUG_JTAG_IO("(bits=%02x..., nb_bits=%d)", bits[0], nb_bits);
for (i = 0; i < nb_bits; i++)
ublast_clock_tms((bits[i / 8] >> (i % 8)) & 0x01);
ublast_idle_clock();
}

/**
* ublast_tms - write a tms command
* @cmd: tms command
*/
static void ublast_tms(struct tms_command *cmd)
{
DEBUG_JTAG_IO("(num_bits=%d)", cmd->num_bits);
ublast_tms_seq(cmd->bits, cmd->num_bits);
}

/**
* ublast_path_move - write a TMS sequence transition to JTAG
* @cmd: path transition
*
* Write a serie of TMS transitions, where each transition consists in :
* - writing out TCK=0, TMS=<new_state>, TDI=<???>
* - writing out TCK=1, TMS=<new_state>, TDI=<???> which triggers the transition
* The function ensures that at the end of the sequence, the clock (TCK) is put
* low.
*/
static void ublast_path_move(struct pathmove_command *cmd)
{
int i;

DEBUG_JTAG_IO("(num_states=%d, last_state=%d)",
cmd->num_states, cmd->path[cmd->num_states - 1]);
for (i = 0; i < cmd->num_states; i++) {
if (tap_state_transition(tap_get_state(), false) == cmd->path[i])
ublast_clock_tms(0);
if (tap_state_transition(tap_get_state(), true) == cmd->path[i])
ublast_clock_tms(1);
tap_set_state(cmd->path[i]);
}
ublast_idle_clock();
}

/**
* ublast_state_move - move JTAG state to the target state
* @state: the target state
*
* Input the correct TMS sequence to the JTAG TAP so that we end up in the
* target state. This assumes the current state (tap_get_state()) is correct.
*/
static void ublast_state_move(tap_state_t state)
{
uint8_t tms_scan;
int tms_len;

DEBUG_JTAG_IO("(from %s to %s)", tap_state_name(tap_get_state()),
tap_state_name(state));
if (tap_get_state() == state)
return;
tms_scan = tap_get_tms_path(tap_get_state(), state);
tms_len = tap_get_tms_path_len(tap_get_state(), state);
ublast_tms_seq(&tms_scan, tms_len);
tap_set_state(state);
}

/**
* ublast_read_byteshifted_tdos - read TDO of byteshift writes
* @buf: the buffer to store the bits
* @nb_bits: the number of bits
*
* Reads back from USB Blaster TDO bits, triggered by a 'byteshift write', ie. eight
* bits per received byte from USB interface, and store them in buffer.
*
* As the USB blaster stores the TDO bits in LSB (ie. first bit in (byte0,
* bit0), second bit in (byte0, bit1), ...), which is what we want to return,
* simply read bytes from USB interface and store them.
*
* Returns ERROR_OK if OK, ERROR_xxx if a read error occured
*/
static int ublast_read_byteshifted_tdos(uint8_t *buf, int nb_bytes)
{
unsigned int retlen;
int ret = ERROR_OK;

DEBUG_JTAG_IO("%s(buf=%p, num_bits=%d)", __func__, buf, nb_bytes * 8);
ublast_flush_buffer();
while (ret == ERROR_OK && nb_bytes > 0) {
ret = ublast_buf_read(buf, nb_bytes, &retlen);
nb_bytes -= retlen;
}
return ret;
}

/**
* ublast_read_bitbang_tdos - read TDO of bitbang writes
* @buf: the buffer to store the bits
* @nb_bits: the number of bits
*
* Reads back from USB Blaster TDO bits, triggered by a 'bitbang write', ie. one
* bit per received byte from USB interface, and store them in buffer, where :
* - first bit is stored in byte0, bit0 (LSB)
* - second bit is stored in byte0, bit 1
* ...
* - eight bit is sotred in byte0, bit 7
* - ninth bit is sotred in byte1, bit 0
* - etc ...
*
* Returns ERROR_OK if OK, ERROR_xxx if a read error occured
*/
static int ublast_read_bitbang_tdos(uint8_t *buf, int nb_bits)
{
int nb1 = nb_bits;
int i, ret = ERROR_OK;
unsigned int retlen;
uint8_t tmp[8];

DEBUG_JTAG_IO("%s(buf=%p, num_bits=%d)", __func__, buf, nb_bits);

/*
* Ensure all previous bitbang writes were issued to the dongle, so that
* it returns back the read values.
*/
ublast_flush_buffer();

ret = ublast_buf_read(tmp, nb1, &retlen);
for (i = 0; ret == ERROR_OK && i < nb1; i++)
if (tmp[i] & READ_TDO)
*buf |= (1 << i);
else
*buf &= ~(1 << i);
return ret;
}

/**
* ublast_queue_tdi - short description
* @bits: bits to be queued on TDI (or NULL if 0 are to be queued)
* @nb_bits: number of bits
* @scan: scan type (ie. if TDO read back is required or not)
*
* Outputs a serie of TDI bits on TDI.
* As a side effect, the last TDI bit is sent along a TMS=1, and triggers a JTAG
* TAP state shift if input bits were non NULL.
*
* In order to not saturate the USB Blaster queues, this method reads back TDO
* if the scan type requests it, and stores them back in bits.
*
* As a side note, the state of TCK when entering this function *must* be
* low. This is because byteshift mode outputs TDI on rising TCK and reads TDO
* on falling TCK if and only if TCK is low before queuing byteshift mode bytes.
* If TCK was high, the USB blaster will queue TDI on falling edge, and read TDO
* on rising edge !!!
*/
static void ublast_queue_tdi(uint8_t *bits, int nb_bits, enum scan_type scan)
{
int nb8 = nb_bits / 8;
int nb1 = nb_bits % 8;
int nbfree_in_packet, i, trans = 0, read_tdos;
uint8_t *tdos = calloc(1, nb_bits / 8 + 1);
static uint8_t byte0[BUF_LEN];

/*
* As the last TDI bit should always be output in bitbang mode in order
* to activate the TMS=1 transition to EXIT_?R state. Therefore a
* situation where nb_bits is a multiple of 8 is handled as follows:
* - the number of TDI shifted out in "byteshift mode" is 8 less than
* nb_bits
* - nb1 = 8
* This ensures that nb1 is never 0, and allows the TMS transition.
*/
if (nb8 > 0 && nb1 == 0) {
nb8--;
nb1 = 8;
}

read_tdos = (scan == SCAN_IN || scan == SCAN_IO);
for (i = 0; i < nb8; i += trans) {
/*
* Calculate number of bytes to fill USB packet of size MAX_PACKET_SIZE
*/
nbfree_in_packet = (MAX_PACKET_SIZE - (info.bufidx%MAX_PACKET_SIZE));
trans = MIN(nbfree_in_packet - 1, nb8 - i);

/*
* Queue a byte-shift mode transmission, with as many bytes as
* is possible with regard to :
* - current filling level of write buffer
* - remaining bytes to write in byte-shift mode
*/
if (read_tdos)
ublast_queue_byte(SHMODE | READ | trans);
else
ublast_queue_byte(SHMODE | trans);
if (bits)
ublast_queue_bytes(&bits[i], trans);
else
ublast_queue_bytes(byte0, trans);
if (read_tdos)
ublast_read_byteshifted_tdos(&tdos[i], trans);
}

/*
* Queue the remaining TDI bits in bitbang mode.
*/
for (i = 0; i < nb1; i++) {
int tdi = bits ? bits[nb8 + i / 8] & (1 << i) : 0;
if (bits && i == nb1 - 1)
ublast_clock_tdi_flip_tms(tdi, scan);
else
ublast_clock_tdi(tdi, scan);
}
if (nb1 && read_tdos)
ublast_read_bitbang_tdos(&tdos[nb8], nb1);

if (bits)
memcpy(bits, tdos, DIV_ROUND_UP(nb_bits, 8));
free(tdos);

/*
* Ensure clock is in lower state
*/
ublast_idle_clock();
}

static void ublast_runtest(int cycles, tap_state_t state)
{
DEBUG_JTAG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state);

ublast_state_move(TAP_IDLE);
ublast_queue_tdi(NULL, cycles, SCAN_OUT);
ublast_state_move(state);
}

static void ublast_stableclocks(int cycles)
{
DEBUG_JTAG_IO("%s(cycles=%i)", __func__, cycles);
ublast_queue_tdi(NULL, cycles, SCAN_OUT);
}

/**
* ublast_scan - launches a DR-scan or IR-scan
* @cmd: the command to launch
*
* Launch a JTAG IR-scan or DR-scan
*
* Returns ERROR_OK if OK, ERROR_xxx if a read/write error occured.
*/
static int ublast_scan(struct scan_command *cmd)
{
int scan_bits;
uint8_t *buf = NULL;
enum scan_type type;
int ret = ERROR_OK;
static const char * const type2str[] = { "", "SCAN_IN", "SCAN_OUT", "SCAN_IO" };
char *log_buf = NULL;

type = jtag_scan_type(cmd);
scan_bits = jtag_build_buffer(cmd, &buf);

if (cmd->ir_scan)
ublast_state_move(TAP_IRSHIFT);
else
ublast_state_move(TAP_DRSHIFT);

log_buf = hexdump(buf, DIV_ROUND_UP(scan_bits, 8));
DEBUG_JTAG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", __func__,
cmd->ir_scan ? "IRSCAN" : "DRSCAN",
type2str[type],
scan_bits, log_buf, cmd->end_state);
free(log_buf);

ublast_queue_tdi(buf, scan_bits, type);

/*
* As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it
* forward to a stable IRPAUSE or DRPAUSE.
*/
ublast_clock_tms(0);
if (cmd->ir_scan)
tap_set_state(TAP_IRPAUSE);
else
tap_set_state(TAP_DRPAUSE);

ret = jtag_read_buffer(buf, cmd);
if (buf)
free(buf);
ublast_state_move(cmd->end_state);
return ret;
}

static void ublast_msleep(int ms)
{
DEBUG_JTAG_IO("%s(ms=%d)", __func__, ms);
jtag_sleep(ms);
}

static int ublast_execute_queue(void)
{
struct jtag_command *cmd;
int ret = ERROR_OK;

for (cmd = jtag_command_queue; ret == ERROR_OK && cmd != NULL;
cmd = cmd->next) {
switch (cmd->type) {
case JTAG_RESET:
ublast_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst);
break;
case JTAG_RUNTEST:
ublast_runtest(cmd->cmd.runtest->num_cycles,
cmd->cmd.runtest->end_state);
break;
case JTAG_STABLECLOCKS:
ublast_stableclocks(cmd->cmd.stableclocks->num_cycles);
break;
case JTAG_TLR_RESET:
ublast_state_move(cmd->cmd.statemove->end_state);
break;
case JTAG_PATHMOVE:
ublast_path_move(cmd->cmd.pathmove);
break;
case JTAG_TMS:
ublast_tms(cmd->cmd.tms);
break;
case JTAG_SLEEP:
ublast_msleep(cmd->cmd.sleep->us);
break;
case JTAG_SCAN:
ret = ublast_scan(cmd->cmd.scan);
break;
}
}

ublast_flush_buffer();
return ret;
}

/**
* ublast_init - Initialize the Altera device
*
* Initialize the device :
* - open the USB device
* - empty the write FIFO (128 bytes)
* - empty the read FIFO (384 bytes)
*
* Returns ERROR_OK if USB device found, error if not.
*/
static int ublast_init(void)
{
static uint8_t tms_reset = 0xff;
int ret, i;

if (info.lowlevel_name) {
for (i = 0; lowlevel_drivers_map[i].name; i++)
if (!strcmp(lowlevel_drivers_map[i].name, info.lowlevel_name))
break;
if (lowlevel_drivers_map[i].name)
info.drv = lowlevel_drivers_map[i].drv_register();
if (!info.drv) {
LOG_ERROR("no lowlevel driver found for %s or lowlevel driver opening error",
info.lowlevel_name);
return ERROR_JTAG_DEVICE_ERROR;
}
} else {
LOG_INFO("No lowlevel driver configured, will try them all");
for (i = 0; !info.drv && lowlevel_drivers_map[i].name; i++)
info.drv = lowlevel_drivers_map[i].drv_register();
if (!info.drv) {
LOG_ERROR("no lowlevel driver found");
return ERROR_JTAG_DEVICE_ERROR;
}
}

/*
* Register the lowlevel driver
*/
info.drv->ublast_vid = info.ublast_vid;
info.drv->ublast_pid = info.ublast_pid;
info.drv->ublast_device_desc = info.ublast_device_desc;

ret = info.drv->open(info.drv);
if (ret == ERROR_OK) {
/*
* Flush USB-Blaster queue fifos
*/
uint32_t retlen;
ublast_buf_write(info.buf, BUF_LEN, &retlen);
/*
* Put JTAG in RESET state (five 1 on TMS)
*/
ublast_tms_seq(&tms_reset, 5);
tap_set_state(TAP_RESET);
}
return ret;
}

/**
* ublast_quit - Release the Altera device
*
* Releases the device :
* - put the device pins in 'high impedance' mode
* - close the USB device
*
* Returns always ERROR_OK
*/
static int ublast_quit(void)
{
uint8_t byte0 = 0;
unsigned int retlen;

ublast_buf_write(&byte0, 1, &retlen);
return info.drv->close(info.drv);
}

COMMAND_HANDLER(ublast_handle_device_desc_command)
{
if (CMD_ARGC == 1)
info.ublast_device_desc = strdup(CMD_ARGV[0]);
else
LOG_ERROR("require exactly one argument to "
"ublast_device_desc <description>");

return ERROR_OK;
}

COMMAND_HANDLER(ublast_handle_vid_pid_command)
{
if (CMD_ARGC > 2) {
LOG_WARNING("ignoring extra IDs in ublast_vid_pid "
"(maximum is 1 pair)");
CMD_ARGC = 2;
}
if (CMD_ARGC == 2) {
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], info.ublast_vid);
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], info.ublast_pid);
} else {
LOG_WARNING("incomplete ublast_vid_pid configuration");
}

return ERROR_OK;
}

COMMAND_HANDLER(ublast_handle_pin_command)
{
uint8_t out_value;

if (CMD_ARGC == 2) {
const char * const pin_name = CMD_ARGV[0];
unsigned int state;

COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], state);
if ((state != 0) && (state != 1)) {
LOG_ERROR("%s: pin state must be 0 or 1", CMD_NAME);
return ERROR_COMMAND_SYNTAX_ERROR;
}

if (!strcmp(pin_name, "pin6")) {
info.pin6 = state;
} else if (!strcmp(pin_name, "pin8")) {
info.pin8 = state;
} else {
LOG_ERROR("%s: pin name must be \"pin6\" or \"pin8\"",
CMD_NAME);
return ERROR_COMMAND_SYNTAX_ERROR;
}

if (info.drv) {
out_value = ublast_build_out(SCAN_OUT);
ublast_queue_byte(out_value);
ublast_flush_buffer();
}
return ERROR_OK;
} else {
LOG_ERROR("%s takes exactly two arguments", CMD_NAME);
return ERROR_COMMAND_SYNTAX_ERROR;
}
}

COMMAND_HANDLER(ublast_handle_lowlevel_drv_command)
{
if (CMD_ARGC == 1)
info.lowlevel_name = strdup(CMD_ARGV[0]);
else
LOG_ERROR("require exactly one argument to "
"usb_blaster_lowlevel_driver (ftdi|ftd2xx)");
return ERROR_OK;
}

static const struct command_registration ublast_command_handlers[] = {
{
.name = "usb_blaster_device_desc",
.handler = ublast_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 = ublast_handle_vid_pid_command,
.mode = COMMAND_CONFIG,
.help = "the vendor ID and product ID of the USB-Blaster",
.usage = "vid pid",
},
{
.name = "usb_blaster_lowlevel_driver",
.handler = ublast_handle_lowlevel_drv_command,
.mode = COMMAND_CONFIG,
.help = "set the lowlevel access for the USB Blaster (ftdi, ftd2xx)",
.usage = "(ftdi|ftd2xx)",
},
{
.name = "usb_blaster",
.handler = ublast_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 = ublast_command_handlers,
.supported = DEBUG_CAP_TMS_SEQ,

.execute_queue = ublast_execute_queue,
.init = ublast_init,
.quit = ublast_quit,
};

Loading…
Cancel
Save