stm32: Add floating point register read/write.
[openocd.git] / src / target / stm32_stlink.c
1 /***************************************************************************
2 * Copyright (C) 2011 by Mathias Kuester *
3 * Mathias Kuester <kesmtp@freenet.de> *
4 * *
5 * Copyright (C) 2011 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "jtag/jtag.h"
29 #include "jtag/stlink/stlink_transport.h"
30 #include "jtag/stlink/stlink_interface.h"
31 #include "jtag/stlink/stlink_layout.h"
32 #include "register.h"
33 #include "algorithm.h"
34 #include "target.h"
35 #include "breakpoints.h"
36 #include "target_type.h"
37 #include "armv7m.h"
38 #include "cortex_m.h"
39 #include "arm_semihosting.h"
40
41 #define ARMV7M_SCS_DCRSR 0xe000edf4
42 #define ARMV7M_SCS_DCRDR 0xe000edf8
43
44 static inline struct stlink_interface_s *target_to_stlink(struct target *target)
45 {
46 return target->tap->priv;
47 }
48
49 static int stm32_stlink_load_core_reg_u32(struct target *target,
50 enum armv7m_regtype type,
51 uint32_t num, uint32_t *value)
52 {
53 int retval;
54 struct stlink_interface_s *stlink_if = target_to_stlink(target);
55
56 LOG_DEBUG("%s", __func__);
57
58 /* NOTE: we "know" here that the register identifiers used
59 * in the v7m header match the Cortex-M3 Debug Core Register
60 * Selector values for R0..R15, xPSR, MSP, and PSP.
61 */
62 switch (num) {
63 case 0 ... 18:
64 /* read a normal core register */
65 retval = stlink_if->layout->api->read_reg(stlink_if->fd, num, value);
66
67 if (retval != ERROR_OK) {
68 LOG_ERROR("JTAG failure %i", retval);
69 return ERROR_JTAG_DEVICE_ERROR;
70 }
71 LOG_DEBUG("load from core reg %i value 0x%" PRIx32 "", (int)num, *value);
72 break;
73
74 case 33:
75 case 64 ... 96:
76 /* Floating-point Status and Registers */
77 retval = target_write_u32(target, ARMV7M_SCS_DCRSR, num);
78 if (retval != ERROR_OK)
79 return retval;
80 retval = target_read_u32(target, ARMV7M_SCS_DCRDR, value);
81 if (retval != ERROR_OK)
82 return retval;
83 LOG_DEBUG("load from core reg %i value 0x%" PRIx32 "", (int)num, *value);
84 break;
85
86 case ARMV7M_PRIMASK:
87 case ARMV7M_BASEPRI:
88 case ARMV7M_FAULTMASK:
89 case ARMV7M_CONTROL:
90 /* Cortex-M3 packages these four registers as bitfields
91 * in one Debug Core register. So say r0 and r2 docs;
92 * it was removed from r1 docs, but still works.
93 */
94 retval = stlink_if->layout->api->read_reg(stlink_if->fd, 20, value);
95
96 switch (num) {
97 case ARMV7M_PRIMASK:
98 *value = buf_get_u32((uint8_t *) value, 0, 1);
99 break;
100
101 case ARMV7M_BASEPRI:
102 *value = buf_get_u32((uint8_t *) value, 8, 8);
103 break;
104
105 case ARMV7M_FAULTMASK:
106 *value = buf_get_u32((uint8_t *) value, 16, 1);
107 break;
108
109 case ARMV7M_CONTROL:
110 *value = buf_get_u32((uint8_t *) value, 24, 2);
111 break;
112 }
113
114 LOG_DEBUG("load from special reg %i value 0x%" PRIx32 "",
115 (int)num, *value);
116 break;
117
118 default:
119 return ERROR_COMMAND_SYNTAX_ERROR;
120 }
121
122 return ERROR_OK;
123 }
124
125 static int stm32_stlink_store_core_reg_u32(struct target *target,
126 enum armv7m_regtype type,
127 uint32_t num, uint32_t value)
128 {
129 int retval;
130 uint32_t reg;
131 struct armv7m_common *armv7m = target_to_armv7m(target);
132 struct stlink_interface_s *stlink_if = target_to_stlink(target);
133
134 LOG_DEBUG("%s", __func__);
135
136 #ifdef ARMV7_GDB_HACKS
137 /* If the LR register is being modified, make sure it will put us
138 * in "thumb" mode, or an INVSTATE exception will occur. This is a
139 * hack to deal with the fact that gdb will sometimes "forge"
140 * return addresses, and doesn't set the LSB correctly (i.e., when
141 * printing expressions containing function calls, it sets LR = 0.)
142 * Valid exception return codes have bit 0 set too.
143 */
144 if (num == ARMV7M_R14)
145 value |= 0x01;
146 #endif
147
148 /* NOTE: we "know" here that the register identifiers used
149 * in the v7m header match the Cortex-M3 Debug Core Register
150 * Selector values for R0..R15, xPSR, MSP, and PSP.
151 */
152 switch (num) {
153 case 0 ... 18:
154 retval = stlink_if->layout->api->write_reg(stlink_if->fd, num, value);
155
156 if (retval != ERROR_OK) {
157 struct reg *r;
158
159 LOG_ERROR("JTAG failure");
160 r = armv7m->core_cache->reg_list + num;
161 r->dirty = r->valid;
162 return ERROR_JTAG_DEVICE_ERROR;
163 }
164 LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", (int)num, value);
165 break;
166
167 case 33:
168 case 64 ... 96:
169 /* Floating-point Status and Registers */
170 retval = target_write_u32(target, ARMV7M_SCS_DCRDR, value);
171 if (retval != ERROR_OK)
172 return retval;
173 retval = target_write_u32(target, ARMV7M_SCS_DCRSR, num | (1<<16));
174 if (retval != ERROR_OK)
175 return retval;
176 LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", (int)num, value);
177 break;
178
179 case ARMV7M_PRIMASK:
180 case ARMV7M_BASEPRI:
181 case ARMV7M_FAULTMASK:
182 case ARMV7M_CONTROL:
183 /* Cortex-M3 packages these four registers as bitfields
184 * in one Debug Core register. So say r0 and r2 docs;
185 * it was removed from r1 docs, but still works.
186 */
187
188 stlink_if->layout->api->read_reg(stlink_if->fd, 20, &reg);
189
190 switch (num) {
191 case ARMV7M_PRIMASK:
192 buf_set_u32((uint8_t *) &reg, 0, 1, value);
193 break;
194
195 case ARMV7M_BASEPRI:
196 buf_set_u32((uint8_t *) &reg, 8, 8, value);
197 break;
198
199 case ARMV7M_FAULTMASK:
200 buf_set_u32((uint8_t *) &reg, 16, 1, value);
201 break;
202
203 case ARMV7M_CONTROL:
204 buf_set_u32((uint8_t *) &reg, 24, 2, value);
205 break;
206 }
207
208 stlink_if->layout->api->write_reg(stlink_if->fd, 20, reg);
209
210 LOG_DEBUG("write special reg %i value 0x%" PRIx32 " ", (int)num, value);
211 break;
212
213 default:
214 return ERROR_COMMAND_SYNTAX_ERROR;
215 }
216
217 return ERROR_OK;
218 }
219
220 static int stm32_stlink_examine_debug_reason(struct target *target)
221 {
222 if ((target->debug_reason != DBG_REASON_DBGRQ)
223 && (target->debug_reason != DBG_REASON_SINGLESTEP)) {
224 target->debug_reason = DBG_REASON_BREAKPOINT;
225 }
226
227 return ERROR_OK;
228 }
229
230 static int stm32_stlink_init_arch_info(struct target *target,
231 struct cortex_m3_common *cortex_m3,
232 struct jtag_tap *tap)
233 {
234 struct armv7m_common *armv7m;
235
236 LOG_DEBUG("%s", __func__);
237
238 armv7m = &cortex_m3->armv7m;
239 armv7m_init_arch_info(target, armv7m);
240
241 armv7m->load_core_reg_u32 = stm32_stlink_load_core_reg_u32;
242 armv7m->store_core_reg_u32 = stm32_stlink_store_core_reg_u32;
243
244 armv7m->examine_debug_reason = stm32_stlink_examine_debug_reason;
245
246 return ERROR_OK;
247 }
248
249 static int stm32_stlink_init_target(struct command_context *cmd_ctx,
250 struct target *target)
251 {
252 LOG_DEBUG("%s", __func__);
253
254 armv7m_build_reg_cache(target);
255
256 return ERROR_OK;
257 }
258
259 static int stm32_stlink_target_create(struct target *target,
260 Jim_Interp *interp)
261 {
262 LOG_DEBUG("%s", __func__);
263
264 struct cortex_m3_common *cortex_m3 = calloc(1, sizeof(struct cortex_m3_common));
265
266 if (!cortex_m3)
267 return ERROR_COMMAND_SYNTAX_ERROR;
268
269 stm32_stlink_init_arch_info(target, cortex_m3, target->tap);
270
271 return ERROR_OK;
272 }
273
274 static int stm32_stlink_load_context(struct target *target)
275 {
276 struct armv7m_common *armv7m = target_to_armv7m(target);
277 int num_regs = armv7m->core_cache->num_regs;
278
279 for (int i = 0; i < num_regs; i++) {
280 if (!armv7m->core_cache->reg_list[i].valid)
281 armv7m->read_core_reg(target, i);
282 }
283
284 return ERROR_OK;
285 }
286
287 static int stlink_debug_entry(struct target *target)
288 {
289 struct armv7m_common *armv7m = target_to_armv7m(target);
290 struct arm *arm = &armv7m->arm;
291 struct reg *r;
292 uint32_t xPSR;
293 int retval;
294
295 retval = armv7m->examine_debug_reason(target);
296 if (retval != ERROR_OK)
297 return retval;
298
299 stm32_stlink_load_context(target);
300
301 r = armv7m->core_cache->reg_list + ARMV7M_xPSR;
302 xPSR = buf_get_u32(r->value, 0, 32);
303
304 /* Are we in an exception handler */
305 if (xPSR & 0x1FF) {
306 armv7m->core_mode = ARMV7M_MODE_HANDLER;
307 armv7m->exception_number = (xPSR & 0x1FF);
308
309 arm->core_mode = ARM_MODE_HANDLER;
310 arm->map = armv7m_msp_reg_map;
311 } else {
312 unsigned control = buf_get_u32(armv7m->core_cache
313 ->reg_list[ARMV7M_CONTROL].value, 0, 2);
314
315 /* is this thread privileged? */
316 armv7m->core_mode = control & 1;
317 arm->core_mode = armv7m->core_mode
318 ? ARM_MODE_USER_THREAD
319 : ARM_MODE_THREAD;
320
321 /* which stack is it using? */
322 if (control & 2)
323 arm->map = armv7m_psp_reg_map;
324 else
325 arm->map = armv7m_msp_reg_map;
326
327 armv7m->exception_number = 0;
328 }
329
330 LOG_DEBUG("entered debug state in core mode: %s at PC 0x%" PRIx32 ", target->state: %s",
331 armv7m_mode_strings[armv7m->core_mode],
332 *(uint32_t *)(arm->pc->value),
333 target_state_name(target));
334
335 return retval;
336 }
337
338 static int stm32_stlink_poll(struct target *target)
339 {
340 enum target_state state;
341 struct stlink_interface_s *stlink_if = target_to_stlink(target);
342 struct armv7m_common *armv7m = target_to_armv7m(target);
343
344 state = stlink_if->layout->api->state(stlink_if->fd);
345
346 if (state == TARGET_UNKNOWN) {
347 LOG_ERROR("jtag status contains invalid mode value - communication failure");
348 return ERROR_TARGET_FAILURE;
349 }
350
351 if (target->state == state)
352 return ERROR_OK;
353
354 if (state == TARGET_HALTED) {
355 target->state = state;
356
357 int retval = stlink_debug_entry(target);
358 if (retval != ERROR_OK)
359 return retval;
360
361 if (arm_semihosting(target, &retval) != 0)
362 return retval;
363
364 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
365 LOG_DEBUG("halted: PC: 0x%x", buf_get_u32(armv7m->arm.pc->value, 0, 32));
366 }
367
368 return ERROR_OK;
369 }
370
371 static int stm32_stlink_assert_reset(struct target *target)
372 {
373 int res;
374 struct stlink_interface_s *stlink_if = target_to_stlink(target);
375 struct armv7m_common *armv7m = target_to_armv7m(target);
376
377 LOG_DEBUG("%s", __func__);
378
379 res = stlink_if->layout->api->reset(stlink_if->fd);
380
381 if (res != ERROR_OK)
382 return res;
383
384 /* virtual assert reset, we need it for the internal
385 * jtag state machine
386 */
387 jtag_add_reset(1, 1);
388
389 /* registers are now invalid */
390 register_cache_invalidate(armv7m->core_cache);
391
392 if (target->reset_halt) {
393 target->state = TARGET_RESET;
394 target->debug_reason = DBG_REASON_DBGRQ;
395 } else {
396 target->state = TARGET_HALTED;
397 }
398
399 return ERROR_OK;
400 }
401
402 static int stm32_stlink_deassert_reset(struct target *target)
403 {
404 int res;
405
406 LOG_DEBUG("%s", __func__);
407
408 /* virtual deassert reset, we need it for the internal
409 * jtag state machine
410 */
411 jtag_add_reset(0, 0);
412
413 if (!target->reset_halt) {
414 res = target_resume(target, 1, 0, 0, 0);
415
416 if (res != ERROR_OK)
417 return res;
418 }
419
420 return ERROR_OK;
421 }
422
423 static int stm32_stlink_soft_reset_halt(struct target *target)
424 {
425 LOG_DEBUG("%s", __func__);
426 return ERROR_OK;
427 }
428
429 static int stm32_stlink_halt(struct target *target)
430 {
431 int res;
432 struct stlink_interface_s *stlink_if = target_to_stlink(target);
433
434 LOG_DEBUG("%s", __func__);
435
436 if (target->state == TARGET_HALTED) {
437 LOG_DEBUG("target was already halted");
438 return ERROR_OK;
439 }
440
441 if (target->state == TARGET_UNKNOWN)
442 LOG_WARNING("target was in unknown state when halt was requested");
443
444 res = stlink_if->layout->api->halt(stlink_if->fd);
445
446 if (res != ERROR_OK)
447 return res;
448
449 target->debug_reason = DBG_REASON_DBGRQ;
450
451 return ERROR_OK;
452 }
453
454 static int stm32_stlink_resume(struct target *target, int current,
455 uint32_t address, int handle_breakpoints,
456 int debug_execution)
457 {
458 int res;
459 struct stlink_interface_s *stlink_if = target_to_stlink(target);
460 struct armv7m_common *armv7m = target_to_armv7m(target);
461 uint32_t resume_pc;
462 struct breakpoint *breakpoint = NULL;
463 struct reg *pc;
464
465 LOG_DEBUG("%s %d %x %d %d", __func__, current, address,
466 handle_breakpoints, debug_execution);
467
468 if (target->state != TARGET_HALTED) {
469 LOG_WARNING("target not halted");
470 return ERROR_TARGET_NOT_HALTED;
471 }
472
473 pc = armv7m->arm.pc;
474 if (!current) {
475 buf_set_u32(pc->value, 0, 32, address);
476 pc->dirty = true;
477 pc->valid = true;
478 }
479
480 if (!breakpoint_find(target, buf_get_u32(pc->value, 0, 32))
481 && !debug_execution) {
482 armv7m_maybe_skip_bkpt_inst(target, NULL);
483 }
484
485 resume_pc = buf_get_u32(pc->value, 0, 32);
486
487 armv7m_restore_context(target);
488
489 /* registers are now invalid */
490 register_cache_invalidate(armv7m->core_cache);
491
492 /* the front-end may request us not to handle breakpoints */
493 if (handle_breakpoints) {
494 /* Single step past breakpoint at current address */
495 breakpoint = breakpoint_find(target, resume_pc);
496 if (breakpoint) {
497 LOG_DEBUG("unset breakpoint at 0x%8.8" PRIx32 " (ID: %d)",
498 breakpoint->address,
499 breakpoint->unique_id);
500 cortex_m3_unset_breakpoint(target, breakpoint);
501
502 res = stlink_if->layout->api->step(stlink_if->fd);
503
504 if (res != ERROR_OK)
505 return res;
506
507 cortex_m3_set_breakpoint(target, breakpoint);
508 }
509 }
510
511 res = stlink_if->layout->api->run(stlink_if->fd);
512
513 if (res != ERROR_OK)
514 return res;
515
516 target->state = TARGET_RUNNING;
517
518 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
519
520 return ERROR_OK;
521 }
522
523 static int stm32_stlink_step(struct target *target, int current,
524 uint32_t address, int handle_breakpoints)
525 {
526 int res;
527 struct stlink_interface_s *stlink_if = target_to_stlink(target);
528 struct armv7m_common *armv7m = target_to_armv7m(target);
529 struct breakpoint *breakpoint = NULL;
530 struct reg *pc = armv7m->arm.pc;
531 bool bkpt_inst_found = false;
532
533 LOG_DEBUG("%s", __func__);
534
535 if (target->state != TARGET_HALTED) {
536 LOG_WARNING("target not halted");
537 return ERROR_TARGET_NOT_HALTED;
538 }
539
540 if (!current) {
541 buf_set_u32(pc->value, 0, 32, address);
542 pc->dirty = true;
543 pc->valid = true;
544 }
545
546 uint32_t pc_value = buf_get_u32(pc->value, 0, 32);
547
548 /* the front-end may request us not to handle breakpoints */
549 if (handle_breakpoints) {
550 breakpoint = breakpoint_find(target, pc_value);
551 if (breakpoint)
552 cortex_m3_unset_breakpoint(target, breakpoint);
553 }
554
555 armv7m_maybe_skip_bkpt_inst(target, &bkpt_inst_found);
556
557 target->debug_reason = DBG_REASON_SINGLESTEP;
558
559 armv7m_restore_context(target);
560
561 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
562
563 res = stlink_if->layout->api->step(stlink_if->fd);
564
565 if (res != ERROR_OK)
566 return res;
567
568 /* registers are now invalid */
569 register_cache_invalidate(armv7m->core_cache);
570
571 if (breakpoint)
572 cortex_m3_set_breakpoint(target, breakpoint);
573
574 stlink_debug_entry(target);
575 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
576
577 LOG_INFO("halted: PC: 0x%x", buf_get_u32(armv7m->arm.pc->value, 0, 32));
578
579 return ERROR_OK;
580 }
581
582 static int stm32_stlink_read_memory(struct target *target, uint32_t address,
583 uint32_t size, uint32_t count,
584 uint8_t *buffer)
585 {
586 int res;
587 uint32_t buffer_threshold = 128;
588 uint32_t addr_increment = 4;
589 uint8_t *dst = buffer;
590 uint32_t c;
591 struct stlink_interface_s *stlink_if = target_to_stlink(target);
592
593 if (!count || !buffer)
594 return ERROR_COMMAND_SYNTAX_ERROR;
595
596 LOG_DEBUG("%s %x %d %d", __func__, address, size, count);
597
598 /* prepare byte count, buffer threshold
599 * and address increment for none 32bit access
600 */
601 if (size != 4) {
602 count *= size;
603 buffer_threshold = 64;
604 addr_increment = 1;
605 }
606
607 while (count) {
608 if (count > buffer_threshold)
609 c = buffer_threshold;
610 else
611 c = count;
612
613 if (size != 4)
614 res = stlink_if->layout->api->read_mem8(stlink_if->fd,
615 address, c, dst);
616 else
617 res = stlink_if->layout->api->read_mem32(stlink_if->fd,
618 address, c, (uint32_t *)dst);
619
620 if (res != ERROR_OK)
621 return res;
622
623 address += (c * addr_increment);
624 dst += (c * addr_increment);
625 count -= c;
626 }
627
628 return ERROR_OK;
629 }
630
631 static int stm32_stlink_write_memory(struct target *target, uint32_t address,
632 uint32_t size, uint32_t count,
633 const uint8_t *buffer)
634 {
635 int res;
636 uint32_t buffer_threshold = 128;
637 uint32_t addr_increment = 4;
638 const uint8_t *dst = buffer;
639 uint32_t c;
640 struct stlink_interface_s *stlink_if = target_to_stlink(target);
641
642 if (!count || !buffer)
643 return ERROR_COMMAND_SYNTAX_ERROR;
644
645 LOG_DEBUG("%s %x %d %d", __func__, address, size, count);
646
647 /* prepare byte count, buffer threshold
648 * and address increment for none 32bit access
649 */
650 if (size != 4) {
651 count *= size;
652 buffer_threshold = 64;
653 addr_increment = 1;
654 }
655
656 while (count) {
657 if (count > buffer_threshold)
658 c = buffer_threshold;
659 else
660 c = count;
661
662 if (size != 4)
663 res = stlink_if->layout->api->write_mem8(stlink_if->fd,
664 address, c, dst);
665 else
666 res = stlink_if->layout->api->write_mem32(stlink_if->fd,
667 address, c, (uint32_t *)dst);
668
669 if (res != ERROR_OK)
670 return res;
671
672 address += (c * addr_increment);
673 dst += (c * addr_increment);
674 count -= c;
675 }
676
677 return ERROR_OK;
678 }
679
680 static int stm32_stlink_bulk_write_memory(struct target *target,
681 uint32_t address, uint32_t count,
682 const uint8_t *buffer)
683 {
684 return stm32_stlink_write_memory(target, address, 4, count, buffer);
685 }
686
687 struct target_type stm32_stlink_target = {
688 .name = "stm32_stlink",
689
690 .init_target = stm32_stlink_init_target,
691 .target_create = stm32_stlink_target_create,
692 .examine = cortex_m3_examine,
693
694 .poll = stm32_stlink_poll,
695 .arch_state = armv7m_arch_state,
696
697 .assert_reset = stm32_stlink_assert_reset,
698 .deassert_reset = stm32_stlink_deassert_reset,
699 .soft_reset_halt = stm32_stlink_soft_reset_halt,
700
701 .halt = stm32_stlink_halt,
702 .resume = stm32_stlink_resume,
703 .step = stm32_stlink_step,
704
705 .get_gdb_reg_list = armv7m_get_gdb_reg_list,
706
707 .read_memory = stm32_stlink_read_memory,
708 .write_memory = stm32_stlink_write_memory,
709 .bulk_write_memory = stm32_stlink_bulk_write_memory,
710 .checksum_memory = armv7m_checksum_memory,
711 .blank_check_memory = armv7m_blank_check_memory,
712
713 .run_algorithm = armv7m_run_algorithm,
714 .start_algorithm = armv7m_start_algorithm,
715 .wait_algorithm = armv7m_wait_algorithm,
716
717 .add_breakpoint = cortex_m3_add_breakpoint,
718 .remove_breakpoint = cortex_m3_remove_breakpoint,
719 .add_watchpoint = cortex_m3_add_watchpoint,
720 .remove_watchpoint = cortex_m3_remove_watchpoint,
721 };

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)