X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fjtag%2Fdrivers%2Fjtag_vpi.c;h=f19e9ab44ba2666d3a8fc03a5b4843551ebf0959;hb=382148e4dd437978997d668f6ec715ddcec1c46e;hp=1a42b3a05f41b910603424cd703143f98c4f8011;hpb=fe38562dbebfd02a6f0ca77af37bba642e5bb0f7;p=fw%2Fopenocd diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c index 1a42b3a05..f19e9ab44 100644 --- a/src/jtag/drivers/jtag_vpi.c +++ b/src/jtag/drivers/jtag_vpi.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* * JTAG to VPI driver * @@ -5,19 +7,6 @@ * * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H @@ -29,11 +18,17 @@ #include #endif +#ifndef _WIN32 +#include +#endif + +#include "helper/replacements.h" + #define NO_TAP_SHIFT 0 #define TAP_SHIFT 1 -#define SERVER_ADDRESS "127.0.0.1" -#define SERVER_PORT 5555 +#define DEFAULT_SERVER_ADDRESS "127.0.0.1" +#define DEFAULT_SERVER_PORT 5555 #define XFERT_MAX_SIZE 512 @@ -43,46 +38,172 @@ #define CMD_SCAN_CHAIN_FLIP_TMS 3 #define CMD_STOP_SIMU 4 -int server_port = SERVER_PORT; -char *server_address; +/* jtag_vpi server port and address to connect to */ +static int server_port = DEFAULT_SERVER_PORT; +static char *server_address; + +/* Send CMD_STOP_SIMU to server when OpenOCD exits? */ +static bool stop_sim_on_exit; -int sockfd; -struct sockaddr_in serv_addr; +static int sockfd; +static struct sockaddr_in serv_addr; +/* One jtag_vpi "packet" as sent over a TCP channel. */ struct vpi_cmd { - int cmd; + union { + uint32_t cmd; + unsigned char cmd_buf[4]; + }; unsigned char buffer_out[XFERT_MAX_SIZE]; unsigned char buffer_in[XFERT_MAX_SIZE]; - int length; - int nb_bits; + union { + uint32_t length; + unsigned char length_buf[4]; + }; + union { + uint32_t nb_bits; + unsigned char nb_bits_buf[4]; + }; }; +static char *jtag_vpi_cmd_to_str(int cmd_num) +{ + switch (cmd_num) { + case CMD_RESET: + return "CMD_RESET"; + case CMD_TMS_SEQ: + return "CMD_TMS_SEQ"; + case CMD_SCAN_CHAIN: + return "CMD_SCAN_CHAIN"; + case CMD_SCAN_CHAIN_FLIP_TMS: + return "CMD_SCAN_CHAIN_FLIP_TMS"; + case CMD_STOP_SIMU: + return "CMD_STOP_SIMU"; + default: + return ""; + } +} + static int jtag_vpi_send_cmd(struct vpi_cmd *vpi) { - int retval = write_socket(sockfd, vpi, sizeof(struct vpi_cmd)); - if (retval <= 0) - return ERROR_FAIL; + int retval; + + /* Optional low-level JTAG debug */ + if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) { + if (vpi->nb_bits > 0) { + /* command with a non-empty data payload */ + char *char_buf = buf_to_hex_str(vpi->buffer_out, + (vpi->nb_bits > DEBUG_JTAG_IOZ) + ? DEBUG_JTAG_IOZ + : vpi->nb_bits); + LOG_DEBUG_IO("sending JTAG VPI cmd: cmd=%s, " + "length=%" PRIu32 ", " + "nb_bits=%" PRIu32 ", " + "buf_out=0x%s%s", + jtag_vpi_cmd_to_str(vpi->cmd), + vpi->length, + vpi->nb_bits, + char_buf, + (vpi->nb_bits > DEBUG_JTAG_IOZ) ? "(...)" : ""); + free(char_buf); + } else { + /* command without data payload */ + LOG_DEBUG_IO("sending JTAG VPI cmd: cmd=%s, " + "length=%" PRIu32 ", " + "nb_bits=%" PRIu32, + jtag_vpi_cmd_to_str(vpi->cmd), + vpi->length, + vpi->nb_bits); + } + } + /* Use little endian when transmitting/receiving jtag_vpi cmds. + The choice of little endian goes against usual networking conventions + but is intentional to remain compatible with most older OpenOCD builds + (i.e. builds on little-endian platforms). */ + h_u32_to_le(vpi->cmd_buf, vpi->cmd); + h_u32_to_le(vpi->length_buf, vpi->length); + h_u32_to_le(vpi->nb_bits_buf, vpi->nb_bits); + +retry_write: + retval = write_socket(sockfd, vpi, sizeof(struct vpi_cmd)); + + if (retval < 0) { + /* Account for the case when socket write is interrupted. */ +#ifdef _WIN32 + int wsa_err = WSAGetLastError(); + if (wsa_err == WSAEINTR) + goto retry_write; +#else + if (errno == EINTR) + goto retry_write; +#endif + /* Otherwise this is an error using the socket, most likely fatal + for the connection. B*/ + log_socket_error("jtag_vpi xmit"); + /* TODO: Clean way how adapter drivers can report fatal errors + to upper layers of OpenOCD and let it perform an orderly shutdown? */ + exit(-1); + } else if (retval < (int)sizeof(struct vpi_cmd)) { + /* This means we could not send all data, which is most likely fatal + for the jtag_vpi connection (the underlying TCP connection likely not + usable anymore) */ + LOG_ERROR("jtag_vpi: Could not send all data through jtag_vpi connection."); + exit(-1); + } + + /* Otherwise the packet has been sent successfully. */ return ERROR_OK; } static int jtag_vpi_receive_cmd(struct vpi_cmd *vpi) { - int retval = read_socket(sockfd, vpi, sizeof(struct vpi_cmd)); - if (retval < (int)sizeof(struct vpi_cmd)) - return ERROR_FAIL; + unsigned bytes_buffered = 0; + while (bytes_buffered < sizeof(struct vpi_cmd)) { + int bytes_to_receive = sizeof(struct vpi_cmd) - bytes_buffered; + int retval = read_socket(sockfd, ((char *)vpi) + bytes_buffered, bytes_to_receive); + if (retval < 0) { +#ifdef _WIN32 + int wsa_err = WSAGetLastError(); + if (wsa_err == WSAEINTR) { + /* socket read interrupted by WSACancelBlockingCall() */ + continue; + } +#else + if (errno == EINTR) { + /* socket read interrupted by a signal */ + continue; + } +#endif + /* Otherwise, this is an error when accessing the socket. */ + log_socket_error("jtag_vpi recv"); + exit(-1); + } else if (retval == 0) { + /* Connection closed by the other side */ + LOG_ERROR("Connection prematurely closed by jtag_vpi server."); + exit(-1); + } + /* Otherwise, we have successfully received some data */ + bytes_buffered += retval; + } + + /* Use little endian when transmitting/receiving jtag_vpi cmds. */ + vpi->cmd = le_to_h_u32(vpi->cmd_buf); + vpi->length = le_to_h_u32(vpi->length_buf); + vpi->nb_bits = le_to_h_u32(vpi->nb_bits_buf); 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 + * @param trst 1 if TRST is to be asserted + * @param srst 1 if SRST is to be asserted */ static int jtag_vpi_reset(int trst, int srst) { struct vpi_cmd vpi; + memset(&vpi, 0, sizeof(struct vpi_cmd)); vpi.cmd = CMD_RESET; vpi.length = 0; @@ -91,12 +212,12 @@ static int jtag_vpi_reset(int trst, int srst) /** * 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) + * @param bits TMS bits to be written (bit0, bit1 .. bitN) + * @param 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 + * Write a series 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. */ @@ -105,6 +226,7 @@ static int jtag_vpi_tms_seq(const uint8_t *bits, int nb_bits) struct vpi_cmd vpi; int nb_bytes; + memset(&vpi, 0, sizeof(struct vpi_cmd)); nb_bytes = DIV_ROUND_UP(nb_bits, 8); vpi.cmd = CMD_TMS_SEQ; @@ -117,11 +239,11 @@ static int jtag_vpi_tms_seq(const uint8_t *bits, int nb_bits) /** * jtag_vpi_path_move - ask a TMS sequence transition to JTAG - * @cmd: path transition + * @param 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 + * Write a series 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. */ @@ -143,7 +265,7 @@ static int jtag_vpi_path_move(struct pathmove_command *cmd) /** * jtag_vpi_tms - ask a tms command - * @cmd: tms command + * @param cmd tms command */ static int jtag_vpi_tms(struct tms_command *cmd) { @@ -172,6 +294,8 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift) struct vpi_cmd vpi; int nb_bytes = DIV_ROUND_UP(nb_bits, 8); + memset(&vpi, 0, sizeof(struct vpi_cmd)); + vpi.cmd = tap_shift ? CMD_SCAN_CHAIN_FLIP_TMS : CMD_SCAN_CHAIN; if (bits) @@ -190,6 +314,15 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift) if (retval != ERROR_OK) return retval; + /* Optional low-level JTAG debug */ + if (LOG_LEVEL_IS(LOG_LVL_DEBUG_IO)) { + char *char_buf = buf_to_hex_str(vpi.buffer_in, + (nb_bits > DEBUG_JTAG_IOZ) ? DEBUG_JTAG_IOZ : nb_bits); + LOG_DEBUG_IO("recvd JTAG VPI data: nb_bits=%d, buf_in=0x%s%s", + nb_bits, char_buf, (nb_bits > DEBUG_JTAG_IOZ) ? "(...)" : ""); + free(char_buf); + } + if (bits) memcpy(bits, vpi.buffer_in, nb_bytes); @@ -198,8 +331,9 @@ static int jtag_vpi_queue_tdi_xfer(uint8_t *bits, int nb_bits, int tap_shift) /** * 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 + * @param bits bits to be queued on TDI (or NULL if 0 are to be queued) + * @param nb_bits number of bits + * @param tap_shift */ static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift) { @@ -228,7 +362,7 @@ static int jtag_vpi_queue_tdi(uint8_t *bits, int nb_bits, int tap_shift) /** * jtag_vpi_clock_tms - clock a TMS transition - * @tms: the TMS to be sent + * @param tms the TMS to be sent * * Triggers a TMS transition (ie. one JTAG TAP state move). */ @@ -242,11 +376,11 @@ static int jtag_vpi_clock_tms(int tms) /** * jtag_vpi_scan - launches a DR-scan or IR-scan - * @cmd: the command to launch + * @param 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. + * Returns ERROR_OK if OK, ERROR_xxx if a read/write error occurred. */ static int jtag_vpi_scan(struct scan_command *cmd) { @@ -295,8 +429,7 @@ static int jtag_vpi_scan(struct scan_command *cmd) if (retval != ERROR_OK) return retval; - if (buf) - free(buf); + free(buf); if (cmd->end_state != TAP_DRSHIFT) { retval = jtag_vpi_state_move(cmd->end_state); @@ -315,7 +448,7 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state) if (retval != ERROR_OK) return retval; - retval = jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT); + retval = jtag_vpi_queue_tdi(NULL, cycles, NO_TAP_SHIFT); if (retval != ERROR_OK) return retval; @@ -324,7 +457,27 @@ static int jtag_vpi_runtest(int cycles, tap_state_t state) static int jtag_vpi_stableclocks(int cycles) { - return jtag_vpi_queue_tdi(NULL, cycles, TAP_SHIFT); + uint8_t tms_bits[4]; + int cycles_remain = cycles; + int nb_bits; + int retval; + const int CYCLES_ONE_BATCH = sizeof(tms_bits) * 8; + + assert(cycles >= 0); + + /* use TMS=1 in TAP RESET state, TMS=0 in all other stable states */ + memset(&tms_bits, (tap_get_state() == TAP_RESET) ? 0xff : 0x00, sizeof(tms_bits)); + + /* send the TMS bits */ + while (cycles_remain > 0) { + nb_bits = (cycles_remain < CYCLES_ONE_BATCH) ? cycles_remain : CYCLES_ONE_BATCH; + retval = jtag_vpi_tms_seq(tms_bits, nb_bits); + if (retval != ERROR_OK) + return retval; + cycles_remain -= nb_bits; + } + + return ERROR_OK; } static int jtag_vpi_execute_queue(void) @@ -332,7 +485,7 @@ 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; + for (cmd = jtag_command_queue; retval == ERROR_OK && cmd; cmd = cmd->next) { switch (cmd->type) { case JTAG_RESET: @@ -360,6 +513,11 @@ static int jtag_vpi_execute_queue(void) case JTAG_SCAN: retval = jtag_vpi_scan(cmd->cmd.scan); break; + default: + LOG_ERROR("BUG: unknown JTAG command type 0x%X", + cmd->type); + retval = ERROR_FAIL; + break; } } @@ -368,9 +526,11 @@ static int jtag_vpi_execute_queue(void) static int jtag_vpi_init(void) { + int flag = 1; + sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { - LOG_ERROR("Could not create socket"); + LOG_ERROR("jtag_vpi: Could not create client socket"); return ERROR_FAIL; } @@ -380,84 +540,145 @@ static int jtag_vpi_init(void) serv_addr.sin_port = htons(server_port); if (!server_address) - server_address = strdup(SERVER_ADDRESS); + server_address = strdup(DEFAULT_SERVER_ADDRESS); serv_addr.sin_addr.s_addr = inet_addr(server_address); if (serv_addr.sin_addr.s_addr == INADDR_NONE) { - LOG_ERROR("inet_addr error occured"); + LOG_ERROR("jtag_vpi: inet_addr error occurred"); 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); + LOG_ERROR("jtag_vpi: 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); + if (serv_addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + /* This increases performance dramatically for local + * connections, which is the most likely arrangement + * for a VPI connection. */ + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + } + + LOG_INFO("jtag_vpi: Connection to %s : %u successful", server_address, server_port); return ERROR_OK; } +static int jtag_vpi_stop_simulation(void) +{ + struct vpi_cmd cmd; + memset(&cmd, 0, sizeof(struct vpi_cmd)); + cmd.length = 0; + cmd.nb_bits = 0; + cmd.cmd = CMD_STOP_SIMU; + return jtag_vpi_send_cmd(&cmd); +} + static int jtag_vpi_quit(void) { + if (stop_sim_on_exit) { + if (jtag_vpi_stop_simulation() != ERROR_OK) + LOG_WARNING("jtag_vpi: failed to send \"stop simulation\" command"); + } + if (close_socket(sockfd) != 0) { + LOG_WARNING("jtag_vpi: could not close jtag_vpi client socket"); + log_socket_error("jtag_vpi"); + } free(server_address); - return close(sockfd); + return ERROR_OK; } 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); + if (CMD_ARGC == 0) { + LOG_ERROR("Command \"jtag_vpi set_port\" expects 1 argument (TCP port number)"); + return ERROR_COMMAND_SYNTAX_ERROR; + } - LOG_INFO("Set server port to %u", server_port); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port); + LOG_INFO("jtag_vpi: server port set to %u", server_port); return ERROR_OK; } COMMAND_HANDLER(jtag_vpi_set_address) { - free(server_address); if (CMD_ARGC == 0) { - LOG_WARNING("You need to set an address"); - server_address = strdup(SERVER_ADDRESS); - } else - server_address = strdup(CMD_ARGV[0]); + LOG_ERROR("Command \"jtag_vpi set_address\" expects 1 argument (IP address)"); + return ERROR_COMMAND_SYNTAX_ERROR; + } - LOG_INFO("Set server address to %s", server_address); + free(server_address); + server_address = strdup(CMD_ARGV[0]); + LOG_INFO("jtag_vpi: server address set to %s", server_address); return ERROR_OK; } -static const struct command_registration jtag_vpi_command_handlers[] = { +COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command \"jtag_vpi stop_sim_on_exit\" expects 1 argument (on|off)"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], stop_sim_on_exit); + return ERROR_OK; +} + +static const struct command_registration jtag_vpi_subcommand_handlers[] = { { - .name = "jtag_vpi_set_port", + .name = "set_port", .handler = &jtag_vpi_set_port, .mode = COMMAND_CONFIG, - .help = "set the port of the VPI server", - .usage = "description_string", + .help = "set the TCP port number of the jtag_vpi server (default: 5555)", + .usage = "tcp_port_num", }, { - .name = "jtag_vpi_set_address", + .name = "set_address", .handler = &jtag_vpi_set_address, .mode = COMMAND_CONFIG, - .help = "set the address of the VPI server", - .usage = "description_string", + .help = "set the IP address of the jtag_vpi server (default: 127.0.0.1)", + .usage = "ipv4_addr", + }, + { + .name = "stop_sim_on_exit", + .handler = &jtag_vpi_stop_sim_on_exit_handler, + .mode = COMMAND_CONFIG, + .help = "Configure if simulation stop command shall be sent " + "before OpenOCD exits (default: off)", + .usage = "", }, COMMAND_REGISTRATION_DONE }; -struct jtag_interface jtag_vpi_interface = { - .name = "jtag_vpi", +static const struct command_registration jtag_vpi_command_handlers[] = { + { + .name = "jtag_vpi", + .mode = COMMAND_ANY, + .help = "perform jtag_vpi management", + .chain = jtag_vpi_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static struct jtag_interface jtag_vpi_interface = { .supported = DEBUG_CAP_TMS_SEQ, - .commands = jtag_vpi_command_handlers, + .execute_queue = jtag_vpi_execute_queue, +}; + +struct adapter_driver jtag_vpi_adapter_driver = { + .name = "jtag_vpi", .transports = jtag_only, + .commands = jtag_vpi_command_handlers, .init = jtag_vpi_init, .quit = jtag_vpi_quit, - .execute_queue = jtag_vpi_execute_queue, + + .jtag_ops = &jtag_vpi_interface, };