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

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)