Buffering for up to 64 bytes in USB Blaster.
[openocd.git] / src / jtag / drivers / usb_blaster.c
1 /***************************************************************************
2 * Driver for USB-JTAG, Altera USB-Blaster and compatibles *
3 * Original code from Kolja Waschk's USB-JTAG project *
4 * (http://www.ixo.de/info/usb_jtag/). *
5 * Some updates by Anthony Liu (2006). *
6 * Minor updates and cleanup by Catalin Patulea (2009). *
7 * Speed updates by Ali Lown (2011). *
8 * *
9 * Copyright (C) 2011 Ali Lown *
10 * ali@lown.me.uk *
11 * *
12 * Copyright (C) 2009 Catalin Patulea *
13 * cat@vv.carleton.ca *
14 * *
15 * Copyright (C) 2006 Kolja Waschk *
16 * usbjtag@ixo.de *
17 * *
18 * Based on ft2232.c and bitbang.c, *
19 * Copyright (C) 2004,2006 by Dominic Rath *
20 * *
21 * This program is free software; you can redistribute it and/or modify *
22 * it under the terms of the GNU General Public License as published by *
23 * the Free Software Foundation; either version 2 of the License, or *
24 * (at your option) any later version. *
25 * *
26 * This program is distributed in the hope that it will be useful, *
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
29 * GNU General Public License for more details. *
30 * *
31 * You should have received a copy of the GNU General Public License *
32 * along with this program; if not, write to the *
33 * Free Software Foundation, Inc., *
34 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
35 ***************************************************************************/
36
37 /*
38 * The following information is originally from Kolja Waschk's USB-JTAG,
39 * where it was obtained by reverse engineering an Altera USB-Blaster.
40 * See http://www.ixo.de/info/usb_jtag/ for USB-Blaster block diagram and
41 * usb_jtag-20080705-1200.zip#usb_jtag/host/openocd for protocol.
42 *
43 * The same information is also on the UrJTAG mediawiki, with some additional
44 * notes on bits marked as "unknown" by usb_jtag.
45 * (http://sourceforge.net/apps/mediawiki/urjtag/index.php?
46 * title=Cable_Altera_USB-Blaster)
47 *
48 * USB-JTAG, Altera USB-Blaster and compatibles are typically implemented as
49 * an FTDIChip FT245 followed by a CPLD which handles a two-mode protocol:
50 *
51 * _________
52 * | |
53 * | AT93C46 |
54 * |_________|
55 * __|__________ _________
56 * | | | |
57 * USB__| FTDI 245BM |__| EPM7064 |__JTAG (B_TDO,B_TDI,B_TMS,B_TCK)
58 * |_____________| |_________|
59 * __|__________ _|___________
60 * | | | |
61 * | 6 MHz XTAL | | 24 MHz Osc. |
62 * |_____________| |_____________|
63 *
64 * Protocol details are given in the code below.
65 *
66 * It is also possible to emulate this configuration using a single-chip USB
67 * controller like the Cypress FX2 (again, see usb_jtag for details).
68 */
69 #ifdef HAVE_CONFIG_H
70 #include "config.h"
71 #endif
72
73 #if IS_CYGWIN == 1
74 #include "windows.h"
75 #undef LOG_ERROR
76 #endif
77
78 /* project specific includes */
79 #include <jtag/interface.h>
80 #include <jtag/commands.h>
81 #include <helper/time_support.h>
82
83 /* system includes */
84 #include <string.h>
85 #include <stdlib.h>
86 #include <unistd.h>
87
88 #include "bitbang.h"
89
90 #if (BUILD_USB_BLASTER_FTD2XX == 1 && BUILD_USB_BLASTER_LIBFTDI == 1)
91 #error "BUILD_USB_BLASTER_FTD2XX && BUILD_USB_BLASTER_LIBFTDI "
92 "are mutually exclusive"
93 #elif (BUILD_USB_BLASTER_FTD2XX != 1 && BUILD_USB_BLASTER_LIBFTDI != 1)
94 #error "BUILD_USB_BLASTER_FTD2XX || BUILD_USB_BLASTER_LIBFTDI must be chosen"
95 #endif
96
97 /* USB_BLASTER access library includes */
98 #if BUILD_USB_BLASTER_FTD2XX == 1
99 #include <ftd2xx.h>
100 #elif BUILD_USB_BLASTER_LIBFTDI == 1
101 #include <ftdi.h>
102 #endif
103
104 #include <sys/time.h>
105 #include <time.h>
106
107 static char *usb_blaster_device_desc;
108 static uint16_t usb_blaster_vid = 0x09fb; /* Altera */
109 static uint16_t usb_blaster_pid = 0x6001; /* USB-Blaster */
110
111 /* last output byte in simple bit banging (legacy) mode */
112 static uint8_t out_value;
113 /* global output buffer for bit banging */
114 #define BUF_LEN 64 //Size of EP1
115 static uint8_t out_buffer[BUF_LEN];
116 static uint16_t out_count = 0;
117
118 #if BUILD_USB_BLASTER_FTD2XX == 1
119 static FT_HANDLE ftdih;
120 #elif BUILD_USB_BLASTER_LIBFTDI == 1
121 static struct ftdi_context ftdic;
122 #endif
123
124 static int usb_blaster_buf_write(
125 uint8_t *buf, int size, uint32_t *bytes_written)
126 {
127 #if BUILD_USB_BLASTER_FTD2XX == 1
128 FT_STATUS status;
129 DWORD dw_bytes_written;
130
131 #ifdef _DEBUG_JTAG_IO_
132 LOG_DEBUG("usb_blaster_buf_write %02X (%d)", buf[0], size);
133 #endif
134 status = FT_Write(ftdih, buf, size, &dw_bytes_written);
135 if (status != FT_OK)
136 {
137 *bytes_written = dw_bytes_written;
138 LOG_ERROR("FT_Write returned: %lu", status);
139 return ERROR_JTAG_DEVICE_ERROR;
140 }
141 *bytes_written = dw_bytes_written;
142 return ERROR_OK;
143 #elif BUILD_USB_BLASTER_LIBFTDI == 1
144 int retval;
145 #ifdef _DEBUG_JTAG_IO_
146 LOG_DEBUG("usb_blaster_buf_write %02X (%d)", buf[0], size);
147 #endif
148 retval = ftdi_write_data(&ftdic, buf, size);
149 if (retval < 0)
150 {
151 *bytes_written = 0;
152 LOG_ERROR("ftdi_write_data: %s", ftdi_get_error_string(&ftdic));
153 return ERROR_JTAG_DEVICE_ERROR;
154 }
155 *bytes_written = retval;
156 return ERROR_OK;
157 #endif
158 }
159
160 static int
161 usb_blaster_buf_read(uint8_t *buf, unsigned size, uint32_t *bytes_read)
162 {
163 #if BUILD_USB_BLASTER_FTD2XX == 1
164 DWORD dw_bytes_read;
165 FT_STATUS status;
166
167 status = FT_Read(ftdih, buf, size, &dw_bytes_read);
168 if (status != FT_OK)
169 {
170 *bytes_read = dw_bytes_read;
171 LOG_ERROR("FT_Read returned: %lu", status);
172 return ERROR_JTAG_DEVICE_ERROR;
173 }
174 #ifdef _DEBUG_JTAG_IO_
175 LOG_DEBUG("usb_blaster_buf_read %02X (%lu)", buf[0], dw_bytes_read);
176 #endif
177 *bytes_read = dw_bytes_read;
178 return ERROR_OK;
179
180 #elif BUILD_USB_BLASTER_LIBFTDI == 1
181 int retval;
182 int timeout = 100;
183
184 *bytes_read = 0;
185 while ((*bytes_read < size) && timeout--)
186 {
187 retval = ftdi_read_data(&ftdic, buf + *bytes_read,
188 size - *bytes_read);
189 if (retval < 0)
190 {
191 *bytes_read = 0;
192 LOG_ERROR("ftdi_read_data: %s",
193 ftdi_get_error_string(&ftdic));
194 return ERROR_JTAG_DEVICE_ERROR;
195 }
196 *bytes_read += retval;
197 }
198 #ifdef _DEBUG_JTAG_IO_
199 LOG_DEBUG("usb_blaster_buf_read %02X (%d)", buf[0], *bytes_read);
200 #endif
201 return ERROR_OK;
202 #endif
203 }
204
205 /* The following code doesn't fully utilize the possibilities of the
206 * USB-Blaster. It only buffers data up to the maximum packet size of 64 bytes.
207 *
208 * Actually, the USB-Blaster offers a byte-shift mode to transmit up to 504 data
209 * bits (bidirectional) in a single USB packet. A header byte has to be sent as
210 * the first byte in a packet with the following meaning:
211 *
212 * Bit 7 (0x80): Must be set to indicate byte-shift mode.
213 * Bit 6 (0x40): If set, the USB-Blaster will also read data, not just write.
214 * Bit 5..0: Define the number N of following bytes
215 *
216 * All N following bytes will then be clocked out serially on TDI. If Bit 6 was
217 * set, it will afterwards return N bytes with TDO data read while clocking out
218 * the TDI data. LSB of the first byte after the header byte will appear first
219 * on TDI.
220 */
221
222 /* Simple bit banging mode:
223 *
224 * Bit 7 (0x80): Must be zero (see byte-shift mode above)
225 * Bit 6 (0x40): If set, you will receive a byte indicating the state of TDO
226 * in return.
227 * Bit 5 (0x20): Output Enable/LED.
228 * Bit 4 (0x10): TDI Output.
229 * Bit 3 (0x08): nCS Output (not used in JTAG mode).
230 * Bit 2 (0x04): nCE Output (not used in JTAG mode).
231 * Bit 1 (0x02): TMS Output.
232 * Bit 0 (0x01): TCK Output.
233 *
234 * For transmitting a single data bit, you need to write two bytes. Up to 64
235 * bytes can be combined in a single USB packet.
236 * It isn't possible to read a data without transmitting data.
237 */
238
239 #define TCK (1 << 0)
240 #define TMS (1 << 1)
241 #define NCE (1 << 2)
242 #define NCS (1 << 3)
243 #define TDI (1 << 4)
244 #define LED (1 << 5)
245 #define READ (1 << 6)
246 #define SHMODE (1 << 7)
247 #define OTHERS ((1 << 2) | (1 << 3) | (1 << 5))
248
249 #define READ_TDO (1 << 0)
250
251 static void usb_blaster_write_databuffer(uint8_t* buf, uint16_t len)
252 {
253 uint32_t bytes_written;
254 usb_blaster_buf_write(buf, len, &bytes_written);
255 out_count = 0;
256 #ifdef _DEBUG_JTAG_IO_
257 LOG_DEBUG("---- WROTE %d",bytes_written);
258 #endif
259 }
260
261 static void usb_blaster_addtowritebuffer(uint8_t value, bool forcewrite)
262 {
263 out_buffer[out_count] = value;
264 out_count += 1;
265 if(out_count == BUF_LEN || forcewrite)
266 usb_blaster_write_databuffer(out_buffer, out_count);
267 }
268
269 static int usb_blaster_read_data(void)
270 {
271 int status;
272 uint8_t buf[1];
273 uint32_t bytes_read;
274
275 if(out_count > 0)
276 usb_blaster_write_databuffer(out_buffer, out_count);
277
278 out_value |= READ;
279 usb_blaster_addtowritebuffer(out_value, true);
280 out_value &= ~READ;
281
282 status = usb_blaster_buf_read(buf, 1, &bytes_read);
283 if (status < 0)
284 return 0;
285
286 return !!(buf[0] & READ_TDO);
287 }
288
289 static void usb_blaster_write(int tck, int tms, int tdi)
290 {
291 #ifdef _DEBUG_JTAG_IO_
292 LOG_DEBUG("---- usb_blaster_write(%d,%d,%d)", tck, tms, tdi);
293 #endif
294 out_value &= ~(TCK | TMS | TDI);
295 if (tck)
296 out_value |= TCK;
297 if (tms)
298 out_value |= TMS;
299 if (tdi)
300 out_value |= TDI;
301
302 usb_blaster_addtowritebuffer(out_value, false);
303 }
304
305 static int usb_blaster_speed(int speed)
306 {
307 #if BUILD_USB_BLASTER_FTD2XX == 1
308 LOG_DEBUG("TODO: usb_blaster_speed() isn't implemented for libftd2xx!");
309 #elif BUILD_USB_BLASTER_LIBFTDI == 1
310 LOG_DEBUG("TODO: usb_blaster_speed() isn't optimally implemented!");
311
312 /* TODO: libftdi's ftdi_set_baudrate chokes on high rates, use lowlevel
313 * usb function instead! And additionally allow user to throttle.
314 */
315 if (ftdi_set_baudrate(&ftdic, 3000000 / 4) < 0)
316 {
317 LOG_ERROR("Can't set baud rate to max: %s",
318 ftdi_get_error_string(&ftdic));
319 return ERROR_JTAG_DEVICE_ERROR;
320 };
321 #endif
322
323 return ERROR_OK;
324 }
325
326 static void usb_blaster_reset(int trst, int srst)
327 {
328 LOG_DEBUG("TODO: usb_blaster_reset(%d,%d) isn't implemented!",
329 trst, srst);
330 }
331
332 static void usb_blaster_blink(int state)
333 {
334 out_value = 0x00;
335 if(state)
336 out_value |= LED;
337
338 usb_blaster_addtowritebuffer(out_value, true);
339 }
340
341 static struct bitbang_interface usb_blaster_bitbang = {
342 .read = usb_blaster_read_data,
343 .write = usb_blaster_write,
344 .reset = usb_blaster_reset,
345 .blink = usb_blaster_blink,
346 };
347
348 static int usb_blaster_init(void)
349 {
350 uint8_t latency_timer;
351
352 #if BUILD_USB_BLASTER_FTD2XX == 1
353 FT_STATUS status;
354 #endif
355
356 #if BUILD_USB_BLASTER_FTD2XX == 1
357 LOG_DEBUG("'usb_blaster' interface using FTD2XX");
358 #elif BUILD_USB_BLASTER_LIBFTDI == 1
359 LOG_DEBUG("'usb_blaster' interface using libftdi");
360 #endif
361
362 #if BUILD_USB_BLASTER_FTD2XX == 1
363 /* Open by device description */
364 if (usb_blaster_device_desc == NULL)
365 {
366 LOG_WARNING("no usb_blaster device description specified, "
367 "using default 'USB-Blaster'");
368 usb_blaster_device_desc = "USB-Blaster";
369 }
370
371 #if IS_WIN32 == 0
372 /* Add non-standard Vid/Pid to the linux driver */
373 status = FT_SetVIDPID(usb_blaster_vid, usb_blaster_pid);
374 if (status != FT_OK)
375 {
376 LOG_WARNING("couldn't add %4.4x:%4.4x",
377 usb_blaster_vid, usb_blaster_pid);
378 }
379 #endif
380
381 status = FT_OpenEx(usb_blaster_device_desc, FT_OPEN_BY_DESCRIPTION,
382 &ftdih);
383 if (status != FT_OK)
384 {
385 DWORD num_devices;
386
387 LOG_ERROR("unable to open ftdi device: %lu", status);
388 status = FT_ListDevices(&num_devices, NULL,
389 FT_LIST_NUMBER_ONLY);
390 if (status == FT_OK)
391 {
392 char **desc_array = malloc(sizeof(char *)
393 * (num_devices + 1));
394 unsigned int i;
395
396 for (i = 0; i < num_devices; i++)
397 desc_array[i] = malloc(64);
398 desc_array[num_devices] = NULL;
399
400 status = FT_ListDevices(desc_array, &num_devices,
401 FT_LIST_ALL | FT_OPEN_BY_DESCRIPTION);
402
403 if (status == FT_OK)
404 {
405 LOG_ERROR("ListDevices: %lu", num_devices);
406 for (i = 0; i < num_devices; i++)
407 LOG_ERROR("%i: %s", i, desc_array[i]);
408 }
409
410 for (i = 0; i < num_devices; i++)
411 free(desc_array[i]);
412 free(desc_array);
413 }
414 else
415 {
416 printf("ListDevices: NONE\n");
417 }
418 return ERROR_JTAG_INIT_FAILED;
419 }
420
421 status = FT_SetLatencyTimer(ftdih, 2);
422 if (status != FT_OK)
423 {
424 LOG_ERROR("unable to set latency timer: %lu", status);
425 return ERROR_JTAG_INIT_FAILED;
426 }
427
428 status = FT_GetLatencyTimer(ftdih, &latency_timer);
429 if (status != FT_OK)
430 {
431 LOG_ERROR("unable to get latency timer: %lu", status);
432 return ERROR_JTAG_INIT_FAILED;
433 }
434 LOG_DEBUG("current latency timer: %i", latency_timer);
435
436 status = FT_SetBitMode(ftdih, 0x00, 0);
437 if (status != FT_OK)
438 {
439 LOG_ERROR("unable to disable bit i/o mode: %lu", status);
440 return ERROR_JTAG_INIT_FAILED;
441 }
442 #elif BUILD_USB_BLASTER_LIBFTDI == 1
443 if (ftdi_init(&ftdic) < 0)
444 return ERROR_JTAG_INIT_FAILED;
445
446 /* context, vendor id, product id */
447 if (ftdi_usb_open(&ftdic, usb_blaster_vid, usb_blaster_pid) < 0)
448 {
449 LOG_ERROR("unable to open ftdi device: %s", ftdic.error_str);
450 return ERROR_JTAG_INIT_FAILED;
451 }
452
453 if (ftdi_usb_reset(&ftdic) < 0)
454 {
455 LOG_ERROR("unable to reset ftdi device");
456 return ERROR_JTAG_INIT_FAILED;
457 }
458
459 if (ftdi_set_latency_timer(&ftdic, 2) < 0)
460 {
461 LOG_ERROR("unable to set latency timer");
462 return ERROR_JTAG_INIT_FAILED;
463 }
464
465 if (ftdi_get_latency_timer(&ftdic, &latency_timer) < 0)
466 {
467 LOG_ERROR("unable to get latency timer");
468 return ERROR_JTAG_INIT_FAILED;
469 }
470 LOG_DEBUG("current latency timer: %u", latency_timer);
471
472 ftdi_disable_bitbang(&ftdic);
473 #endif
474
475 bitbang_interface = &usb_blaster_bitbang;
476
477 int jtag_speed_var;
478 int retval = jtag_get_speed(&jtag_speed_var);
479 if (retval != ERROR_OK)
480 return retval;
481 usb_blaster_speed(jtag_speed_var);
482
483 #if 0
484 #if BUILD_USB_BLASTER_FTD2XX == 1
485 if ((status = FT_Purge(ftdih, FT_PURGE_RX | FT_PURGE_TX)) != FT_OK)
486 {
487 LOG_ERROR("error purging ftd2xx device: %i", status);
488 return ERROR_JTAG_INIT_FAILED;
489 }
490 #elif BUILD_USB_BLASTER_LIBFTDI == 1
491 if (ftdi_usb_purge_buffers(&ftdic) < 0)
492 {
493 LOG_ERROR("ftdi_purge_buffers: %s", ftdic.error_str);
494 return ERROR_JTAG_INIT_FAILED;
495 }
496 #endif
497 #endif
498
499 return ERROR_OK;
500 }
501
502 static int usb_blaster_quit(void)
503 {
504 if(out_count > 0)
505 usb_blaster_write_databuffer(out_buffer, out_count);
506
507 #if BUILD_USB_BLASTER_FTD2XX == 1
508 FT_STATUS status;
509
510 status = FT_Close(ftdih);
511 #elif BUILD_USB_BLASTER_LIBFTDI == 1
512 ftdi_usb_close(&ftdic);
513 ftdi_deinit(&ftdic);
514 #endif
515
516 return ERROR_OK;
517 }
518
519 COMMAND_HANDLER(usb_blaster_handle_device_desc_command)
520 {
521 if (CMD_ARGC == 1)
522 usb_blaster_device_desc = strdup(CMD_ARGV[0]);
523 else
524 LOG_ERROR("require exactly one argument to "
525 "usb_blaster_device_desc <description>");
526
527 return ERROR_OK;
528 }
529
530 COMMAND_HANDLER(usb_blaster_handle_vid_pid_command)
531 {
532 if (CMD_ARGC > 2)
533 {
534 LOG_WARNING("ignoring extra IDs in usb_blaster_vid_pid "
535 "(maximum is 1 pair)");
536 CMD_ARGC = 2;
537 }
538 if (CMD_ARGC == 2)
539 {
540 COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], usb_blaster_vid);
541 COMMAND_PARSE_NUMBER(u16, CMD_ARGV[1], usb_blaster_pid);
542 }
543 else
544 LOG_WARNING("incomplete usb_blaster_vid_pid configuration");
545
546 return ERROR_OK;
547 }
548
549 COMMAND_HANDLER(usb_blaster_handle_pin_command)
550 {
551 if (CMD_ARGC == 2)
552 {
553 const char * const pin_name = CMD_ARGV[0];
554 uint8_t mask;
555 unsigned int state;
556
557 if (!strcmp(pin_name, "pin6"))
558 mask = NCE;
559 else if (!strcmp(pin_name, "pin8"))
560 mask = NCS;
561 else
562 {
563 LOG_ERROR("%s: pin name must be \"pin6\" or \"pin8\"",
564 CMD_NAME);
565 return ERROR_COMMAND_SYNTAX_ERROR;
566 }
567
568 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], state);
569 if (state == 0)
570 {
571 out_value &= ~mask;
572 usb_blaster_addtowritebuffer(out_value, true);
573 }
574 else if (state == 1)
575 {
576 out_value |= mask;
577 usb_blaster_addtowritebuffer(out_value, true);
578 }
579 else
580 {
581 LOG_ERROR("%s: pin state must be 0 or 1", CMD_NAME);
582 return ERROR_COMMAND_SYNTAX_ERROR;
583 }
584
585 return ERROR_OK;
586 }
587 else
588 {
589 LOG_ERROR("%s takes exactly two arguments", CMD_NAME);
590 return ERROR_COMMAND_SYNTAX_ERROR;
591 }
592 }
593
594 static const struct command_registration usb_blaster_command_handlers[] = {
595 {
596 .name = "usb_blaster_device_desc",
597 .handler = usb_blaster_handle_device_desc_command,
598 .mode = COMMAND_CONFIG,
599 .help = "set the USB device description of the USB-Blaster",
600 .usage = "description-string",
601 },
602 {
603 .name = "usb_blaster_vid_pid",
604 .handler = usb_blaster_handle_vid_pid_command,
605 .mode = COMMAND_CONFIG,
606 .help = "the vendor ID and product ID of the USB-Blaster",
607 .usage = "vid pid",
608 },
609 {
610 .name = "usb_blaster",
611 .handler = usb_blaster_handle_pin_command,
612 .mode = COMMAND_ANY,
613 .help = "set pin state for the unused GPIO pins",
614 .usage = "(pin6|pin8) (0|1)",
615 },
616 COMMAND_REGISTRATION_DONE
617 };
618
619 struct jtag_interface usb_blaster_interface = {
620 .name = "usb_blaster",
621 .commands = usb_blaster_command_handlers,
622 .supported = DEBUG_CAP_TMS_SEQ,
623
624 .execute_queue = bitbang_execute_queue,
625
626 .speed = usb_blaster_speed,
627 .init = usb_blaster_init,
628 .quit = usb_blaster_quit,
629 };

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)