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

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)