flash/stm32l4x: fix dual bank support for STM32L552xC devices
[openocd.git] / src / target / hla_target.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 * revised: 4/25/13 by brent@mbari.org [DCC target request support] *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
22 ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "jtag/interface.h"
29 #include "jtag/jtag.h"
30 #include "jtag/hla/hla_transport.h"
31 #include "jtag/hla/hla_interface.h"
32 #include "jtag/hla/hla_layout.h"
33 #include "register.h"
34 #include "algorithm.h"
35 #include "target.h"
36 #include "breakpoints.h"
37 #include "target_type.h"
38 #include "armv7m.h"
39 #include "cortex_m.h"
40 #include "arm_semihosting.h"
41 #include "target_request.h"
42 #include <rtt/rtt.h>
43
44 #define SAVED_DCRDR dbgbase /* FIXME: using target->dbgbase to preserve DCRDR */
45
46 #define ARMV7M_SCS_DCRSR DCB_DCRSR
47 #define ARMV7M_SCS_DCRDR DCB_DCRDR
48
49 static inline struct hl_interface_s *target_to_adapter(struct target *target)
50 {
51 return target->tap->priv;
52 }
53
54 static int adapter_load_core_reg_u32(struct target *target,
55 uint32_t regsel, uint32_t *value)
56 {
57 struct hl_interface_s *adapter = target_to_adapter(target);
58 return adapter->layout->api->read_reg(adapter->handle, regsel, value);
59 }
60
61 static int adapter_store_core_reg_u32(struct target *target,
62 uint32_t regsel, uint32_t value)
63 {
64 struct hl_interface_s *adapter = target_to_adapter(target);
65 return adapter->layout->api->write_reg(adapter->handle, regsel, value);
66 }
67
68 static int adapter_examine_debug_reason(struct target *target)
69 {
70 if ((target->debug_reason != DBG_REASON_DBGRQ)
71 && (target->debug_reason != DBG_REASON_SINGLESTEP)) {
72 target->debug_reason = DBG_REASON_BREAKPOINT;
73 }
74
75 return ERROR_OK;
76 }
77
78 static int hl_dcc_read(struct hl_interface_s *hl_if, uint8_t *value, uint8_t *ctrl)
79 {
80 uint16_t dcrdr;
81 int retval = hl_if->layout->api->read_mem(hl_if->handle,
82 DCB_DCRDR, 1, sizeof(dcrdr), (uint8_t *)&dcrdr);
83 if (retval == ERROR_OK) {
84 *ctrl = (uint8_t)dcrdr;
85 *value = (uint8_t)(dcrdr >> 8);
86
87 LOG_DEBUG("data 0x%x ctrl 0x%x", *value, *ctrl);
88
89 if (dcrdr & 1) {
90 /* write ack back to software dcc register
91 * to signify we have read data */
92 /* atomically clear just the byte containing the busy bit */
93 static const uint8_t zero;
94 retval = hl_if->layout->api->write_mem(hl_if->handle, DCB_DCRDR, 1, 1, &zero);
95 }
96 }
97 return retval;
98 }
99
100 static int hl_target_request_data(struct target *target,
101 uint32_t size, uint8_t *buffer)
102 {
103 struct hl_interface_s *hl_if = target_to_adapter(target);
104 uint8_t data;
105 uint8_t ctrl;
106 uint32_t i;
107
108 for (i = 0; i < (size * 4); i++) {
109 int err = hl_dcc_read(hl_if, &data, &ctrl);
110 if (err != ERROR_OK)
111 return err;
112
113 buffer[i] = data;
114 }
115
116 return ERROR_OK;
117 }
118
119 static int hl_handle_target_request(void *priv)
120 {
121 struct target *target = priv;
122 int err;
123
124 if (!target_was_examined(target))
125 return ERROR_OK;
126 struct hl_interface_s *hl_if = target_to_adapter(target);
127
128 if (!target->dbg_msg_enabled)
129 return ERROR_OK;
130
131 if (target->state == TARGET_RUNNING) {
132 uint8_t data;
133 uint8_t ctrl;
134
135 err = hl_dcc_read(hl_if, &data, &ctrl);
136 if (err != ERROR_OK)
137 return err;
138
139 /* check if we have data */
140 if (ctrl & (1 << 0)) {
141 uint32_t request;
142
143 /* we assume target is quick enough */
144 request = data;
145 err = hl_dcc_read(hl_if, &data, &ctrl);
146 if (err != ERROR_OK)
147 return err;
148
149 request |= (data << 8);
150 err = hl_dcc_read(hl_if, &data, &ctrl);
151 if (err != ERROR_OK)
152 return err;
153
154 request |= (data << 16);
155 err = hl_dcc_read(hl_if, &data, &ctrl);
156 if (err != ERROR_OK)
157 return err;
158
159 request |= (data << 24);
160 target_request(target, request);
161 }
162 }
163
164 return ERROR_OK;
165 }
166
167 static int adapter_init_arch_info(struct target *target,
168 struct cortex_m_common *cortex_m,
169 struct jtag_tap *tap)
170 {
171 struct armv7m_common *armv7m;
172
173 LOG_DEBUG("%s", __func__);
174
175 armv7m = &cortex_m->armv7m;
176 armv7m_init_arch_info(target, armv7m);
177
178 armv7m->load_core_reg_u32 = adapter_load_core_reg_u32;
179 armv7m->store_core_reg_u32 = adapter_store_core_reg_u32;
180
181 armv7m->examine_debug_reason = adapter_examine_debug_reason;
182 armv7m->is_hla_target = true;
183
184 target_register_timer_callback(hl_handle_target_request, 1,
185 TARGET_TIMER_TYPE_PERIODIC, target);
186
187 return ERROR_OK;
188 }
189
190 static int adapter_init_target(struct command_context *cmd_ctx,
191 struct target *target)
192 {
193 LOG_DEBUG("%s", __func__);
194
195 armv7m_build_reg_cache(target);
196 arm_semihosting_init(target);
197 return ERROR_OK;
198 }
199
200 static int adapter_target_create(struct target *target,
201 Jim_Interp *interp)
202 {
203 LOG_DEBUG("%s", __func__);
204 struct adiv5_private_config *pc = target->private_config;
205 if (pc && pc->ap_num > 0) {
206 LOG_ERROR("hla_target: invalid parameter -ap-num (> 0)");
207 return ERROR_COMMAND_SYNTAX_ERROR;
208 }
209
210 struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common));
211 if (!cortex_m) {
212 LOG_ERROR("No memory creating target");
213 return ERROR_FAIL;
214 }
215
216 adapter_init_arch_info(target, cortex_m, target->tap);
217
218 return ERROR_OK;
219 }
220
221 static int adapter_load_context(struct target *target)
222 {
223 struct armv7m_common *armv7m = target_to_armv7m(target);
224 int num_regs = armv7m->arm.core_cache->num_regs;
225
226 for (int i = 0; i < num_regs; i++) {
227
228 struct reg *r = &armv7m->arm.core_cache->reg_list[i];
229 if (r->exist && !r->valid)
230 armv7m->arm.read_core_reg(target, r, i, ARM_MODE_ANY);
231 }
232
233 return ERROR_OK;
234 }
235
236 static int adapter_debug_entry(struct target *target)
237 {
238 struct hl_interface_s *adapter = target_to_adapter(target);
239 struct armv7m_common *armv7m = target_to_armv7m(target);
240 struct arm *arm = &armv7m->arm;
241 struct reg *r;
242 uint32_t xPSR;
243 int retval;
244
245 /* preserve the DCRDR across halts */
246 retval = target_read_u32(target, DCB_DCRDR, &target->SAVED_DCRDR);
247 if (retval != ERROR_OK)
248 return retval;
249
250 retval = armv7m->examine_debug_reason(target);
251 if (retval != ERROR_OK)
252 return retval;
253
254 adapter_load_context(target);
255
256 /* make sure we clear the vector catch bit */
257 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DEMCR, TRCENA);
258
259 r = arm->cpsr;
260 xPSR = buf_get_u32(r->value, 0, 32);
261
262 /* Are we in an exception handler */
263 if (xPSR & 0x1FF) {
264 armv7m->exception_number = (xPSR & 0x1FF);
265
266 arm->core_mode = ARM_MODE_HANDLER;
267 arm->map = armv7m_msp_reg_map;
268 } else {
269 unsigned control = buf_get_u32(arm->core_cache
270 ->reg_list[ARMV7M_CONTROL].value, 0, 3);
271
272 /* is this thread privileged? */
273 arm->core_mode = control & 1
274 ? ARM_MODE_USER_THREAD
275 : ARM_MODE_THREAD;
276
277 /* which stack is it using? */
278 if (control & 2)
279 arm->map = armv7m_psp_reg_map;
280 else
281 arm->map = armv7m_msp_reg_map;
282
283 armv7m->exception_number = 0;
284 }
285
286 LOG_DEBUG("entered debug state in core mode: %s at PC 0x%08" PRIx32 ", target->state: %s",
287 arm_mode_name(arm->core_mode),
288 buf_get_u32(arm->pc->value, 0, 32),
289 target_state_name(target));
290
291 return retval;
292 }
293
294 static int adapter_poll(struct target *target)
295 {
296 enum target_state state;
297 struct hl_interface_s *adapter = target_to_adapter(target);
298 struct armv7m_common *armv7m = target_to_armv7m(target);
299 enum target_state prev_target_state = target->state;
300
301 state = adapter->layout->api->state(adapter->handle);
302
303 if (state == TARGET_UNKNOWN) {
304 LOG_ERROR("jtag status contains invalid mode value - communication failure");
305 return ERROR_TARGET_FAILURE;
306 }
307
308 if (prev_target_state == state)
309 return ERROR_OK;
310
311 if (prev_target_state == TARGET_DEBUG_RUNNING && state == TARGET_RUNNING)
312 return ERROR_OK;
313
314 target->state = state;
315
316 if (state == TARGET_HALTED) {
317
318 int retval = adapter_debug_entry(target);
319 if (retval != ERROR_OK)
320 return retval;
321
322 if (prev_target_state == TARGET_DEBUG_RUNNING) {
323 target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
324 } else {
325 if (arm_semihosting(target, &retval) != 0)
326 return retval;
327
328 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
329 }
330
331 LOG_DEBUG("halted: PC: 0x%08" PRIx32, buf_get_u32(armv7m->arm.pc->value, 0, 32));
332 }
333
334 return ERROR_OK;
335 }
336
337 static int hl_assert_reset(struct target *target)
338 {
339 int res = ERROR_OK;
340 struct hl_interface_s *adapter = target_to_adapter(target);
341 struct armv7m_common *armv7m = target_to_armv7m(target);
342 bool use_srst_fallback = true;
343
344 LOG_DEBUG("%s", __func__);
345
346 enum reset_types jtag_reset_config = jtag_get_reset_config();
347
348 bool srst_asserted = false;
349
350 if ((jtag_reset_config & RESET_HAS_SRST) &&
351 (jtag_reset_config & RESET_SRST_NO_GATING)) {
352 res = adapter_assert_reset();
353 srst_asserted = true;
354 }
355
356 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DHCSR, DBGKEY|C_DEBUGEN);
357
358 /* only set vector catch if halt is requested */
359 if (target->reset_halt)
360 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DEMCR, TRCENA|VC_CORERESET);
361 else
362 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DEMCR, TRCENA);
363
364 if (jtag_reset_config & RESET_HAS_SRST) {
365 if (!srst_asserted) {
366 res = adapter_assert_reset();
367 }
368 if (res == ERROR_COMMAND_NOTFOUND)
369 LOG_ERROR("Hardware srst not supported, falling back to software reset");
370 else if (res == ERROR_OK) {
371 /* hardware srst supported */
372 use_srst_fallback = false;
373 }
374 }
375
376 if (use_srst_fallback) {
377 /* stlink v1 api does not support hardware srst, so we use a software reset fallback */
378 adapter->layout->api->write_debug_reg(adapter->handle, NVIC_AIRCR, AIRCR_VECTKEY | AIRCR_SYSRESETREQ);
379 }
380
381 res = adapter->layout->api->reset(adapter->handle);
382
383 if (res != ERROR_OK)
384 return res;
385
386 /* registers are now invalid */
387 register_cache_invalidate(armv7m->arm.core_cache);
388
389 if (target->reset_halt) {
390 target->state = TARGET_RESET;
391 target->debug_reason = DBG_REASON_DBGRQ;
392 } else {
393 target->state = TARGET_HALTED;
394 }
395
396 return ERROR_OK;
397 }
398
399 static int hl_deassert_reset(struct target *target)
400 {
401 enum reset_types jtag_reset_config = jtag_get_reset_config();
402
403 LOG_DEBUG("%s", __func__);
404
405 if (jtag_reset_config & RESET_HAS_SRST)
406 adapter_deassert_reset();
407
408 target->SAVED_DCRDR = 0; /* clear both DCC busy bits on initial resume */
409
410 return target->reset_halt ? ERROR_OK : target_resume(target, 1, 0, 0, 0);
411 }
412
413 static int adapter_halt(struct target *target)
414 {
415 int res;
416 struct hl_interface_s *adapter = target_to_adapter(target);
417
418 LOG_DEBUG("%s", __func__);
419
420 if (target->state == TARGET_HALTED) {
421 LOG_DEBUG("target was already halted");
422 return ERROR_OK;
423 }
424
425 if (target->state == TARGET_UNKNOWN)
426 LOG_WARNING("target was in unknown state when halt was requested");
427
428 res = adapter->layout->api->halt(adapter->handle);
429
430 if (res != ERROR_OK)
431 return res;
432
433 target->debug_reason = DBG_REASON_DBGRQ;
434
435 return ERROR_OK;
436 }
437
438 static int adapter_resume(struct target *target, int current,
439 target_addr_t address, int handle_breakpoints,
440 int debug_execution)
441 {
442 int res;
443 struct hl_interface_s *adapter = target_to_adapter(target);
444 struct armv7m_common *armv7m = target_to_armv7m(target);
445 uint32_t resume_pc;
446 struct breakpoint *breakpoint = NULL;
447 struct reg *pc;
448
449 LOG_DEBUG("%s %d " TARGET_ADDR_FMT " %d %d", __func__, current,
450 address, handle_breakpoints, debug_execution);
451
452 if (target->state != TARGET_HALTED) {
453 LOG_WARNING("target not halted");
454 return ERROR_TARGET_NOT_HALTED;
455 }
456
457 if (!debug_execution) {
458 target_free_all_working_areas(target);
459 cortex_m_enable_breakpoints(target);
460 cortex_m_enable_watchpoints(target);
461 }
462
463 pc = armv7m->arm.pc;
464 if (!current) {
465 buf_set_u32(pc->value, 0, 32, address);
466 pc->dirty = true;
467 pc->valid = true;
468 }
469
470 if (!breakpoint_find(target, buf_get_u32(pc->value, 0, 32))
471 && !debug_execution) {
472 armv7m_maybe_skip_bkpt_inst(target, NULL);
473 }
474
475 resume_pc = buf_get_u32(pc->value, 0, 32);
476
477 /* write any user vector flags */
478 res = target_write_u32(target, DCB_DEMCR, TRCENA | armv7m->demcr);
479 if (res != ERROR_OK)
480 return res;
481
482 armv7m_restore_context(target);
483
484 /* restore SAVED_DCRDR */
485 res = target_write_u32(target, DCB_DCRDR, target->SAVED_DCRDR);
486 if (res != ERROR_OK)
487 return res;
488
489 /* registers are now invalid */
490 register_cache_invalidate(armv7m->arm.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 " TARGET_ADDR_FMT " (ID: %" PRIu32 ")",
498 breakpoint->address,
499 breakpoint->unique_id);
500 cortex_m_unset_breakpoint(target, breakpoint);
501
502 res = adapter->layout->api->step(adapter->handle);
503
504 if (res != ERROR_OK)
505 return res;
506
507 cortex_m_set_breakpoint(target, breakpoint);
508 }
509 }
510
511 res = adapter->layout->api->run(adapter->handle);
512
513 if (res != ERROR_OK)
514 return res;
515
516 target->debug_reason = DBG_REASON_NOTHALTED;
517
518 if (!debug_execution) {
519 target->state = TARGET_RUNNING;
520 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
521 } else {
522 target->state = TARGET_DEBUG_RUNNING;
523 target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
524 }
525
526 return ERROR_OK;
527 }
528
529 static int adapter_step(struct target *target, int current,
530 target_addr_t address, int handle_breakpoints)
531 {
532 int res;
533 struct hl_interface_s *adapter = target_to_adapter(target);
534 struct armv7m_common *armv7m = target_to_armv7m(target);
535 struct breakpoint *breakpoint = NULL;
536 struct reg *pc = armv7m->arm.pc;
537 bool bkpt_inst_found = false;
538
539 LOG_DEBUG("%s", __func__);
540
541 if (target->state != TARGET_HALTED) {
542 LOG_WARNING("target not halted");
543 return ERROR_TARGET_NOT_HALTED;
544 }
545
546 if (!current) {
547 buf_set_u32(pc->value, 0, 32, address);
548 pc->dirty = true;
549 pc->valid = true;
550 }
551
552 uint32_t pc_value = buf_get_u32(pc->value, 0, 32);
553
554 /* the front-end may request us not to handle breakpoints */
555 if (handle_breakpoints) {
556 breakpoint = breakpoint_find(target, pc_value);
557 if (breakpoint)
558 cortex_m_unset_breakpoint(target, breakpoint);
559 }
560
561 armv7m_maybe_skip_bkpt_inst(target, &bkpt_inst_found);
562
563 target->debug_reason = DBG_REASON_SINGLESTEP;
564
565 armv7m_restore_context(target);
566
567 /* restore SAVED_DCRDR */
568 res = target_write_u32(target, DCB_DCRDR, target->SAVED_DCRDR);
569 if (res != ERROR_OK)
570 return res;
571
572 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
573
574 res = adapter->layout->api->step(adapter->handle);
575
576 if (res != ERROR_OK)
577 return res;
578
579 /* registers are now invalid */
580 register_cache_invalidate(armv7m->arm.core_cache);
581
582 if (breakpoint)
583 cortex_m_set_breakpoint(target, breakpoint);
584
585 adapter_debug_entry(target);
586 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
587
588 LOG_INFO("halted: PC: 0x%08" PRIx32, buf_get_u32(armv7m->arm.pc->value, 0, 32));
589
590 return ERROR_OK;
591 }
592
593 static int adapter_read_memory(struct target *target, target_addr_t address,
594 uint32_t size, uint32_t count,
595 uint8_t *buffer)
596 {
597 struct hl_interface_s *adapter = target_to_adapter(target);
598
599 if (!count || !buffer)
600 return ERROR_COMMAND_SYNTAX_ERROR;
601
602 LOG_DEBUG("%s " TARGET_ADDR_FMT " %" PRIu32 " %" PRIu32,
603 __func__, address, size, count);
604
605 return adapter->layout->api->read_mem(adapter->handle, address, size, count, buffer);
606 }
607
608 static int adapter_write_memory(struct target *target, target_addr_t address,
609 uint32_t size, uint32_t count,
610 const uint8_t *buffer)
611 {
612 struct hl_interface_s *adapter = target_to_adapter(target);
613
614 if (!count || !buffer)
615 return ERROR_COMMAND_SYNTAX_ERROR;
616
617 LOG_DEBUG("%s " TARGET_ADDR_FMT " %" PRIu32 " %" PRIu32,
618 __func__, address, size, count);
619
620 return adapter->layout->api->write_mem(adapter->handle, address, size, count, buffer);
621 }
622
623 static const struct command_registration adapter_command_handlers[] = {
624 {
625 .chain = arm_command_handlers,
626 },
627 {
628 .chain = armv7m_trace_command_handlers,
629 },
630 {
631 .chain = rtt_target_command_handlers,
632 },
633 /* START_DEPRECATED_TPIU */
634 {
635 .chain = arm_tpiu_deprecated_command_handlers,
636 },
637 /* END_DEPRECATED_TPIU */
638 COMMAND_REGISTRATION_DONE
639 };
640
641 struct target_type hla_target = {
642 .name = "hla_target",
643
644 .init_target = adapter_init_target,
645 .deinit_target = cortex_m_deinit_target,
646 .target_create = adapter_target_create,
647 .target_jim_configure = adiv5_jim_configure,
648 .examine = cortex_m_examine,
649 .commands = adapter_command_handlers,
650
651 .poll = adapter_poll,
652 .arch_state = armv7m_arch_state,
653
654 .target_request_data = hl_target_request_data,
655 .assert_reset = hl_assert_reset,
656 .deassert_reset = hl_deassert_reset,
657
658 .halt = adapter_halt,
659 .resume = adapter_resume,
660 .step = adapter_step,
661
662 .get_gdb_arch = arm_get_gdb_arch,
663 .get_gdb_reg_list = armv7m_get_gdb_reg_list,
664
665 .read_memory = adapter_read_memory,
666 .write_memory = adapter_write_memory,
667 .checksum_memory = armv7m_checksum_memory,
668 .blank_check_memory = armv7m_blank_check_memory,
669
670 .run_algorithm = armv7m_run_algorithm,
671 .start_algorithm = armv7m_start_algorithm,
672 .wait_algorithm = armv7m_wait_algorithm,
673
674 .add_breakpoint = cortex_m_add_breakpoint,
675 .remove_breakpoint = cortex_m_remove_breakpoint,
676 .add_watchpoint = cortex_m_add_watchpoint,
677 .remove_watchpoint = cortex_m_remove_watchpoint,
678 .profiling = cortex_m_profiling,
679 };

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)