This is an automated email from Gerrit. "Jose Borja Castillo Sanchez <joscas...@uma.es>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7923
-- gerrit commit 05314cb90a5285ff79261cdd647e617e63298eb9 Author: José Borja Castillo <joscas...@uma.es> Date: Mon Oct 2 10:20:14 2023 +0200 Rebased patchset #6867 onto HEAD Change-Id: I2b275b61defb645478f68411c3f1fc65d4629609 Signed-off-by: José Borja Castillo <joscas...@uma.es> diff --git a/configure.ac b/configure.ac index a2442d40b5..9e5746053a 100644 --- a/configure.ac +++ b/configure.ac @@ -116,6 +116,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])]) m4_define([USB1_ADAPTERS], [[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]], [[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]], + [[xvc], [Xilinx XVC TCP], [xvc]], [[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]], [[ulink], [Keil ULINK JTAG Programmer], [ULINK]], [[angie], [ANGIE Adapter], [ANGIE]], @@ -385,6 +386,10 @@ AC_ARG_ENABLE([remote-bitbang], AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang jtag driver]), [build_remote_bitbang=$enableval], [build_remote_bitbang=no]) +AC_ARG_ENABLE([xvc], + AS_HELP_STRING([--enable-xvc], [Enable building support for the Xilinx XVC TCP jtag driver]), + [build_xvc=$enableval], [build_xvc=yes]) + AS_CASE(["${host_cpu}"], [i?86|x86*], [], [ @@ -603,6 +608,13 @@ AS_IF([test "x$build_remote_bitbang" = "xyes"], [ AC_DEFINE([BUILD_REMOTE_BITBANG], [0], [0 if you don't want the Remote Bitbang JTAG driver.]) ]) +AS_IF([test "x$build_xvc" = "xyes"], [ + build_bitbang=yes + AC_DEFINE([BUILD_XVC], [1], [1 if you want the Xilinx XVC TCP driver.]) +], [ + AC_DEFINE([BUILD_XVC], [0], [0 if you don't want the Xilinx XVC TCP driver.]) +]) + AS_IF([test "x$build_sysfsgpio" = "xyes"], [ build_bitbang=yes AC_DEFINE([BUILD_SYSFSGPIO], [1], [1 if you want the SysfsGPIO driver.]) @@ -770,6 +782,7 @@ AM_CONDITIONAL([USE_LIBJAYLINK], [test "x$use_libjaylink" = "xyes"]) AM_CONDITIONAL([RSHIM], [test "x$build_rshim" = "xyes"]) AM_CONDITIONAL([DMEM], [test "x$build_dmem" = "xyes"]) AM_CONDITIONAL([HAVE_CAPSTONE], [test "x$enable_capstone" != "xno"]) +AM_CONDITIONAL([XVC], [test "x$build_xvc" = "xyes"]) AM_CONDITIONAL([INTERNAL_JIMTCL], [test "x$use_internal_jimtcl" = "xyes"]) AM_CONDITIONAL([INTERNAL_LIBJAYLINK], [test "x$use_internal_libjaylink" = "xyes"]) diff --git a/doc/openocd.texi b/doc/openocd.texi index 2d59238b80..f70b8ba3d0 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -615,6 +615,9 @@ emulation model of target hardware. @item @b{xlnx_pcie_xvc} @* A JTAG driver exposing Xilinx Virtual Cable over PCI Express to OpenOCD as JTAG/SWD interface. +@item @b{xvc} +@* A JTAG driver using Xilinx Virtual Cable over TCP to OpenOCD as JTAG interface. + @item @b{linuxgpiod} @* A bitbang JTAG driver using Linux GPIO through library libgpiod. @@ -3534,6 +3537,28 @@ Espressif JTAG driver to communicate with ESP32-C3, ESP32-S3 chips and ESP USB B These chips have built-in JTAG circuitry and can be debugged without any additional hardware. Only an USB cable connected to the D+/D- pins is necessary. +@deffn {Interface Driver} {xvc} +This driver implements bitbang mode of JTAG devices +using Xilinx Virtual Cable messages over TCP/IP. +This implementation is XVC protocol 1.0 only, does not support 1.1 extension. +Example configuration: + +@example +xvc_host 192.168.3.192 +xvc_port 5000 +@end example + +@deffn {Config Command} {xvc_host} @var{hostname} +Specifies the hostname of the remote debugger to connect to. Albeit +it is possible to use UNIX sockets, it is not recommended. +@end deffn + +@deffn {Config Command} {xvc_port} @var{number} +Specifies the port of the remote process to connect to. +@end deffn + +@end deffn + @deffn {Command} {espusbjtag tdo} Returns the current state of the TDO line @end deffn diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index e404afe9f0..9da86dea8b 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -204,6 +204,9 @@ endif if AM335XGPIO DRIVERFILES += %D%/am335xgpio.c endif +if XVC +DRIVERFILES += %D%/xvc.c +endif DRIVERHEADERS = \ %D%/bitbang.h \ diff --git a/src/jtag/drivers/xvc.c b/src/jtag/drivers/xvc.c new file mode 100644 index 0000000000..2dee51e73f --- /dev/null +++ b/src/jtag/drivers/xvc.c @@ -0,0 +1,725 @@ +/*************************************************************************** + * Copyright (C) 2021 by Jose Borja Castillo, DTE-UMA * + * joscas...@uma.es * + * * + * This implementation is XVC protocol 1.0 only, does not support * + * 1.1 extension. * + * Some parts of the code are inspired on both bitbang.c and jlink.c * + * by Øyvind Harboe and Paul Fertser, respectively. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef _WIN32 +#include <netdb.h> +#include <netinet/tcp.h> +#include <sys/un.h> +#else +#include <winsock.h> +#endif +#include "helper/replacements.h" +#include <jtag/interface.h> +#include <jtag/commands.h> +#include "helper/log.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <time.h> + +static char *xvc_host; +static char *xvc_port; +static uint32_t xvc_tck; + +static int xvc_fd; +static uint8_t *xvc_tms_buf; +static uint8_t *xvc_tdi_buf; +static uint8_t *xvc_send_buf; +static uint8_t *xvc_tdo_buf; +/* Being realistic, the protocol won't use as many bits. */ +static uint32_t xvc_used_bits; + +/* XVC implementation specifics. */ +static unsigned int xvc_max_vector_size; +/* max_vector_size discounting command header. */ +static unsigned int xvc_max_usable_vector_size; + +struct shift_result { + /* First bit position in TDO to read. */ + unsigned int first; + /* Number of bits to read. */ + unsigned int length; + /* Destination address to store the result */ + void *buffer; + /* Offset in the destination buffer. */ + unsigned int buffer_offset; +}; + +#define MAX_SHIFT_RESULTS 256 +static unsigned int last_used_bits; +static int pending_shift_results; +static struct shift_result shift_result_buffer[MAX_SHIFT_RESULTS]; + +static int xvc_settck(void); +static int xvc_fill_buffer(void); + +/* Auxiliary and TCL helper functions*/ +static unsigned int xvc_bits_to_bytes(unsigned int bits) +{ + return (bits + 7) / 8; +} + +static int xvc_speed(int speed) +{ + /* Converts TCK speed in kHz to ns. */ + xvc_tck = 1000000 / speed; + /* Changes default speed to adapter speed */ + return xvc_settck(); +} + +static int xvc_speed_div(int speed, int *khz) +{ + *khz = speed; + + return ERROR_OK; +} + +static int xvc_khz(int khz, int *jtag_speed) +{ + *jtag_speed = khz; + + return ERROR_OK; +} + +static int read_frame(int sock_id, unsigned char *ptr, int32_t size) +{ + int32_t i = size; + while (i > 0) { + int state = read_socket(sock_id, ptr, i); + if (state > 0) { + ptr += state; + i -= state; + } else { + LOG_ERROR("Reading error in read_frame"); + return state; + } + } + return size; +} + +static int xvc_flush(void) +{ + if (xvc_used_bits == 0) { + /* Nothing to send, so we don't expect any bit back either */ + last_used_bits = 0; + LOG_DEBUG("XVC flush: no bits to flush"); + return ERROR_OK; + } + + /* Converts bits to bytes, to reckon how many bytes we should send. */ + unsigned int number_of_bytes = xvc_bits_to_bytes(xvc_used_bits); + /* Creates the header. */ + const char *shift = "shift:"; + int shift_len = strlen(shift); + int cp_offset = 0; + /* Copies the header */ + memcpy(xvc_send_buf + cp_offset, shift, shift_len); + /* Updates the offset. */ + cp_offset += shift_len; + /* Copies number of bytes. */ + h_u32_to_le(xvc_send_buf + cp_offset, xvc_used_bits); + cp_offset += sizeof(xvc_used_bits); + /* Copies TMS vector */ + memcpy(xvc_send_buf + cp_offset, xvc_tms_buf, number_of_bytes); + cp_offset += number_of_bytes; + /* Copies TDI vector */ + memcpy(xvc_send_buf + cp_offset, xvc_tdi_buf, number_of_bytes); + cp_offset += number_of_bytes; + /* Updates the number of bytes used. */ + LOG_DEBUG("XVC flush: cp_offset: %d", cp_offset); + LOG_DEBUG("XVC flush: used_bits: %d", xvc_used_bits); + + ssize_t written = write_socket(xvc_fd, xvc_send_buf, cp_offset); + if (written != cp_offset) { + LOG_ERROR("Error writing socket in xvc_flush"); + return ERROR_FAIL; + } + + memset(xvc_tms_buf, 0, xvc_max_usable_vector_size / 2); + memset(xvc_tdi_buf, 0, xvc_max_usable_vector_size / 2); + last_used_bits = xvc_used_bits; + xvc_used_bits = 0; + + return ERROR_OK; +} + +static int xvc_queue(const uint8_t *tms, unsigned int tms_offset, const uint8_t *tdi, + unsigned int tdi_offset, uint8_t *tdo, unsigned int tdo_offset, unsigned int length) +{ + do { + unsigned int available_length = + (xvc_max_usable_vector_size / 2) - (xvc_used_bits / 8); + if ((!available_length) || (pending_shift_results >= MAX_SHIFT_RESULTS)) { + xvc_flush(); + xvc_fill_buffer(); + } + + struct shift_result *shift_result = + &shift_result_buffer[pending_shift_results]; + unsigned int scan_length = + length > available_length ? available_length : length; + if (tdi) + buf_set_buf(tdi, tdi_offset, xvc_tdi_buf, xvc_used_bits, scan_length); + if (tms) + buf_set_buf(tms, tms_offset, xvc_tms_buf, xvc_used_bits, scan_length); + if (tdo) { + shift_result->buffer = tdo; + shift_result->buffer_offset = tdo_offset; + shift_result->first = xvc_used_bits; + shift_result->length = scan_length; + pending_shift_results++; + } + xvc_used_bits += scan_length; + tdi_offset += scan_length; + tms_offset += scan_length; + tdo_offset += scan_length; + length -= scan_length; + } while (length > 0); + + return ERROR_OK; +} + +static int xvc_getinfo(void) +{ + const char *getinfo = "getinfo:"; + int len = strlen(getinfo); + /* Sends getinfo command */ + ssize_t written = write_socket(xvc_fd, getinfo, len); + if (written != len) { + LOG_ERROR("xvc_getinfo: write"); + return ERROR_FAIL; + } + char info_recv_buf[20]; + /* Potentially waits until response gets received. */ + ssize_t read = read_socket(xvc_fd, info_recv_buf, 20); + if (read < 0) { + LOG_ERROR("xvc_getinfo: read"); + return ERROR_FAIL; + } + LOG_INFO("XVC HW server version: %.19s", info_recv_buf); + if (strncmp(info_recv_buf, "xvcServer_v1.0:", 15) != 0) { + /* We got something unexpected. */ + LOG_ERROR("Unexpected response from XVC server"); + return ERROR_FAIL; + } + xvc_max_usable_vector_size = strtoul(&info_recv_buf[15], NULL, 10); + if (xvc_max_usable_vector_size > 32757) { + LOG_DEBUG("Exceeded maximum vector size, outputting to 32757 bytes"); + xvc_max_usable_vector_size = 32757; + } + xvc_max_vector_size = xvc_max_usable_vector_size + 10; + LOG_DEBUG("Maximum vector size set to: %u\n", xvc_max_vector_size); + /*Usable size: maximum vector size determined by the server minus the + sizeof the command, 10 bytes in worst-case (6 bytes from shift: and 4 + additional ones for bit_length).*/ + /* Updates TX Buffer sizes: */ + xvc_send_buf = malloc(xvc_max_vector_size * sizeof(uint8_t)); + xvc_tms_buf = malloc(xvc_max_usable_vector_size / 2 * sizeof(uint8_t)); + xvc_tdi_buf = malloc(xvc_max_usable_vector_size / 2 * sizeof(uint8_t)); + xvc_tdo_buf = malloc(xvc_max_usable_vector_size / 2 * sizeof(uint8_t)); + if (!xvc_send_buf || !xvc_tms_buf || !xvc_tdi_buf || !xvc_tdo_buf) { + LOG_ERROR("Out of memory"); + free(xvc_send_buf); + free(xvc_tms_buf); + free(xvc_tdi_buf); + free(xvc_tdo_buf); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int xvc_settck(void) +{ + /*Creates the command: + * copies the header and appends the value. + * */ + uint8_t settck[12]; + const char *header = "settck:"; + memcpy(settck, header, 7); + h_u32_to_le(settck + 7, xvc_tck); + /* Writes the request */ + ssize_t written = write_socket(xvc_fd, settck, 11); + if (written != 11) { + LOG_ERROR("xvc_settck: write"); + return ERROR_FAIL; + } + uint32_t tck_recv_buf; + /* Potentially waits for a response. */ + ssize_t read = read_socket(xvc_fd, &tck_recv_buf, 4); + if (read < 0) { + LOG_ERROR("xvc_settck: read"); + return ERROR_FAIL; + } + /* Prints response, regardless of machine endianness. */ + uint32_t xvc_tck_period_ns; + xvc_tck_period_ns = le_to_h_u32((uint8_t *)&tck_recv_buf); + LOG_INFO("XVC tck period ns: %u", xvc_tck_period_ns); + return ERROR_OK; +} + +static int xvc_fill_buffer(void) +{ + if (read_frame(xvc_fd, xvc_tdo_buf, (7 + last_used_bits) / 8) < 0) { + LOG_ERROR("Read_frame"); + return ERROR_FAIL; + } + for (int i = 0; i < pending_shift_results; i++) { + struct shift_result *shift_result = &shift_result_buffer[i]; + buf_set_buf(xvc_tdo_buf, shift_result->first, shift_result->buffer, + shift_result->buffer_offset, shift_result->length); + } + memset(xvc_tdo_buf, 0, xvc_max_usable_vector_size / 2); + pending_shift_results = 0; + return ERROR_OK; +} + +static int xvc_reset(int trst, int srst) +{ + /*XVC does not have dedicated Reset lines. */ + static bool first_time = true; + if (first_time) { + LOG_WARNING("Adapter has no reset lines. Fix \"reset_config\" command in " + "config file"); + first_time = false; + } + return ERROR_OK; +} + +static int xvc_init_tcp(void) +{ + struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM}; + struct addrinfo *result, *rp; + int fd = 0; + + LOG_INFO("Connecting to %s:%s", xvc_host ? xvc_host : "localhost", xvc_port); + + /* Obtain address(es) matching host/port */ + int s = getaddrinfo(xvc_host, xvc_port, &hints, &result); + if (s != 0) { + LOG_ERROR("getaddrinfo: %s\n", gai_strerror(s)); + return ERROR_FAIL; + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully connect(2). + If socket(2) (or connect(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#ifndef _WIN32 + if (fd == -1) + continue; +#else + if (fd == INVALID_SOCKET) + continue; +#endif + + if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + + close(fd); + } + + /* We work hard to collapse the writes into the minimum number, so when + * we write something we want to get it to the other end of the + * connection as fast as possible. */ + int one = 1; + /* On Windows optval has to be a const char *. */ + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one)); + + freeaddrinfo(result); /* No longer needed */ + + if (!rp) { /* No address succeeded */ + LOG_ERROR("Failed to connect"); + return ERROR_FAIL; + } + + return fd; +} + +static int xvc_init_unix(void) +{ + if (!xvc_host) { + LOG_ERROR("host/socket not specified"); + return ERROR_FAIL; + } + + LOG_INFO("Connecting to unix socket %s", xvc_host); + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + LOG_ERROR("socket"); + return ERROR_FAIL; + } + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, xvc_host, sizeof(addr.sun_path)); + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + + if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) { + LOG_ERROR("connect"); + return ERROR_FAIL; + } + + return fd; +} + +/* + * COMMAND_HANDLERS + * */ +COMMAND_HANDLER(xvc_handle_port_command) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + uint16_t port; + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], port); + free(xvc_port); + xvc_port = (port == 0) ? NULL : strdup(CMD_ARGV[0]); + return ERROR_OK; +} + +COMMAND_HANDLER(xvc_handle_host_command) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + free(xvc_host); + xvc_host = strdup(CMD_ARGV[0]); + return ERROR_OK; +} + +static const struct command_registration xvc_command_handlers[] = { + { + .name = "xvc_port", + .handler = xvc_handle_port_command, + .mode = COMMAND_CONFIG, + .help = + "Set the port to use to connect to the XVC remote server.\n" + " If 0 or unset, use unix sockets to connect to the remote server.", + .usage = "port_number", + }, + { + .name = "xvc_host", + .handler = xvc_handle_host_command, + .mode = COMMAND_CONFIG, + .help = "Set the host to use to connect to the remote XVC server.\n", + .usage = "host_name", + }, + COMMAND_REGISTRATION_DONE +}; + +static int xvc_init(void) +{ + xvc_used_bits = 0; + last_used_bits = 0; + pending_shift_results = 0; + /* Default clock: 1000 ns period */ + xvc_tck = 1000; + + LOG_INFO("Initializing XVC driver"); + if (!xvc_port) + xvc_fd = xvc_init_unix(); + else + xvc_fd = xvc_init_tcp(); + if (xvc_fd < 0) + return xvc_fd; + + xvc_getinfo(); + xvc_settck(); + + LOG_INFO("XVC driver initialized"); + + return ERROR_OK; +} + +static int xvc_quit(void) +{ + if (close_socket(xvc_fd) != 0) { + LOG_ERROR("close_socket"); + return ERROR_FAIL; + } + free(xvc_port); + free(xvc_host); + free(xvc_tms_buf); + free(xvc_tdi_buf); + free(xvc_send_buf); + free(xvc_tdo_buf); + LOG_INFO("XVC interface quit"); + return ERROR_OK; +} + +/* The driver leaves the TCK 0 when in idle */ +static void xvc_tap_end_state(tap_state_t state) +{ + assert(tap_is_state_stable(state)); + tap_set_end_state(state); +} + +static int xvc_tap_state_move(int skip) +{ + uint8_t tms_scan = tap_get_tms_path(tap_get_state(), tap_get_end_state()); + int tms_count = tap_get_tms_path_len(tap_get_state(), tap_get_end_state()); + + xvc_queue(&tms_scan, 0, NULL, 0, NULL, 0, tms_count); + + tap_set_state(tap_get_end_state()); + return ERROR_OK; +} + +/* + * Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG + * (or SWD) state machine. "Legacy enqueue" + */ +static int xvc_tap_execute_tms(struct jtag_command *cmd) +{ + unsigned int num_bits = cmd->cmd.tms->num_bits; + const uint8_t *bits = cmd->cmd.tms->bits; + + LOG_DEBUG_IO("TMS: %d bits", num_bits); + + uint8_t tms = 0; + + for (unsigned int i = 0; i < num_bits; i++) { + tms = ((bits[i / 8] >> (i % 8)) & 1); + if (xvc_queue(&tms, 0, NULL, 0, NULL, 0, 1) != ERROR_OK) + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int xvc_tap_path_move(struct pathmove_command *cmd) +{ + int num_states = cmd->num_states; + int state_count; + uint8_t tms = 0xff; + + state_count = 0; + while (num_states) { + if (tap_state_transition(tap_get_state(), false) == cmd->path[state_count]) { + xvc_queue(NULL, 0, NULL, 0, NULL, 0, 1); + } else if (tap_state_transition(tap_get_state(), true) == + cmd->path[state_count]) { + xvc_queue(&tms, 0, NULL, 0, NULL, 0, 1); + } else { + LOG_ERROR("BUG: %s -> %s isn't a valid TAP transition", + tap_state_name(tap_get_state()), + tap_state_name(cmd->path[state_count])); + return ERROR_FAIL; + } + + tap_set_state(cmd->path[state_count]); + state_count++; + num_states--; + } + + tap_set_end_state(tap_get_state()); + return ERROR_OK; +} + +static int xvc_tap_stableclocks(int num_cycles) +{ + uint8_t tms = (tap_get_state() == TAP_RESET ? 0xff : 0); + + for (int i = 0; i < num_cycles; i++) + xvc_queue(&tms, 0, NULL, 0, NULL, 0, 1); + + return ERROR_OK; +} + +static int xvc_tap_runtest(int num_cycles) +{ + tap_state_t saved_end_state = tap_get_end_state(); + + /* only do a state_move when we're not already in IDLE */ + if (tap_get_state() != TAP_IDLE) { + xvc_tap_end_state(TAP_IDLE); + if (xvc_tap_state_move(0) != ERROR_OK) + return ERROR_FAIL; + } + + xvc_tap_stableclocks(num_cycles); + + /* finish in end_state */ + xvc_tap_end_state(saved_end_state); + if (tap_get_state() != tap_get_end_state()) + if (xvc_tap_state_move(0) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int xvc_tap_scan_write(struct scan_command *cmd) +{ + /* Make sure there are no trailing fields with num_bits == 0, or the logic + * below will fail. */ + while (cmd->num_fields > 0 && cmd->fields[cmd->num_fields - 1].num_bits == 0) { + cmd->num_fields--; + LOG_DEBUG("discarding trailing empty field"); + } + if (cmd->num_fields == 0) { + LOG_DEBUG("empty scan, doing nothing"); + return ERROR_OK; + } + + bool ir_scan = cmd->ir_scan; + if (ir_scan) { + if (tap_get_state() != TAP_IRSHIFT) { + tap_set_end_state(TAP_IRSHIFT); + xvc_tap_state_move(0); + } + } else { + if (tap_get_state() != TAP_DRSHIFT) { + xvc_tap_end_state(TAP_DRSHIFT); + xvc_tap_state_move(0); + } + } + xvc_tap_end_state(cmd->end_state); + + for (int i = 0; i < cmd->num_fields; i++) { + + /* + * Last field + * */ + if (i == (cmd->num_fields - 1) && tap_get_state() != tap_get_end_state()) { + /* All bits except the last one */ + xvc_queue(NULL, 0, cmd->fields[i].out_value, 0, cmd->fields[i].in_value, 0, + cmd->fields[i].num_bits - 1); + /* Last bit to copy */ + uint8_t last_bit = 0; + if (cmd->fields[i].out_value) + bit_copy(&last_bit, 0, cmd->fields[i].out_value, + cmd->fields[i].num_bits - 1, 1); + /* TMS set to 1 to leave the current state. */ + uint8_t tms_bits = 0x01; + xvc_queue(&tms_bits, 0, &last_bit, 0, cmd->fields[i].in_value, + cmd->fields[i].num_bits - 1, 1); + tap_set_state(tap_state_transition(tap_get_state(), 1)); + xvc_queue(&tms_bits, 1, NULL, 0, NULL, 0, 1); + tap_set_state(tap_state_transition(tap_get_state(), 0)); + } else { + xvc_queue(NULL, 0, cmd->fields[i].out_value, 0, cmd->fields[i].in_value, 0, + cmd->fields[i].num_bits); + } + } + + if (tap_get_state() != tap_get_end_state()) { + /* we *KNOW* the above loop transitioned out of + * the shift state, so we skip the first state + * and move directly to the end state. + */ + + if (xvc_tap_state_move(0) != ERROR_OK) + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int xvc_tap_execute_queue(void) +{ + struct jtag_command *cmd = + jtag_command_queue; /* currently processed command */ + + while (cmd) { + switch (cmd->type) { + case JTAG_RUNTEST: + LOG_DEBUG_IO("runtest %i cycles, end in %s", cmd->cmd.runtest->num_cycles, + tap_state_name(cmd->cmd.runtest->end_state)); + xvc_tap_end_state(cmd->cmd.runtest->end_state); + if (xvc_tap_runtest(cmd->cmd.runtest->num_cycles) != ERROR_OK) + return ERROR_FAIL; + break; + case JTAG_STABLECLOCKS: + /* this is only allowed while in a stable state. A check for a stable + * state was done in jtag_add_clocks() + */ + if (xvc_tap_stableclocks(cmd->cmd.stableclocks->num_cycles) != ERROR_OK) + return ERROR_FAIL; + break; + case JTAG_TLR_RESET: + LOG_DEBUG_IO("statemove end in %s", + tap_state_name(cmd->cmd.statemove->end_state)); + xvc_tap_end_state(cmd->cmd.statemove->end_state); + if (xvc_tap_state_move(0) != ERROR_OK) + return ERROR_FAIL; + break; + case JTAG_PATHMOVE: + LOG_DEBUG_IO( + "pathmove: %i states, end in %s", cmd->cmd.pathmove->num_states, + tap_state_name( + cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1])); + if (xvc_tap_path_move(cmd->cmd.pathmove) != ERROR_OK) + return ERROR_FAIL; + break; + case JTAG_SCAN: + if (xvc_tap_scan_write(cmd->cmd.scan) != ERROR_OK) + return ERROR_FAIL; + break; + case JTAG_SLEEP: + LOG_DEBUG_IO("sleep %" PRIi32, cmd->cmd.sleep->us); + jtag_sleep(cmd->cmd.sleep->us); + break; + case JTAG_TMS: + if (xvc_tap_execute_tms(cmd) != ERROR_OK) + return ERROR_FAIL; + break; + default: + LOG_ERROR("BUG: unknown JTAG command type encountered"); + return ERROR_FAIL; + } + cmd = cmd->next; + } + + if (xvc_flush() != ERROR_OK) + return ERROR_FAIL; + if (xvc_fill_buffer() != ERROR_OK) + return ERROR_FAIL; + /* return ERROR_OK, unless a previous ERROR has been + * returned. + */ + return ERROR_OK; +} + +static struct jtag_interface xvc_interface = { + .execute_queue = &xvc_tap_execute_queue, + .supported = DEBUG_CAP_TMS_SEQ, +}; + +struct adapter_driver xvc_driver = { + .name = "xvc", + .transports = jtag_only, + .commands = xvc_command_handlers, + .init = xvc_init, + .quit = xvc_quit, + .reset = &xvc_reset, + .speed = &xvc_speed, + .khz = &xvc_khz, + .speed_div = &xvc_speed_div, + .jtag_ops = &xvc_interface, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index c24ead8cd9..32d998405f 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -152,6 +152,9 @@ struct adapter_driver *adapter_drivers[] = { #endif #if BUILD_AM335XGPIO == 1 &am335xgpio_adapter_driver, +#endif +#if BUILD_XVC == 1 + &xvc_driver, #endif NULL, }; --