mips32, change in pracc_list for dynamic allocation
[openocd.git] / src / target / mips32_pracc.c
1 /***************************************************************************
2 * Copyright (C) 2008 by Spencer Oliver *
3 * spen@spen-soft.co.uk *
4 * *
5 * Copyright (C) 2008 by David T.L. Wong *
6 * *
7 * Copyright (C) 2009 by David N. Claffey <dnclaffey@gmail.com> *
8 * *
9 * Copyright (C) 2011 by Drasko DRASKOVIC *
10 * drasko.draskovic@gmail.com *
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * This program is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20 * GNU General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
24 ***************************************************************************/
25
26 /*
27 * This version has optimized assembly routines for 32 bit operations:
28 * - read word
29 * - write word
30 * - write array of words
31 *
32 * One thing to be aware of is that the MIPS32 cpu will execute the
33 * instruction after a branch instruction (one delay slot).
34 *
35 * For example:
36 * LW $2, ($5 +10)
37 * B foo
38 * LW $1, ($2 +100)
39 *
40 * The LW $1, ($2 +100) instruction is also executed. If this is
41 * not wanted a NOP can be inserted:
42 *
43 * LW $2, ($5 +10)
44 * B foo
45 * NOP
46 * LW $1, ($2 +100)
47 *
48 * or the code can be changed to:
49 *
50 * B foo
51 * LW $2, ($5 +10)
52 * LW $1, ($2 +100)
53 *
54 * The original code contained NOPs. I have removed these and moved
55 * the branches.
56 *
57 * These changes result in a 35% speed increase when programming an
58 * external flash.
59 *
60 * More improvement could be gained if the registers do no need
61 * to be preserved but in that case the routines should be aware
62 * OpenOCD is used as a flash programmer or as a debug tool.
63 *
64 * Nico Coesel
65 */
66
67 #ifdef HAVE_CONFIG_H
68 #include "config.h"
69 #endif
70
71 #include <helper/time_support.h>
72
73 #include "mips32.h"
74 #include "mips32_pracc.h"
75
76 static int wait_for_pracc_rw(struct mips_ejtag *ejtag_info)
77 {
78 int64_t then = timeval_ms();
79
80 /* wait for the PrAcc to become "1" */
81 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_CONTROL);
82
83 while (1) {
84 ejtag_info->pa_ctrl = ejtag_info->ejtag_ctrl;
85 int retval = mips_ejtag_drscan_32(ejtag_info, &ejtag_info->pa_ctrl);
86 if (retval != ERROR_OK)
87 return retval;
88
89 if (ejtag_info->pa_ctrl & EJTAG_CTRL_PRACC)
90 break;
91
92 int64_t timeout = timeval_ms() - then;
93 if (timeout > 1000) {
94 LOG_DEBUG("DEBUGMODULE: No memory access in progress!");
95 return ERROR_JTAG_DEVICE_ERROR;
96 }
97 }
98
99 return ERROR_OK;
100 }
101
102 /* Shift in control and address for a new processor access, save them in ejtag_info */
103 static int mips32_pracc_read_ctrl_addr(struct mips_ejtag *ejtag_info)
104 {
105 int retval = wait_for_pracc_rw(ejtag_info);
106 if (retval != ERROR_OK)
107 return retval;
108
109 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ADDRESS);
110
111 ejtag_info->pa_addr = 0;
112 return mips_ejtag_drscan_32(ejtag_info, &ejtag_info->pa_addr);
113 }
114
115 /* Finish processor access */
116 static void mips32_pracc_finish(struct mips_ejtag *ejtag_info)
117 {
118 uint32_t ctrl = ejtag_info->ejtag_ctrl & ~EJTAG_CTRL_PRACC;
119 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_CONTROL);
120 mips_ejtag_drscan_32_out(ejtag_info, ctrl);
121 }
122
123 int mips32_pracc_clean_text_jump(struct mips_ejtag *ejtag_info)
124 {
125 uint32_t jt_code = MIPS32_J((0x0FFFFFFF & MIPS32_PRACC_TEXT) >> 2);
126
127 /* do 3 0/nops to clean pipeline before a jump to pracc text, NOP in delay slot */
128 for (int i = 0; i != 5; i++) {
129 /* Wait for pracc */
130 int retval = wait_for_pracc_rw(ejtag_info);
131 if (retval != ERROR_OK)
132 return retval;
133
134 /* Data or instruction out */
135 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
136 uint32_t data = (i == 3) ? jt_code : MIPS32_NOP;
137 mips_ejtag_drscan_32_out(ejtag_info, data);
138
139 /* finish pa */
140 mips32_pracc_finish(ejtag_info);
141 }
142
143 if (ejtag_info->mode != 0) /* async mode support only for MIPS ... */
144 return ERROR_OK;
145
146 for (int i = 0; i != 2; i++) {
147 int retval = mips32_pracc_read_ctrl_addr(ejtag_info);
148 if (retval != ERROR_OK)
149 return retval;
150
151 if (ejtag_info->pa_addr != MIPS32_PRACC_TEXT) { /* LEXRA/BMIPS ?, shift out another NOP, max 2 */
152 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
153 mips_ejtag_drscan_32_out(ejtag_info, MIPS32_NOP);
154 mips32_pracc_finish(ejtag_info);
155 } else
156 break;
157 }
158
159 return ERROR_OK;
160 }
161
162 int mips32_pracc_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx, uint32_t *param_out)
163 {
164 int code_count = 0;
165 int store_pending = 0; /* increases with every store instruction at dmseg, decreases with every store pa */
166 uint32_t max_store_addr = 0; /* for store pa address testing */
167 bool restart = 0; /* restarting control */
168 int restart_count = 0;
169 uint32_t instr = 0;
170 bool final_check = 0; /* set to 1 if in final checks after function code shifted out */
171 bool pass = 0; /* to check the pass through pracc text after function code sent */
172 int retval;
173
174 while (1) {
175 if (restart) {
176 if (restart_count < 3) { /* max 3 restarts allowed */
177 retval = mips32_pracc_clean_text_jump(ejtag_info);
178 if (retval != ERROR_OK)
179 return retval;
180 } else
181 return ERROR_JTAG_DEVICE_ERROR;
182 restart_count++;
183 restart = 0;
184 code_count = 0;
185 LOG_DEBUG("restarting code");
186 }
187
188 retval = mips32_pracc_read_ctrl_addr(ejtag_info); /* update current pa info: control and address */
189 if (retval != ERROR_OK)
190 return retval;
191
192 /* Check for read or write access */
193 if (ejtag_info->pa_ctrl & EJTAG_CTRL_PRNW) { /* write/store access */
194 /* Check for pending store from a previous store instruction at dmseg */
195 if (store_pending == 0) {
196 LOG_DEBUG("unexpected write at address %" PRIx32, ejtag_info->pa_addr);
197 if (code_count < 2) { /* allow for restart */
198 restart = 1;
199 continue;
200 } else
201 return ERROR_JTAG_DEVICE_ERROR;
202 } else {
203 /* check address */
204 if (ejtag_info->pa_addr < MIPS32_PRACC_PARAM_OUT || ejtag_info->pa_addr > max_store_addr) {
205
206 LOG_DEBUG("writing at unexpected address %" PRIx32, ejtag_info->pa_addr);
207 return ERROR_JTAG_DEVICE_ERROR;
208 }
209 }
210 /* read data */
211 uint32_t data = 0;
212 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
213 retval = mips_ejtag_drscan_32(ejtag_info, &data);
214 if (retval != ERROR_OK)
215 return retval;
216
217 /* store data at param out, address based offset */
218 param_out[(ejtag_info->pa_addr - MIPS32_PRACC_PARAM_OUT) / 4] = data;
219 store_pending--;
220
221 } else { /* read/fetch access */
222 if (!final_check) { /* executing function code */
223 /* check address */
224 if (ejtag_info->pa_addr != (MIPS32_PRACC_TEXT + code_count * 4)) {
225 LOG_DEBUG("reading at unexpected address %" PRIx32 ", expected %x",
226 ejtag_info->pa_addr, MIPS32_PRACC_TEXT + code_count * 4);
227
228 /* restart code execution only in some cases */
229 if (code_count == 1 && ejtag_info->pa_addr == MIPS32_PRACC_TEXT && restart_count == 0) {
230 LOG_DEBUG("restarting, without clean jump");
231 restart_count++;
232 code_count = 0;
233 continue;
234 } else if (code_count < 2) {
235 restart = 1;
236 continue;
237 }
238 return ERROR_JTAG_DEVICE_ERROR;
239 }
240 /* check for store instruction at dmseg */
241 uint32_t store_addr = ctx->pracc_list[code_count].addr;
242 if (store_addr != 0) {
243 if (store_addr > max_store_addr)
244 max_store_addr = store_addr;
245 store_pending++;
246 }
247
248 instr = ctx->pracc_list[code_count++].instr;
249 if (code_count == ctx->code_count) /* last instruction, start final check */
250 final_check = 1;
251
252 } else { /* final check after function code shifted out */
253 /* check address */
254 if (ejtag_info->pa_addr == MIPS32_PRACC_TEXT) {
255 if (!pass) { /* first pass through pracc text */
256 if (store_pending == 0) /* done, normal exit */
257 return ERROR_OK;
258 pass = 1; /* pracc text passed */
259 code_count = 0; /* restart code count */
260 } else {
261 LOG_DEBUG("unexpected second pass through pracc text");
262 return ERROR_JTAG_DEVICE_ERROR;
263 }
264 } else {
265 if (ejtag_info->pa_addr != (MIPS32_PRACC_TEXT + code_count * 4)) {
266 LOG_DEBUG("unexpected read address in final check: %" PRIx32 ", expected: %x",
267 ejtag_info->pa_addr, MIPS32_PRACC_TEXT + code_count * 4);
268 return ERROR_JTAG_DEVICE_ERROR;
269 }
270 }
271 if (!pass) {
272 if ((code_count - ctx->code_count) > 1) { /* allow max 2 instruction delay slot */
273 LOG_DEBUG("failed to jump back to pracc text");
274 return ERROR_JTAG_DEVICE_ERROR;
275 }
276 } else
277 if (code_count > 10) { /* enough, abandone */
278 LOG_DEBUG("execution abandoned, store pending: %d", store_pending);
279 return ERROR_JTAG_DEVICE_ERROR;
280 }
281 instr = MIPS32_NOP; /* shift out NOPs instructions */
282 code_count++;
283 }
284
285 /* Send instruction out */
286 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
287 mips_ejtag_drscan_32_out(ejtag_info, instr);
288 }
289 /* finish processor access, let the processor eat! */
290 mips32_pracc_finish(ejtag_info);
291
292 if (instr == MIPS32_DRET) /* after leaving debug mode nothing to do */
293 return jtag_execute_queue();
294
295 if (store_pending == 0 && pass) { /* store access done, but after passing pracc text */
296 LOG_DEBUG("warning: store access pass pracc text");
297 return ERROR_OK;
298 }
299 }
300 }
301
302 inline void pracc_queue_init(struct pracc_queue_info *ctx)
303 {
304 ctx->retval = ERROR_OK;
305 ctx->code_count = 0;
306 ctx->store_count = 0;
307
308 ctx->pracc_list = malloc(ctx->max_code * sizeof(pa_list));
309 if (ctx->pracc_list == NULL) {
310 LOG_ERROR("Out of memory");
311 ctx->retval = ERROR_FAIL;
312 }
313 }
314
315 inline void pracc_add(struct pracc_queue_info *ctx, uint32_t addr, uint32_t instr)
316 {
317 ctx->pracc_list[ctx->code_count].instr = instr;
318 ctx->pracc_list[ctx->code_count++].addr = addr;
319 if (addr)
320 ctx->store_count++;
321 }
322
323 inline void pracc_add_li32(struct pracc_queue_info *ctx, uint32_t reg_num, uint32_t data, bool optimize)
324 {
325 if (LOWER16(data) == 0 && optimize)
326 pracc_add(ctx, 0, MIPS32_LUI(reg_num, UPPER16(data))); /* load only upper value */
327 else if (UPPER16(data) == 0 && optimize)
328 pracc_add(ctx, 0, MIPS32_ORI(reg_num, 0, LOWER16(data))); /* load only lower */
329 else {
330 pracc_add(ctx, 0, MIPS32_LUI(reg_num, UPPER16(data))); /* load upper and lower */
331 pracc_add(ctx, 0, MIPS32_ORI(reg_num, reg_num, LOWER16(data)));
332 }
333 }
334
335 inline void pracc_queue_free(struct pracc_queue_info *ctx)
336 {
337 if (ctx->code_count > ctx->max_code) /* Only for internal check, will be erased */
338 LOG_ERROR("Internal error, code count: %d > max code: %d", ctx->code_count, ctx->max_code);
339 if (ctx->pracc_list != NULL)
340 free(ctx->pracc_list);
341 }
342
343 int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx, uint32_t *buf)
344 {
345 if (ejtag_info->mode == 0)
346 return mips32_pracc_exec(ejtag_info, ctx, buf);
347
348 union scan_in {
349 uint8_t scan_96[12];
350 struct {
351 uint8_t ctrl[4];
352 uint8_t data[4];
353 uint8_t addr[4];
354 } scan_32;
355
356 } *scan_in = malloc(sizeof(union scan_in) * (ctx->code_count + ctx->store_count));
357 if (scan_in == NULL) {
358 LOG_ERROR("Out of memory");
359 return ERROR_FAIL;
360 }
361
362 unsigned num_clocks =
363 ((uint64_t)(ejtag_info->scan_delay) * jtag_get_speed_khz() + 500000) / 1000000;
364
365 uint32_t ejtag_ctrl = ejtag_info->ejtag_ctrl & ~EJTAG_CTRL_PRACC;
366 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ALL);
367
368 int scan_count = 0;
369 for (int i = 0; i != ctx->code_count; i++) {
370 jtag_add_clocks(num_clocks);
371 mips_ejtag_add_scan_96(ejtag_info, ejtag_ctrl, ctx->pracc_list[i].instr,
372 scan_in[scan_count++].scan_96);
373
374 /* Check store address from previous instruction, if not the first */
375 if (i > 0 && ctx->pracc_list[i - 1].addr) {
376 jtag_add_clocks(num_clocks);
377 mips_ejtag_add_scan_96(ejtag_info, ejtag_ctrl, 0, scan_in[scan_count++].scan_96);
378 }
379 }
380
381 int retval = jtag_execute_queue(); /* execute queued scans */
382 if (retval != ERROR_OK)
383 goto exit;
384
385 uint32_t fetch_addr = MIPS32_PRACC_TEXT; /* start address */
386 scan_count = 0;
387 for (int i = 0; i != ctx->code_count; i++) { /* verify every pracc access */
388 /* check pracc bit */
389 ejtag_ctrl = buf_get_u32(scan_in[scan_count].scan_32.ctrl, 0, 32);
390 uint32_t addr = buf_get_u32(scan_in[scan_count].scan_32.addr, 0, 32);
391 if (!(ejtag_ctrl & EJTAG_CTRL_PRACC)) {
392 LOG_ERROR("Error: access not pending count: %d", scan_count);
393 retval = ERROR_FAIL;
394 goto exit;
395 }
396 if (ejtag_ctrl & EJTAG_CTRL_PRNW) {
397 LOG_ERROR("Not a fetch/read access, count: %d", scan_count);
398 retval = ERROR_FAIL;
399 goto exit;
400 }
401 if (addr != fetch_addr) {
402 LOG_ERROR("Fetch addr mismatch, read: %" PRIx32 " expected: %" PRIx32 " count: %d",
403 addr, fetch_addr, scan_count);
404 retval = ERROR_FAIL;
405 goto exit;
406 }
407 fetch_addr += 4;
408 scan_count++;
409
410 /* check if previous intrucction is a store instruction at dmesg */
411 if (i > 0 && ctx->pracc_list[i - 1].addr) {
412 uint32_t store_addr = ctx->pracc_list[i - 1].addr;
413 ejtag_ctrl = buf_get_u32(scan_in[scan_count].scan_32.ctrl, 0, 32);
414 addr = buf_get_u32(scan_in[scan_count].scan_32.addr, 0, 32);
415
416 if (!(ejtag_ctrl & EJTAG_CTRL_PRNW)) {
417 LOG_ERROR("Not a store/write access, count: %d", scan_count);
418 retval = ERROR_FAIL;
419 goto exit;
420 }
421 if (addr != store_addr) {
422 LOG_ERROR("Store address mismatch, read: %" PRIx32 " expected: %" PRIx32 " count: %d",
423 addr, store_addr, scan_count);
424 retval = ERROR_FAIL;
425 goto exit;
426 }
427 int buf_index = (addr - MIPS32_PRACC_PARAM_OUT) / 4;
428 buf[buf_index] = buf_get_u32(scan_in[scan_count].scan_32.data, 0, 32);
429 scan_count++;
430 }
431 }
432 exit:
433 free(scan_in);
434 return retval;
435 }
436
437 int mips32_pracc_read_u32(struct mips_ejtag *ejtag_info, uint32_t addr, uint32_t *buf)
438 {
439 struct pracc_queue_info ctx = {.max_code = 8};
440 pracc_queue_init(&ctx);
441 if (ctx.retval != ERROR_OK)
442 goto exit;
443
444 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
445 pracc_add(&ctx, 0, MIPS32_LUI(8, UPPER16((addr + 0x8000)))); /* load $8 with modified upper address */
446 pracc_add(&ctx, 0, MIPS32_LW(8, LOWER16(addr), 8)); /* lw $8, LOWER16(addr)($8) */
447 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
448 MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* sw $8,PRACC_OUT_OFFSET($15) */
449 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
450 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
451 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
452
453 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, buf);
454 exit:
455 pracc_queue_free(&ctx);
456 return ctx.retval;
457 }
458
459 int mips32_pracc_read_mem(struct mips_ejtag *ejtag_info, uint32_t addr, int size, int count, void *buf)
460 {
461 if (count == 1 && size == 4)
462 return mips32_pracc_read_u32(ejtag_info, addr, (uint32_t *)buf);
463
464 uint32_t *data = NULL;
465 struct pracc_queue_info ctx = {.max_code = 256 * 3 + 8 + 1}; /* alloc memory for the worst case */
466 pracc_queue_init(&ctx);
467 if (ctx.retval != ERROR_OK)
468 goto exit;
469
470 if (size != 4) {
471 data = malloc(256 * sizeof(uint32_t));
472 if (data == NULL) {
473 LOG_ERROR("Out of memory");
474 goto exit;
475 }
476 }
477
478 uint32_t *buf32 = buf;
479 uint16_t *buf16 = buf;
480 uint8_t *buf8 = buf;
481
482 while (count) {
483 ctx.code_count = 0;
484 ctx.store_count = 0;
485 int this_round_count = (count > 256) ? 256 : count;
486 uint32_t last_upper_base_addr = UPPER16((addr + 0x8000));
487
488 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
489 pracc_add(&ctx, 0, MIPS32_LUI(9, last_upper_base_addr)); /* load the upper memory address in $9 */
490
491 for (int i = 0; i != this_round_count; i++) { /* Main code loop */
492 uint32_t upper_base_addr = UPPER16((addr + 0x8000));
493 if (last_upper_base_addr != upper_base_addr) { /* if needed, change upper address in $9 */
494 pracc_add(&ctx, 0, MIPS32_LUI(9, upper_base_addr));
495 last_upper_base_addr = upper_base_addr;
496 }
497
498 if (size == 4)
499 pracc_add(&ctx, 0, MIPS32_LW(8, LOWER16(addr), 9)); /* load from memory to $8 */
500 else if (size == 2)
501 pracc_add(&ctx, 0, MIPS32_LHU(8, LOWER16(addr), 9));
502 else
503 pracc_add(&ctx, 0, MIPS32_LBU(8, LOWER16(addr), 9));
504
505 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + i * 4,
506 MIPS32_SW(8, PRACC_OUT_OFFSET + i * 4, 15)); /* store $8 at param out */
507 addr += size;
508 }
509 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
510 pracc_add_li32(&ctx, 9, ejtag_info->reg9, 0); /* restore $9 */
511
512 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
513 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
514
515 if (size == 4) {
516 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, buf32);
517 if (ctx.retval != ERROR_OK)
518 goto exit;
519 buf32 += this_round_count;
520 } else {
521 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, data);
522 if (ctx.retval != ERROR_OK)
523 goto exit;
524
525 uint32_t *data_p = data;
526 for (int i = 0; i != this_round_count; i++) {
527 if (size == 2)
528 *buf16++ = *data_p++;
529 else
530 *buf8++ = *data_p++;
531 }
532 }
533 count -= this_round_count;
534 }
535 exit:
536 pracc_queue_free(&ctx);
537 if (data != NULL)
538 free(data);
539 return ctx.retval;
540 }
541
542 int mips32_cp0_read(struct mips_ejtag *ejtag_info, uint32_t *val, uint32_t cp0_reg, uint32_t cp0_sel)
543 {
544 struct pracc_queue_info ctx = {.max_code = 7};
545 pracc_queue_init(&ctx);
546 if (ctx.retval != ERROR_OK)
547 goto exit;
548
549 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
550 pracc_add(&ctx, 0, MIPS32_MFC0(8, cp0_reg, cp0_sel)); /* move cp0 reg / sel to $8 */
551 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
552 MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
553 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
554 pracc_add(&ctx, 0, MIPS32_LUI(8, UPPER16(ejtag_info->reg8))); /* restore upper 16 bits of $8 */
555 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
556 pracc_add(&ctx, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info->reg8))); /* restore lower 16 bits of $8 */
557
558 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, val);
559 exit:
560 pracc_queue_free(&ctx);
561 return ctx.retval;
562 }
563
564 int mips32_cp0_write(struct mips_ejtag *ejtag_info, uint32_t val, uint32_t cp0_reg, uint32_t cp0_sel)
565 {
566 struct pracc_queue_info ctx = {.max_code = 6};
567 pracc_queue_init(&ctx);
568 if (ctx.retval != ERROR_OK)
569 goto exit;
570
571 pracc_add_li32(&ctx, 15, val, 0); /* Load val to $15 */
572
573 pracc_add(&ctx, 0, MIPS32_MTC0(15, cp0_reg, cp0_sel)); /* write $15 to cp0 reg / sel */
574 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
575 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
576
577 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
578 exit:
579 pracc_queue_free(&ctx);
580 return ctx.retval;
581 }
582
583 /**
584 * \b mips32_pracc_sync_cache
585 *
586 * Synchronize Caches to Make Instruction Writes Effective
587 * (ref. doc. MIPS32 Architecture For Programmers Volume II: The MIPS32 Instruction Set,
588 * Document Number: MD00086, Revision 2.00, June 9, 2003)
589 *
590 * When the instruction stream is written, the SYNCI instruction should be used
591 * in conjunction with other instructions to make the newly-written instructions effective.
592 *
593 * Explanation :
594 * A program that loads another program into memory is actually writing the D- side cache.
595 * The instructions it has loaded can't be executed until they reach the I-cache.
596 *
597 * After the instructions have been written, the loader should arrange
598 * to write back any containing D-cache line and invalidate any locations
599 * already in the I-cache.
600 *
601 * If the cache coherency attribute (CCA) is set to zero, it's a write through cache, there is no need
602 * to write back.
603 *
604 * In the latest MIPS32/64 CPUs, MIPS provides the synci instruction,
605 * which does the whole job for a cache-line-sized chunk of the memory you just loaded:
606 * That is, it arranges a D-cache write-back (if CCA = 3) and an I-cache invalidate.
607 *
608 * The line size is obtained with the rdhwr SYNCI_Step in release 2 or from cp0 config 1 register in release 1.
609 */
610 static int mips32_pracc_synchronize_cache(struct mips_ejtag *ejtag_info,
611 uint32_t start_addr, uint32_t end_addr, int cached, int rel)
612 {
613 struct pracc_queue_info ctx = {.max_code = 256 * 2 + 5};
614 pracc_queue_init(&ctx);
615 if (ctx.retval != ERROR_OK)
616 goto exit;
617 /** Find cache line size in bytes */
618 uint32_t clsiz;
619 if (rel) { /* Release 2 (rel = 1) */
620 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
621
622 pracc_add(&ctx, 0, MIPS32_RDHWR(8, MIPS32_SYNCI_STEP)); /* load synci_step value to $8 */
623
624 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
625 MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
626
627 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
628
629 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
630 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
631
632 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, &clsiz);
633 if (ctx.retval != ERROR_OK)
634 goto exit;
635
636 } else { /* Release 1 (rel = 0) */
637 uint32_t conf;
638 ctx.retval = mips32_cp0_read(ejtag_info, &conf, 16, 1);
639 if (ctx.retval != ERROR_OK)
640 goto exit;
641
642 uint32_t dl = (conf & MIPS32_CONFIG1_DL_MASK) >> MIPS32_CONFIG1_DL_SHIFT;
643
644 /* dl encoding : dl=1 => 4 bytes, dl=2 => 8 bytes, etc... max dl=6 => 128 bytes cache line size */
645 clsiz = 0x2 << dl;
646 if (dl == 0)
647 clsiz = 0;
648 }
649
650 if (clsiz == 0)
651 goto exit; /* Nothing to do */
652
653 /* make sure clsiz is power of 2 */
654 if (clsiz & (clsiz - 1)) {
655 LOG_DEBUG("clsiz must be power of 2");
656 ctx.retval = ERROR_FAIL;
657 goto exit;
658 }
659
660 /* make sure start_addr and end_addr have the same offset inside de cache line */
661 start_addr |= clsiz - 1;
662 end_addr |= clsiz - 1;
663
664 ctx.code_count = 0;
665 int count = 0;
666 uint32_t last_upper_base_addr = UPPER16((start_addr + 0x8000));
667
668 pracc_add(&ctx, 0, MIPS32_LUI(15, last_upper_base_addr)); /* load upper memory base address to $15 */
669
670 while (start_addr <= end_addr) { /* main loop */
671 uint32_t upper_base_addr = UPPER16((start_addr + 0x8000));
672 if (last_upper_base_addr != upper_base_addr) { /* if needed, change upper address in $15 */
673 pracc_add(&ctx, 0, MIPS32_LUI(15, upper_base_addr));
674 last_upper_base_addr = upper_base_addr;
675 }
676 if (rel)
677 pracc_add(&ctx, 0, MIPS32_SYNCI(LOWER16(start_addr), 15)); /* synci instruction, offset($15) */
678
679 else {
680 if (cached == 3)
681 pracc_add(&ctx, 0, MIPS32_CACHE(MIPS32_CACHE_D_HIT_WRITEBACK,
682 LOWER16(start_addr), 15)); /* cache Hit_Writeback_D, offset($15) */
683
684 pracc_add(&ctx, 0, MIPS32_CACHE(MIPS32_CACHE_I_HIT_INVALIDATE,
685 LOWER16(start_addr), 15)); /* cache Hit_Invalidate_I, offset($15) */
686 }
687 start_addr += clsiz;
688 count++;
689 if (count == 256 && start_addr <= end_addr) { /* more ?, then execute code list */
690 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
691 pracc_add(&ctx, 0, MIPS32_NOP); /* nop in delay slot */
692
693 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
694 if (ctx.retval != ERROR_OK)
695 goto exit;
696
697 ctx.code_count = 0;
698 count = 0;
699 }
700 }
701 pracc_add(&ctx, 0, MIPS32_SYNC);
702 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
703 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave*/
704
705 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
706 exit:
707 pracc_queue_free(&ctx);
708 return ctx.retval;
709 }
710
711 static int mips32_pracc_write_mem_generic(struct mips_ejtag *ejtag_info,
712 uint32_t addr, int size, int count, const void *buf)
713 {
714 struct pracc_queue_info ctx = {.max_code = 128 * 3 + 5 + 1}; /* alloc memory for the worst case */
715 pracc_queue_init(&ctx);
716 if (ctx.retval != ERROR_OK)
717 goto exit;
718
719 const uint32_t *buf32 = buf;
720 const uint16_t *buf16 = buf;
721 const uint8_t *buf8 = buf;
722
723 while (count) {
724 ctx.code_count = 0;
725 ctx.store_count = 0;
726 int this_round_count = (count > 128) ? 128 : count;
727 uint32_t last_upper_base_addr = UPPER16((addr + 0x8000));
728
729 pracc_add(&ctx, 0, MIPS32_LUI(15, last_upper_base_addr)); /* load $15 with memory base address */
730
731 for (int i = 0; i != this_round_count; i++) {
732 uint32_t upper_base_addr = UPPER16((addr + 0x8000));
733 if (last_upper_base_addr != upper_base_addr) {
734 pracc_add(&ctx, 0, MIPS32_LUI(15, upper_base_addr)); /* if needed, change upper address in $15*/
735 last_upper_base_addr = upper_base_addr;
736 }
737
738 if (size == 4) {
739 pracc_add_li32(&ctx, 8, *buf32, 1); /* load with li32, optimize */
740 pracc_add(&ctx, 0, MIPS32_SW(8, LOWER16(addr), 15)); /* store word to memory */
741 buf32++;
742
743 } else if (size == 2) {
744 pracc_add(&ctx, 0, MIPS32_ORI(8, 0, *buf16)); /* load lower value */
745 pracc_add(&ctx, 0, MIPS32_SH(8, LOWER16(addr), 15)); /* store half word to memory */
746 buf16++;
747
748 } else {
749 pracc_add(&ctx, 0, MIPS32_ORI(8, 0, *buf8)); /* load lower value */
750 pracc_add(&ctx, 0, MIPS32_SB(8, LOWER16(addr), 15)); /* store byte to memory */
751 buf8++;
752 }
753 addr += size;
754 }
755
756 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
757
758 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
759 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
760
761 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
762 if (ctx.retval != ERROR_OK)
763 goto exit;
764 count -= this_round_count;
765 }
766 exit:
767 pracc_queue_free(&ctx);
768 return ctx.retval;
769 }
770
771 int mips32_pracc_write_mem(struct mips_ejtag *ejtag_info, uint32_t addr, int size, int count, const void *buf)
772 {
773 int retval = mips32_pracc_write_mem_generic(ejtag_info, addr, size, count, buf);
774 if (retval != ERROR_OK)
775 return retval;
776
777 /**
778 * If we are in the cacheable region and cache is activated,
779 * we must clean D$ (if Cache Coherency Attribute is set to 3) + invalidate I$ after we did the write,
780 * so that changes do not continue to live only in D$ (if CCA = 3), but to be
781 * replicated in I$ also (maybe we wrote the istructions)
782 */
783 uint32_t conf = 0;
784 int cached = 0;
785
786 if ((KSEGX(addr) == KSEG1) || ((addr >= 0xff200000) && (addr <= 0xff3fffff)))
787 return retval; /*Nothing to do*/
788
789 mips32_cp0_read(ejtag_info, &conf, 16, 0);
790
791 switch (KSEGX(addr)) {
792 case KUSEG:
793 cached = (conf & MIPS32_CONFIG0_KU_MASK) >> MIPS32_CONFIG0_KU_SHIFT;
794 break;
795 case KSEG0:
796 cached = (conf & MIPS32_CONFIG0_K0_MASK) >> MIPS32_CONFIG0_K0_SHIFT;
797 break;
798 case KSEG2:
799 case KSEG3:
800 cached = (conf & MIPS32_CONFIG0_K23_MASK) >> MIPS32_CONFIG0_K23_SHIFT;
801 break;
802 default:
803 /* what ? */
804 break;
805 }
806
807 /**
808 * Check cachablitiy bits coherency algorithm
809 * is the region cacheable or uncached.
810 * If cacheable we have to synchronize the cache
811 */
812 if (cached == 3 || cached == 0) { /* Write back cache or write through cache */
813 uint32_t start_addr = addr;
814 uint32_t end_addr = addr + count * size;
815 uint32_t rel = (conf & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
816 if (rel > 1) {
817 LOG_DEBUG("Unknown release in cache code");
818 return ERROR_FAIL;
819 }
820 retval = mips32_pracc_synchronize_cache(ejtag_info, start_addr, end_addr, cached, rel);
821 }
822
823 return retval;
824 }
825
826 int mips32_pracc_write_regs(struct mips_ejtag *ejtag_info, uint32_t *regs)
827 {
828 static const uint32_t cp0_write_code[] = {
829 MIPS32_MTC0(1, 12, 0), /* move $1 to status */
830 MIPS32_MTLO(1), /* move $1 to lo */
831 MIPS32_MTHI(1), /* move $1 to hi */
832 MIPS32_MTC0(1, 8, 0), /* move $1 to badvaddr */
833 MIPS32_MTC0(1, 13, 0), /* move $1 to cause*/
834 MIPS32_MTC0(1, 24, 0), /* move $1 to depc (pc) */
835 };
836
837 struct pracc_queue_info ctx = {.max_code = 37 * 2 + 7 + 1};
838 pracc_queue_init(&ctx);
839 if (ctx.retval != ERROR_OK)
840 goto exit;
841
842 /* load registers 2 to 31 with li32, optimize */
843 for (int i = 2; i < 32; i++)
844 pracc_add_li32(&ctx, i, regs[i], 1);
845
846 for (int i = 0; i != 6; i++) {
847 pracc_add_li32(&ctx, 1, regs[i + 32], 0); /* load CPO value in $1 */
848 pracc_add(&ctx, 0, cp0_write_code[i]); /* write value from $1 to CPO register */
849 }
850 pracc_add(&ctx, 0, MIPS32_MTC0(15, 31, 0)); /* load $15 in DeSave */
851 pracc_add(&ctx, 0, MIPS32_LUI(1, UPPER16((regs[1])))); /* load upper half word in $1 */
852 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
853 pracc_add(&ctx, 0, MIPS32_ORI(1, 1, LOWER16((regs[1])))); /* load lower half word in $1 */
854
855 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
856
857 ejtag_info->reg8 = regs[8];
858 ejtag_info->reg9 = regs[9];
859 exit:
860 pracc_queue_free(&ctx);
861 return ctx.retval;
862 }
863
864 int mips32_pracc_read_regs(struct mips_ejtag *ejtag_info, uint32_t *regs)
865 {
866 static int cp0_read_code[] = {
867 MIPS32_MFC0(8, 12, 0), /* move status to $8 */
868 MIPS32_MFLO(8), /* move lo to $8 */
869 MIPS32_MFHI(8), /* move hi to $8 */
870 MIPS32_MFC0(8, 8, 0), /* move badvaddr to $8 */
871 MIPS32_MFC0(8, 13, 0), /* move cause to $8 */
872 MIPS32_MFC0(8, 24, 0), /* move depc (pc) to $8 */
873 };
874
875 struct pracc_queue_info ctx = {.max_code = 49};
876 pracc_queue_init(&ctx);
877 if (ctx.retval != ERROR_OK)
878 goto exit;
879
880 pracc_add(&ctx, 0, MIPS32_MTC0(1, 31, 0)); /* move $1 to COP0 DeSave */
881 pracc_add(&ctx, 0, MIPS32_LUI(1, PRACC_UPPER_BASE_ADDR)); /* $1 = MIP32_PRACC_BASE_ADDR */
882
883 for (int i = 2; i != 32; i++) /* store GPR's 2 to 31 */
884 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i * 4),
885 MIPS32_SW(i, PRACC_OUT_OFFSET + (i * 4), 1));
886
887 for (int i = 0; i != 6; i++) {
888 pracc_add(&ctx, 0, cp0_read_code[i]); /* load COP0 needed registers to $8 */
889 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i + 32) * 4, /* store $8 at PARAM OUT */
890 MIPS32_SW(8, PRACC_OUT_OFFSET + (i + 32) * 4, 1));
891 }
892 pracc_add(&ctx, 0, MIPS32_MFC0(8, 31, 0)); /* move DeSave to $8, reg1 value */
893 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + 4, /* store reg1 value from $8 to param out */
894 MIPS32_SW(8, PRACC_OUT_OFFSET + 4, 1));
895
896 pracc_add(&ctx, 0, MIPS32_MFC0(1, 31, 0)); /* move COP0 DeSave to $1, restore reg1 */
897 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
898 pracc_add(&ctx, 0, MIPS32_MTC0(15, 31, 0)); /* load $15 in DeSave */
899
900 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, regs);
901
902 ejtag_info->reg8 = regs[8]; /* reg8 is saved but not restored, next called function should restore it */
903 ejtag_info->reg9 = regs[9];
904 exit:
905 pracc_queue_free(&ctx);
906 return ctx.retval;
907 }
908
909 /* fastdata upload/download requires an initialized working area
910 * to load the download code; it should not be called otherwise
911 * fetch order from the fastdata area
912 * 1. start addr
913 * 2. end addr
914 * 3. data ...
915 */
916 int mips32_pracc_fastdata_xfer(struct mips_ejtag *ejtag_info, struct working_area *source,
917 int write_t, uint32_t addr, int count, uint32_t *buf)
918 {
919 uint32_t handler_code[] = {
920 /* r15 points to the start of this code */
921 MIPS32_SW(8, MIPS32_FASTDATA_HANDLER_SIZE - 4, 15),
922 MIPS32_SW(9, MIPS32_FASTDATA_HANDLER_SIZE - 8, 15),
923 MIPS32_SW(10, MIPS32_FASTDATA_HANDLER_SIZE - 12, 15),
924 MIPS32_SW(11, MIPS32_FASTDATA_HANDLER_SIZE - 16, 15),
925 /* start of fastdata area in t0 */
926 MIPS32_LUI(8, UPPER16(MIPS32_PRACC_FASTDATA_AREA)),
927 MIPS32_ORI(8, 8, LOWER16(MIPS32_PRACC_FASTDATA_AREA)),
928 MIPS32_LW(9, 0, 8), /* start addr in t1 */
929 MIPS32_LW(10, 0, 8), /* end addr to t2 */
930 /* loop: */
931 write_t ? MIPS32_LW(11, 0, 8) : MIPS32_LW(11, 0, 9), /* from xfer area : from memory */
932 write_t ? MIPS32_SW(11, 0, 9) : MIPS32_SW(11, 0, 8), /* to memory : to xfer area */
933
934 MIPS32_BNE(10, 9, NEG16(3)), /* bne $t2,t1,loop */
935 MIPS32_ADDI(9, 9, 4), /* addi t1,t1,4 */
936
937 MIPS32_LW(8, MIPS32_FASTDATA_HANDLER_SIZE - 4, 15),
938 MIPS32_LW(9, MIPS32_FASTDATA_HANDLER_SIZE - 8, 15),
939 MIPS32_LW(10, MIPS32_FASTDATA_HANDLER_SIZE - 12, 15),
940 MIPS32_LW(11, MIPS32_FASTDATA_HANDLER_SIZE - 16, 15),
941
942 MIPS32_LUI(15, UPPER16(MIPS32_PRACC_TEXT)),
943 MIPS32_ORI(15, 15, LOWER16(MIPS32_PRACC_TEXT)),
944 MIPS32_JR(15), /* jr start */
945 MIPS32_MFC0(15, 31, 0), /* move COP0 DeSave to $15 */
946 };
947
948 if (source->size < MIPS32_FASTDATA_HANDLER_SIZE)
949 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
950
951 /* write program into RAM */
952 if (write_t != ejtag_info->fast_access_save) {
953 mips32_pracc_write_mem(ejtag_info, source->address, 4, ARRAY_SIZE(handler_code), handler_code);
954 /* save previous operation to speed to any consecutive read/writes */
955 ejtag_info->fast_access_save = write_t;
956 }
957
958 LOG_DEBUG("%s using 0x%.8" TARGET_PRIxADDR " for write handler", __func__, source->address);
959
960 uint32_t jmp_code[] = {
961 MIPS32_LUI(15, UPPER16(source->address)), /* load addr of jump in $15 */
962 MIPS32_ORI(15, 15, LOWER16(source->address)),
963 MIPS32_JR(15), /* jump to ram program */
964 MIPS32_NOP,
965 };
966
967 /* execute jump code, with no address check */
968 for (unsigned i = 0; i < ARRAY_SIZE(jmp_code); i++) {
969 int retval = wait_for_pracc_rw(ejtag_info);
970 if (retval != ERROR_OK)
971 return retval;
972
973 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
974 mips_ejtag_drscan_32_out(ejtag_info, jmp_code[i]);
975
976 /* Clear the access pending bit (let the processor eat!) */
977 mips32_pracc_finish(ejtag_info);
978 }
979
980 /* wait PrAcc pending bit for FASTDATA write, read address */
981 int retval = mips32_pracc_read_ctrl_addr(ejtag_info);
982 if (retval != ERROR_OK)
983 return retval;
984
985 /* next fetch to dmseg should be in FASTDATA_AREA, check */
986 if (ejtag_info->pa_addr != MIPS32_PRACC_FASTDATA_AREA)
987 return ERROR_FAIL;
988
989 /* Send the load start address */
990 uint32_t val = addr;
991 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_FASTDATA);
992 mips_ejtag_fastdata_scan(ejtag_info, 1, &val);
993
994 retval = wait_for_pracc_rw(ejtag_info);
995 if (retval != ERROR_OK)
996 return retval;
997
998 /* Send the load end address */
999 val = addr + (count - 1) * 4;
1000 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_FASTDATA);
1001 mips_ejtag_fastdata_scan(ejtag_info, 1, &val);
1002
1003 unsigned num_clocks = 0; /* like in legacy code */
1004 if (ejtag_info->mode != 0)
1005 num_clocks = ((uint64_t)(ejtag_info->scan_delay) * jtag_get_speed_khz() + 500000) / 1000000;
1006
1007 for (int i = 0; i < count; i++) {
1008 jtag_add_clocks(num_clocks);
1009 mips_ejtag_fastdata_scan(ejtag_info, write_t, buf++);
1010 }
1011
1012 retval = jtag_execute_queue();
1013 if (retval != ERROR_OK) {
1014 LOG_ERROR("fastdata load failed");
1015 return retval;
1016 }
1017
1018 retval = mips32_pracc_read_ctrl_addr(ejtag_info);
1019 if (retval != ERROR_OK)
1020 return retval;
1021
1022 if (ejtag_info->pa_addr != MIPS32_PRACC_TEXT)
1023 LOG_ERROR("mini program did not return to start");
1024
1025 return retval;
1026 }

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)