flash/nor: Rename flash_address() to cfi_flash_address()
[openocd.git] / src / jtag / adapter.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2007-2010 Øyvind Harboe *
6 * oyvind.harboe@zylin.com *
7 * *
8 * Copyright (C) 2009 SoftPLC Corporation *
9 * http://softplc.com *
10 * dick@softplc.com *
11 * *
12 * Copyright (C) 2009 Zachary T Welch *
13 * zw@superlucidity.net *
14 * *
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; either version 2 of the License, or *
18 * (at your option) any later version. *
19 * *
20 * This program is distributed in the hope that it will be useful, *
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
23 * GNU General Public License for more details. *
24 * *
25 * You should have received a copy of the GNU General Public License *
26 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
27 ***************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "jtag.h"
34 #include "minidriver.h"
35 #include "interface.h"
36 #include "interfaces.h"
37 #include <transport/transport.h>
38 #include <jtag/drivers/jtag_usb_common.h>
39
40 #ifdef HAVE_STRINGS_H
41 #include <strings.h>
42 #endif
43
44 /**
45 * @file
46 * Holds support for configuring debug adapters from TCl scripts.
47 */
48
49 struct adapter_driver *adapter_driver;
50 const char * const jtag_only[] = { "jtag", NULL };
51
52 static int jim_adapter_name(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
53 {
54 Jim_GetOptInfo goi;
55 Jim_GetOpt_Setup(&goi, interp, argc-1, argv + 1);
56
57 /* return the name of the interface */
58 /* TCL code might need to know the exact type... */
59 /* FUTURE: we allow this as a means to "set" the interface. */
60 if (goi.argc != 0) {
61 Jim_WrongNumArgs(goi.interp, 1, goi.argv-1, "(no params)");
62 return JIM_ERR;
63 }
64 const char *name = adapter_driver ? adapter_driver->name : NULL;
65 Jim_SetResultString(goi.interp, name ? : "undefined", -1);
66 return JIM_OK;
67 }
68
69 COMMAND_HANDLER(interface_transport_command)
70 {
71 char **transports;
72 int retval;
73
74 retval = CALL_COMMAND_HANDLER(transport_list_parse, &transports);
75 if (retval != ERROR_OK)
76 return retval;
77
78 retval = allow_transports(CMD_CTX, (const char **)transports);
79
80 if (retval != ERROR_OK) {
81 for (unsigned i = 0; transports[i]; i++)
82 free(transports[i]);
83 free(transports);
84 }
85 return retval;
86 }
87
88 COMMAND_HANDLER(handle_interface_list_command)
89 {
90 if (strcmp(CMD_NAME, "interface_list") == 0 && CMD_ARGC > 0)
91 return ERROR_COMMAND_SYNTAX_ERROR;
92
93 command_print(CMD, "The following debug interfaces are available:");
94 for (unsigned i = 0; NULL != adapter_drivers[i]; i++) {
95 const char *name = adapter_drivers[i]->name;
96 command_print(CMD, "%u: %s", i + 1, name);
97 }
98
99 return ERROR_OK;
100 }
101
102 COMMAND_HANDLER(handle_interface_command)
103 {
104 int retval;
105
106 /* check whether the interface is already configured */
107 if (adapter_driver) {
108 LOG_WARNING("Interface already configured, ignoring");
109 return ERROR_OK;
110 }
111
112 /* interface name is a mandatory argument */
113 if (CMD_ARGC != 1 || CMD_ARGV[0][0] == '\0')
114 return ERROR_COMMAND_SYNTAX_ERROR;
115
116 for (unsigned i = 0; NULL != adapter_drivers[i]; i++) {
117 if (strcmp(CMD_ARGV[0], adapter_drivers[i]->name) != 0)
118 continue;
119
120 if (NULL != adapter_drivers[i]->commands) {
121 retval = register_commands(CMD_CTX, NULL,
122 adapter_drivers[i]->commands);
123 if (ERROR_OK != retval)
124 return retval;
125 }
126
127 adapter_driver = adapter_drivers[i];
128
129 return allow_transports(CMD_CTX, adapter_driver->transports);
130 }
131
132 /* no valid interface was found (i.e. the configuration option,
133 * didn't match one of the compiled-in interfaces
134 */
135 LOG_ERROR("The specified debug interface was not found (%s)",
136 CMD_ARGV[0]);
137 CALL_COMMAND_HANDLER(handle_interface_list_command);
138 return ERROR_JTAG_INVALID_INTERFACE;
139 }
140
141 COMMAND_HANDLER(handle_reset_config_command)
142 {
143 int new_cfg = 0;
144 int mask = 0;
145
146 /* Original versions cared about the order of these tokens:
147 * reset_config signals [combination [trst_type [srst_type]]]
148 * They also clobbered the previous configuration even on error.
149 *
150 * Here we don't care about the order, and only change values
151 * which have been explicitly specified.
152 */
153 for (; CMD_ARGC; CMD_ARGC--, CMD_ARGV++) {
154 int tmp = 0;
155 int m;
156
157 /* gating */
158 m = RESET_SRST_NO_GATING;
159 if (strcmp(*CMD_ARGV, "srst_gates_jtag") == 0)
160 /* default: don't use JTAG while SRST asserted */;
161 else if (strcmp(*CMD_ARGV, "srst_nogate") == 0)
162 tmp = RESET_SRST_NO_GATING;
163 else
164 m = 0;
165 if (mask & m) {
166 LOG_ERROR("extra reset_config %s spec (%s)",
167 "gating", *CMD_ARGV);
168 return ERROR_COMMAND_SYNTAX_ERROR;
169 }
170 if (m)
171 goto next;
172
173 /* signals */
174 m = RESET_HAS_TRST | RESET_HAS_SRST;
175 if (strcmp(*CMD_ARGV, "none") == 0)
176 tmp = RESET_NONE;
177 else if (strcmp(*CMD_ARGV, "trst_only") == 0)
178 tmp = RESET_HAS_TRST;
179 else if (strcmp(*CMD_ARGV, "srst_only") == 0)
180 tmp = RESET_HAS_SRST;
181 else if (strcmp(*CMD_ARGV, "trst_and_srst") == 0)
182 tmp = RESET_HAS_TRST | RESET_HAS_SRST;
183 else
184 m = 0;
185 if (mask & m) {
186 LOG_ERROR("extra reset_config %s spec (%s)",
187 "signal", *CMD_ARGV);
188 return ERROR_COMMAND_SYNTAX_ERROR;
189 }
190 if (m)
191 goto next;
192
193 /* combination (options for broken wiring) */
194 m = RESET_SRST_PULLS_TRST | RESET_TRST_PULLS_SRST;
195 if (strcmp(*CMD_ARGV, "separate") == 0)
196 /* separate reset lines - default */;
197 else if (strcmp(*CMD_ARGV, "srst_pulls_trst") == 0)
198 tmp |= RESET_SRST_PULLS_TRST;
199 else if (strcmp(*CMD_ARGV, "trst_pulls_srst") == 0)
200 tmp |= RESET_TRST_PULLS_SRST;
201 else if (strcmp(*CMD_ARGV, "combined") == 0)
202 tmp |= RESET_SRST_PULLS_TRST | RESET_TRST_PULLS_SRST;
203 else
204 m = 0;
205 if (mask & m) {
206 LOG_ERROR("extra reset_config %s spec (%s)",
207 "combination", *CMD_ARGV);
208 return ERROR_COMMAND_SYNTAX_ERROR;
209 }
210 if (m)
211 goto next;
212
213 /* trst_type (NOP without HAS_TRST) */
214 m = RESET_TRST_OPEN_DRAIN;
215 if (strcmp(*CMD_ARGV, "trst_open_drain") == 0)
216 tmp |= RESET_TRST_OPEN_DRAIN;
217 else if (strcmp(*CMD_ARGV, "trst_push_pull") == 0)
218 /* push/pull from adapter - default */;
219 else
220 m = 0;
221 if (mask & m) {
222 LOG_ERROR("extra reset_config %s spec (%s)",
223 "trst_type", *CMD_ARGV);
224 return ERROR_COMMAND_SYNTAX_ERROR;
225 }
226 if (m)
227 goto next;
228
229 /* srst_type (NOP without HAS_SRST) */
230 m = RESET_SRST_PUSH_PULL;
231 if (strcmp(*CMD_ARGV, "srst_push_pull") == 0)
232 tmp |= RESET_SRST_PUSH_PULL;
233 else if (strcmp(*CMD_ARGV, "srst_open_drain") == 0)
234 /* open drain from adapter - default */;
235 else
236 m = 0;
237 if (mask & m) {
238 LOG_ERROR("extra reset_config %s spec (%s)",
239 "srst_type", *CMD_ARGV);
240 return ERROR_COMMAND_SYNTAX_ERROR;
241 }
242 if (m)
243 goto next;
244
245 /* connect_type - only valid when srst_nogate */
246 m = RESET_CNCT_UNDER_SRST;
247 if (strcmp(*CMD_ARGV, "connect_assert_srst") == 0)
248 tmp |= RESET_CNCT_UNDER_SRST;
249 else if (strcmp(*CMD_ARGV, "connect_deassert_srst") == 0)
250 /* connect normally - default */;
251 else
252 m = 0;
253 if (mask & m) {
254 LOG_ERROR("extra reset_config %s spec (%s)",
255 "connect_type", *CMD_ARGV);
256 return ERROR_COMMAND_SYNTAX_ERROR;
257 }
258 if (m)
259 goto next;
260
261 /* caller provided nonsense; fail */
262 LOG_ERROR("unknown reset_config flag (%s)", *CMD_ARGV);
263 return ERROR_COMMAND_SYNTAX_ERROR;
264
265 next:
266 /* Remember the bits which were specified (mask)
267 * and their new values (new_cfg).
268 */
269 mask |= m;
270 new_cfg |= tmp;
271 }
272
273 /* clear previous values of those bits, save new values */
274 if (mask) {
275 int old_cfg = jtag_get_reset_config();
276
277 old_cfg &= ~mask;
278 new_cfg |= old_cfg;
279 jtag_set_reset_config(new_cfg);
280 } else
281 new_cfg = jtag_get_reset_config();
282
283 /*
284 * Display the (now-)current reset mode
285 */
286 char *modes[6];
287
288 /* minimal JTAG has neither SRST nor TRST (so that's the default) */
289 switch (new_cfg & (RESET_HAS_TRST | RESET_HAS_SRST)) {
290 case RESET_HAS_SRST:
291 modes[0] = "srst_only";
292 break;
293 case RESET_HAS_TRST:
294 modes[0] = "trst_only";
295 break;
296 case RESET_TRST_AND_SRST:
297 modes[0] = "trst_and_srst";
298 break;
299 default:
300 modes[0] = "none";
301 break;
302 }
303
304 /* normally SRST and TRST are decoupled; but bugs happen ... */
305 switch (new_cfg & (RESET_SRST_PULLS_TRST | RESET_TRST_PULLS_SRST)) {
306 case RESET_SRST_PULLS_TRST:
307 modes[1] = "srst_pulls_trst";
308 break;
309 case RESET_TRST_PULLS_SRST:
310 modes[1] = "trst_pulls_srst";
311 break;
312 case RESET_SRST_PULLS_TRST | RESET_TRST_PULLS_SRST:
313 modes[1] = "combined";
314 break;
315 default:
316 modes[1] = "separate";
317 break;
318 }
319
320 /* TRST-less connectors include Altera, Xilinx, and minimal JTAG */
321 if (new_cfg & RESET_HAS_TRST) {
322 if (new_cfg & RESET_TRST_OPEN_DRAIN)
323 modes[3] = " trst_open_drain";
324 else
325 modes[3] = " trst_push_pull";
326 } else
327 modes[3] = "";
328
329 /* SRST-less connectors include TI-14, Xilinx, and minimal JTAG */
330 if (new_cfg & RESET_HAS_SRST) {
331 if (new_cfg & RESET_SRST_NO_GATING)
332 modes[2] = " srst_nogate";
333 else
334 modes[2] = " srst_gates_jtag";
335
336 if (new_cfg & RESET_SRST_PUSH_PULL)
337 modes[4] = " srst_push_pull";
338 else
339 modes[4] = " srst_open_drain";
340
341 if (new_cfg & RESET_CNCT_UNDER_SRST)
342 modes[5] = " connect_assert_srst";
343 else
344 modes[5] = " connect_deassert_srst";
345 } else {
346 modes[2] = "";
347 modes[4] = "";
348 modes[5] = "";
349 }
350
351 command_print(CMD, "%s %s%s%s%s%s",
352 modes[0], modes[1],
353 modes[2], modes[3], modes[4], modes[5]);
354
355 return ERROR_OK;
356 }
357
358 COMMAND_HANDLER(handle_adapter_nsrst_delay_command)
359 {
360 if (CMD_ARGC > 1)
361 return ERROR_COMMAND_SYNTAX_ERROR;
362 if (CMD_ARGC == 1) {
363 unsigned delay;
364 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], delay);
365
366 jtag_set_nsrst_delay(delay);
367 }
368 command_print(CMD, "adapter_nsrst_delay: %u", jtag_get_nsrst_delay());
369 return ERROR_OK;
370 }
371
372 COMMAND_HANDLER(handle_adapter_nsrst_assert_width_command)
373 {
374 if (CMD_ARGC > 1)
375 return ERROR_COMMAND_SYNTAX_ERROR;
376 if (CMD_ARGC == 1) {
377 unsigned width;
378 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], width);
379
380 jtag_set_nsrst_assert_width(width);
381 }
382 command_print(CMD, "adapter_nsrst_assert_width: %u", jtag_get_nsrst_assert_width());
383 return ERROR_OK;
384 }
385
386 COMMAND_HANDLER(handle_adapter_khz_command)
387 {
388 if (CMD_ARGC > 1)
389 return ERROR_COMMAND_SYNTAX_ERROR;
390
391 int retval = ERROR_OK;
392 if (CMD_ARGC == 1) {
393 unsigned khz = 0;
394 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], khz);
395
396 retval = jtag_config_khz(khz);
397 if (ERROR_OK != retval)
398 return retval;
399 }
400
401 int cur_speed = jtag_get_speed_khz();
402 retval = jtag_get_speed_readable(&cur_speed);
403 if (ERROR_OK != retval)
404 return retval;
405
406 if (cur_speed)
407 command_print(CMD, "adapter speed: %d kHz", cur_speed);
408 else
409 command_print(CMD, "adapter speed: RCLK - adaptive");
410
411 return retval;
412 }
413
414 COMMAND_HANDLER(handle_adapter_reset_de_assert)
415 {
416 enum values {
417 VALUE_UNDEFINED = -1,
418 VALUE_DEASSERT = 0,
419 VALUE_ASSERT = 1,
420 };
421 enum values value;
422 enum values srst = VALUE_UNDEFINED;
423 enum values trst = VALUE_UNDEFINED;
424 enum reset_types jtag_reset_config = jtag_get_reset_config();
425 char *signal;
426
427 if (CMD_ARGC == 0) {
428 if (transport_is_jtag()) {
429 if (jtag_reset_config & RESET_HAS_TRST)
430 signal = jtag_get_trst() ? "asserted" : "deasserted";
431 else
432 signal = "not present";
433 command_print(CMD, "trst %s", signal);
434 }
435
436 if (jtag_reset_config & RESET_HAS_SRST)
437 signal = jtag_get_srst() ? "asserted" : "deasserted";
438 else
439 signal = "not present";
440 command_print(CMD, "srst %s", signal);
441
442 return ERROR_OK;
443 }
444
445 if (CMD_ARGC != 1 && CMD_ARGC != 3)
446 return ERROR_COMMAND_SYNTAX_ERROR;
447
448 value = (strcmp(CMD_NAME, "assert") == 0) ? VALUE_ASSERT : VALUE_DEASSERT;
449 if (strcmp(CMD_ARGV[0], "srst") == 0)
450 srst = value;
451 else if (strcmp(CMD_ARGV[0], "trst") == 0)
452 trst = value;
453 else
454 return ERROR_COMMAND_SYNTAX_ERROR;
455
456 if (CMD_ARGC == 3) {
457 if (strcmp(CMD_ARGV[1], "assert") == 0)
458 value = VALUE_ASSERT;
459 else if (strcmp(CMD_ARGV[1], "deassert") == 0)
460 value = VALUE_DEASSERT;
461 else
462 return ERROR_COMMAND_SYNTAX_ERROR;
463
464 if (strcmp(CMD_ARGV[2], "srst") == 0 && srst == VALUE_UNDEFINED)
465 srst = value;
466 else if (strcmp(CMD_ARGV[2], "trst") == 0 && trst == VALUE_UNDEFINED)
467 trst = value;
468 else
469 return ERROR_COMMAND_SYNTAX_ERROR;
470 }
471
472 if (trst == VALUE_UNDEFINED) {
473 if (transport_is_jtag())
474 trst = jtag_get_trst() ? VALUE_ASSERT : VALUE_DEASSERT;
475 else
476 trst = VALUE_DEASSERT; /* unused, safe value */
477 }
478
479 if (srst == VALUE_UNDEFINED) {
480 if (jtag_reset_config & RESET_HAS_SRST)
481 srst = jtag_get_srst() ? VALUE_ASSERT : VALUE_DEASSERT;
482 else
483 srst = VALUE_DEASSERT; /* unused, safe value */
484 }
485
486 if (trst == VALUE_ASSERT && !transport_is_jtag()) {
487 LOG_ERROR("transport has no trst signal");
488 return ERROR_FAIL;
489 }
490
491 if (srst == VALUE_ASSERT && !(jtag_reset_config & RESET_HAS_SRST)) {
492 LOG_ERROR("adapter has no srst signal");
493 return ERROR_FAIL;
494 }
495
496 return adapter_resets((trst == VALUE_DEASSERT) ? TRST_DEASSERT : TRST_ASSERT,
497 (srst == VALUE_DEASSERT) ? SRST_DEASSERT : SRST_ASSERT);
498 }
499
500 #ifndef HAVE_JTAG_MINIDRIVER_H
501 #ifdef HAVE_LIBUSB_GET_PORT_NUMBERS
502 COMMAND_HANDLER(handle_usb_location_command)
503 {
504 if (CMD_ARGC == 1)
505 jtag_usb_set_location(CMD_ARGV[0]);
506
507 command_print(CMD, "adapter usb location: %s", jtag_usb_get_location());
508
509 return ERROR_OK;
510 }
511 #endif /* HAVE_LIBUSB_GET_PORT_NUMBERS */
512
513 static const struct command_registration adapter_usb_command_handlers[] = {
514 #ifdef HAVE_LIBUSB_GET_PORT_NUMBERS
515 {
516 .name = "location",
517 .handler = &handle_usb_location_command,
518 .mode = COMMAND_CONFIG,
519 .help = "display or set the USB bus location of the USB device",
520 .usage = "[<bus>-port[.port]...]",
521 },
522 #endif /* HAVE_LIBUSB_GET_PORT_NUMBERS */
523 COMMAND_REGISTRATION_DONE
524 };
525 #endif /* MINIDRIVER */
526
527 static const struct command_registration adapter_command_handlers[] = {
528 #ifndef HAVE_JTAG_MINIDRIVER_H
529 {
530 .name = "usb",
531 .mode = COMMAND_ANY,
532 .help = "usb adapter command group",
533 .usage = "",
534 .chain = adapter_usb_command_handlers,
535 },
536 #endif /* MINIDRIVER */
537 {
538 .name = "assert",
539 .handler = handle_adapter_reset_de_assert,
540 .mode = COMMAND_EXEC,
541 .help = "Controls SRST and TRST lines.",
542 .usage = "|deassert [srst|trst [assert|deassert srst|trst]]",
543 },
544 {
545 .name = "deassert",
546 .handler = handle_adapter_reset_de_assert,
547 .mode = COMMAND_EXEC,
548 .help = "Controls SRST and TRST lines.",
549 .usage = "|assert [srst|trst [deassert|assert srst|trst]]",
550 },
551 COMMAND_REGISTRATION_DONE
552 };
553
554 static const struct command_registration interface_command_handlers[] = {
555 {
556 .name = "adapter",
557 .mode = COMMAND_ANY,
558 .help = "adapter command group",
559 .usage = "",
560 .chain = adapter_command_handlers,
561 },
562 {
563 .name = "adapter_khz",
564 .handler = handle_adapter_khz_command,
565 .mode = COMMAND_ANY,
566 .help = "With an argument, change to the specified maximum "
567 "jtag speed. For JTAG, 0 KHz signifies adaptive "
568 " clocking. "
569 "With or without argument, display current setting.",
570 .usage = "[khz]",
571 },
572 {
573 .name = "adapter_name",
574 .mode = COMMAND_ANY,
575 .jim_handler = jim_adapter_name,
576 .help = "Returns the name of the currently "
577 "selected adapter (driver)",
578 },
579 {
580 .name = "adapter_nsrst_delay",
581 .handler = handle_adapter_nsrst_delay_command,
582 .mode = COMMAND_ANY,
583 .help = "delay after deasserting SRST in ms",
584 .usage = "[milliseconds]",
585 },
586 {
587 .name = "adapter_nsrst_assert_width",
588 .handler = handle_adapter_nsrst_assert_width_command,
589 .mode = COMMAND_ANY,
590 .help = "delay after asserting SRST in ms",
591 .usage = "[milliseconds]",
592 },
593 {
594 .name = "interface",
595 .handler = handle_interface_command,
596 .mode = COMMAND_CONFIG,
597 .help = "Select a debug adapter interface (driver)",
598 .usage = "driver_name",
599 },
600 {
601 .name = "interface_transports",
602 .handler = interface_transport_command,
603 .mode = COMMAND_CONFIG,
604 .help = "Declare transports the interface supports.",
605 .usage = "transport ... ",
606 },
607 {
608 .name = "interface_list",
609 .handler = handle_interface_list_command,
610 .mode = COMMAND_ANY,
611 .help = "List all built-in debug adapter interfaces (drivers)",
612 .usage = "",
613 },
614 {
615 .name = "reset_config",
616 .handler = handle_reset_config_command,
617 .mode = COMMAND_ANY,
618 .help = "configure adapter reset behavior",
619 .usage = "[none|trst_only|srst_only|trst_and_srst] "
620 "[srst_pulls_trst|trst_pulls_srst|combined|separate] "
621 "[srst_gates_jtag|srst_nogate] "
622 "[trst_push_pull|trst_open_drain] "
623 "[srst_push_pull|srst_open_drain] "
624 "[connect_deassert_srst|connect_assert_srst]",
625 },
626 COMMAND_REGISTRATION_DONE
627 };
628
629 /**
630 * Register the commands which deal with arbitrary debug adapter drivers.
631 *
632 * @todo Remove internal assumptions that all debug adapters use JTAG for
633 * transport. Various types and data structures are not named generically.
634 */
635 int interface_register_commands(struct command_context *ctx)
636 {
637 return register_commands(ctx, NULL, interface_command_handlers);
638 }

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)