pld: add support for lattice certus devices 98/7398/16
authorDaniel Anselmi <danselmi@gmx.ch>
Mon, 12 Dec 2022 08:49:51 +0000 (09:49 +0100)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sun, 30 Apr 2023 14:54:04 +0000 (14:54 +0000)
Change-Id: Ic50a724e5793000fca11f35ba848c2d317c3cbab
Signed-off-by: Daniel Anselmi <danselmi@gmx.ch>
Reviewed-on: https://review.openocd.org/c/openocd/+/7398
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
doc/openocd.texi
src/pld/Makefile.am
src/pld/certus.c [new file with mode: 0644]
src/pld/certus.h [new file with mode: 0644]
src/pld/lattice.c
src/pld/lattice.h
tcl/board/certuspro_evaluation.cfg [new file with mode: 0644]
tcl/fpga/lattice_certus.cfg [new file with mode: 0644]
tcl/fpga/lattice_certuspro.cfg [new file with mode: 0644]

index 2d3ccfebe385c197eb7b33ce9deb49d323a6fde1..24de2ce94da27d341e8f58c96e77919570274c32 100644 (file)
@@ -8497,10 +8497,10 @@ for FPGA @var{num}.
 
 
 @deffn {FPGA Driver} {lattice} [family]
-The FGPA families ECP2, ECP3 and ECP5 by Lattice are supported.
+The FGPA families ECP2, ECP3, ECP5, Certus and CertusPro by Lattice are supported.
 This driver can be used to load the bitstream into the FPGA or read the status register and read/write the usercode register.
 
-The option @option{family} is one of @var{ecp2 ecp3 ecp5}. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices).
+The option @option{family} is one of @var{ecp2 ecp3 ecp5 certus}. This is needed when the JTAG ID of the device is not known by openocd (newer NX devices).
 
 @deffn {Command} {lattice read_status} num
 Reads and displays the status register
index 459792f645e9b3b9370c3f6cc8aa5621fe35cb3e..8ad4296f3aeda525417a5dcc7e74412d8e6aa9d5 100644 (file)
@@ -2,6 +2,7 @@
 
 noinst_LTLIBRARIES += %D%/libpld.la
 %C%_libpld_la_SOURCES = \
+       %D%/certus.c \
        %D%/ecp2_3.c \
        %D%/ecp5.c \
        %D%/lattice.c \
@@ -10,6 +11,7 @@ noinst_LTLIBRARIES += %D%/libpld.la
        %D%/raw_bit.c \
        %D%/xilinx_bit.c \
        %D%/virtex2.c \
+       %D%/certus.h \
        %D%/ecp2_3.h \
        %D%/ecp5.h \
        %D%/lattice.h \
diff --git a/src/pld/certus.c b/src/pld/certus.c
new file mode 100644 (file)
index 0000000..692ea19
--- /dev/null
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   Copyright (C) 2022 by Daniel Anselmi                                  *
+ *   danselmi@gmx.ch                                                       *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "lattice.h"
+#include "lattice_cmd.h"
+
+#define LSC_ENABLE_X    0x74
+#define LSC_REFRESH     0x79
+#define LSC_DEVICE_CTRL 0x7D
+
+int lattice_certus_read_status(struct jtag_tap *tap, uint64_t *status, uint64_t out)
+{
+       return lattice_read_u64_register(tap, LSC_READ_STATUS, status, out);
+}
+
+int lattice_certus_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out)
+{
+       return lattice_read_u32_register(tap, READ_USERCODE, usercode, out, false);
+}
+
+int lattice_certus_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode)
+{
+       LOG_ERROR("Not supported to write usercode on certus devices");
+       return ERROR_FAIL;
+}
+
+static int lattice_certus_enable_transparent_mode(struct jtag_tap *tap)
+{
+       struct scan_field field;
+
+       int retval = lattice_set_instr(tap, LSC_ENABLE_X, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+
+       uint8_t buffer = 0x0;
+       field.num_bits = 8;
+       field.out_value = &buffer;
+       field.in_value = NULL;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+       jtag_add_runtest(2, TAP_IDLE);
+
+       return jtag_execute_queue();
+}
+
+static int lattice_certus_erase_device(struct lattice_pld_device *lattice_device)
+{
+       struct jtag_tap *tap = lattice_device->tap;
+       if (!tap)
+               return ERROR_FAIL;
+
+       int retval = lattice_set_instr(tap, LSC_DEVICE_CTRL, TAP_IRPAUSE);
+       if (retval != ERROR_OK)
+               return retval;
+
+       struct scan_field field;
+       uint8_t buffer = 8;
+       field.num_bits = 8;
+       field.out_value = &buffer;
+       field.in_value = NULL;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+       jtag_add_runtest(2, TAP_IDLE);
+       retval = jtag_execute_queue();
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = lattice_set_instr(tap, LSC_DEVICE_CTRL, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       buffer = 0;
+       field.num_bits = 8;
+       field.out_value = &buffer;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+       jtag_add_runtest(2, TAP_IDLE);
+       retval = jtag_execute_queue();
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = lattice_set_instr(tap, ISC_ERASE, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       buffer = 0;
+       field.num_bits = 8;
+       field.out_value = &buffer;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+       jtag_add_runtest(100, TAP_IDLE);
+       jtag_add_sleep(5000);
+       retval = jtag_execute_queue();
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* check done is cleared and fail is cleared */
+       const uint64_t status_done_flag =  0x100;
+       const uint64_t status_fail_flag = 0x2000;
+       return lattice_verify_status_register_u64(lattice_device, 0x0, 0x0, status_done_flag | status_fail_flag);
+}
+
+static int lattice_certus_enable_programming(struct jtag_tap *tap)
+{
+       struct scan_field field;
+
+       int retval = lattice_set_instr(tap, LSC_REFRESH, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       jtag_add_runtest(2, TAP_IDLE);
+       retval = jtag_execute_queue();
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = lattice_set_instr(tap, ISC_ENABLE, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       uint8_t buffer = 0;
+       field.num_bits = 8;
+       field.out_value = &buffer;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+       jtag_add_runtest(2, TAP_IDLE);
+       return jtag_execute_queue();
+}
+
+static int lattice_certus_init_address(struct jtag_tap *tap)
+{
+       int retval = lattice_set_instr(tap, LSC_INIT_ADDRESS, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       jtag_add_runtest(2, TAP_IDLE);
+       return jtag_execute_queue();
+}
+
+static int lattice_certus_exit_programming_mode(struct jtag_tap *tap)
+{
+       int retval = lattice_set_instr(tap, ISC_DISABLE, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       jtag_add_runtest(2, TAP_IDLE);
+       retval = lattice_set_instr(tap, BYPASS, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       jtag_add_runtest(100, TAP_IDLE);
+       return jtag_execute_queue();
+}
+
+static int lattice_certus_program_config_map(struct jtag_tap *tap, struct lattice_bit_file *bit_file)
+{
+       struct scan_field field;
+
+       int retval = lattice_set_instr(tap, LSC_BITSTREAM_BURST, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+
+       field.num_bits = (bit_file->raw_bit.length - bit_file->offset) * 8;
+       field.out_value = bit_file->raw_bit.data + bit_file->offset;
+       field.in_value = NULL;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+
+       return jtag_execute_queue();
+}
+
+int lattice_certus_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file)
+{
+       struct jtag_tap *tap = lattice_device->tap;
+       if (!tap)
+               return ERROR_FAIL;
+
+       int retval = lattice_preload(lattice_device);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /*  check password protection is disabled */
+       const uint64_t status_pwd_protection = 0x20000;
+       retval = lattice_verify_status_register_u64(lattice_device, 0x0, 0x0, status_pwd_protection);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Password protection is set");
+               return retval;
+       }
+
+       retval = lattice_certus_enable_transparent_mode(tap);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* Check the SRAM Erase Lock */
+       const uint64_t status_otp = 0x40;
+       retval = lattice_verify_status_register_u64(lattice_device, 0x0, status_otp, status_otp);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("NV User Feature Sector OTP is Set");
+               return retval;
+       }
+
+       /* Check the SRAM Lock */
+       const uint64_t status_write_protected = 0x400;
+       retval = lattice_verify_status_register_u64(lattice_device, 0x0, 0x0, status_write_protected);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("NV User Feature Sector OTP is Set");
+               return retval;
+       }
+
+       retval = lattice_certus_enable_programming(tap);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("failed to enable programming mode");
+               return retval;
+       }
+
+       retval = lattice_certus_erase_device(lattice_device);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("erasing device failed");
+               return retval;
+       }
+
+       retval = lattice_certus_init_address(tap);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = lattice_certus_program_config_map(tap, bit_file);
+       if (retval != ERROR_OK)
+               return retval;
+       const uint32_t expected = 0x100; // done
+       const uint32_t mask = expected |
+                       0x3000 | // Busy Flag and Fail Flag
+                       0xf000000; // BSE Error
+       retval = lattice_verify_status_register_u64(lattice_device, 0x0, 0x100, mask);
+       if (retval != ERROR_OK)
+               return retval;
+
+       return lattice_certus_exit_programming_mode(tap);
+}
diff --git a/src/pld/certus.h b/src/pld/certus.h
new file mode 100644 (file)
index 0000000..51defc5
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   Copyright (C) 2022 by Daniel Anselmi                                  *
+ *   danselmi@gmx.ch                                                       *
+ ***************************************************************************/
+
+#ifndef OPENOCD_PLD_CERTUS_H
+#define OPENOCD_PLD_CERTUS_H
+
+#include "lattice.h"
+
+int lattice_certus_read_status(struct jtag_tap *tap, uint64_t *status, uint64_t out);
+int lattice_certus_read_usercode(struct jtag_tap *tap, uint32_t *usercode, uint32_t out);
+int lattice_certus_write_usercode(struct lattice_pld_device *lattice_device, uint32_t usercode);
+int lattice_certus_load(struct lattice_pld_device *lattice_device, struct lattice_bit_file *bit_file);
+
+#endif /* OPENOCD_PLD_CERTUS_H */
index f3d9c0da79de61977e30fed4dd3492f9de6ea90c..4ab5f63c0bd794e5527a5e82b085b66350d0c5ab 100644 (file)
@@ -15,6 +15,7 @@
 #include "lattice_bit.h"
 #include "ecp2_3.h"
 #include "ecp5.h"
+#include "certus.h"
 
 #define PRELOAD              0x1C
 
@@ -50,6 +51,9 @@ static const struct lattice_devices_elem lattice_devices[] = {
        {0x01111043,  409, LATTICE_ECP5 /* "LAE5UM-25F" */},
        {0x01112043,  510, LATTICE_ECP5 /* "LAE5UM-45F" */},
        {0x01113043,  750, LATTICE_ECP5 /* "LAE5UM-85F" */},
+       {0x310f0043,  362, LATTICE_CERTUS /* LFD2NX-17 */},
+       {0x310f1043,  362, LATTICE_CERTUS /* LFD2NX-40 */},
+       {0x010f4043,  362, LATTICE_CERTUS /* LFCPNX-100 */},
 };
 
 int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate)
@@ -116,6 +120,27 @@ int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_va
        return retval;
 }
 
+int lattice_read_u64_register(struct jtag_tap *tap, uint8_t cmd, uint64_t *in_val,
+                                                       uint64_t out_val)
+{
+       struct scan_field field;
+       uint8_t buffer[8];
+
+       int retval = lattice_set_instr(tap, cmd, TAP_IDLE);
+       if (retval != ERROR_OK)
+               return retval;
+       h_u64_to_le(buffer, out_val);
+       field.num_bits = 64;
+       field.out_value = buffer;
+       field.in_value = buffer;
+       jtag_add_dr_scan(tap, 1, &field, TAP_IDLE);
+       retval = jtag_execute_queue();
+       if (retval == ERROR_OK)
+               *in_val = le_to_h_u64(buffer);
+
+       return retval;
+}
+
 int lattice_preload(struct lattice_pld_device *lattice_device)
 {
        struct scan_field field;
@@ -150,6 +175,8 @@ static int lattice_read_usercode(struct lattice_pld_device *lattice_device, uint
                return lattice_ecp2_3_read_usercode(tap, usercode, out);
        else if (lattice_device->family == LATTICE_ECP5)
                return lattice_ecp5_read_usercode(tap, usercode, out);
+       else if (lattice_device->family == LATTICE_CERTUS)
+               return lattice_certus_read_usercode(tap, usercode, out);
 
        return ERROR_FAIL;
 }
@@ -177,6 +204,8 @@ static int lattice_write_usercode(struct lattice_pld_device *lattice_device, uin
                return lattice_ecp2_3_write_usercode(lattice_device, usercode);
        else if (lattice_device->family == LATTICE_ECP5)
                return lattice_ecp5_write_usercode(lattice_device, usercode);
+       else if (lattice_device->family == LATTICE_CERTUS)
+               return lattice_certus_write_usercode(lattice_device, usercode);
 
        return ERROR_FAIL;
 }
@@ -194,12 +223,22 @@ static int lattice_read_status_u32(struct lattice_pld_device *lattice_device, ui
 
        return ERROR_FAIL;
 }
+static int lattice_read_status_u64(struct lattice_pld_device *lattice_device, uint64_t *status,
+                                                               uint64_t out)
+{
+       if (!lattice_device->tap)
+               return ERROR_FAIL;
+
+       if (lattice_device->family == LATTICE_CERTUS)
+               return lattice_certus_read_status(lattice_device->tap, status, out);
+
+       return ERROR_FAIL;
+}
 
 int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
                                                uint32_t expected, uint32_t mask, bool do_idle)
 {
        uint32_t status;
-
        int retval = lattice_read_status_u32(lattice_device, &status, out, do_idle);
        if (retval != ERROR_OK)
                return retval;
@@ -212,13 +251,28 @@ int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device
        return ERROR_OK;
 }
 
+int lattice_verify_status_register_u64(struct lattice_pld_device *lattice_device, uint64_t out,
+                                               uint64_t expected, uint64_t mask)
+{
+       uint64_t status;
+       int retval = lattice_read_status_u64(lattice_device, &status, out);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if ((status & mask) != expected) {
+               LOG_ERROR("verifying status register failed got: 0x%08" PRIx64 " expected: 0x%08" PRIx64,
+                       status & mask, expected);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
 static int lattice_load_command(struct pld_device *pld_device, const char *filename)
 {
        if (!pld_device)
                return ERROR_FAIL;
 
        struct lattice_pld_device *lattice_device = pld_device->driver_priv;
-
        if (!lattice_device || !lattice_device->tap)
                return ERROR_FAIL;
        struct jtag_tap *tap = lattice_device->tap;
@@ -245,10 +299,14 @@ static int lattice_load_command(struct pld_device *pld_device, const char *filen
                retval = lattice_ecp3_load(lattice_device, &bit_file);
                break;
        case LATTICE_ECP5:
+       case LATTICE_CERTUS:
                if (bit_file.has_id && id != bit_file.idcode)
                        LOG_WARNING("Id on device (0x%8.8" PRIx32 ") and id in bit-stream (0x%8.8" PRIx32 ") don't match.",
                                id, bit_file.idcode);
-               retval = lattice_ecp5_load(lattice_device, &bit_file);
+               if (lattice_device->family == LATTICE_ECP5)
+                       retval = lattice_ecp5_load(lattice_device, &bit_file);
+               else
+                       retval = lattice_certus_load(lattice_device, &bit_file);
                break;
        default:
                LOG_ERROR("loading unknown device family");
@@ -283,6 +341,8 @@ PLD_DEVICE_COMMAND_HANDLER(lattice_pld_device_command)
                        family = LATTICE_ECP3;
                } else if (strcasecmp(CMD_ARGV[2], "ecp5") == 0) {
                        family = LATTICE_ECP5;
+               } else if (strcasecmp(CMD_ARGV[2], "certus") == 0) {
+                       family = LATTICE_CERTUS;
                } else {
                        command_print(CMD, "unknown family");
                        free(lattice_device);
@@ -405,11 +465,18 @@ COMMAND_HANDLER(lattice_read_status_command_handler)
        if (retval != ERROR_OK)
                return retval;
 
-       uint32_t status;
-       const bool do_idle = lattice_device->family == LATTICE_ECP5;
-       retval = lattice_read_status_u32(lattice_device, &status, 0x0, do_idle);
-       if (retval == ERROR_OK)
-               command_print(CMD, "0x%8.8" PRIx32, status);
+       if (lattice_device->family == LATTICE_CERTUS) {
+               uint64_t status;
+               retval = lattice_read_status_u64(lattice_device, &status, 0x0);
+               if (retval == ERROR_OK)
+                       command_print(CMD, "0x%016" PRIx64, status);
+       } else {
+               uint32_t status;
+               const bool do_idle = lattice_device->family == LATTICE_ECP5;
+               retval = lattice_read_status_u32(lattice_device, &status, 0x0, do_idle);
+               if (retval == ERROR_OK)
+                       command_print(CMD, "0x%8.8" PRIx32, status);
+       }
 
        return retval;
 }
index 6d692cbe480a4c75a1928335c6261a3720dc2bfc..9a76a4ec37cab20216dfcdaa86652cb78ac476c7 100644 (file)
@@ -23,10 +23,14 @@ struct lattice_pld_device {
 int lattice_set_instr(struct jtag_tap *tap, uint8_t new_instr, tap_state_t endstate);
 int lattice_read_u32_register(struct jtag_tap *tap, uint8_t cmd, uint32_t *in_val,
                                                        uint32_t out_val, bool do_idle);
+int lattice_read_u64_register(struct jtag_tap *tap, uint8_t cmd, uint64_t *in_val,
+                                                       uint64_t out_val);
 int lattice_verify_usercode(struct lattice_pld_device *lattice_device, uint32_t out,
                                                uint32_t expected, uint32_t mask);
 int lattice_verify_status_register_u32(struct lattice_pld_device *lattice_device, uint32_t out,
                                                uint32_t expected, uint32_t mask, bool do_idle);
+int lattice_verify_status_register_u64(struct lattice_pld_device *lattice_device, uint64_t out,
+                                               uint64_t expected, uint64_t mask);
 int lattice_preload(struct lattice_pld_device *lattice_device);
 
 #endif /* OPENOCD_PLD_LATTICE_H */
diff --git a/tcl/board/certuspro_evaluation.cfg b/tcl/board/certuspro_evaluation.cfg
new file mode 100644 (file)
index 0000000..5ff2a1e
--- /dev/null
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+# https://www.latticesemi.com/products/developmentboardsandkits/certuspro-nx-versa-board
+
+adapter driver ftdi
+ftdi vid_pid 0x0403 0x6010
+
+ftdi channel 0
+ftdi layout_init 0x0008 0x008b
+reset_config none
+transport select jtag
+adapter speed 10000
+
+source [find fpga/lattice_certuspro.cfg]
diff --git a/tcl/fpga/lattice_certus.cfg b/tcl/fpga/lattice_certus.cfg
new file mode 100644 (file)
index 0000000..95b6e59
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $_CHIPNAME
+} else {
+       set _CHIPNAME certus
+}
+
+# Lattice Certus
+#
+# Certus NX LFD2NX-17     0x310f0043
+# Certus NX LFD2NX-40     0x310f1043
+
+
+jtag newtap $_CHIPNAME tap -irlen 8 -irmask 0x83 -ircapture 0x1 \
+       -expected-id 0x310F1043 -expected-id 0x310F0043
+
+pld device lattice $_CHIPNAME.tap
diff --git a/tcl/fpga/lattice_certuspro.cfg b/tcl/fpga/lattice_certuspro.cfg
new file mode 100644 (file)
index 0000000..c15a379
--- /dev/null
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+if { [info exists CHIPNAME] } {
+       set _CHIPNAME $_CHIPNAME
+} else {
+       set _CHIPNAME certuspro
+}
+
+# Lattice CertusPro
+#
+# 0x010f4043 - LFCPNX-100
+# 0x     043 - LFCPNX-50
+
+jtag newtap $_CHIPNAME tap -irlen 8 -irmask 0x83 -ircapture 0x1 \
+       -expected-id 0x010f4043
+#    -expected-id 0x01112043
+
+pld device lattice $_CHIPNAME.tap

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)