This is an automated email from Gerrit.

Jan Matyas (mat...@codasip.com) just uploaded a new patch set to Gerrit, which 
you can find at http://openocd.zylin.com/5412

-- gerrit

commit 45c400bb2a3e8c20374e87986e391f7175bcd2b7
Author: Jan Matyas <mat...@codasip.com>
Date:   Thu Jan 23 07:54:34 2020 +0100

    jtag_vpi: added timeout for socket receive
    
    Adding timeout for socket receive operations in jtag_vpi driver.
    Should the jtag_vpi server got broken and stopped responding (for
    any reason), we need to have a means how to detect the situation
    and report to the user - hence the introduction of the timeout.
    
    The default jtag_vpi recv timeout is set to a very large (safe)
    value - 15 sec - which is many orders of magnitude greater than
    the typical exchange rate over the jtag_vpi socket (milliseconds).
    Exceeding such an timeout always means a problem - either
    the jtag_vpi server (RTL simulation) is terribly slow, or it
    broke down and stopped responding for good.
    
    TCL command "jtag_vpi_set_recv_timeout" has been also added to
    make the timeout configurable, should the user ever need to
    change it.
    
    Change-Id: Ide2c5a52c86f1fc72febcc4bd115fa87f3e307ad
    Signed-off-by: Jan Matyas <mat...@codasip.com>

diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c
index 6abc702..4b5795a 100644
--- a/src/jtag/drivers/jtag_vpi.c
+++ b/src/jtag/drivers/jtag_vpi.c
@@ -33,6 +33,7 @@
 #include <netinet/tcp.h>
 #endif
 
+#include <helper/time_support.h>
 #include <string.h>
 
 #define NO_TAP_SHIFT   0
@@ -49,6 +50,10 @@
 #define CMD_SCAN_CHAIN_FLIP_TMS        3
 #define CMD_STOP_SIMU          4
 
+#define RECV_DEFAULT_TIMEOUT_SEC   15
+#define RECV_TIMEOUT_WARN_PERCENT  30  /* warn if XX % of the timeout is 
reached */
+#define RECV_TIMEOUT_MAX_WARNINGS  16
+
 /* jtag_vpi server port and address to connect to */
 static int server_port = SERVER_PORT;
 static char *server_address;
@@ -56,6 +61,16 @@ static char *server_address;
 /* Send CMD_STOP_SIMU to server when OpenOCD exits? */
 static bool stop_sim_on_exit;
 
+/* Maximum wait time for reception of a message from jtag_vpi server */
+static int recv_timeout_sec = RECV_DEFAULT_TIMEOUT_SEC;
+
+/* Threshold for wait time after which a warning will be printed. */
+static int64_t recv_warn_threshold_ms = (RECV_DEFAULT_TIMEOUT_SEC * 1000)
+       * RECV_TIMEOUT_WARN_PERCENT / 100;
+
+/* Number of timeout warnings emitted so far (avoid printing too many of 
them). */
+static int num_recv_warnings;
+
 static int sockfd;
 static struct sockaddr_in serv_addr;
 
@@ -172,8 +187,71 @@ static int jtag_vpi_receive_cmd(struct vpi_cmd *vpi)
 {
        unsigned bytes_buffered = 0;
        while (bytes_buffered < sizeof(struct vpi_cmd)) {
+               /* How many bytes left to receive? */
                int bytes_to_receive = sizeof(struct vpi_cmd) - bytes_buffered;
-               int retval = read_socket(sockfd, ((char *)vpi) + 
bytes_buffered, bytes_to_receive);
+
+               /* Use select() to wait for incoming data just for a limited 
period of time. */
+               const int64_t wait_start = timeval_ms();
+               fd_set read_fds;
+               FD_ZERO(&read_fds);
+               FD_SET(sockfd, &read_fds);
+               struct timeval tv;
+               tv.tv_usec = 0;
+               tv.tv_sec = recv_timeout_sec;
+               int retval = socket_select(sockfd + 1, &read_fds, NULL, NULL, 
&tv);
+               if (retval < 0) {
+                       /* select() failed */
+#ifdef _WIN32
+                       int wsa_err = WSAGetLastError();
+                       if (wsa_err == WSAEINTR) {
+                               /* Wait interrupted by WSACancelBlockingCall(). 
Keep waiting. */
+                               continue;
+                       }
+#else
+                       if (errno == EINTR) {
+                               /* Wait interrupted by a signal. Keep waiting. 
*/
+                               continue;
+                       }
+#endif
+                       else {
+                               /* This should not happen. It would mean that 
select() failed miserably.
+                                * Jtag_vpi protocol cannot recover from it, so 
exit. */
+                               LOG_ERROR("Error when waiting for jtag_vpi 
socket readiness "
+                                               "(select failed, errno=%d)", 
errno);
+                               exit(-1);
+                       }
+               } else if (retval == 0) {
+                       /* select() timed out.
+                          This should hardly ever happen and if it does, it 
always indicates a problem.
+                          Most likely the performance of the jtag_vpi server 
(RTL simulation)
+                          is extremely low. Or the server may have broken down 
and stopped
+                          responding for good. */
+                       LOG_ERROR("Timeout when waiting for response from 
jtag_vpi server. "
+                                       "(>%d seconds)", recv_timeout_sec);
+                       LOG_INFO("Hint: Command 'jtag_vpi_set_recv_timeout' can 
be used to adjust the "
+                                       "jtag_vpi timeout.");
+
+                       /* Jtag_vpi protocol, as designed, cannot recover from 
this.
+                        * The best we can do is to terminate and notify the 
user. */
+                       exit(-1);
+               }
+               /* Otherwise select() succeeded. New data is available at the 
socket. */
+               /* Just warn the user if the data arrived with a long delay. */
+               int64_t wait_duration = timeval_ms() - wait_start;
+               if (wait_duration > recv_warn_threshold_ms) {
+                       if (num_recv_warnings < RECV_TIMEOUT_MAX_WARNINGS) {
+                               /* Early warning to the user that the 
performance of jtag_vpi server
+                                * is too low. */
+                               LOG_WARNING("Slow response from jtag_vpi: %.1f 
sec(s). (Low performance of "
+                                               "jtag_vpi server?)", 
(double)(wait_duration/1000.0));
+                               num_recv_warnings++;
+                               if (num_recv_warnings == 
RECV_TIMEOUT_MAX_WARNINGS)
+                                       LOG_WARNING("Too many slow responses 
about jtag_vpi. Suppressing further warnings.");
+                       }
+               }
+
+               /* Read the data from the socket. */
+               retval = read_socket(sockfd, ((char *)vpi) + bytes_buffered, 
bytes_to_receive);
                if (retval < 0) {
 #ifdef _WIN32
                        int wsa_err = WSAGetLastError();
@@ -199,7 +277,8 @@ static int jtag_vpi_receive_cmd(struct vpi_cmd *vpi)
                bytes_buffered += retval;
        }
 
-       /* Use little endian when transmitting/receiving jtag_vpi cmds. */
+       /* Handle endianness of received data.
+        * Jtag_vpi datagrams are transmitted in little endian over network. */
        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);
@@ -638,6 +717,27 @@ COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler)
        return ERROR_OK;
 }
 
+COMMAND_HANDLER(jtag_vpi_set_recv_timeout_handler)
+{
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("jtag_vpi_set_recv_timeout expects 1 argument");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       } else {
+               int val;
+               COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], val);
+               if (val < 1) {
+                       LOG_ERROR("The argument of 
\"jtag_vpi_set_recv_timeout\" must be a positive integer");
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+               /* Hard receive timeout - store the new value given by user */
+               recv_timeout_sec = val;
+               /* Soft receive timeout (just for warnings) - recompute the 
value */
+               recv_warn_threshold_ms = (recv_timeout_sec * 1000)
+                       * RECV_TIMEOUT_WARN_PERCENT / 100;
+       }
+       return ERROR_OK;
+}
+
 static const struct command_registration jtag_vpi_command_handlers[] = {
        {
                .name = "jtag_vpi_set_port",
@@ -661,6 +761,13 @@ static const struct command_registration 
jtag_vpi_command_handlers[] = {
                        "before OpenOCD exits (default: off)",
                .usage = "<on|off>",
        },
+       {
+               .name = "jtag_vpi_set_recv_timeout",
+               .handler = &jtag_vpi_set_recv_timeout_handler,
+               .mode = COMMAND_CONFIG,
+               .help = "Set the timeout for responses from jtag_vpi 
connection",
+               .usage = "<seconds>",
+       },
        COMMAND_REGISTRATION_DONE
 };
 

-- 


_______________________________________________
OpenOCD-devel mailing list
OpenOCD-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to