979c643e903d0a028b221bf254ccfc81e4fa868c
[openocd.git] / src / target / adi_v5_swd.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 *
5 * Copyright (C) 2010 by David Brownell
6 ***************************************************************************/
7
8 /**
9 * @file
10 * Utilities to support ARM "Serial Wire Debug" (SWD), a low pin-count debug
11 * link protocol used in cases where JTAG is not wanted. This is coupled to
12 * recent versions of ARM's "CoreSight" debug framework. This specific code
13 * is a transport level interface, with "target/arm_adi_v5.[hc]" code
14 * understanding operation semantics, shared with the JTAG transport.
15 *
16 * Single-DAP support only.
17 *
18 * for details, see "ARM IHI 0031A"
19 * ARM Debug Interface v5 Architecture Specification
20 * especially section 5.3 for SWD protocol
21 * and "ARM IHI 0074C" ARM Debug Interface Architecture Specification ADIv6.0
22 *
23 * On many chips (most current Cortex-M3 parts) SWD is a run-time alternative
24 * to JTAG. Boards may support one or both. There are also SWD-only chips,
25 * (using SW-DP not SWJ-DP).
26 *
27 * Even boards that also support JTAG can benefit from SWD support, because
28 * usually there's no way to access the SWO trace view mechanism in JTAG mode.
29 * That is, trace access may require SWD support.
30 *
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "arm.h"
38 #include "arm_adi_v5.h"
39 #include <helper/time_support.h>
40
41 #include <transport/transport.h>
42 #include <jtag/interface.h>
43
44 #include <jtag/swd.h>
45
46 /* for debug, set do_sync to true to force synchronous transfers */
47 static bool do_sync;
48
49 static struct adiv5_dap *swd_multidrop_selected_dap;
50
51
52 static int swd_queue_dp_write_inner(struct adiv5_dap *dap, unsigned int reg,
53 uint32_t data);
54
55
56 static int swd_send_sequence(struct adiv5_dap *dap, enum swd_special_seq seq)
57 {
58 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
59 assert(swd);
60
61 return swd->switch_seq(seq);
62 }
63
64 static void swd_finish_read(struct adiv5_dap *dap)
65 {
66 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
67 if (dap->last_read) {
68 swd->read_reg(swd_cmd(true, false, DP_RDBUFF), dap->last_read, 0);
69 dap->last_read = NULL;
70 }
71 }
72
73 static void swd_clear_sticky_errors(struct adiv5_dap *dap)
74 {
75 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
76 assert(swd);
77
78 swd->write_reg(swd_cmd(false, false, DP_ABORT),
79 STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
80 }
81
82 static int swd_run_inner(struct adiv5_dap *dap)
83 {
84 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
85 int retval;
86
87 retval = swd->run();
88
89 if (retval != ERROR_OK) {
90 /* fault response */
91 dap->do_reconnect = true;
92 }
93
94 return retval;
95 }
96
97 static inline int check_sync(struct adiv5_dap *dap)
98 {
99 return do_sync ? swd_run_inner(dap) : ERROR_OK;
100 }
101
102 /** Select the DP register bank matching bits 7:4 of reg. */
103 static int swd_queue_dp_bankselect(struct adiv5_dap *dap, unsigned int reg)
104 {
105 /* Only register address 0 and 4 are banked. */
106 if ((reg & 0xf) > 4)
107 return ERROR_OK;
108
109 uint64_t sel = (reg & 0x000000F0) >> 4;
110 if (dap->select != DP_SELECT_INVALID)
111 sel |= dap->select & ~0xfULL;
112
113 if (sel == dap->select)
114 return ERROR_OK;
115
116 dap->select = sel;
117
118 int retval = swd_queue_dp_write_inner(dap, DP_SELECT, (uint32_t)sel);
119 if (retval != ERROR_OK)
120 dap->select = DP_SELECT_INVALID;
121
122 return retval;
123 }
124
125 static int swd_queue_dp_read_inner(struct adiv5_dap *dap, unsigned int reg,
126 uint32_t *data)
127 {
128 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
129 assert(swd);
130
131 int retval = swd_queue_dp_bankselect(dap, reg);
132 if (retval != ERROR_OK)
133 return retval;
134
135 swd->read_reg(swd_cmd(true, false, reg), data, 0);
136
137 return check_sync(dap);
138 }
139
140 static int swd_queue_dp_write_inner(struct adiv5_dap *dap, unsigned int reg,
141 uint32_t data)
142 {
143 int retval;
144 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
145 assert(swd);
146
147 swd_finish_read(dap);
148
149 if (reg == DP_SELECT) {
150 dap->select = data & (DP_SELECT_APSEL | DP_SELECT_APBANK | DP_SELECT_DPBANK);
151
152 swd->write_reg(swd_cmd(false, false, reg), data, 0);
153
154 retval = check_sync(dap);
155 if (retval != ERROR_OK)
156 dap->select = DP_SELECT_INVALID;
157
158 return retval;
159 }
160
161 retval = swd_queue_dp_bankselect(dap, reg);
162 if (retval != ERROR_OK)
163 return retval;
164
165 swd->write_reg(swd_cmd(false, false, reg), data, 0);
166
167 return check_sync(dap);
168 }
169
170
171 static int swd_multidrop_select_inner(struct adiv5_dap *dap, uint32_t *dpidr_ptr,
172 uint32_t *dlpidr_ptr, bool clear_sticky)
173 {
174 int retval;
175 uint32_t dpidr, dlpidr;
176
177 assert(dap_is_multidrop(dap));
178
179 swd_send_sequence(dap, LINE_RESET);
180
181 retval = swd_queue_dp_write_inner(dap, DP_TARGETSEL, dap->multidrop_targetsel);
182 if (retval != ERROR_OK)
183 return retval;
184
185 retval = swd_queue_dp_read_inner(dap, DP_DPIDR, &dpidr);
186 if (retval != ERROR_OK)
187 return retval;
188
189 if (clear_sticky) {
190 /* Clear all sticky errors (including ORUN) */
191 swd_clear_sticky_errors(dap);
192 } else {
193 /* Ideally just clear ORUN flag which is set by reset */
194 retval = swd_queue_dp_write_inner(dap, DP_ABORT, ORUNERRCLR);
195 if (retval != ERROR_OK)
196 return retval;
197 }
198
199 retval = swd_queue_dp_read_inner(dap, DP_DLPIDR, &dlpidr);
200 if (retval != ERROR_OK)
201 return retval;
202
203 retval = swd_run_inner(dap);
204 if (retval != ERROR_OK)
205 return retval;
206
207 if ((dpidr & DP_DPIDR_VERSION_MASK) < (2UL << DP_DPIDR_VERSION_SHIFT)) {
208 LOG_INFO("Read DPIDR 0x%08" PRIx32
209 " has version < 2. A non multidrop capable device connected?",
210 dpidr);
211 return ERROR_FAIL;
212 }
213
214 /* TODO: check TARGETID if DLIPDR is same for more than one DP */
215 uint32_t expected_dlpidr = DP_DLPIDR_PROTVSN |
216 (dap->multidrop_targetsel & DP_TARGETSEL_INSTANCEID_MASK);
217 if (dlpidr != expected_dlpidr) {
218 LOG_INFO("Read incorrect DLPIDR 0x%08" PRIx32
219 " (possibly CTRL/STAT value)",
220 dlpidr);
221 return ERROR_FAIL;
222 }
223
224 LOG_DEBUG_IO("Selected DP_TARGETSEL 0x%08" PRIx32, dap->multidrop_targetsel);
225 swd_multidrop_selected_dap = dap;
226
227 if (dpidr_ptr)
228 *dpidr_ptr = dpidr;
229
230 if (dlpidr_ptr)
231 *dlpidr_ptr = dlpidr;
232
233 return retval;
234 }
235
236 static int swd_multidrop_select(struct adiv5_dap *dap)
237 {
238 if (!dap_is_multidrop(dap))
239 return ERROR_OK;
240
241 if (swd_multidrop_selected_dap == dap)
242 return ERROR_OK;
243
244 int retval = ERROR_OK;
245 for (unsigned int retry = 0; ; retry++) {
246 bool clear_sticky = retry > 0;
247
248 retval = swd_multidrop_select_inner(dap, NULL, NULL, clear_sticky);
249 if (retval == ERROR_OK)
250 break;
251
252 swd_multidrop_selected_dap = NULL;
253 if (retry > 3) {
254 LOG_ERROR("Failed to select multidrop %s", adiv5_dap_name(dap));
255 return retval;
256 }
257
258 LOG_DEBUG("Failed to select multidrop %s, retrying...",
259 adiv5_dap_name(dap));
260 }
261
262 return retval;
263 }
264
265 static int swd_connect_multidrop(struct adiv5_dap *dap)
266 {
267 int retval;
268 uint32_t dpidr = 0xdeadbeef;
269 uint32_t dlpidr = 0xdeadbeef;
270 int64_t timeout = timeval_ms() + 500;
271
272 do {
273 swd_send_sequence(dap, JTAG_TO_DORMANT);
274 swd_send_sequence(dap, DORMANT_TO_SWD);
275
276 /* Clear link state, including the SELECT cache. */
277 dap->do_reconnect = false;
278 dap_invalidate_cache(dap);
279 swd_multidrop_selected_dap = NULL;
280
281 retval = swd_multidrop_select_inner(dap, &dpidr, &dlpidr, true);
282 if (retval == ERROR_OK)
283 break;
284
285 alive_sleep(1);
286
287 } while (timeval_ms() < timeout);
288
289 if (retval != ERROR_OK) {
290 swd_multidrop_selected_dap = NULL;
291 LOG_ERROR("Failed to connect multidrop %s", adiv5_dap_name(dap));
292 return retval;
293 }
294
295 LOG_INFO("SWD DPIDR 0x%08" PRIx32 ", DLPIDR 0x%08" PRIx32,
296 dpidr, dlpidr);
297
298 return retval;
299 }
300
301 static int swd_connect_single(struct adiv5_dap *dap)
302 {
303 int retval;
304 uint32_t dpidr = 0xdeadbeef;
305 int64_t timeout = timeval_ms() + 500;
306
307 do {
308 if (dap->switch_through_dormant) {
309 swd_send_sequence(dap, JTAG_TO_DORMANT);
310 swd_send_sequence(dap, DORMANT_TO_SWD);
311 } else {
312 swd_send_sequence(dap, JTAG_TO_SWD);
313 }
314
315 /* Clear link state, including the SELECT cache. */
316 dap->do_reconnect = false;
317 dap_invalidate_cache(dap);
318
319 /* The sequences to enter in SWD (JTAG_TO_SWD and DORMANT_TO_SWD) end
320 * with a SWD line reset sequence (50 clk with SWDIO high).
321 * From ARM IHI 0074C ADIv6.0, chapter B4.3.3 "Connection and line reset
322 * sequence":
323 * - line reset sets DP_SELECT_DPBANK to zero;
324 * - read of DP_DPIDR takes the connection out of reset;
325 * - write of DP_TARGETSEL keeps the connection in reset;
326 * - other accesses return protocol error (SWDIO not driven by target).
327 *
328 * Read DP_DPIDR to get out of reset. Initialize dap->select to zero to
329 * skip the write to DP_SELECT, avoiding the protocol error. Set again
330 * dap->select to DP_SELECT_INVALID because the rest of the register is
331 * unknown after line reset.
332 */
333 dap->select = 0;
334 retval = swd_queue_dp_read_inner(dap, DP_DPIDR, &dpidr);
335 if (retval == ERROR_OK) {
336 retval = swd_run_inner(dap);
337 if (retval == ERROR_OK)
338 break;
339 }
340
341 alive_sleep(1);
342
343 dap->switch_through_dormant = !dap->switch_through_dormant;
344 } while (timeval_ms() < timeout);
345 dap->select = DP_SELECT_INVALID;
346
347 if (retval != ERROR_OK) {
348 LOG_ERROR("Error connecting DP: cannot read IDR");
349 return retval;
350 }
351
352 LOG_INFO("SWD DPIDR 0x%08" PRIx32, dpidr);
353
354 do {
355 dap->do_reconnect = false;
356
357 /* force clear all sticky faults */
358 swd_clear_sticky_errors(dap);
359
360 retval = swd_run_inner(dap);
361 if (retval != ERROR_WAIT)
362 break;
363
364 alive_sleep(10);
365
366 } while (timeval_ms() < timeout);
367
368 return retval;
369 }
370
371 static int swd_connect(struct adiv5_dap *dap)
372 {
373 int status;
374
375 /* FIXME validate transport config ... is the
376 * configured DAP present (check IDCODE)?
377 */
378
379 /* Check if we should reset srst already when connecting, but not if reconnecting. */
380 if (!dap->do_reconnect) {
381 enum reset_types jtag_reset_config = jtag_get_reset_config();
382
383 if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
384 if (jtag_reset_config & RESET_SRST_NO_GATING)
385 adapter_assert_reset();
386 else
387 LOG_WARNING("\'srst_nogate\' reset_config option is required");
388 }
389 }
390
391 if (dap_is_multidrop(dap))
392 status = swd_connect_multidrop(dap);
393 else
394 status = swd_connect_single(dap);
395
396 /* IHI 0031E B4.3.2:
397 * "A WAIT response must not be issued to the ...
398 * ... writes to the ABORT register"
399 * swd_clear_sticky_errors() writes to the ABORT register only.
400 *
401 * Unfortunately at least Microchip SAMD51/E53/E54 returns WAIT
402 * in a corner case. Just try if ABORT resolves the problem.
403 */
404 if (status == ERROR_WAIT) {
405 LOG_WARNING("Connecting DP: stalled AP operation, issuing ABORT");
406
407 dap->do_reconnect = false;
408
409 status = swd_queue_dp_write_inner(dap, DP_ABORT,
410 DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR);
411
412 if (status == ERROR_OK)
413 status = swd_run_inner(dap);
414 }
415
416 if (status == ERROR_OK)
417 status = dap_dp_init(dap);
418
419 return status;
420 }
421
422 static int swd_check_reconnect(struct adiv5_dap *dap)
423 {
424 if (dap->do_reconnect)
425 return swd_connect(dap);
426
427 return ERROR_OK;
428 }
429
430 static int swd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
431 {
432 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
433 assert(swd);
434
435 /* TODO: Send DAPABORT in swd_multidrop_select_inner()
436 * in the case the multidrop dap is not selected?
437 * swd_queue_ap_abort() is not currently used anyway...
438 */
439 int retval = swd_multidrop_select(dap);
440 if (retval != ERROR_OK)
441 return retval;
442
443 swd->write_reg(swd_cmd(false, false, DP_ABORT),
444 DAPABORT | STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0);
445 return check_sync(dap);
446 }
447
448 static int swd_queue_dp_read(struct adiv5_dap *dap, unsigned reg,
449 uint32_t *data)
450 {
451 int retval = swd_check_reconnect(dap);
452 if (retval != ERROR_OK)
453 return retval;
454
455 retval = swd_multidrop_select(dap);
456 if (retval != ERROR_OK)
457 return retval;
458
459 return swd_queue_dp_read_inner(dap, reg, data);
460 }
461
462 static int swd_queue_dp_write(struct adiv5_dap *dap, unsigned reg,
463 uint32_t data)
464 {
465 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
466 assert(swd);
467
468 int retval = swd_check_reconnect(dap);
469 if (retval != ERROR_OK)
470 return retval;
471
472 retval = swd_multidrop_select(dap);
473 if (retval != ERROR_OK)
474 return retval;
475
476 return swd_queue_dp_write_inner(dap, reg, data);
477 }
478
479 /** Select the AP register bank matching bits 7:4 of reg. */
480 static int swd_queue_ap_bankselect(struct adiv5_ap *ap, unsigned reg)
481 {
482 int retval;
483 struct adiv5_dap *dap = ap->dap;
484 uint64_t sel;
485
486 if (is_adiv6(dap)) {
487 sel = ap->ap_num | (reg & 0x00000FF0);
488 if (sel == (dap->select & ~0xfULL))
489 return ERROR_OK;
490
491 if (dap->select != DP_SELECT_INVALID)
492 sel |= dap->select & 0xf;
493 dap->select = sel;
494 LOG_DEBUG("AP BANKSEL: %" PRIx64, sel);
495
496 retval = swd_queue_dp_write(dap, DP_SELECT, (uint32_t)sel);
497
498 if (retval == ERROR_OK && dap->asize > 32)
499 retval = swd_queue_dp_write(dap, DP_SELECT1, (uint32_t)(sel >> 32));
500
501 if (retval != ERROR_OK)
502 dap->select = DP_SELECT_INVALID;
503
504 return retval;
505 }
506
507 /* ADIv5 */
508 sel = (ap->ap_num << 24) | (reg & 0x000000F0);
509 if (dap->select != DP_SELECT_INVALID)
510 sel |= dap->select & DP_SELECT_DPBANK;
511
512 if (sel == dap->select)
513 return ERROR_OK;
514
515 dap->select = sel;
516
517 retval = swd_queue_dp_write_inner(dap, DP_SELECT, sel);
518 if (retval != ERROR_OK)
519 dap->select = DP_SELECT_INVALID;
520
521 return retval;
522 }
523
524 static int swd_queue_ap_read(struct adiv5_ap *ap, unsigned reg,
525 uint32_t *data)
526 {
527 struct adiv5_dap *dap = ap->dap;
528 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
529 assert(swd);
530
531 int retval = swd_check_reconnect(dap);
532 if (retval != ERROR_OK)
533 return retval;
534
535 retval = swd_multidrop_select(dap);
536 if (retval != ERROR_OK)
537 return retval;
538
539 retval = swd_queue_ap_bankselect(ap, reg);
540 if (retval != ERROR_OK)
541 return retval;
542
543 swd->read_reg(swd_cmd(true, true, reg), dap->last_read, ap->memaccess_tck);
544 dap->last_read = data;
545
546 return check_sync(dap);
547 }
548
549 static int swd_queue_ap_write(struct adiv5_ap *ap, unsigned reg,
550 uint32_t data)
551 {
552 struct adiv5_dap *dap = ap->dap;
553 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
554 assert(swd);
555
556 int retval = swd_check_reconnect(dap);
557 if (retval != ERROR_OK)
558 return retval;
559
560 retval = swd_multidrop_select(dap);
561 if (retval != ERROR_OK)
562 return retval;
563
564 swd_finish_read(dap);
565
566 retval = swd_queue_ap_bankselect(ap, reg);
567 if (retval != ERROR_OK)
568 return retval;
569
570 swd->write_reg(swd_cmd(false, true, reg), data, ap->memaccess_tck);
571
572 return check_sync(dap);
573 }
574
575 /** Executes all queued DAP operations. */
576 static int swd_run(struct adiv5_dap *dap)
577 {
578 int retval = swd_multidrop_select(dap);
579 if (retval != ERROR_OK)
580 return retval;
581
582 swd_finish_read(dap);
583
584 return swd_run_inner(dap);
585 }
586
587 /** Put the SWJ-DP back to JTAG mode */
588 static void swd_quit(struct adiv5_dap *dap)
589 {
590 const struct swd_driver *swd = adiv5_dap_swd_driver(dap);
591 static bool done;
592
593 /* There is no difference if the sequence is sent at the last
594 * or the first swd_quit() call, send it just once */
595 if (done)
596 return;
597
598 done = true;
599 if (dap_is_multidrop(dap)) {
600 swd->switch_seq(SWD_TO_DORMANT);
601 /* Revisit!
602 * Leaving DPs in dormant state was tested and offers some safety
603 * against DPs mismatch in case of unintentional use of non-multidrop SWD.
604 * To put SWJ-DPs to power-on state issue
605 * swd->switch_seq(DORMANT_TO_JTAG);
606 */
607 } else {
608 if (dap->switch_through_dormant) {
609 swd->switch_seq(SWD_TO_DORMANT);
610 swd->switch_seq(DORMANT_TO_JTAG);
611 } else {
612 swd->switch_seq(SWD_TO_JTAG);
613 }
614 }
615
616 /* flush the queue to shift out the sequence before exit */
617 swd->run();
618 }
619
620 const struct dap_ops swd_dap_ops = {
621 .connect = swd_connect,
622 .send_sequence = swd_send_sequence,
623 .queue_dp_read = swd_queue_dp_read,
624 .queue_dp_write = swd_queue_dp_write,
625 .queue_ap_read = swd_queue_ap_read,
626 .queue_ap_write = swd_queue_ap_write,
627 .queue_ap_abort = swd_queue_ap_abort,
628 .run = swd_run,
629 .quit = swd_quit,
630 };
631
632 static const struct command_registration swd_commands[] = {
633 {
634 /*
635 * Set up SWD and JTAG targets identically, unless/until
636 * infrastructure improves ... meanwhile, ignore all
637 * JTAG-specific stuff like IR length for SWD.
638 *
639 * REVISIT can we verify "just one SWD DAP" here/early?
640 */
641 .name = "newdap",
642 .jim_handler = jim_jtag_newtap,
643 .mode = COMMAND_CONFIG,
644 .help = "declare a new SWD DAP"
645 },
646 COMMAND_REGISTRATION_DONE
647 };
648
649 static const struct command_registration swd_handlers[] = {
650 {
651 .name = "swd",
652 .mode = COMMAND_ANY,
653 .help = "SWD command group",
654 .chain = swd_commands,
655 .usage = "",
656 },
657 COMMAND_REGISTRATION_DONE
658 };
659
660 static int swd_select(struct command_context *ctx)
661 {
662 /* FIXME: only place where global 'adapter_driver' is still needed */
663 extern struct adapter_driver *adapter_driver;
664 const struct swd_driver *swd = adapter_driver->swd_ops;
665 int retval;
666
667 retval = register_commands(ctx, NULL, swd_handlers);
668 if (retval != ERROR_OK)
669 return retval;
670
671 /* be sure driver is in SWD mode; start
672 * with hardware default TRN (1), it can be changed later
673 */
674 if (!swd || !swd->read_reg || !swd->write_reg || !swd->init) {
675 LOG_DEBUG("no SWD driver?");
676 return ERROR_FAIL;
677 }
678
679 retval = swd->init();
680 if (retval != ERROR_OK) {
681 LOG_DEBUG("can't init SWD driver");
682 return retval;
683 }
684
685 return retval;
686 }
687
688 static int swd_init(struct command_context *ctx)
689 {
690 /* nothing done here, SWD is initialized
691 * together with the DAP */
692 return ERROR_OK;
693 }
694
695 static struct transport swd_transport = {
696 .name = "swd",
697 .select = swd_select,
698 .init = swd_init,
699 };
700
701 static void swd_constructor(void) __attribute__((constructor));
702 static void swd_constructor(void)
703 {
704 transport_register(&swd_transport);
705 }
706
707 /** Returns true if the current debug session
708 * is using SWD as its transport.
709 */
710 bool transport_is_swd(void)
711 {
712 return get_current_transport() == &swd_transport;
713 }

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)