This is an automated email from Gerrit. Eric Hoffman ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/4218
-- gerrit commit ea4251d5dcae8991c8c2e91a31c3b179572b097e Author: Eric Hoffman <[email protected]> Date: Wed Sep 6 13:25:00 2017 -0400 topic: USB Blaster massive speedup / handling of USB error timeout / fixes - USB blaster JTAG driver speedup by an order of magnitude - Proper handling of USB timeouts - Bug fixes USB blaster JTAG driver speedup by an order of magnitude -------------------------------------------------------- This is the main item on which I worked upon. I'm using an Altera USB Blaster (the original Blaster, not the Blaster-II) with a MIPS target. I don't know if it's because I'm under a virtualized environment (VMWare), but I found it to be extremely slow. Upon code review, I found that the OpenOCD blaster driver is very inefficient with the use of the USB stack. For every JTAG command, it would scan out to JTAG DR, and scan in the result immediately. So, if you have a JTAG queue with 30 commands, then this would require 30 USB turnaround to complete. I devised a scheme to speed up (tremendously in my case) the communication by sending and receiving USB data asynchronously. I have checked with the libftdi asynchronous IO functions, but libftdi is pretty dumb regarding this, and there may be some platforms with an older libftdi which does not support asynchronous IO. Even with the latest libftdi there are some issues. For ex, you can not tell it to look for data and return if no data is yet received. Even though it has asynchronous functions, when you fetch from the input, if no data arrive within the set timeout, libftdi fail and abort. So, I finally went for a simpler method, which have the advantage that it does not need to change the functions used by the ftdi (for Blaster) and libusb (for Blaster-II) interface drivers. I simply create a second thread when executing the JTAG queue, which does all the USB listening. So, all the data out (PC to Blaster) is still done as before, and all inputs (Blaster to PC) is done in it's own thread. Here's a summary of the change: - In the ublast_queue_tdi() function, I removed every USB reads. The function now only concentrate on sending data to USB Blaster (I or II). - The allocation of the input buffer (for USB data in) has been moved from ublast_queue_tdi() to ublast_execute_queue(). - The idea is that now, instead of sending one DR scan out, reading one DR scan in, filling the appropriate JTAG queue command scan-in buffer, one by one for each of the queue scan commands, I now allocate a single buffer, with all of the bytes expected to be received from USB for the whole queue, and dispatch it to a separate thread, which listen to USB inputs and fill the buffer as the bytes come in. Then, I shift the data out as before. At the end, once I have done sending all the data to USB, and that the listening thread have filled in the input buffer with all the data received from USB, I then go once again parsing the JTAG queue and fill in all of the scan-in fields. So, as this is now done in 2 separate threads, the USB inputs and outputs will not block each other in turn. The data sending may block because the Blaster is saturated, but at that time, the input thread will read back any data that the Blaster has queued toward the PC, and vice-versa. There is no more artificial 'wait time' added (which was slowing down the communication a lot), and there is no waiting after each JTAG scan-out, to wait for scan-in. Everything output (PC->Blaster) is done in a single shot, which will go as fast as the Blaster can take it (and as efficient as the USB stack can pack the data bursts), throttling when needed, and the receiving thread will read out any inputs (Blaster-->PC) as fast as the Blaster send them (and also in the most efficient way the USB stack can handle bursts). In my setup, this have effectively reduced long commands chains that took almost a full second to a few tens of milliseconds. !!!!!!!!!!!!!!!!!!!!! IMPORTANT NOTE: make distclean That fix required the use of pthread, so pthread functionality had to be added to OpenOCD. That means addition to configure.ac and Makefile.am, plus the addition of the file ax_pthread.m4. As a consequence, after applying this patch, *IT IS REQUIRED* to perform a make distclean, followed by ./bootstrap and .configure again, to update the autogenerated configure/Makefile.in/Makefile. OpenOCD does not properly detect changes in the autoconf/automake files and propagate the dependencies down to the source files. Otherwise you may get compile error such as undefined references to pthread functions and/or failing to compile the source with re-entrancy support. Actually, for now, only USB Blaster is using pthread, for the efficient USB communication. I don't know how the other dongles are programmed, but my guess is that they could certainly benefit from a similar scheme :-) !!!!!!!!!!!!!!!!!!!!! Proper handling of USB timeouts ------------------------------- This is something that I had to fix when designing the above. There was a generalized issue regarding the way the libusb interface driver handled the timeouts and errors from libusb itself (and the same for the ftdi counterpart). For example, it was not possible to distinguish between a timeout (let's say you want to read up to 100 bytes, and 50 bytes arrive and you get a timeout). Then that was treated the same way as an error, returning 0 bytes, and flushing those 50 received bytes to the deep emptiness of the void... So, for the USB blaster interface driver, I now check and treat timeouts and errors in a separate way. When an error with libusb (or libftdi) occurs, then the interface function return the error. When a timeout occur, it return the number of bytes actually transferred so far, which could be zero or higher. That is effectively the same way most of the socket IO functions works. A read or write will return negative value on error, 0 or more on success. Timeout can be detected by checking that number of bytes transferred is equal to number of bytes that you wanted to transfer. In the libusb0/libusb1 driver files, I now return proper error code instead of dumbly converting error codes to 0 for the USB control transfer function. I had to make a few very minor modifications to some of the other JTAG drivers for this fix (very minor). Also, the libusb1 driver read/write functions will return proper error codes. Finally, the libusb1 will convert the libusb1 error codes constants to generic system errors, to save as libusb0 was doing. The goal is that when you include libusb-common.h (and use either libusb0 or libusb1 depending on your environment), the returned error codes are the same. That makes error handling much more generic. Finally, the USB Blaster adaptation layer between libusb/libftdi and the blaster driver (mainly the ublast2_access_libusb.c and ublast_access_ftdi.c files) did not publish the timeout parameter at all. It hardcoded some timeouts. Now it expose the timeout parameter, and the usb_blaster.c file can select desired timeout. Bug fixes --------- - The usb_flush_buffer() function was doing a loop until the complete buffer was flushed out, so there was a reasonable possibility that the data could get sent in 2 separate chunks (in 2 separate calls of the USB write function). However, the flush function did not advance the buffer pointer after a partial write. So, if you had 100 bytes to flush out, and only 60 bytes get flushed on first loop, the second loop would try to send the 40 remaining bytes from the initial pointer location instead of the buffer at position 60. - openjtag.c function openjtag_buf_write_standard() added the number of bytes written to the uninitialized *bytes_written parameter instead of simply setting *bytes_written to the number of bytes transferred. The caller does not actually look at this parameter. It should, I have not fixed that, as this caller does not handle any error, and simply return void. So that bug never really manifested itself. But at least this initial issue is fixed. Change-Id: Ie51336d97d9a4f3c1df619b08ccd1831f67fe9c0 Signed-off-by: Eric Hoffman <[email protected]> diff --git a/Makefile.am b/Makefile.am index 930a307..54c05b7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,9 +30,10 @@ DIST_SUBDIRS += jimtcl endif # common flags used in openocd build -AM_CFLAGS = $(GCC_WARNINGS) +AM_CFLAGS = $(GCC_WARNINGS) $(PTHREAD_CFLAGS) AM_CPPFLAGS = $(HOST_CPPFLAGS)\ + $(PTHREAD_CFLAGS) \ -I$(top_srcdir)/src \ -I$(top_builddir)/src \ -I$(top_srcdir)/src/helper \ @@ -43,6 +44,11 @@ if INTERNAL_JIMTCL AM_CPPFLAGS += -I$(top_srcdir)/jimtcl \ -I$(top_builddir)/jimtcl endif + +AM_LDFLAGS = $(PTHREAD_LDFLAGS) + +CC = $(PTHREAD_CC) + EXTRA_DIST += \ BUGS \ HACKING \ diff --git a/ax_pthread.m4 b/ax_pthread.m4 new file mode 100644 index 0000000..d90de34 --- /dev/null +++ b/ax_pthread.m4 @@ -0,0 +1,309 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson <[email protected]> +# Copyright (c) 2011 Daniel Richard G. <[email protected]> +# +# 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 3 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 <http://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 18 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) + AC_MSG_RESULT($ax_pthread_ok) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case ${host_os} in + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + darwin*) + ax_pthread_flags="-pthread $ax_pthread_flags" + ;; +esac + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h> + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($ax_pthread_ok) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>], + [int attr = $attr; return attr /* ; */])], + [attr_name=$attr; break], + []) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case ${host_os} in + aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; + osf* | hpux*) flag="-D_REENTRANT";; + solaris*) + if test "$GCC" = "yes"; then + flag="-D_REENTRANT" + else + flag="-mt -D_REENTRANT" + fi + ;; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + ax_cv_PTHREAD_PRIO_INHERIT, [ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], + AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/configure.ac b/configure.ac index c680bda..d75f121 100644 --- a/configure.ac +++ b/configure.ac @@ -4,6 +4,7 @@ AC_INIT([openocd], [0.10.0+dev], AC_CONFIG_SRCDIR([src/openocd.c]) m4_include([config_subdir.m4])dnl +m4_include([ax_pthread.m4])dnl # check for makeinfo before calling AM_INIT_AUTOMAKE AC_CHECK_PROG([MAKEINFO], [makeinfo], [makeinfo]) @@ -29,6 +30,8 @@ AM_PROG_CC_C_O AC_PROG_RANLIB PKG_PROG_PKG_CONFIG([0.23]) +AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])]) + dnl disable checks for C++, Fortran and GNU Java Compiler m4_defun([_LT_AC_LANG_CXX_CONFIG], [:]) m4_defun([_LT_AC_LANG_F77_CONFIG], [:]) diff --git a/src/jtag/drivers/kitprog.c b/src/jtag/drivers/kitprog.c index c689848..26a44b4 100644 --- a/src/jtag/drivers/kitprog.c +++ b/src/jtag/drivers/kitprog.c @@ -421,7 +421,7 @@ static int kitprog_set_protocol(uint8_t protocol) (CONTROL_MODE_SET_PROGRAMMER_PROTOCOL << 8) | CONTROL_COMMAND_PROGRAM, protocol, &status, 1, 0); - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } @@ -440,7 +440,7 @@ static int kitprog_get_status(void) char status = PROGRAMMER_NOK_NACK; /* Try a maximum of three times */ - for (int i = 0; (i < 3) && (transferred == 0); i++) { + for (int i = 0; (i < 3) && (transferred <= 0); i++) { transferred = jtag_libusb_control_transfer(kitprog_handle->usb_handle, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, CONTROL_TYPE_READ, @@ -449,7 +449,7 @@ static int kitprog_get_status(void) jtag_sleep(1000); } - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } @@ -473,7 +473,7 @@ static int kitprog_set_unknown(void) (0x03 << 8) | 0x04, 0, &status, 1, 0); - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } @@ -498,7 +498,7 @@ static int kitprog_acquire_psoc(uint8_t psoc_type, uint8_t acquire_mode, (CONTROL_MODE_ACQUIRE_SWD_TARGET << 8) | CONTROL_COMMAND_PROGRAM, (max_attempts << 8) | (acquire_mode << 4) | psoc_type, &status, 1, 0); - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } @@ -522,7 +522,7 @@ static int kitprog_reset_target(void) (CONTROL_MODE_RESET_TARGET << 8) | CONTROL_COMMAND_PROGRAM, 0, &status, 1, 0); - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } @@ -546,7 +546,7 @@ static int kitprog_swd_sync(void) (CONTROL_MODE_SYNCHRONIZE_TRANSFER << 8) | CONTROL_COMMAND_PROGRAM, 0, &status, 1, 0); - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } @@ -570,7 +570,7 @@ static int kitprog_swd_seq(uint8_t seq_type) (CONTROL_MODE_SEND_SWD_SEQUENCE << 8) | CONTROL_COMMAND_PROGRAM, seq_type, &status, 1, 0); - if (transferred == 0) { + if (transferred <= 0) { LOG_DEBUG("Zero bytes transferred"); return ERROR_FAIL; } diff --git a/src/jtag/drivers/libusb0_common.c b/src/jtag/drivers/libusb0_common.c index 1825543..1d79f1c 100644 --- a/src/jtag/drivers/libusb0_common.c +++ b/src/jtag/drivers/libusb0_common.c @@ -111,15 +111,8 @@ int jtag_libusb_control_transfer(jtag_libusb_device_handle *dev, uint8_t request uint8_t request, uint16_t wValue, uint16_t wIndex, char *bytes, uint16_t size, unsigned int timeout) { - int transferred = 0; - - transferred = usb_control_msg(dev, requestType, request, wValue, wIndex, + return usb_control_msg(dev, requestType, request, wValue, wIndex, bytes, size, timeout); - - if (transferred < 0) - transferred = 0; - - return transferred; } int jtag_libusb_bulk_write(jtag_libusb_device_handle *dev, int ep, char *bytes, diff --git a/src/jtag/drivers/libusb1_common.c b/src/jtag/drivers/libusb1_common.c index 89f8092..f56dcde 100644 --- a/src/jtag/drivers/libusb1_common.c +++ b/src/jtag/drivers/libusb1_common.c @@ -121,38 +121,91 @@ void jtag_libusb_close(jtag_libusb_device_handle *dev) libusb_exit(jtag_libusb_context); } +/* Convert libusb1 specific error codes to generic error codes, same as libusb0 + returns. This abstract the version of libusb used. + Took from libusb-compat-1.0 */ +static int libusb1_to_generic_result(int result) +{ + if (result >= 0) + return result; + + switch (result) { + case LIBUSB_ERROR_IO: + return -EIO; + case LIBUSB_ERROR_INVALID_PARAM: + return -EINVAL; + case LIBUSB_ERROR_ACCESS: + return -EACCES; + case LIBUSB_ERROR_NO_DEVICE: + return -ENXIO; + case LIBUSB_ERROR_NOT_FOUND: + return -ENOENT; + case LIBUSB_ERROR_BUSY: + return -EBUSY; + case LIBUSB_ERROR_TIMEOUT: + return -ETIMEDOUT; + case -ETIMEDOUT: + /* libusb1 bug, sometime, -ETIMEDOUT is returned instead of + LIBUSB_ERROR_TIMEOUT, so handle this case */ + return -ETIMEDOUT; + case LIBUSB_ERROR_OVERFLOW: + return -EOVERFLOW; + case LIBUSB_ERROR_PIPE: + return -EPIPE; + case LIBUSB_ERROR_INTERRUPTED: + return -EINTR; + case LIBUSB_ERROR_NO_MEM: + return -ENOMEM; + case LIBUSB_ERROR_NOT_SUPPORTED: + return -ENOSYS; + default: + return -ERANGE; + } +} + int jtag_libusb_control_transfer(jtag_libusb_device_handle *dev, uint8_t requestType, uint8_t request, uint16_t wValue, uint16_t wIndex, char *bytes, uint16_t size, unsigned int timeout) { - int transferred = 0; + int retcode; - transferred = libusb_control_transfer(dev, requestType, request, wValue, wIndex, + retcode = libusb_control_transfer(dev, requestType, request, wValue, wIndex, (unsigned char *)bytes, size, timeout); - if (transferred < 0) - transferred = 0; - - return transferred; + return libusb1_to_generic_result(retcode); } int jtag_libusb_bulk_write(jtag_libusb_device_handle *dev, int ep, char *bytes, int size, int timeout) { + int retcode; int transferred = 0; - libusb_bulk_transfer(dev, ep, (unsigned char *)bytes, size, + retcode = libusb_bulk_transfer(dev, ep, (unsigned char *)bytes, size, &transferred, timeout); + if (retcode < 0) { + /* Convert libusb1 specific error codes to generic error codes, same as + libusb0 return. This abstract the version of libusb used. */ + return libusb1_to_generic_result(retcode); + } + return transferred; } int jtag_libusb_bulk_read(jtag_libusb_device_handle *dev, int ep, char *bytes, int size, int timeout) { + int retcode; int transferred = 0; - libusb_bulk_transfer(dev, ep, (unsigned char *)bytes, size, + retcode = libusb_bulk_transfer(dev, ep, (unsigned char *)bytes, size, &transferred, timeout); + if (retcode < 0) { + /* Convert libusb1 specific error codes to generic error codes, same as + libusb0 return. This abstract the version of libusb used. */ + return libusb1_to_generic_result(retcode); + } + return transferred; } diff --git a/src/jtag/drivers/openjtag.c b/src/jtag/drivers/openjtag.c index 8f11b4b..a6332cf 100644 --- a/src/jtag/drivers/openjtag.c +++ b/src/jtag/drivers/openjtag.c @@ -229,7 +229,7 @@ static int openjtag_buf_write_standard( return ERROR_JTAG_DEVICE_ERROR; } - *bytes_written += retval; + *bytes_written = retval; return ERROR_OK; } @@ -251,7 +251,7 @@ static int openjtag_buf_write_cy7c65215( ret = jtag_libusb_control_transfer(usbh, CY7C65215_JTAG_REQUEST, CY7C65215_JTAG_WRITE, size, 0, NULL, 0, CY7C65215_USB_TIMEOUT); - if (ret < 0) { + if (ret != 0) { LOG_ERROR("vendor command failed, error %d", ret); return ERROR_JTAG_DEVICE_ERROR; } @@ -319,7 +319,7 @@ static int openjtag_buf_read_cy7c65215( ret = jtag_libusb_control_transfer(usbh, CY7C65215_JTAG_REQUEST, CY7C65215_JTAG_READ, qty, 0, NULL, 0, CY7C65215_USB_TIMEOUT); - if (ret < 0) { + if (ret != 0) { LOG_ERROR("vendor command failed, error %d", ret); return ERROR_JTAG_DEVICE_ERROR; } @@ -469,7 +469,7 @@ static int openjtag_init_cy7c65215(void) CY7C65215_JTAG_REQUEST, CY7C65215_JTAG_ENABLE, 0, 0, NULL, 0, CY7C65215_USB_TIMEOUT); - if (ret < 0) { + if (ret != 0) { LOG_ERROR("could not enable JTAG module"); goto err; } @@ -522,7 +522,7 @@ static int openjtag_quit_cy7c65215(void) CY7C65215_JTAG_REQUEST, CY7C65215_JTAG_DISABLE, 0, 0, NULL, 0, CY7C65215_USB_TIMEOUT); - if (ret < 0) + if (ret != 0) LOG_WARNING("could not disable JTAG module"); jtag_libusb_close(usbh); diff --git a/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c b/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c index d991733..6686e31 100644 --- a/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c +++ b/src/jtag/drivers/usb_blaster/ublast2_access_libusb.c @@ -40,26 +40,54 @@ #define SECTION_BUFFERSIZE 16384 static int ublast2_libusb_read(struct ublast_lowlevel *low, uint8_t *buf, - unsigned size, uint32_t *bytes_read) + unsigned size, uint32_t *bytes_read, int timeout) { - *bytes_read = jtag_libusb_bulk_read(low->libusb_dev, + int retval; + + retval = jtag_libusb_bulk_read(low->libusb_dev, USBBLASTER_EPIN | \ LIBUSB_ENDPOINT_IN, (char *)buf, size, - 100); + timeout); + + if (retval < 0) { + *bytes_read = 0; + + /* Check if timeout. If so, return OK (with 0 bytes transferred) */ + if (-ETIMEDOUT == retval) + return ERROR_OK; + + return retval; + } + + *bytes_read = (uint32_t)retval; return ERROR_OK; } static int ublast2_libusb_write(struct ublast_lowlevel *low, uint8_t *buf, - int size, uint32_t *bytes_written) + int size, uint32_t *bytes_written, int timeout) { - *bytes_written = jtag_libusb_bulk_write(low->libusb_dev, + int retval; + + retval = jtag_libusb_bulk_write(low->libusb_dev, USBBLASTER_EPOUT | \ LIBUSB_ENDPOINT_OUT, (char *)buf, size, - 100); + timeout); + + if (retval < 0) { + *bytes_written = 0; + + /* Check if timeout. If so, return OK (with 0 bytes transferred) */ + if (-ETIMEDOUT == retval) + return ERROR_OK; + + return retval; + } + + *bytes_written = (uint32_t)retval; return ERROR_OK; } @@ -96,7 +124,7 @@ static int ublast2_write_firmware_section(struct jtag_libusb_device_handle *libu else chunk_size = bytes_remaining; - jtag_libusb_control_transfer(libusb_dev, + ret = jtag_libusb_control_transfer(libusb_dev, LIBUSB_REQUEST_TYPE_VENDOR | \ LIBUSB_ENDPOINT_OUT, USBBLASTER_CTRL_LOAD_FIRM, @@ -106,6 +134,12 @@ static int ublast2_write_firmware_section(struct jtag_libusb_device_handle *libu chunk_size, 100); + /* Error? */ + if (ret != chunk_size) { + LOG_ERROR("Error sending USB firmware data"); + return ERROR_FAIL; + } + bytes_remaining -= chunk_size; addr += chunk_size; data_ptr += chunk_size; @@ -142,7 +176,7 @@ static int load_usb_blaster_firmware(struct jtag_libusb_device_handle *libusb_de */ char value = CPU_RESET; - jtag_libusb_control_transfer(libusb_dev, + ret = jtag_libusb_control_transfer(libusb_dev, LIBUSB_REQUEST_TYPE_VENDOR | \ LIBUSB_ENDPOINT_OUT, USBBLASTER_CTRL_LOAD_FIRM, @@ -151,6 +185,11 @@ static int load_usb_blaster_firmware(struct jtag_libusb_device_handle *libusb_de &value, 1, 100); + /* Error? */ + if (ret != 1) { + LOG_ERROR("Error while downloading the firmware"); + return ERROR_FAIL; + } /* Download all sections in the image to ULINK */ for (int i = 0; i < ublast2_firmware_image.num_sections; i++) { @@ -163,7 +202,7 @@ static int load_usb_blaster_firmware(struct jtag_libusb_device_handle *libusb_de } value = !CPU_RESET; - jtag_libusb_control_transfer(libusb_dev, + ret = jtag_libusb_control_transfer(libusb_dev, LIBUSB_REQUEST_TYPE_VENDOR | \ LIBUSB_ENDPOINT_OUT, USBBLASTER_CTRL_LOAD_FIRM, @@ -172,6 +211,11 @@ static int load_usb_blaster_firmware(struct jtag_libusb_device_handle *libusb_de &value, 1, 100); + /* Error? */ + if (ret != 1) { + LOG_ERROR("Error while downloading the firmware"); + return ERROR_FAIL; + } image_close(&ublast2_firmware_image); @@ -218,7 +262,7 @@ static int ublast2_libusb_init(struct ublast_lowlevel *low) } char buffer[5]; - jtag_libusb_control_transfer(low->libusb_dev, + ret = jtag_libusb_control_transfer(low->libusb_dev, LIBUSB_REQUEST_TYPE_VENDOR | \ LIBUSB_ENDPOINT_IN, USBBLASTER_CTRL_READ_REV, @@ -227,6 +271,11 @@ static int ublast2_libusb_init(struct ublast_lowlevel *low) buffer, 5, 100); + /* Error? */ + if (ret != 5) { + LOG_ERROR("Error getting firmware revision"); + return ERROR_FAIL; + } LOG_INFO("Altera USB-Blaster II found (Firm. rev. = %s)", buffer); diff --git a/src/jtag/drivers/usb_blaster/ublast_access.h b/src/jtag/drivers/usb_blaster/ublast_access.h index 252f003..b9e2019 100644 --- a/src/jtag/drivers/usb_blaster/ublast_access.h +++ b/src/jtag/drivers/usb_blaster/ublast_access.h @@ -43,9 +43,9 @@ struct ublast_lowlevel { char *firmware_path; int (*write)(struct ublast_lowlevel *low, uint8_t *buf, int size, - uint32_t *bytes_written); + uint32_t *bytes_written, int timeout); int (*read)(struct ublast_lowlevel *low, uint8_t *buf, unsigned size, - uint32_t *bytes_read); + uint32_t *bytes_read, int timeout); int (*open)(struct ublast_lowlevel *low); int (*close)(struct ublast_lowlevel *low); int (*speed)(struct ublast_lowlevel *low, int speed); diff --git a/src/jtag/drivers/usb_blaster/ublast_access_ftdi.c b/src/jtag/drivers/usb_blaster/ublast_access_ftdi.c index cb442f2..29035fd 100644 --- a/src/jtag/drivers/usb_blaster/ublast_access_ftdi.c +++ b/src/jtag/drivers/usb_blaster/ublast_access_ftdi.c @@ -29,6 +29,7 @@ #endif #include <jtag/interface.h> #include <jtag/commands.h> +#include <helper/time_support.h> #include "ublast_access.h" #include <ftdi.h> @@ -39,41 +40,73 @@ static struct ftdi_context *ublast_getftdic(struct ublast_lowlevel *low) } static int ublast_ftdi_read(struct ublast_lowlevel *low, uint8_t *buf, - unsigned size, uint32_t *bytes_read) + unsigned size, uint32_t *bytes_read, int timeout) { int retval; - int timeout = 100; struct ftdi_context *ftdic = ublast_getftdic(low); + int64_t timeout_time = timeval_ms() + timeout; + + if (timeout) + ftdic->usb_read_timeout = timeout; *bytes_read = 0; - while ((*bytes_read < size) && timeout--) { - retval = ftdi_read_data(ftdic, buf + *bytes_read, - size - *bytes_read); - if (retval < 0) { + + /* Work around libftdi bug, which return immediately withouth honoring the + timeout value */ + while (*bytes_read < size) { + retval = ftdi_read_data(ftdic, buf + *bytes_read, size - *bytes_read); + /* Discard timeout errors here */ +#if (defined LIBUSB_ERROR_TIMEOUT) + /* Maybe compiled on a system with an old libftdi, with only + libusb0 installed, and no definition of LIBUSB_ERROR_TIMEOUT */ + if ((retval == LIBUSB_ERROR_TIMEOUT) && (retval == -ETIMEDOUT)) { +#else + if (retval == -ETIMEDOUT) +#endif + retval = 0; /* No data */ + + /* Check if error */ + if (retval < 0) { + /* Return error */ *bytes_read = 0; LOG_ERROR("ftdi_read_data: %s", ftdi_get_error_string(ftdic)); return ERROR_JTAG_DEVICE_ERROR; } - *bytes_read += retval; + + *bytes_read += (uint32_t)retval; + + /* Exit if we have timed-out. */ + if (timeout) { + if (timeval_ms() >= timeout_time) + return ERROR_OK; /* Return OK, with what we have so far. */ + } } return ERROR_OK; } static int ublast_ftdi_write(struct ublast_lowlevel *low, uint8_t *buf, int size, - uint32_t *bytes_written) + uint32_t *bytes_written, int timeout) { int retval; struct ftdi_context *ftdic = ublast_getftdic(low); + ftdic->usb_write_timeout = timeout; + retval = ftdi_write_data(ftdic, buf, size); if (retval < 0) { *bytes_written = 0; + + /* Check for timeout. */ + if ((retval == LIBUSB_ERROR_TIMEOUT) || + (retval == -ETIMEDOUT)) + return -ETIMEDOUT; /* Return what we have so far */ + LOG_ERROR("ftdi_write_data: %s", ftdi_get_error_string(ftdic)); return ERROR_JTAG_DEVICE_ERROR; } - *bytes_written = retval; + *bytes_written = (uint32_t)retval; return ERROR_OK; } diff --git a/src/jtag/drivers/usb_blaster/usb_blaster.c b/src/jtag/drivers/usb_blaster/usb_blaster.c index a975bd1..9bc5a80 100644 --- a/src/jtag/drivers/usb_blaster/usb_blaster.c +++ b/src/jtag/drivers/usb_blaster/usb_blaster.c @@ -85,6 +85,7 @@ #include <unistd.h> #include <sys/time.h> #include <time.h> +#include <pthread.h> /* Size of USB endpoint max packet size, ie. 64 bytes */ #define MAX_PACKET_SIZE 64 @@ -99,6 +100,16 @@ /* USB-Blaster II specific command */ #define CMD_COPY_TDO_BUFFER 0x5F +#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) + enum gpio_steer { FIXED_0 = 0, FIXED_1, @@ -115,6 +126,15 @@ struct ublast_info { bool srst_asserted; uint8_t buf[BUF_LEN]; int bufidx; + /* Use async USB input buf for much lower latency */ + pthread_t in_thread; + volatile int in_thread_terminate; /* Signal input thread to terminate. + Not using a mutex here, only used + as a simple one-time signal */ + int in_thread_status; + uint8_t *in_buf; + uint32_t in_buf_size; + uint32_t in_bufidx; char *lowlevel_name; struct ublast_lowlevel *drv; @@ -169,9 +189,9 @@ static char *hexdump(uint8_t *buf, unsigned int size) return str; } -static int ublast_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read) +static int ublast_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read, int timeout) { - int ret = info.drv->read(info.drv, buf, size, bytes_read); + int ret = info.drv->read(info.drv, buf, size, bytes_read, timeout); char *str = hexdump(buf, *bytes_read); DEBUG_JTAG_IO("(size=%d, buf=[%s]) -> %u", size, str, @@ -180,9 +200,9 @@ static int ublast_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read) return ret; } -static int ublast_buf_write(uint8_t *buf, int size, uint32_t *bytes_written) +static int ublast_buf_write(uint8_t *buf, int size, uint32_t *bytes_written, int timeout) { - int ret = info.drv->write(info.drv, buf, size, bytes_written); + int ret = info.drv->write(info.drv, buf, size, bytes_written, timeout); char *str = hexdump(buf, *bytes_written); DEBUG_JTAG_IO("(size=%d, buf=[%s]) -> %u", size, str, @@ -198,16 +218,242 @@ static int nb_buf_remaining(void) static void ublast_flush_buffer(void) { + int ret = ERROR_OK; unsigned int retlen; - int nb = info.bufidx, ret = ERROR_OK; + int nb_left = info.bufidx; + int nb_sent = 0; + + while (nb_left > 0) { + ret = ublast_buf_write(&info.buf[nb_sent], nb_left, &retlen, 500); + if (ret != ERROR_OK) { + LOG_ERROR("Error flushing data"); + /* We should propagate and handle the error properly... */ + break; + } - while (ret == ERROR_OK && nb > 0) { - ret = ublast_buf_write(info.buf, nb, &retlen); - nb -= retlen; + nb_sent += retlen; + nb_left -= retlen; } info.bufidx = 0; } +static void *ublast_in_buf_read_thread(void *arg) +{ + (void)arg; /* Unused, just prevent compiler from whining... */ + + int ret = ERROR_OK; + uint32_t num_left_to_read, num_read; + int last_pass = 0; + + while (((num_left_to_read = info.in_buf_size - info.in_bufidx) > 0) && + (!last_pass)) { + /* One last pass? */ + if (info.in_thread_terminate) + last_pass = 1; /* Last pass, wait 500ms more */ + + /* Get any bytes that are available */ + ret = ublast_buf_read(&info.in_buf[info.in_bufidx], num_left_to_read, &num_read, 500); + if (ret != ERROR_OK) + break; /* Stop here, we'll report this status... */ + + info.in_bufidx += num_read; + } + + /* If no error, check that we read all the required bytes before being asked to terminate */ + if (ret == ERROR_OK) { + if (info.in_bufidx != info.in_buf_size) + ret = ERROR_FAIL; /* Did not receive all the expected data before termination */ + } + + /* Done */ + info.in_thread_status = ret; + pthread_exit(NULL); +} + +static int ublast_in_buf_read_thread_start(void) +{ + pthread_attr_t attr; + + info.in_thread_terminate = 0; /* Reset this */ + + /* Set thread attribute, as joinable. */ + if (pthread_attr_init(&attr) < 0) + return ERROR_FAIL; + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) < 0) { + pthread_attr_destroy(&attr); + return ERROR_FAIL; + } + + /* Create thread */ + if (pthread_create(&info.in_thread, &attr, ublast_in_buf_read_thread, NULL) < 0) { + pthread_attr_destroy(&attr); + return ERROR_FAIL; + } + + /* Done with attribute */ + (void)pthread_attr_destroy(&attr); + + return ERROR_OK; +} + +static int ublast_in_buf_read_thread_wait(void) +{ + /* Signal thread to terminate */ + info.in_thread_terminate = 1; + + /* Wait for thread to terminate and get status */ + if (pthread_join(info.in_thread, NULL) < 0) + return ERROR_FAIL; + + /* Clear thread ID. */ + memset(&info.in_thread, 0, sizeof(info.in_thread)); + + return info.in_thread_status; +} + +/** + * ublast_read_byteshifted_tdos - read TDO of byteshift writes from USB input buffer + * @inbuf: the buffer where to get the byte shifted TDO + * @outbuf: the buffer to store the bytes + * @nb_bytes: the number of bytes + * + * Store the byteshifted TDO data received from the scan, from the USB input 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. + */ +static void ublast_read_byteshifted_tdos(uint8_t *inbuf, uint8_t *outbuf, int nb_bytes) +{ + memcpy(outbuf, inbuf, nb_bytes); +} + +/** + * ublast_read_bitbang_tdos - read TDO of bitbang writes from USB input buffer + * @inbuf: the buffer where to get the byte shifted TDO + * @outbuf: the buffer to store the bits + * @nb_bits: the number of bits + * + * Store the bitbanged TDO data received from the scan, from the USB input buffer, + * 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 ... + */ +static void ublast_read_bitbang_tdos(uint8_t *inbuf, uint8_t *outbuf, int nb_bits) +{ + int i; + int bit; + int byte; + + for (i = 0; i < nb_bits; i++) { + byte = i / 8; + bit = i % 8; + /* Clear new bytes */ + if (bit == 0) + outbuf[byte] = 0; + /* Set bits for which TDO read high */ + if (inbuf[i] & READ_TDO) + outbuf[byte] |= (1 << bit); + } +} + +static void ublast_in_buf_build(void) +{ + struct jtag_command *cmd; + + /* Find total number of input USB bytes */ + info.in_buf_size = 0; + for (cmd = jtag_command_queue; cmd != NULL; cmd = cmd->next) { + /* Scan in? */ + if ((cmd->type == JTAG_SCAN) && (jtag_scan_type(cmd->cmd.scan) & SCAN_IN)) { + int nb_bits = jtag_scan_size(cmd->cmd.scan); + int nb8 = nb_bits / 8; + int nb1 = nb_bits % 8; + + /* + * As stated in ublast_queue_tdi(): + * -------------------------------------------------------------------- + * 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; + } + info.in_buf_size += nb8 + nb1; + } + } + info.in_bufidx = 0; + if (info.in_buf_size > 0) + info.in_buf = calloc(1, info.in_buf_size); +} + +static void ublast_in_buf_read(void) +{ + struct jtag_command *cmd; + int inbuf_pos; + + if (info.in_buf_size == 0) + return; /* Nothing to do */ + + /* Find total number of input USB bytes */ + inbuf_pos = 0; + for (cmd = jtag_command_queue; cmd != NULL; cmd = cmd->next) { + /* Scan in? */ + if ((cmd->type == JTAG_SCAN) && (jtag_scan_type(cmd->cmd.scan) & SCAN_IN)) { + int nb_bits = jtag_scan_size(cmd->cmd.scan); + int nb8 = nb_bits / 8; + int nb1 = nb_bits % 8; + + /* + * Same calculations as in ublast_queue_tdi(). As stated: + * ---------------------------------------------------------------- + * 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; + } + + uint8_t *tdos = calloc(1, (nb_bits + 7) / 8); + + ublast_read_byteshifted_tdos(&info.in_buf[inbuf_pos], tdos, nb8); + ublast_read_bitbang_tdos(&info.in_buf[inbuf_pos + nb8], &tdos[nb8], nb1); + inbuf_pos += nb8 + nb1; + + /* Fill input fields */ + jtag_read_buffer(tdos, cmd->cmd.scan); + + free(tdos); + } + } + + /* Clear input buffer */ + info.in_bufidx = 0; + info.in_buf_size = 0; + free(info.in_buf); + info.in_buf = NULL; +} + /* * 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 @@ -241,16 +487,6 @@ static void ublast_flush_buffer(void) * 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_queue_byte - queue one 'bitbang mode' byte for USB Blaster * @abyte: the byte to queue @@ -521,74 +757,6 @@ static void ublast_state_move(tap_state_t 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 @@ -612,7 +780,6 @@ 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]; /* @@ -654,7 +821,6 @@ static void ublast_queue_tdi(uint8_t *bits, int nb_bits, enum scan_type scan) if (read_tdos) { if (info.flags & COPY_TDO_BUFFER) ublast_queue_byte(CMD_COPY_TDO_BUFFER); - ublast_read_byteshifted_tdos(&tdos[i], trans); } } @@ -671,13 +837,8 @@ static void ublast_queue_tdi(uint8_t *bits, int nb_bits, enum scan_type scan) if (nb1 && read_tdos) { if (info.flags & COPY_TDO_BUFFER) ublast_queue_byte(CMD_COPY_TDO_BUFFER); - 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 */ @@ -743,7 +904,6 @@ static int ublast_scan(struct scan_command *cmd) else tap_set_state(TAP_DRPAUSE); - ret = jtag_read_buffer(buf, cmd); if (buf) free(buf); ublast_state_move(cmd->end_state); @@ -772,7 +932,8 @@ static void ublast_initial_wipeout(void) * - empty the write FIFO (128 bytes) * - empty the read FIFO (384 bytes) */ - ublast_buf_write(info.buf, BUF_LEN, &retlen); + ublast_buf_write(info.buf, BUF_LEN, &retlen, 100); + ublast_buf_read(info.buf, BUF_LEN, &retlen, 100); /* * Put JTAG in RESET state (five 1 on TMS) */ @@ -791,6 +952,16 @@ static int ublast_execute_queue(void) ublast_initial_wipeout(); } + /* Prepage input buffer */ + ublast_in_buf_build(); + if (info.in_buf_size > 0) { + ret = ublast_in_buf_read_thread_start(); + if (ret != ERROR_OK) { + LOG_ERROR("Unable to start input buffer trhread"); + return ret; + } + } + for (cmd = jtag_command_queue; ret == ERROR_OK && cmd != NULL; cmd = cmd->next) { switch (cmd->type) { @@ -823,6 +994,18 @@ static int ublast_execute_queue(void) } ublast_flush_buffer(); + + if (info.in_buf_size > 0) { + ret = ublast_in_buf_read_thread_wait(); + if ((ret != ERROR_OK) || + (info.in_bufidx != info.in_buf_size)) { + LOG_ERROR("Error receiving data bytes from USB"); + return ret; + } + /* Write back the received data */ + ublast_in_buf_read(); + } + return ret; } @@ -898,7 +1081,7 @@ static int ublast_quit(void) uint8_t byte0 = 0; unsigned int retlen; - ublast_buf_write(&byte0, 1, &retlen); + ublast_buf_write(&byte0, 1, &retlen, 100); return info.drv->close(info.drv); } -- ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
