4739237c8db3895bdb0712b5bb0c596b1e96bdfa
[openocd.git] / src / target / arm946e.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 * *
8 * Copyright (C) 2010 by Drasko DRASKOVIC *
9 * drasko.draskovic@gmail.com *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "arm946e.h"
32 #include "target_type.h"
33 #include "arm_opcodes.h"
34
35 #include "breakpoints.h"
36
37 #if 0
38 #define _DEBUG_INSTRUCTION_EXECUTION_
39 #endif
40
41 #define NB_CACHE_WAYS 4
42
43 static uint32_t dc;
44 static uint32_t ic;
45
46 /**
47 * flag to give info about cache manipulation during debug :
48 * "0" - cache lines are invalidated "on the fly", for affected addresses.
49 * This is prefered from performance point of view.
50 * "1" - cache is invalidated and switched off on debug_entry, and switched back on on restore.
51 * It is kept off during debugging.
52 */
53 static uint8_t arm946e_preserve_cache;
54
55 int arm946e_post_debug_entry(struct target *target);
56 void arm946e_pre_restore_context(struct target *target);
57 static int arm946e_read_cp15(struct target *target, int reg_addr, uint32_t *value);
58
59 int arm946e_init_arch_info(struct target *target,
60 struct arm946e_common *arm946e,
61 struct jtag_tap *tap)
62 {
63 struct arm7_9_common *arm7_9 = &arm946e->arm7_9_common;
64
65 /* initialize arm7/arm9 specific info (including armv4_5) */
66 arm9tdmi_init_arch_info(target, arm7_9, tap);
67
68 arm946e->common_magic = ARM946E_COMMON_MAGIC;
69
70 /**
71 * The ARM946E-S implements the ARMv5TE architecture which
72 * has the BKPT instruction, so we don't have to use a watchpoint comparator
73 */
74 arm7_9->arm_bkpt = ARMV5_BKPT(0x0);
75 arm7_9->thumb_bkpt = ARMV5_T_BKPT(0x0) & 0xffff;
76
77
78 arm7_9->post_debug_entry = arm946e_post_debug_entry;
79 arm7_9->pre_restore_context = arm946e_pre_restore_context;
80
81 /**
82 * disabling linefills leads to lockups, so keep them enabled for now
83 * this doesn't affect correctness, but might affect timing issues, if
84 * important data is evicted from the cache during the debug session
85 */
86 arm946e_preserve_cache = 0;
87
88 /* override hw single-step capability from ARM9TDMI */
89 /* arm7_9->has_single_step = 1; */
90
91 return ERROR_OK;
92 }
93
94 static int arm946e_target_create(struct target *target, Jim_Interp *interp)
95 {
96 struct arm946e_common *arm946e = calloc(1, sizeof(struct arm946e_common));
97
98 arm946e_init_arch_info(target, arm946e, target->tap);
99
100 return ERROR_OK;
101 }
102
103 static int arm946e_verify_pointer(struct command_context *cmd_ctx,
104 struct arm946e_common *arm946e)
105 {
106 if (arm946e->common_magic != ARM946E_COMMON_MAGIC) {
107 command_print(cmd_ctx, "target is not an ARM946");
108 return ERROR_TARGET_INVALID;
109 }
110 return ERROR_OK;
111 }
112
113 /*
114 * REVISIT: The "read_cp15" and "write_cp15" commands could hook up
115 * to eventual mrc() and mcr() routines ... the reg_addr values being
116 * constructed (for CP15 only) from Opcode_1, Opcode_2, and CRn values.
117 * See section 7.3 of the ARM946E-S TRM.
118 */
119 static int arm946e_read_cp15(struct target *target, int reg_addr, uint32_t *value)
120 {
121 int retval = ERROR_OK;
122 struct arm7_9_common *arm7_9 = target_to_arm7_9(target);
123 struct arm_jtag *jtag_info = &arm7_9->jtag_info;
124 struct scan_field fields[3];
125 uint8_t reg_addr_buf = reg_addr & 0x3f;
126 uint8_t nr_w_buf = 0;
127
128 retval = arm_jtag_scann(jtag_info, 0xf, TAP_IDLE);
129 if (retval != ERROR_OK)
130 return retval;
131 retval = arm_jtag_set_instr(jtag_info, jtag_info->intest_instr, NULL, TAP_IDLE);
132 if (retval != ERROR_OK)
133 return retval;
134
135 fields[0].num_bits = 32;
136 /* REVISIT: table 7-2 shows that bits 31-31 need to be
137 * specified for accessing BIST registers ...
138 */
139 fields[0].out_value = NULL;
140 fields[0].in_value = NULL;
141
142 fields[1].num_bits = 6;
143 fields[1].out_value = &reg_addr_buf;
144 fields[1].in_value = NULL;
145
146 fields[2].num_bits = 1;
147 fields[2].out_value = &nr_w_buf;
148 fields[2].in_value = NULL;
149
150 jtag_add_dr_scan(jtag_info->tap, 3, fields, TAP_IDLE);
151
152 fields[0].in_value = (uint8_t *)value;
153 jtag_add_dr_scan(jtag_info->tap, 3, fields, TAP_IDLE);
154
155 jtag_add_callback(arm_le_to_h_u32, (jtag_callback_data_t)value);
156
157 #ifdef _DEBUG_INSTRUCTION_EXECUTION_
158 LOG_DEBUG("addr: 0x%x value: %8.8x", reg_addr, *value);
159 #endif
160
161 retval = jtag_execute_queue();
162 if (retval != ERROR_OK)
163 return retval;
164
165 return ERROR_OK;
166 }
167
168 int arm946e_write_cp15(struct target *target, int reg_addr, uint32_t value)
169 {
170 int retval = ERROR_OK;
171 struct arm7_9_common *arm7_9 = target_to_arm7_9(target);
172 struct arm_jtag *jtag_info = &arm7_9->jtag_info;
173 struct scan_field fields[3];
174 uint8_t reg_addr_buf = reg_addr & 0x3f;
175 uint8_t nr_w_buf = 1;
176 uint8_t value_buf[4];
177
178 buf_set_u32(value_buf, 0, 32, value);
179
180 retval = arm_jtag_scann(jtag_info, 0xf, TAP_IDLE);
181 if (retval != ERROR_OK)
182 return retval;
183 retval = arm_jtag_set_instr(jtag_info, jtag_info->intest_instr, NULL, TAP_IDLE);
184 if (retval != ERROR_OK)
185 return retval;
186
187 fields[0].num_bits = 32;
188 fields[0].out_value = value_buf;
189 fields[0].in_value = NULL;
190
191 fields[1].num_bits = 6;
192 fields[1].out_value = &reg_addr_buf;
193 fields[1].in_value = NULL;
194
195 fields[2].num_bits = 1;
196 fields[2].out_value = &nr_w_buf;
197 fields[2].in_value = NULL;
198
199 jtag_add_dr_scan(jtag_info->tap, 3, fields, TAP_IDLE);
200
201 #ifdef _DEBUG_INSTRUCTION_EXECUTION_
202 LOG_DEBUG("addr: 0x%x value: %8.8x", reg_addr, value);
203 #endif
204
205 retval = jtag_execute_queue();
206 if (retval != ERROR_OK)
207 return retval;
208
209 return ERROR_OK;
210 }
211
212 uint32_t arm946e_invalidate_whole_dcache(struct target *target)
213 {
214
215 uint32_t csize = 0;
216 uint32_t shift = 0;
217 uint32_t cp15_idx, seg, dtag;
218 int nb_idx, idx = 0;
219 int retval;
220
221 /* Get cache type */
222 arm946e_read_cp15(target, 0x01, (uint32_t *) &csize);
223
224 csize = (csize >> 18) & 0x0F;
225
226 if (csize == 0)
227 shift = 0;
228 else
229 shift = csize - 0x3; /* Now 0 = 4KB, 1 = 8KB, ... */
230
231 /* Cache size, given in bytes */
232 csize = 1 << (12 + shift);
233 /* One line (index) is 32 bytes (8 words) long */
234 nb_idx = (csize / 32); /* gives nb of lines (indexes) in the cache */
235
236 /* Loop for all segmentde (i.e. ways) */
237 for (seg = 0; seg < NB_CACHE_WAYS; seg++) {
238 /* Loop for all indexes */
239 for (idx = 0; idx < nb_idx; idx++) {
240 /* Form and write cp15 index (segment + line idx) */
241 cp15_idx = seg << 30 | idx << 5;
242 retval = arm946e_write_cp15(target, 0x3a, cp15_idx);
243 if (retval != ERROR_OK) {
244 LOG_DEBUG("ERROR writing index");
245 return retval;
246 }
247
248 /* Read dtag */
249 arm946e_read_cp15(target, 0x16, (uint32_t *) &dtag);
250
251 /* Check cache line VALID bit */
252 if (!(dtag >> 4 & 0x1))
253 continue;
254
255 /* Clean data cache line */
256 retval = arm946e_write_cp15(target, 0x35, 0x1);
257 if (retval != ERROR_OK) {
258 LOG_DEBUG("ERROR cleaning cache line");
259 return retval;
260 }
261
262 /* Flush data cache line */
263 retval = arm946e_write_cp15(target, 0x1a, 0x1);
264 if (retval != ERROR_OK) {
265 LOG_DEBUG("ERROR flushing cache line");
266 return retval;
267 }
268 }
269 }
270
271 return ERROR_OK;
272 }
273
274 uint32_t arm946e_invalidate_whole_icache(struct target *target)
275 {
276 int retval;
277
278 LOG_DEBUG("FLUSHING I$");
279
280 /**
281 * Invalidate (flush) I$
282 * mcr 15, 0, r0, cr7, cr5, {0}
283 */
284 retval = arm946e_write_cp15(target, 0x0f, 0x1);
285 if (retval != ERROR_OK) {
286 LOG_DEBUG("ERROR flushing I$");
287 return retval;
288 }
289
290 return ERROR_OK;
291 }
292
293 int arm946e_post_debug_entry(struct target *target)
294 {
295 uint32_t ctr_reg = 0x0;
296 uint32_t retval = ERROR_OK;
297
298 /* See if CACHES are enabled, and save that info
299 * in the global vars, so that arm946e_pre_restore_context() can use them */
300 arm946e_read_cp15(target, 0x02, (uint32_t *) &ctr_reg);
301 dc = (ctr_reg >> 2) & 0x01;
302 ic = (ctr_reg >> 12) & 0x01;
303
304 if (arm946e_preserve_cache) {
305 if (dc == 1) {
306 /* Clean and flush D$ */
307 arm946e_invalidate_whole_dcache(target);
308
309 /* Disable D$ */
310 ctr_reg &= ~(1 << 2);
311 }
312
313 if (ic == 1) {
314 /* Flush I$ */
315 arm946e_invalidate_whole_icache(target);
316
317 /* Disable I$ */
318 ctr_reg &= ~(1 << 12);
319 }
320
321 /* Write the new configuration */
322 retval = arm946e_write_cp15(target, 0x02, ctr_reg);
323 if (retval != ERROR_OK) {
324 LOG_DEBUG("ERROR disabling cache");
325 return retval;
326 }
327 } /* if preserve_cache */
328
329 return ERROR_OK;
330 }
331
332 void arm946e_pre_restore_context(struct target *target)
333 {
334 uint32_t ctr_reg = 0x0;
335 uint32_t retval;
336
337 if (arm946e_preserve_cache) {
338 /* Get the contents of the CTR reg */
339 arm946e_read_cp15(target, 0x02, (uint32_t *) &ctr_reg);
340
341 /**
342 * Read-modify-write CP15 test state register
343 * to reenable I/D-cache linefills
344 */
345 if (dc == 1) {
346 /* Enable D$ */
347 ctr_reg |= 1 << 2;
348 }
349
350 if (ic == 1) {
351 /* Enable I$ */
352 ctr_reg |= 1 << 12;
353 }
354
355 /* Write the new configuration */
356 retval = arm946e_write_cp15(target, 0x02, ctr_reg);
357 if (retval != ERROR_OK)
358 LOG_DEBUG("ERROR enabling cache");
359 } /* if preserve_cache */
360 }
361
362 uint32_t arm946e_invalidate_dcache(struct target *target, uint32_t address,
363 uint32_t size, uint32_t count)
364 {
365 uint32_t csize = 0x0;
366 uint32_t shift = 0;
367 uint32_t cur_addr = 0x0;
368 uint32_t cp15_idx, set, way, dtag;
369 uint32_t i = 0;
370 int retval;
371
372 for (i = 0; i < count*size; i++) {
373 cur_addr = address + i;
374
375 /* Get cache type */
376 arm946e_read_cp15(target, 0x01, (uint32_t *) &csize);
377
378 /* Conclude cache size to find number of lines */
379 csize = (csize >> 18) & 0x0F;
380
381 if (csize == 0)
382 shift = 0;
383 else
384 shift = csize - 0x3; /* Now 0 = 4KB, 1 = 8KB, ... */
385
386 csize = 1 << (12 + shift);
387
388 set = (cur_addr >> 5) & 0xff; /* set field is 8 bits long */
389
390 for (way = 0; way < NB_CACHE_WAYS; way++) {
391 /**
392 * Find if the affected address is kept in the cache.
393 * Because JTAG Scan Chain 15 offers limited approach,
394 * we have to loop through all cache ways (segments) and
395 * read cache tags, then compare them with with address.
396 */
397
398 /* Form and write cp15 index (segment + line idx) */
399 cp15_idx = way << 30 | set << 5;
400 retval = arm946e_write_cp15(target, 0x3a, cp15_idx);
401 if (retval != ERROR_OK) {
402 LOG_DEBUG("ERROR writing index");
403 return retval;
404 }
405
406 /* Read dtag */
407 arm946e_read_cp15(target, 0x16, (uint32_t *) &dtag);
408
409 /* Check cache line VALID bit */
410 if (!(dtag >> 4 & 0x1))
411 continue;
412
413 /* If line is valid and corresponds to affected address - invalidate it */
414 if (dtag >> 5 == cur_addr >> 5) {
415 /* Clean data cache line */
416 retval = arm946e_write_cp15(target, 0x35, 0x1);
417 if (retval != ERROR_OK) {
418 LOG_DEBUG("ERROR cleaning cache line");
419 return retval;
420 }
421
422 /* Flush data cache line */
423 retval = arm946e_write_cp15(target, 0x1c, 0x1);
424 if (retval != ERROR_OK) {
425 LOG_DEBUG("ERROR flushing cache line");
426 return retval;
427 }
428
429 break;
430 }
431 } /* loop through all 4 ways */
432 } /* loop through all addresses */
433
434 return ERROR_OK;
435 }
436
437 uint32_t arm946e_invalidate_icache(struct target *target, uint32_t address,
438 uint32_t size, uint32_t count)
439 {
440 uint32_t cur_addr = 0x0;
441 uint32_t cp15_idx, set, way, itag;
442 uint32_t i = 0;
443 int retval;
444
445 for (i = 0; i < count*size; i++) {
446 cur_addr = address + i;
447
448 set = (cur_addr >> 5) & 0xff; /* set field is 8 bits long */
449
450 for (way = 0; way < NB_CACHE_WAYS; way++) {
451 /* Form and write cp15 index (segment + line idx) */
452 cp15_idx = way << 30 | set << 5;
453 retval = arm946e_write_cp15(target, 0x3a, cp15_idx);
454 if (retval != ERROR_OK) {
455 LOG_DEBUG("ERROR writing index");
456 return retval;
457 }
458
459 /* Read itag */
460 arm946e_read_cp15(target, 0x17, (uint32_t *) &itag);
461
462 /* Check cache line VALID bit */
463 if (!(itag >> 4 & 0x1))
464 continue;
465
466 /* If line is valid and corresponds to affected address - invalidate it */
467 if (itag >> 5 == cur_addr >> 5) {
468 /* Flush I$ line */
469 retval = arm946e_write_cp15(target, 0x1d, 0x0);
470 if (retval != ERROR_OK) {
471 LOG_DEBUG("ERROR flushing cache line");
472 return retval;
473 }
474
475 break;
476 }
477 } /* way loop */
478 } /* addr loop */
479
480 return ERROR_OK;
481 }
482
483 /** Writes a buffer, in the specified word size, with current MMU settings. */
484 int arm946e_write_memory(struct target *target, uint32_t address,
485 uint32_t size, uint32_t count, const uint8_t *buffer)
486 {
487 int retval;
488
489 LOG_DEBUG("-");
490
491 /* Invalidate D$ if it is ON */
492 if (!arm946e_preserve_cache && dc == 1)
493 arm946e_invalidate_dcache(target, address, size, count);
494
495 /**
496 * Write memory
497 */
498 retval = arm7_9_write_memory(target, address, size, count, buffer);
499 if (retval != ERROR_OK)
500 return retval;
501
502 /* *
503 * Invalidate I$ if it is ON.
504 *
505 * D$ has been cleaned and flushed before mem write thus forcing it to behave like write-through,
506 * because arm7_9_write_memory() has seen non-valid bit in D$
507 * and wrote data into physical RAM (without touching or allocating the cache line).
508 * From ARM946ES Technical Reference Manual we can see that it uses "allocate on read-miss"
509 * policy for both I$ and D$ (Chapter 3.2 and 3.3)
510 *
511 * Explanation :
512 * "ARM system developer's guide: designing and optimizing system software" by
513 * Andrew N. Sloss, Dominic Symes and Chris Wright,
514 * Chapter 12.3.3 Allocating Policy on a Cache Miss :
515 * A read allocate on cache miss policy allocates a cache line only during a read from main memory.
516 * If the victim cache line contains valid data, then it is written to main memory before the cache line
517 * is filled with new data.
518 * Under this strategy, a write of new data to memory does not update the contents of the cache memory
519 * unless a cache line was allocated on a previous read from main memory.
520 * If the cache line contains valid data, then the write updates the cache and may update the main memory if
521 * the cache write policy is write-through.
522 * If the data is not in the cache, the controller writes to main memory only.
523 */
524 if (!arm946e_preserve_cache && ic == 1)
525 arm946e_invalidate_icache(target, address, size, count);
526
527 return ERROR_OK;
528
529 }
530
531 int arm946e_read_memory(struct target *target, uint32_t address,
532 uint32_t size, uint32_t count, uint8_t *buffer)
533 {
534 int retval;
535
536 LOG_DEBUG("-");
537
538 retval = arm7_9_read_memory(target, address, size, count, buffer);
539 if (retval != ERROR_OK)
540 return retval;
541
542 return ERROR_OK;
543 }
544
545
546 COMMAND_HANDLER(arm946e_handle_cp15_command)
547 {
548 int retval;
549 struct target *target = get_current_target(CMD_CTX);
550 struct arm946e_common *arm946e = target_to_arm946(target);
551
552 retval = arm946e_verify_pointer(CMD_CTX, arm946e);
553 if (retval != ERROR_OK)
554 return retval;
555
556 if (target->state != TARGET_HALTED) {
557 command_print(CMD_CTX, "target must be stopped for \"%s\" command", CMD_NAME);
558 return ERROR_OK;
559 }
560
561 /* one or more argument, access a single register (write if second argument is given */
562 if (CMD_ARGC >= 1) {
563 uint32_t address;
564 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
565
566 if (CMD_ARGC == 1) {
567 uint32_t value;
568 retval = arm946e_read_cp15(target, address, &value);
569 if (retval != ERROR_OK) {
570 command_print(CMD_CTX, "couldn't access reg %" PRIi32, address);
571 return ERROR_OK;
572 }
573 retval = jtag_execute_queue();
574 if (retval != ERROR_OK)
575 return retval;
576
577 command_print(CMD_CTX, "%" PRIi32 ": %8.8" PRIx32, address, value);
578 } else if (CMD_ARGC == 2) {
579 uint32_t value;
580 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
581 retval = arm946e_write_cp15(target, address, value);
582 if (retval != ERROR_OK) {
583 command_print(CMD_CTX, "couldn't access reg %" PRIi32, address);
584 return ERROR_OK;
585 }
586 command_print(CMD_CTX, "%" PRIi32 ": %8.8" PRIx32, address, value);
587 }
588 }
589
590 return ERROR_OK;
591 }
592
593 static const struct command_registration arm946e_exec_command_handlers[] = {
594 {
595 .name = "cp15",
596 .handler = arm946e_handle_cp15_command,
597 .mode = COMMAND_EXEC,
598 .usage = "regnum [value]",
599 .help = "display/modify cp15 register",
600 },
601 COMMAND_REGISTRATION_DONE
602 };
603
604 const struct command_registration arm946e_command_handlers[] = {
605 {
606 .chain = arm9tdmi_command_handlers,
607 },
608 {
609 .name = "arm946e",
610 .mode = COMMAND_ANY,
611 .help = "arm946e command group",
612 .usage = "",
613 .chain = arm946e_exec_command_handlers,
614 },
615 COMMAND_REGISTRATION_DONE
616 };
617
618 /** Holds methods for ARM946 targets. */
619 struct target_type arm946e_target = {
620 .name = "arm946e",
621
622 .poll = arm7_9_poll,
623 .arch_state = arm_arch_state,
624
625 .target_request_data = arm7_9_target_request_data,
626
627 .halt = arm7_9_halt,
628 .resume = arm7_9_resume,
629 .step = arm7_9_step,
630
631 .assert_reset = arm7_9_assert_reset,
632 .deassert_reset = arm7_9_deassert_reset,
633 .soft_reset_halt = arm7_9_soft_reset_halt,
634
635 .get_gdb_reg_list = arm_get_gdb_reg_list,
636
637 /* .read_memory = arm7_9_read_memory, */
638 /* .write_memory = arm7_9_write_memory, */
639 .read_memory = arm946e_read_memory,
640 .write_memory = arm946e_write_memory,
641
642 .bulk_write_memory = arm7_9_bulk_write_memory,
643
644 .checksum_memory = arm_checksum_memory,
645 .blank_check_memory = arm_blank_check_memory,
646
647 .run_algorithm = armv4_5_run_algorithm,
648
649 .add_breakpoint = arm7_9_add_breakpoint,
650 .remove_breakpoint = arm7_9_remove_breakpoint,
651 /* .add_breakpoint = arm946e_add_breakpoint, */
652 /* .remove_breakpoint = arm946e_remove_breakpoint, */
653
654 .add_watchpoint = arm7_9_add_watchpoint,
655 .remove_watchpoint = arm7_9_remove_watchpoint,
656
657 .commands = arm946e_command_handlers,
658 .target_create = arm946e_target_create,
659 .init_target = arm9tdmi_init_target,
660 .examine = arm7_9_examine,
661 .check_reset = arm7_9_check_reset,
662 };

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)