ARM|Driver: Add DPI Driver for emulation 73/5573/7
authorKevin Burke <kevinb@os.amperecomputing.com>
Sat, 4 Apr 2020 00:47:50 +0000 (20:47 -0400)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 3 Oct 2020 10:21:51 +0000 (11:21 +0100)
This driver provides support for Cadence JTAG BFM

The "jtag_dpi" driver implements a JTAG driver acting as a client for the
SystemVerilog Direct Programming Interface (DPI) for JTAG devices.
DPI allows OpenOCD to connect to the JTAG interface of a hardware model
written in SystemVerilog, for example, on an emulation model of
target hardware.

Tested on Ampere emulation with Altra and Altra Max models

Change-Id: Iaef8ba5cc1398ee2c888f39a606e8cb592484625
Signed-off-by: Kevin Burke <kevinb@os.amperecomputing.com>
Signed-off-by: Daniel Goehring <dgoehrin@os.amperecomputing.com>
Reviewed-on: http://openocd.zylin.com/5573
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
configure.ac
doc/openocd.texi
src/jtag/drivers/Makefile.am
src/jtag/drivers/jtag_dpi.c [new file with mode: 0644]
src/jtag/interfaces.c
tcl/interface/jtag_dpi.cfg [new file with mode: 0644]

index fc6734cf8bdf361d17387c2c07a1bbfcf3125035..569816637ff853ae33a4d838fe25c550a93c2e0b 100644 (file)
@@ -267,6 +267,10 @@ 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([jtag_dpi],
+  AS_HELP_STRING([--enable-jtag_dpi], [Enable building support for JTAG DPI]),
+  [build_jtag_dpi=$enableval], [build_jtag_dpi=no])
+
 AC_ARG_ENABLE([amtjtagaccel],
   AS_HELP_STRING([--enable-amtjtagaccel], [Enable building the Amontec JTAG-Accelerator driver]),
   [build_amtjtagaccel=$enableval], [build_amtjtagaccel=no])
@@ -569,6 +573,13 @@ AS_IF([test "x$build_jtag_vpi" = "xyes"], [
   AC_DEFINE([BUILD_JTAG_VPI], [0], [0 if you don't want JTAG VPI.])
 ])
 
+AS_IF([test "x$build_jtag_dpi" = "xyes"], [
+  AC_DEFINE([BUILD_JTAG_DPI], [1], [1 if you want JTAG DPI.])
+], [
+  AC_DEFINE([BUILD_JTAG_DPI], [0], [0 if you don't want JTAG DPI.])
+])
+
+
 AS_IF([test "x$build_amtjtagaccel" = "xyes"], [
   AC_DEFINE([BUILD_AMTJTAGACCEL], [1], [1 if you want the Amontec JTAG-Accelerator driver.])
 ], [
@@ -746,6 +757,7 @@ AM_CONDITIONAL([BCM2835GPIO], [test "x$build_bcm2835gpio" = "xyes"])
 AM_CONDITIONAL([IMX_GPIO], [test "x$build_imx_gpio" = "xyes"])
 AM_CONDITIONAL([BITBANG], [test "x$build_bitbang" = "xyes"])
 AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes" -o "x$build_jtag_vpi" = "xyes"])
+AM_CONDITIONAL([JTAG_DPI], [test "x$build_jtag_dpi" = "xyes" -o "x$build_jtag_dpi" = "xyes"])
 AM_CONDITIONAL([USB_BLASTER_DRIVER], [test "x$enable_usb_blaster" != "xno" -o "x$enable_usb_blaster_2" != "xno"])
 AM_CONDITIONAL([AMTJTAGACCEL], [test "x$build_amtjtagaccel" = "xyes"])
 AM_CONDITIONAL([GW16012], [test "x$build_gw16012" = "xyes"])
index 317f188d2dc0b7fd816d14623a4645b3156928fb..3506ccab1c3ea31b2a8529010e0f855da90cfd0f 100644 (file)
@@ -615,6 +615,12 @@ produced, PDF schematics are easily found and it is easy to make.
 @* A JTAG driver acting as a client for the JTAG VPI server interface.
 @* Link: @url{http://github.com/fjullien/jtag_vpi}
 
+@item @b{jtag_dpi}
+@* A JTAG driver acting as a client for the SystemVerilog Direct Programming
+Interface (DPI) for JTAG devices. DPI allows OpenOCD to connect to the JTAG
+interface of a hardware model written in SystemVerilog, for example, on an
+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.
 
@@ -3235,6 +3241,22 @@ This value is only used with the standard variant.
 @end deffn
 @end deffn
 
+
+@deffn {Interface Driver} {jtag_dpi}
+SystemVerilog Direct Programming Interface (DPI) compatible driver for
+JTAG devices in emulation. The driver acts as a client for the SystemVerilog
+DPI server interface.
+
+@deffn {Config Command} {jtag_dpi_set_port} port
+Specifies the TCP/IP port number of the SystemVerilog DPI server interface.
+@end deffn
+
+@deffn {Config Command} {jtag_dpi_set_address} address
+Specifies the TCP/IP address of the SystemVerilog DPI server interface.
+@end deffn
+@end deffn
+
+
 @section Transport Configuration
 @cindex Transport
 As noted earlier, depending on the version of OpenOCD you use,
index c860833b3178894aa88d19032d99788b6b115c8b..e8d20ccf8914db4718a232be15937a5c2bdebf79 100644 (file)
@@ -82,6 +82,9 @@ endif
 if JTAG_VPI
 DRIVERFILES += %D%/jtag_vpi.c
 endif
+if JTAG_DPI
+DRIVERFILES += %D%/jtag_dpi.c
+endif
 if USB_BLASTER_DRIVER
 %C%_libocdjtagdrivers_la_LIBADD += %D%/usb_blaster/libocdusbblaster.la
 include %D%/usb_blaster/Makefile.am
diff --git a/src/jtag/drivers/jtag_dpi.c b/src/jtag/drivers/jtag_dpi.c
new file mode 100644 (file)
index 0000000..575c6bc
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * JTAG to DPI driver
+ *
+ * Copyright (C) 2013 Franck Jullien, <elec4fun@gmail.com>
+ *
+ * Copyright (C) 2019-2020, Ampere Computing LLC
+ *
+ * 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 "config.h"
+#endif
+
+#include <jtag/interface.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#endif
+
+#define SERVER_ADDRESS "127.0.0.1"
+#define SERVER_PORT    5555
+
+static uint16_t server_port = SERVER_PORT;
+static char *server_address;
+
+static int sockfd;
+static struct sockaddr_in serv_addr;
+
+static uint8_t *last_ir_buf;
+static int last_ir_num_bits;
+
+static int write_sock(char *buf, size_t len)
+{
+       if (buf == NULL) {
+               LOG_ERROR("%s: NULL 'buf' argument, file %s, line %d",
+                       __func__, __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+       if (write(sockfd, buf, len) != (ssize_t)len) {
+               LOG_ERROR("%s: %s, file %s, line %d", __func__,
+                       strerror(errno), __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+static int read_sock(char *buf, size_t len)
+{
+       if (buf == NULL) {
+               LOG_ERROR("%s: NULL 'buf' argument, file %s, line %d",
+                       __func__, __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+       if (read(sockfd, buf, len) != (ssize_t)len) {
+               LOG_ERROR("%s: %s, file %s, line %d", __func__,
+                       strerror(errno), __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+/**
+ * jtag_dpi_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_dpi_reset(int trst, int srst)
+{
+       char *buf = "reset\n";
+       int ret = ERROR_OK;
+
+       LOG_DEBUG_IO("JTAG DRIVER DEBUG: reset trst: %i srst %i", trst, srst);
+
+       if (trst == 1) {
+               /* reset the JTAG TAP controller */
+               ret = write_sock(buf, strlen(buf));
+               if (ret != ERROR_OK) {
+                       LOG_ERROR("write_sock() fail, file %s, line %d",
+                               __FILE__, __LINE__);
+               }
+       }
+
+       if (srst == 1) {
+               /* System target reset not supported */
+               LOG_ERROR("DPI SRST not supported");
+               ret = ERROR_FAIL;
+       }
+
+       return ret;
+}
+
+/**
+ * jtag_dpi_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_dpi_scan(struct scan_command *cmd)
+{
+       char buf[20];
+       uint8_t *data_buf;
+       int num_bits, bytes;
+       int ret = ERROR_OK;
+
+       num_bits = jtag_build_buffer(cmd, &data_buf);
+       if (data_buf == NULL) {
+               LOG_ERROR("jtag_build_buffer call failed, data_buf == NULL, "
+                       "file %s, line %d", __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+
+       bytes = DIV_ROUND_UP(num_bits, 8);
+       if (cmd->ir_scan) {
+               free(last_ir_buf);
+               last_ir_buf = (uint8_t *)malloc(bytes * sizeof(uint8_t));
+               if (last_ir_buf == NULL) {
+                       LOG_ERROR("%s: malloc fail, file %s, line %d",
+                               __func__, __FILE__, __LINE__);
+                       ret = ERROR_FAIL;
+                       goto out;
+               }
+               memcpy(last_ir_buf, data_buf, bytes);
+               last_ir_num_bits = num_bits;
+       }
+       snprintf(buf, sizeof(buf), "%s %d\n", cmd->ir_scan ? "ib" : "db", num_bits);
+       ret = write_sock(buf, strlen(buf));
+       if (ret != ERROR_OK) {
+               LOG_ERROR("write_sock() fail, file %s, line %d",
+                       __FILE__, __LINE__);
+               goto out;
+       }
+       ret = write_sock((char *)data_buf, bytes);
+       if (ret != ERROR_OK) {
+               LOG_ERROR("write_sock() fail, file %s, line %d",
+                       __FILE__, __LINE__);
+               goto out;
+       }
+       ret = read_sock((char *)data_buf, bytes);
+       if (ret != ERROR_OK) {
+               LOG_ERROR("read_sock() fail, file %s, line %d",
+                       __FILE__, __LINE__);
+               goto out;
+       }
+
+       ret = jtag_read_buffer(data_buf, cmd);
+       if (ret != ERROR_OK) {
+               LOG_ERROR("jtag_read_buffer() fail, file %s, line %d",
+                       __FILE__, __LINE__);
+               goto out;
+       }
+
+out:
+       free(data_buf);
+       return ret;
+}
+
+static int jtag_dpi_runtest(int cycles)
+{
+       char buf[20];
+       uint8_t *data_buf = last_ir_buf, *read_scan;
+       int num_bits = last_ir_num_bits, bytes;
+       int ret = ERROR_OK;
+
+       if (data_buf == NULL) {
+               LOG_ERROR("%s: NULL 'data_buf' argument, file %s, line %d",
+                       __func__, __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+       if (num_bits <= 0) {
+               LOG_ERROR("%s: 'num_bits' invalid value, file %s, line %d",
+                       __func__, __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+
+       bytes = DIV_ROUND_UP(num_bits, 8);
+       read_scan = (uint8_t *)malloc(bytes * sizeof(uint8_t));
+       if (read_scan == NULL) {
+               LOG_ERROR("%s: malloc fail, file %s, line %d",
+                       __func__, __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+       snprintf(buf, sizeof(buf), "ib %d\n", num_bits);
+       while (cycles > 0) {
+               ret = write_sock(buf, strlen(buf));
+               if (ret != ERROR_OK) {
+                       LOG_ERROR("write_sock() fail, file %s, line %d",
+                               __FILE__, __LINE__);
+                       goto out;
+               }
+               ret = write_sock((char *)data_buf, bytes);
+               if (ret != ERROR_OK) {
+                       LOG_ERROR("write_sock() fail, file %s, line %d",
+                               __FILE__, __LINE__);
+                       goto out;
+               }
+               ret = read_sock((char *)read_scan, bytes);
+               if (ret != ERROR_OK) {
+                       LOG_ERROR("read_sock() fail, file %s, line %d",
+                               __FILE__, __LINE__);
+                       goto out;
+               }
+
+               cycles -= num_bits + 6;
+       }
+
+out:
+       free(read_scan);
+       return ret;
+}
+
+static int jtag_dpi_stableclocks(int cycles)
+{
+       return jtag_dpi_runtest(cycles);
+}
+
+static int jtag_dpi_execute_queue(void)
+{
+       struct jtag_command *cmd;
+       int ret = ERROR_OK;
+
+       for (cmd = jtag_command_queue; ret == ERROR_OK && cmd != NULL;
+            cmd = cmd->next) {
+               switch (cmd->type) {
+               case JTAG_RUNTEST:
+                       ret = jtag_dpi_runtest(cmd->cmd.runtest->num_cycles);
+                       break;
+               case JTAG_STABLECLOCKS:
+                       ret = jtag_dpi_stableclocks(cmd->cmd.stableclocks->num_cycles);
+                       break;
+               case JTAG_TLR_RESET:
+                       /* Enter Test-Logic-Reset state by asserting TRST */
+                       if (cmd->cmd.statemove->end_state == TAP_RESET)
+                               jtag_dpi_reset(1, 0);
+                       break;
+               case JTAG_PATHMOVE:
+                       /* unsupported */
+                       break;
+               case JTAG_TMS:
+                       /* unsupported */
+                       break;
+               case JTAG_SLEEP:
+                       jtag_sleep(cmd->cmd.sleep->us);
+                       break;
+               case JTAG_SCAN:
+                       ret = jtag_dpi_scan(cmd->cmd.scan);
+                       break;
+               default:
+                       LOG_ERROR("BUG: unknown JTAG command type 0x%X",
+                                 cmd->type);
+                       ret = ERROR_FAIL;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int jtag_dpi_init(void)
+{
+       sockfd = socket(AF_INET, SOCK_STREAM, 0);
+       if (sockfd < 0) {
+               LOG_ERROR("socket: %s, function %s, file %s, line %d",
+                       strerror(errno), __func__, __FILE__, __LINE__);
+               return ERROR_FAIL;
+       }
+
+       memset(&serv_addr, 0, sizeof(serv_addr));
+
+       serv_addr.sin_family = AF_INET;
+       serv_addr.sin_port = htons(server_port);
+
+       if (server_address == NULL) {
+               server_address = strdup(SERVER_ADDRESS);
+               if (server_address == NULL) {
+                       LOG_ERROR("%s: strdup fail, file %s, line %d",
+                               __func__, __FILE__, __LINE__);
+                       return ERROR_FAIL;
+               }
+       }
+
+       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");
+               return ERROR_FAIL;
+       }
+
+       if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+               close(sockfd);
+               LOG_ERROR("Can't connect to %s : %" PRIu16, server_address, server_port);
+               return ERROR_FAIL;
+       }
+       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 DPI connection. */
+               int flag = 1;
+               setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
+       }
+
+       LOG_INFO("Connection to %s : %" PRIu16 " succeed", server_address, server_port);
+
+       return ERROR_OK;
+}
+
+static int jtag_dpi_quit(void)
+{
+       free(server_address);
+       server_address = NULL;
+
+       return close(sockfd);
+}
+
+COMMAND_HANDLER(jtag_dpi_set_port)
+{
+       if (CMD_ARGC > 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       else if (CMD_ARGC == 0)
+               LOG_INFO("Using server port %" PRIu16, server_port);
+       else {
+               COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], server_port);
+               LOG_INFO("Set server port to %" PRIu16, server_port);
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(jtag_dpi_set_address)
+{
+       if (CMD_ARGC > 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       else if (CMD_ARGC == 0) {
+               if (server_address == NULL) {
+                       server_address = strdup(SERVER_ADDRESS);
+                       if (server_address == NULL) {
+                               LOG_ERROR("%s: strdup fail, file %s, line %d",
+                                       __func__, __FILE__, __LINE__);
+                               return ERROR_FAIL;
+                       }
+               }
+               LOG_INFO("Using server address %s", server_address);
+       } else {
+               free(server_address);
+               server_address = strdup(CMD_ARGV[0]);
+               if (server_address == NULL) {
+                       LOG_ERROR("%s: strdup fail, file %s, line %d",
+                               __func__, __FILE__, __LINE__);
+                       return ERROR_FAIL;
+               }
+               LOG_INFO("Set server address to %s", server_address);
+       }
+
+       return ERROR_OK;
+}
+
+static const struct command_registration jtag_dpi_command_handlers[] = {
+       {
+               .name = "jtag_dpi_set_port",
+               .handler = &jtag_dpi_set_port,
+               .mode = COMMAND_CONFIG,
+               .help = "set the port of the DPI server",
+               .usage = "[port]",
+       },
+       {
+               .name = "jtag_dpi_set_address",
+               .handler = &jtag_dpi_set_address,
+               .mode = COMMAND_CONFIG,
+               .help = "set the address of the DPI server",
+               .usage = "[address]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+static struct jtag_interface jtag_dpi_interface = {
+       .supported = DEBUG_CAP_TMS_SEQ,
+       .execute_queue = jtag_dpi_execute_queue,
+};
+
+struct adapter_driver jtag_dpi_adapter_driver = {
+       .name = "jtag_dpi",
+       .transports = jtag_only,
+       .commands = jtag_dpi_command_handlers,
+       .init = jtag_dpi_init,
+       .quit = jtag_dpi_quit,
+       .reset = jtag_dpi_reset,
+       .jtag_ops = &jtag_dpi_interface,
+};
index 45e30c9b0418c79dd695509a802c58d4e682db1c..2fa53be2b68283f6377baf8e6cd2d00e04cf9e36 100644 (file)
@@ -12,6 +12,8 @@
  *   Copyright (C) 2009 Zachary T Welch                                    *
  *   zw@superlucidity.net                                                  *
  *                                                                         *
+ *   Copyright (C) 2020, Ampere Computing LLC                              *
+ *                                                                         *
  *   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     *
@@ -60,6 +62,9 @@ extern struct adapter_driver usb_blaster_adapter_driver;
 #if BUILD_JTAG_VPI == 1
 extern struct adapter_driver jtag_vpi_adapter_driver;
 #endif
+#if BUILD_JTAG_DPI == 1
+extern struct adapter_driver jtag_dpi_adapter_driver;
+#endif
 #if BUILD_FT232R == 1
 extern struct adapter_driver ft232r_adapter_driver;
 #endif
@@ -177,6 +182,9 @@ struct adapter_driver *adapter_drivers[] = {
 #if BUILD_JTAG_VPI == 1
                &jtag_vpi_adapter_driver,
 #endif
+#if BUILD_JTAG_DPI == 1
+               &jtag_dpi_adapter_driver,
+#endif
 #if BUILD_FT232R == 1
                &ft232r_adapter_driver,
 #endif
diff --git a/tcl/interface/jtag_dpi.cfg b/tcl/interface/jtag_dpi.cfg
new file mode 100644 (file)
index 0000000..e43386d
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Provide support for the Cadence JTAG BFM
+#
+# Copyright (c) 2020, Ampere Computing LLC
+#
+# 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;
+#
+#
+
+adapter driver jtag_dpi
+
+# Set the DPI JTAG server port
+if { [info exists DPI_PORT] } {
+   set _DPI_PORT $DPI_PORT
+} else {
+   set _DPI_PORT 5555
+}
+
+# Set the DPI JTAG server address
+if { [info exists DPI_ADDRESS] } {
+   set _DPI_ADDRESS $DPI_ADDRESS
+} else {
+   set _DPI_ADDRESS "127.0.0.1"
+}
+
+jtag_dpi_set_port $_DPI_PORT
+jtag_dpi_set_address $_DPI_ADDRESS

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)