jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / jtag / drivers / jtag_vpi.c
index 1a42b3a05f41b910603424cd703143f98c4f8011..9dec0d19df7396090308ac58cc635e121116e6f4 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <arpa/inet.h>
 #endif
 
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#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
 
 #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 "<unknown>";
+       }
+}
+
 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=<new_state>, TDI=<???>
- *  - writing out TCK=1, TMS=<new_state>, TDI=<???> which triggers the transition
+ * Write a series of TMS transitions, where each transition consists in :
+ *  - writing out TCK=0, TMS=\<new_state>, TDI=\<???>
+ *  - writing out TCK=1, TMS=\<new_state>, 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=<new_state>, TDI=<???>
- *  - writing out TCK=1, TMS=<new_state>, TDI=<???> which triggers the transition
+ * Write a series of TMS transitions, where each transition consists in :
+ *  - writing out TCK=0, TMS=\<new_state>, TDI=\<???>
+ *  - writing out TCK=1, TMS=\<new_state>, 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,15 +457,35 @@ 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)
+static int jtag_vpi_execute_queue(struct jtag_command *cmd_queue)
 {
        struct jtag_command *cmd;
        int retval = ERROR_OK;
 
-       for (cmd = jtag_command_queue; retval == ERROR_OK && cmd != NULL;
+       for (cmd = cmd_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,139 @@ 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);
+               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)
 {
+
+       if (CMD_ARGC == 0)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        free(server_address);
+       server_address = strdup(CMD_ARGV[0]);
+       LOG_INFO("jtag_vpi: server address set to %s", 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]);
+       return ERROR_OK;
+}
 
-       LOG_INFO("Set server address to %s", server_address);
+COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler)
+{
+       if (CMD_ARGC != 1)
+               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_command_handlers[] = {
+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 = "<on|off>",
        },
        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,
 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)