From 4b8f866aebd08bf3169eb61a233707d5b61cd4af Mon Sep 17 00:00:00 2001 From: Franck Jullien Date: Fri, 6 Sep 2013 18:42:39 +0200 Subject: [PATCH] jtag: drivers: Add JTAP VPI client driver This patch adds a driver for the jtag_vpi server [1]. This server is now part of the ORPSoC version 3 (OpenRISC Reference Platform SoC). The jtag_vpi server provides an interface between OpenOCD and a simulated core. [1] http://github.com/fjullien/jtag_vpi Change-Id: I717b72cace4845f66c878581345074f99002e21a Signed-off-by: Franck Jullien Reviewed-on: http://openocd.zylin.com/1609 Tested-by: jenkins Reviewed-by: Spencer Oliver --- configure.ac | 11 + doc/openocd.texi | 4 + src/jtag/drivers/Makefile.am | 3 + src/jtag/drivers/jtag_vpi.c | 440 +++++++++++++++++++++++++++++++++++ src/jtag/interfaces.c | 6 + tcl/interface/jtag_vpi.cfg | 10 + 6 files changed, 474 insertions(+) create mode 100644 src/jtag/drivers/jtag_vpi.c create mode 100644 tcl/interface/jtag_vpi.cfg diff --git a/configure.ac b/configure.ac index 1a647475..c7700dc3 100644 --- a/configure.ac +++ b/configure.ac @@ -355,6 +355,10 @@ AC_ARG_ENABLE([ftdi], AS_HELP_STRING([--enable-ftdi], [Enable building support for the MPSSE mode of FTDI based devices, using libusb-1.0 in asynchronous mode]), [build_ftdi=$enableval], [build_ftdi=no]) +AC_ARG_ENABLE([jtag_vpi], + AS_HELP_STRING([--enable-jtag_vpi], [Enable building support for JTAG VPI]), + [build_jtag_vpi=$enableval], [build_jtag_vpi=no]) + AC_ARG_ENABLE([usb_blaster_libftdi], AS_HELP_STRING([--enable-usb_blaster_libftdi], [Enable building support for the Altera USB-Blaster using the libftdi driver, opensource alternate of FTD2XX]), [build_usb_blaster_libftdi=$enableval], [build_usb_blaster_libftdi=no]) @@ -692,6 +696,12 @@ else AC_DEFINE([BUILD_USB_BLASTER_LIBFTDI], [0], [0 if you don't want libftdi usb_blaster.]) fi +if test $build_jtag_vpi = yes; then + AC_DEFINE([BUILD_JTAG_VPI], [1], [1 if you want JTAG VPI.]) +else + AC_DEFINE([BUILD_JTAG_VPI], [0], [0 if you don't want JTAG VPI.]) +fi + if test $build_usb_blaster_ftd2xx = yes; then AC_DEFINE([BUILD_USB_BLASTER_FTD2XX], [1], [1 if you want ftd2xx usb_blaster.]) else @@ -1206,6 +1216,7 @@ AM_CONDITIONAL([FT2232_DRIVER], [test $build_ft2232_ftd2xx = yes -o $build_ft223 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([JTAG_VPI], [test $build_jtag_vpi = yes -o $build_jtag_vpi = 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]) diff --git a/doc/openocd.texi b/doc/openocd.texi index 78a4e8ed..a747f9ac 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -578,6 +578,10 @@ produced, PDF schematics are easily found and it is easy to make. @item @b{bcm2835gpio} @* A BCM2835-based board (e.g. Raspberry Pi) using the GPIO pins of the expansion header. +@item @b{jtag_vpi} +@* A JTAG driver acting as a client for the JTAG VPI server interface. +@* Link: @url{http://github.com/fjullien/jtag_vpi} + @end itemize @node About Jim-Tcl diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am index e242cc67..e3ed2199 100644 --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -42,6 +42,9 @@ endif if FTDI_DRIVER DRIVERFILES += ftdi.c mpsse.c endif +if JTAG_VPI +DRIVERFILES += jtag_vpi.c +endif if USB_BLASTER_DRIVER SUBDIRS += usb_blaster libocdjtagdrivers_la_LIBADD += $(top_builddir)/src/jtag/drivers/usb_blaster/libocdusbblaster.la diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c new file mode 100644 index 00000000..84cd9470 --- /dev/null +++ b/src/jtag/drivers/jtag_vpi.c @@ -0,0 +1,440 @@ +/* + * JTAG to VPI driver + * + * Copyright (C) 2013 Franck Jullien, + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 +#include + +#define NO_TAP_SHIFT 0 +#define TAP_SHIFT 1 + +#define SERVER_ADDRESS "127.0.0.1" +#define SERVER_PORT 5555 + +#define XFERT_MAX_SIZE 512 + +#define CMD_RESET 0 +#define CMD_TMS_SEQ 1 +#define CMD_SCAN_CHAIN 2 +#define CMD_SCAN_CHAIN_FLIP_TMS 3 +#define CMD_STOP_SIMU 4 + +int server_port = SERVER_PORT; + +int sockfd; +struct sockaddr_in serv_addr; + +struct vpi_cmd { + int cmd; + unsigned char buffer_out[XFERT_MAX_SIZE]; + unsigned char buffer_in[XFERT_MAX_SIZE]; + int length; + int nb_bits; +}; + +static int jtag_vpi_send_cmd(struct vpi_cmd *vpi) +{ + int retval = write(sockfd, vpi, sizeof(struct vpi_cmd)); + if (retval <= 0) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int jtag_vpi_receive_cmd(struct vpi_cmd *vpi) +{ + int retval = read(sockfd, vpi, sizeof(struct vpi_cmd)); + if (retval < (int)sizeof(struct vpi_cmd)) + return ERROR_FAIL; + + return ERROR_OK; +} + +/** + * jtag_vpi_reset - ask to reset the JTAG device + * @trst: 1 if TRST is to be asserted + * @srst: 1 if SRST is to be asserted + */ +static int jtag_vpi_reset(int trst, int srst) +{ + struct vpi_cmd vpi; + + vpi.cmd = CMD_RESET; + vpi.length = 0; + return jtag_vpi_send_cmd(&vpi); +} + +/** + * jtag_vpi_tms_seq - ask 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=, TDI= + * - writing out TCK=1, TMS=, TDI= which triggers the transition + * The function ensures that at the end of the sequence, the clock (TCK) is put + * low. + */ +static int jtag_vpi_tms_seq(const uint8_t *bits, int nb_bits) +{ + struct vpi_cmd vpi; + int nb_bytes; + + nb_bytes = (nb_bits / 8) + !!(nb_bits % 8); + + vpi.cmd = CMD_TMS_SEQ; + memcpy(vpi.buffer_out, bits, nb_bytes); + vpi.length = nb_bytes; + vpi.nb_bits = nb_bits; + + return jtag_vpi_send_cmd(&vpi); +} + +/** + * jtag_vpi_path_move - ask 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=, TDI= + * - writing out TCK=1, TMS=, TDI= which triggers the transition + * The function ensures that at the end of the sequence, the clock (TCK) is put + * low. + */ + +static int jtag_vpi_path_move(struct pathmove_command *cmd) +{ + uint16_t trans = 0; + int retval; + int i; + + for (i = 0; i < cmd->num_states; i++) { + if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) + trans = trans | 1; + trans = trans << 1; + } + + retval = jtag_vpi_tms_seq((uint8_t *)&trans, 1); + if (retval != ERROR_OK) + return retval; + + tap_set_state(cmd->path[i]); + + return ERROR_OK; +} + +/** + * jtag_vpi_tms - ask a tms command + * @cmd: tms command + */ +static int jtag_vpi_tms(struct tms_command *cmd) +{ + return jtag_vpi_tms_seq(cmd->bits, cmd->num_bits); +} + +static int jtag_vpi_state_move(tap_state_t state) +{ + if (tap_get_state() == state) + return ERROR_OK; + + uint8_t tms_scan = tap_get_tms_path(tap_get_state(), state); + int tms_len = tap_get_tms_path_len(tap_get_state(), state); + + int retval = jtag_vpi_tms_seq(&tms_scan, tms_len); + if (retval != ERROR_OK) + return retval; + + tap_set_state(state); + + return ERROR_OK; +} + +static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift) +{ + struct vpi_cmd vpi; + int nb_bytes = (nb_bits / 8) + !!(nb_bits % 8); + + vpi.cmd = tap_shift ? CMD_SCAN_CHAIN_FLIP_TMS : CMD_SCAN_CHAIN; + + if (bits) + memcpy(vpi.buffer_out, bits, nb_bytes); + else + memset(vpi.buffer_out, 0xff, nb_bytes); + + vpi.length = nb_bytes; + vpi.nb_bits = nb_bits; + + int retval = jtag_vpi_send_cmd(&vpi); + if (retval != ERROR_OK) + return retval; + + retval = jtag_vpi_receive_cmd(&vpi); + if (retval != ERROR_OK) + return retval; + + if (bits) + memcpy(bits, vpi.buffer_in, nb_bytes); + + return ERROR_OK; +} + +/** + * jtag_vpi_queue_tdi - short description + * @bits: bits to be queued on TDI (or NULL if 0 are to be queued) + * @nb_bits: number of bits + */ +static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift) +{ + int nb_xfer = (nb_bits / (XFERT_MAX_SIZE * 8)) + !!(nb_bits % (XFERT_MAX_SIZE * 8)); + uint8_t *xmit_buffer = bits; + int xmit_nb_bits = nb_bits; + int i = 0; + int retval; + + while (nb_xfer) { + + if (nb_xfer == 1) { + retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], xmit_nb_bits, tap_shift); + if (retval != ERROR_OK) + return retval; + } else { + retval = jtag_vpi_queue_tdi_xfer(&xmit_buffer[i], XFERT_MAX_SIZE * 8, NO_TAP_SHIFT); + if (retval != ERROR_OK) + return retval; + xmit_nb_bits -= XFERT_MAX_SIZE * 8; + i += XFERT_MAX_SIZE; + } + + nb_xfer--; + } + + return ERROR_OK; +} + +/** + * jtag_vpi_clock_tms - clock a TMS transition + * @tms: the TMS to be sent + * + * Triggers a TMS transition (ie. one JTAG TAP state move). + */ +static int jtag_vpi_clock_tms(int tms) +{ + const uint8_t tms_0 = 0; + const uint8_t tms_1 = 1; + + return jtag_vpi_tms_seq(tms ? &tms_1 : &tms_0, 1); +} + +/** + * jtag_vpi_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 jtag_vpi_scan(struct scan_command *cmd) +{ + int scan_bits; + uint8_t *buf = NULL; + int retval = ERROR_OK; + + scan_bits = jtag_build_buffer(cmd, &buf); + + if (cmd->ir_scan) { + retval = jtag_vpi_state_move(TAP_IRSHIFT); + if (retval != ERROR_OK) + return retval; + } else { + retval = jtag_vpi_state_move(TAP_DRSHIFT); + if (retval != ERROR_OK) + return retval; + } + + if (cmd->end_state == TAP_DRSHIFT) { + retval = jtag_vpi_queue_tdi(buf, scan_bits, NO_TAP_SHIFT); + if (retval != ERROR_OK) + return retval; + } else { + retval = jtag_vpi_queue_tdi(buf, scan_bits, TAP_SHIFT); + if (retval != ERROR_OK) + return retval; + } + + if (cmd->end_state != TAP_DRSHIFT) { + /* + * As our JTAG is in an unstable state (IREXIT1 or DREXIT1), move it + * forward to a stable IRPAUSE or DRPAUSE. + */ + retval = jtag_vpi_clock_tms(0); + if (retval != ERROR_OK) + return retval; + + if (cmd->ir_scan) + tap_set_state(TAP_IRPAUSE); + else + tap_set_state(TAP_DRPAUSE); + } + + retval = jtag_read_buffer(buf, cmd); + if (retval != ERROR_OK) + return retval; + + if (buf) + free(buf); + + if (cmd->end_state != TAP_DRSHIFT) { + retval = jtag_vpi_state_move(cmd->end_state); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + +static int jtag_vpi_runtest(int cycles, tap_state_t state) +{ + int retval; + + retval = jtag_vpi_state_move(TAP_IDLE); + if (retval != ERROR_OK) + return retval; + + retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT); + if (retval != ERROR_OK) + return retval; + + return jtag_vpi_state_move(state); +} + +static int jtag_vpi_stableclocks(int cycles) +{ + return jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT); +} + +static int jtag_vpi_execute_queue(void) +{ + struct jtag_command *cmd; + int retval = ERROR_OK; + + for (cmd = jtag_command_queue; retval == ERROR_OK && cmd != NULL; + cmd = cmd->next) { + switch (cmd->type) { + case JTAG_RESET: + retval = jtag_vpi_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst); + break; + case JTAG_RUNTEST: + retval = jtag_vpi_runtest(cmd->cmd.runtest->num_cycles, + cmd->cmd.runtest->end_state); + break; + case JTAG_STABLECLOCKS: + retval = jtag_vpi_stableclocks(cmd->cmd.stableclocks->num_cycles); + break; + case JTAG_TLR_RESET: + retval = jtag_vpi_state_move(cmd->cmd.statemove->end_state); + break; + case JTAG_PATHMOVE: + retval = jtag_vpi_path_move(cmd->cmd.pathmove); + break; + case JTAG_TMS: + retval = jtag_vpi_tms(cmd->cmd.tms); + break; + case JTAG_SLEEP: + jtag_sleep(cmd->cmd.sleep->us); + break; + case JTAG_SCAN: + retval = jtag_vpi_scan(cmd->cmd.scan); + break; + } + } + + return retval; +} + +static int jtag_vpi_init(void) +{ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + LOG_ERROR("Could not create socket"); + return ERROR_FAIL; + } + + memset(&serv_addr, 0, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(server_port); + + if (inet_pton(AF_INET, SERVER_ADDRESS, &serv_addr.sin_addr) <= 0) { + LOG_ERROR("inet_pton error occured"); + return ERROR_FAIL; + } + + if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + close(sockfd); + LOG_ERROR("Can't connect to %s : %u", SERVER_ADDRESS, server_port); + return ERROR_COMMAND_CLOSE_CONNECTION; + } + + LOG_INFO("Connection to %s : %u succeed", SERVER_ADDRESS, server_port); + + return ERROR_OK; +} + +static int jtag_vpi_quit(void) +{ + return close(sockfd); +} + +COMMAND_HANDLER(jtag_vpi_set_port) +{ + if (CMD_ARGC == 0) + LOG_WARNING("You need to set a port number"); + else + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port); + + LOG_INFO("Set server port to %u", server_port); + + return ERROR_OK; +} + + +static const struct command_registration jtag_vpi_command_handlers[] = { + { + .name = "jtag_vpi_set_port", + .handler = &jtag_vpi_set_port, + .mode = COMMAND_CONFIG, + .help = "set the port of the VPI server", + .usage = "description_string", + }, + COMMAND_REGISTRATION_DONE +}; + +struct jtag_interface jtag_vpi_interface = { + .name = "jtag_vpi", + .supported = DEBUG_CAP_TMS_SEQ, + .commands = jtag_vpi_command_handlers, + .transports = jtag_only, + + .init = jtag_vpi_init, + .quit = jtag_vpi_quit, + .execute_queue = jtag_vpi_execute_queue, +}; diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c index 579e9e74..eb447cdd 100644 --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -65,6 +65,9 @@ extern struct jtag_interface ftdi_interface; #if BUILD_USB_BLASTER_LIBFTDI == 1 || BUILD_USB_BLASTER_FTD2XX == 1 extern struct jtag_interface usb_blaster_interface; #endif +#if BUILD_JTAG_VPI == 1 +extern struct jtag_interface jtag_vpi_interface; +#endif #if BUILD_AMTJTAGACCEL == 1 extern struct jtag_interface amt_jtagaccel_interface; #endif @@ -158,6 +161,9 @@ struct jtag_interface *jtag_interfaces[] = { #if BUILD_USB_BLASTER_LIBFTDI == 1 || BUILD_USB_BLASTER_FTD2XX == 1 &usb_blaster_interface, #endif +#if BUILD_JTAG_VPI == 1 + &jtag_vpi_interface, +#endif #if BUILD_AMTJTAGACCEL == 1 &amt_jtagaccel_interface, #endif diff --git a/tcl/interface/jtag_vpi.cfg b/tcl/interface/jtag_vpi.cfg new file mode 100644 index 00000000..2756b254 --- /dev/null +++ b/tcl/interface/jtag_vpi.cfg @@ -0,0 +1,10 @@ +interface jtag_vpi + +# Set the VPI JTAG server address +if { [info exists VPI_PORT] } { + set _VPI_PORT $VPI_PORT +} else { + set _VPI_PORT 50020 +} + +jtag_vpi_set_port $_VPI_PORT -- 2.39.5