- added support for AT91SAM7A3 flash (patch from andre renaud, thanks)
[openocd.git] / src / jtag / ftdi2232.c
1 /***************************************************************************
2 * Copyright (C) 2004 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
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, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21 /* project specific includes */
22 #include "log.h"
23 #include "types.h"
24 #include "jtag.h"
25 #include "configuration.h"
26 #include "command.h"
27
28 /* system includes */
29 #include <string.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <usb.h>
33 #include <ftdi.h>
34
35 #include <sys/time.h>
36 #include <time.h>
37
38 /* enable this to debug io latency
39 */
40 #if 0
41 #define _DEBUG_USB_IO_
42 #endif
43
44 int ftdi2232_execute_queue(void);
45
46 int ftdi2232_speed(int speed);
47 int ftdi2232_register_commands(struct command_context_s *cmd_ctx);
48 int ftdi2232_init(void);
49 int ftdi2232_quit(void);
50
51 enum { FTDI2232_TRST = 0x10, FTDI2232_SRST = 0x40 };
52 static u8 discrete_output = 0x0 | FTDI2232_TRST | FTDI2232_SRST;
53 static struct ftdi_context ftdic;
54
55 static u8 *ftdi2232_buffer = NULL;
56 static int ftdi2232_buffer_size = 0;
57 static int ftdi2232_read_pointer = 0;
58 static int ftdi2232_expect_read = 0;
59 #define FTDI2232_BUFFER_SIZE 131072
60 #define BUFFER_ADD ftdi2232_buffer[ftdi2232_buffer_size++]
61 #define BUFFER_READ ftdi2232_buffer[ftdi2232_read_pointer++]
62
63 #define FTDI2232_SAVE_SIZE 1024
64
65 int ftdi2232_handle_vid_pid_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
66
67 static u16 ftdi2232_vid = 0x0403;
68 static u16 ftdi2232_pid = 0x6010;
69
70 jtag_interface_t ftdi2232_interface =
71 {
72
73 .name = "ftdi2232",
74
75 .execute_queue = ftdi2232_execute_queue,
76
77 .support_statemove = 1,
78
79 .speed = ftdi2232_speed,
80 .register_commands = ftdi2232_register_commands,
81 .init = ftdi2232_init,
82 .quit = ftdi2232_quit,
83 };
84
85 int ftdi2232_speed(int speed)
86 {
87 u8 buf[3];
88
89 buf[0] = 0x86; /* command "set divisor" */
90 buf[1] = speed & 0xff; /* valueL (0=6MHz, 1=3MHz, 2=1.5MHz, ...*/
91 buf[2] = (speed >> 8) & 0xff; /* valueH */
92
93 DEBUG("%2.2x %2.2x %2.2x", buf[0], buf[1], buf[2]);
94 ftdi_write_data(&ftdic, buf, 3);
95
96 return ERROR_OK;
97 }
98
99 int ftdi2232_register_commands(struct command_context_s *cmd_ctx)
100 {
101 register_command(cmd_ctx, NULL, "ftdi2232_vid_pid", ftdi2232_handle_vid_pid_command,
102 COMMAND_CONFIG, NULL);
103
104 return ERROR_OK;
105 }
106
107 void ftdi2232_end_state(state)
108 {
109 if (tap_move_map[state] != -1)
110 end_state = state;
111 else
112 {
113 ERROR("BUG: %i is not a valid end state", state);
114 exit(-1);
115 }
116 }
117
118 void ftdi2232_read_scan(enum scan_type type, u8* buffer, int scan_size)
119 {
120 int num_bytes = ((scan_size + 7) / 8);
121 int bits_left = scan_size;
122 int cur_byte = 0;
123
124 while(num_bytes-- > 1)
125 {
126 buffer[cur_byte] = BUFFER_READ;
127 cur_byte++;
128 bits_left -= 8;
129 }
130
131 buffer[cur_byte] = 0x0;
132
133 if (bits_left > 1)
134 {
135 buffer[cur_byte] = BUFFER_READ >> 1;
136 }
137
138 buffer[cur_byte] = (buffer[cur_byte] | ((BUFFER_READ & 0x02) << 6)) >> (8 - bits_left);
139
140 }
141
142 void ftdi2232_debug_dump_buffer(void)
143 {
144 int i;
145 for (i = 0; i < ftdi2232_buffer_size; i++)
146 {
147 printf("%2.2x ", ftdi2232_buffer[i]);
148 if (i % 16 == 15)
149 printf("\n");
150 }
151 printf("\n");
152 fflush(stdout);
153 }
154
155 int ftdi2232_send_and_recv(jtag_command_t *first, jtag_command_t *last)
156 {
157 jtag_command_t *cmd;
158 u8 *buffer;
159 int scan_size;
160 enum scan_type type;
161 int retval;
162
163 BUFFER_ADD = 0x87; /* send immediate command */
164
165 if (ftdi2232_buffer_size > FTDI2232_SAVE_SIZE)
166 {
167 ERROR("BUG: ftdi2232_buffer grew beyond %i byte (%i) - this is going to fail", FTDI2232_SAVE_SIZE, ftdi2232_buffer_size);
168 }
169
170 #ifdef _DEBUG_USB_IO_
171 DEBUG("write buffer (size %i):", ftdi2232_buffer_size);
172 ftdi2232_debug_dump_buffer();
173 #endif
174
175 if ((retval = ftdi_write_data(&ftdic, ftdi2232_buffer, ftdi2232_buffer_size)) < 0)
176 {
177 ERROR("ftdi_write_data returned %i", retval);
178 exit(-1);
179 }
180
181 if (ftdi2232_expect_read)
182 {
183 int timeout = 100;
184 ftdi2232_buffer_size = 0;
185
186 while ((ftdi2232_buffer_size < ftdi2232_expect_read) && timeout)
187 {
188 ftdi2232_buffer_size += ftdi_read_data(&ftdic, ftdi2232_buffer + ftdi2232_buffer_size, FTDI2232_BUFFER_SIZE - ftdi2232_buffer_size);
189 timeout--;
190 }
191
192 if (ftdi2232_expect_read != ftdi2232_buffer_size)
193 {
194 ERROR("ftdi2232_expect_read (%i) != ftdi2232_buffer_size (%i) (%i retries)", ftdi2232_expect_read, ftdi2232_buffer_size, 100 - timeout);
195 ftdi2232_debug_dump_buffer();
196
197 exit(-1);
198 }
199
200 #ifdef _DEBUG_USB_IO_
201 DEBUG("read buffer (%i retries): %i bytes", 100 - timeout, ftdi2232_buffer_size);
202 ftdi2232_debug_dump_buffer();
203 #endif
204 }
205
206 ftdi2232_expect_read = 0;
207 ftdi2232_read_pointer = 0;
208
209 cmd = first;
210 while (cmd != last)
211 {
212 switch (cmd->type)
213 {
214 case JTAG_SCAN:
215 type = jtag_scan_type(cmd->cmd.scan);
216 if (type != SCAN_OUT)
217 {
218 scan_size = jtag_scan_size(cmd->cmd.scan);
219 buffer = calloc(CEIL(scan_size, 8), 1);
220 ftdi2232_read_scan(type, buffer, scan_size);
221 jtag_read_buffer(buffer, cmd->cmd.scan);
222 free(buffer);
223 }
224 break;
225 default:
226 break;
227 }
228 cmd = cmd->next;
229 }
230
231 ftdi2232_buffer_size = 0;
232
233 return ERROR_OK;
234 }
235
236 void ftdi2232_add_scan(int ir_scan, enum scan_type type, u8 *buffer, int scan_size)
237 {
238 int num_bytes = (scan_size + 7) / 8;
239 int bits_left = scan_size;
240 int cur_byte = 0;
241 int last_bit;
242
243 /* command "Clock Data to TMS/CS Pin (no Read)" */
244 BUFFER_ADD = 0x4b;
245 /* scan 7 bit */
246 BUFFER_ADD = 0x6;
247 /* TMS data bits */
248 if (ir_scan)
249 {
250 BUFFER_ADD = TAP_MOVE(cur_state, TAP_SI);
251 cur_state = TAP_SI;
252 }
253 else
254 {
255 BUFFER_ADD = TAP_MOVE(cur_state, TAP_SD);
256 cur_state = TAP_SD;
257 }
258 //DEBUG("added TMS scan (no read)");
259
260 /* add command for complete bytes */
261 if (num_bytes > 1)
262 {
263 if (type == SCAN_IO)
264 {
265 /* Clock Data Bytes In and Out LSB First */
266 BUFFER_ADD = 0x39;
267 //DEBUG("added TDI bytes (io %i)", num_bytes);
268 }
269 else if (type == SCAN_OUT)
270 {
271 /* Clock Data Bytes Out on -ve Clock Edge LSB First (no Read) */
272 BUFFER_ADD = 0x19;
273 //DEBUG("added TDI bytes (o)");
274 }
275 else if (type == SCAN_IN)
276 {
277 /* Clock Data Bytes In on +ve Clock Edge LSB First (no Write) */
278 BUFFER_ADD = 0x28;
279 //DEBUG("added TDI bytes (i %i)", num_bytes);
280 }
281 BUFFER_ADD = (num_bytes-2) & 0xff;
282 BUFFER_ADD = ((num_bytes-2) >> 8) & 0xff;
283 }
284 if (type != SCAN_IN)
285 {
286 /* add complete bytes */
287 while(num_bytes-- > 1)
288 {
289 BUFFER_ADD = buffer[cur_byte];
290 cur_byte++;
291 bits_left -= 8;
292 }
293 }
294 if (type == SCAN_IN)
295 {
296 bits_left -= 8 * (num_bytes - 1);
297 }
298
299 /* the most signifcant bit is scanned during TAP movement */
300 if (type != SCAN_IN)
301 last_bit = (buffer[cur_byte] >> (bits_left - 1)) & 0x1;
302 else
303 last_bit = 0;
304
305 /* process remaining bits but the last one */
306 if (bits_left > 1)
307 {
308 if (type == SCAN_IO)
309 {
310 /* Clock Data Bits In and Out LSB First */
311 BUFFER_ADD = 0x3b;
312 //DEBUG("added TDI bits (io) %i", bits_left - 1);
313 }
314 else if (type == SCAN_OUT)
315 {
316 /* Clock Data Bits Out on -ve Clock Edge LSB First (no Read) */
317 BUFFER_ADD = 0x1b;
318 //DEBUG("added TDI bits (o)");
319 }
320 else if (type == SCAN_IN)
321 {
322 /* Clock Data Bits In on +ve Clock Edge LSB First (no Write) */
323 BUFFER_ADD = 0x2a;
324 //DEBUG("added TDI bits (i %i)", bits_left - 1);
325 }
326 BUFFER_ADD = bits_left - 2;
327 if (type != SCAN_IN)
328 BUFFER_ADD = buffer[cur_byte];
329 }
330
331 /* move from Shift-IR/DR to end state */
332 if (type != SCAN_OUT)
333 {
334 /* Clock Data to TMS/CS Pin with Read */
335 BUFFER_ADD = 0x6b;
336 //DEBUG("added TMS scan (read)");
337 }
338 else
339 {
340 /* Clock Data to TMS/CS Pin (no Read) */
341 BUFFER_ADD = 0x4b;
342 //DEBUG("added TMS scan (no read)");
343 }
344 BUFFER_ADD = 0x6;
345 BUFFER_ADD = TAP_MOVE(cur_state, end_state) | (last_bit << 7);
346 cur_state = end_state;
347
348 }
349
350 int ftdi2232_predict_scan_out(int scan_size, enum scan_type type)
351 {
352 int predicted_size = 6;
353 if (type == SCAN_IN) /* only from device to host */
354 {
355 predicted_size += (CEIL(scan_size, 8) > 1) ? 3 : 0;
356 predicted_size += ((scan_size - 1) % 8) ? 2 : 0;
357 }
358 else /* host to device, or bidirectional */
359 {
360 predicted_size += (CEIL(scan_size, 8) > 1) ? (CEIL(scan_size, 8) + 3 - 1) : 0;
361 predicted_size += ((scan_size - 1) % 8) ? 3 : 0;
362 }
363
364 return predicted_size;
365 }
366
367 int ftdi2232_predict_scan_in(int scan_size, enum scan_type type)
368 {
369 int predicted_size = 0;
370
371 if (type != SCAN_OUT)
372 {
373 /* complete bytes */
374 predicted_size += (CEIL(scan_size, 8) > 1) ? (CEIL(scan_size, 8) - 1) : 0;
375 /* remaining bits - 1 */
376 predicted_size += ((scan_size - 1) % 8) ? 1 : 0;
377 /* last bit (from TMS scan) */
378 predicted_size += 1;
379 }
380
381 //DEBUG("scan_size: %i, predicted_size: %i", scan_size, predicted_size);
382
383 return predicted_size;
384 }
385
386 int ftdi2232_execute_queue()
387 {
388 jtag_command_t *cmd = jtag_command_queue; /* currently processed command */
389 jtag_command_t *first_unsent = cmd; /* next command that has to be sent */
390 u8 *buffer;
391 int scan_size; /* size of IR or DR scan */
392 enum scan_type type;
393 int i;
394 int predicted_size = 0;
395 int require_send = 0;
396
397 ftdi2232_buffer_size = 0;
398 ftdi2232_expect_read = 0;
399
400 while (cmd)
401 {
402 switch(cmd->type)
403 {
404 case JTAG_END_STATE:
405 if (cmd->cmd.end_state->end_state != -1)
406 ftdi2232_end_state(cmd->cmd.end_state->end_state);
407 break;
408 case JTAG_RESET:
409 /* only send the maximum buffer size that FT2232C can handle */
410 predicted_size = 3;
411 if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE)
412 {
413 ftdi2232_send_and_recv(first_unsent, cmd);
414 require_send = 0;
415 first_unsent = cmd;
416 }
417
418 if (cmd->cmd.reset->trst == 1)
419 {
420 cur_state = TAP_TLR;
421 discrete_output &= ~FTDI2232_TRST;
422 }
423 else if (cmd->cmd.reset->trst == 0)
424 {
425 discrete_output |= FTDI2232_TRST;
426 }
427
428 if (cmd->cmd.reset->srst == 1)
429 discrete_output &= ~FTDI2232_SRST;
430 else if (cmd->cmd.reset->srst == 0)
431 discrete_output |= FTDI2232_SRST;
432 /* command "set data bits low byte" */
433 BUFFER_ADD = 0x80;
434 /* value (TMS=1,TCK=0, TDI=0, TRST/SRST */
435 BUFFER_ADD = 0x08 | discrete_output;
436 /* dir (output=1), TCK/TDI/TMS=out, TDO=in, TRST/SRST=out */
437 BUFFER_ADD = 0x0b | FTDI2232_SRST | FTDI2232_TRST;
438 require_send = 1;
439 break;
440 case JTAG_RUNTEST:
441 /* only send the maximum buffer size that FT2232C can handle */
442 predicted_size = 0;
443 if (cur_state != TAP_RTI)
444 predicted_size += 3;
445 predicted_size += 3 * CEIL(cmd->cmd.runtest->num_cycles, 7);
446 if ((cmd->cmd.runtest->end_state != -1) && (cmd->cmd.runtest->end_state != TAP_RTI))
447 predicted_size += 3;
448 if ((cmd->cmd.runtest->end_state == -1) && (end_state != TAP_RTI))
449 predicted_size += 3;
450 if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE)
451 {
452 ftdi2232_send_and_recv(first_unsent, cmd);
453 require_send = 0;
454 first_unsent = cmd;
455 }
456 if (cur_state != TAP_RTI)
457 {
458 /* command "Clock Data to TMS/CS Pin (no Read)" */
459 BUFFER_ADD = 0x4b;
460 /* scan 7 bit */
461 BUFFER_ADD = 0x6;
462 /* TMS data bits */
463 BUFFER_ADD = TAP_MOVE(cur_state, TAP_RTI);
464 cur_state = TAP_RTI;
465 require_send = 1;
466 }
467 i = cmd->cmd.runtest->num_cycles;
468 while (i > 0)
469 {
470 /* command "Clock Data to TMS/CS Pin (no Read)" */
471 BUFFER_ADD = 0x4b;
472 /* scan 7 bit */
473 BUFFER_ADD = (i > 7) ? 6 : (i - 1);
474 /* TMS data bits */
475 BUFFER_ADD = 0x0;
476 cur_state = TAP_RTI;
477 i -= (i > 7) ? 7 : i;
478 //DEBUG("added TMS scan (no read)");
479 }
480 if (cmd->cmd.runtest->end_state != -1)
481 ftdi2232_end_state(cmd->cmd.runtest->end_state);
482 if (cur_state != end_state)
483 {
484 /* command "Clock Data to TMS/CS Pin (no Read)" */
485 BUFFER_ADD = 0x4b;
486 /* scan 7 bit */
487 BUFFER_ADD = 0x6;
488 /* TMS data bits */
489 BUFFER_ADD = TAP_MOVE(cur_state, end_state);
490 cur_state = end_state;
491 //DEBUG("added TMS scan (no read)");
492 }
493 require_send = 1;
494 break;
495 case JTAG_STATEMOVE:
496 /* only send the maximum buffer size that FT2232C can handle */
497 predicted_size = 3;
498 if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE)
499 {
500 ftdi2232_send_and_recv(first_unsent, cmd);
501 require_send = 0;
502 first_unsent = cmd;
503 }
504 if (cmd->cmd.statemove->end_state != -1)
505 ftdi2232_end_state(cmd->cmd.statemove->end_state);
506 /* command "Clock Data to TMS/CS Pin (no Read)" */
507 BUFFER_ADD = 0x4b;
508 /* scan 7 bit */
509 BUFFER_ADD = 0x6;
510 /* TMS data bits */
511 BUFFER_ADD = TAP_MOVE(cur_state, end_state);
512 //DEBUG("added TMS scan (no read)");
513 cur_state = end_state;
514 require_send = 1;
515 break;
516 case JTAG_SCAN:
517 scan_size = jtag_build_buffer(cmd->cmd.scan, &buffer);
518 type = jtag_scan_type(cmd->cmd.scan);
519 predicted_size = ftdi2232_predict_scan_out(scan_size, type);
520 if (ftdi2232_buffer_size + predicted_size + 1 > FTDI2232_SAVE_SIZE)
521 {
522 ftdi2232_send_and_recv(first_unsent, cmd);
523 require_send = 0;
524 first_unsent = cmd;
525 }
526 ftdi2232_expect_read += ftdi2232_predict_scan_in(scan_size, type);
527 //DEBUG("new read size: %i", ftdi2232_expect_read);
528 if (cmd->cmd.scan->end_state != -1)
529 ftdi2232_end_state(cmd->cmd.scan->end_state);
530 ftdi2232_add_scan(cmd->cmd.scan->ir_scan, type, buffer, scan_size);
531 require_send = 1;
532 if (buffer)
533 free(buffer);
534 break;
535 case JTAG_SLEEP:
536 jtag_sleep(cmd->cmd.sleep->us);
537 break;
538 default:
539 ERROR("BUG: unknown JTAG command type encountered");
540 exit(-1);
541 }
542 cmd = cmd->next;
543 }
544
545 if (require_send > 0)
546 ftdi2232_send_and_recv(first_unsent, cmd);
547
548 return ERROR_OK;
549 }
550
551 int ftdi2232_init(void)
552 {
553 if (ftdi_init(&ftdic) < 0)
554 return ERROR_JTAG_INIT_FAILED;
555
556 /* context, vendor id, product id */
557 if (ftdi_usb_open(&ftdic, ftdi2232_vid, ftdi2232_pid) < 0)
558 {
559 ERROR("unable to open ftdi device: %s", ftdic.error_str);
560 return ERROR_JTAG_INIT_FAILED;
561 }
562
563 if (ftdi_usb_reset(&ftdic) < 0)
564 {
565 ERROR("unable to reset ftdi device");
566 return ERROR_JTAG_INIT_FAILED;
567 }
568
569 if (ftdi_set_latency_timer(&ftdic, 1) < 0)
570 {
571 ERROR("unable to set latency timer");
572 return ERROR_JTAG_INIT_FAILED;
573 }
574
575 ftdi2232_buffer_size = 0;
576 ftdi2232_buffer = malloc(FTDI2232_BUFFER_SIZE);
577
578 ftdic.bitbang_mode = 0; /* Reset controller */
579 ftdi_enable_bitbang(&ftdic, 0x0b | FTDI2232_SRST | FTDI2232_TRST); /* ctx, i/o mask (out=1, in=0) */
580
581 ftdic.bitbang_mode = 2; /* MPSSE mode */
582 ftdi_enable_bitbang(&ftdic, 0x0b | FTDI2232_SRST | FTDI2232_TRST); /* ctx, i/o mask (out=1, in=0) */
583
584 if (ftdi_usb_purge_buffers(&ftdic) < 0)
585 {
586 ERROR("ftdi_purge_buffers: %s", ftdic.error_str);
587 return ERROR_JTAG_INIT_FAILED;
588 }
589
590 /* initialize low byte for jtag */
591 BUFFER_ADD = 0x80; /* command "set data bits low byte" */
592 BUFFER_ADD = 0x08 | FTDI2232_SRST | FTDI2232_TRST; /* value (TMS=1,TCK=0, TDI=0, xRST high) */
593 BUFFER_ADD = 0x0b | FTDI2232_SRST | FTDI2232_TRST; /* dir (output=1), TCK/TDI/TMS=out, TDO=in */
594 BUFFER_ADD = 0x85; /* command "Disconnect TDI/DO to TDO/DI for Loopback" */
595 ftdi2232_debug_dump_buffer();
596 if (ftdi_write_data(&ftdic, ftdi2232_buffer, ftdi2232_buffer_size) != 4)
597 return ERROR_JTAG_INIT_FAILED;
598
599 ftdi2232_speed(jtag_speed);
600
601 return ERROR_OK;
602 }
603
604 int ftdi2232_quit(void)
605 {
606 ftdi_disable_bitbang(&ftdic);
607
608 ftdi_usb_close(&ftdic);
609
610 ftdi_deinit(&ftdic);
611
612 free(ftdi2232_buffer);
613
614 return ERROR_OK;
615 }
616
617 int ftdi2232_handle_vid_pid_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
618 {
619 if (argc >= 2)
620 {
621 ftdi2232_vid = strtol(args[0], NULL, 0);
622 ftdi2232_pid = strtol(args[1], NULL, 0);
623 }
624 else
625 {
626 WARNING("incomplete ftdi2232_vid_pid configuration directive");
627 }
628
629 return ERROR_OK;
630 }

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)