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

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)