45d22427313b181e8d26c37bc17ea47e2d843953
[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
239 return ERROR_JTAG_DEVICE_ERROR;
240 }
241 /* check for store instruction at dmseg */
242 uint32_t store_addr = ctx->pracc_list[ctx->max_code + code_count];
243 if (store_addr != 0) {
244 if (store_addr > max_store_addr)
245 max_store_addr = store_addr;
246 store_pending++;
247 }
248
249 instr = ctx->pracc_list[code_count++];
250 if (code_count == ctx->code_count) /* last instruction, start final check */
251 final_check = 1;
252
253 } else { /* final check after function code shifted out */
254 /* check address */
255 if (ejtag_info->pa_addr == MIPS32_PRACC_TEXT) {
256 if (!pass) { /* first pass through pracc text */
257 if (store_pending == 0) /* done, normal exit */
258 return ERROR_OK;
259 pass = 1; /* pracc text passed */
260 code_count = 0; /* restart code count */
261 } else {
262 LOG_DEBUG("unexpected second pass through pracc text");
263 return ERROR_JTAG_DEVICE_ERROR;
264 }
265 } else {
266 if (ejtag_info->pa_addr != (MIPS32_PRACC_TEXT + code_count * 4)) {
267 LOG_DEBUG("unexpected read address in final check: %" PRIx32 ", expected: %x",
268 ejtag_info->pa_addr, MIPS32_PRACC_TEXT + code_count * 4);
269 return ERROR_JTAG_DEVICE_ERROR;
270 }
271 }
272 if (!pass) {
273 if ((code_count - ctx->code_count) > 1) { /* allow max 2 instruction delay slot */
274 LOG_DEBUG("failed to jump back to pracc text");
275 return ERROR_JTAG_DEVICE_ERROR;
276 }
277 } else
278 if (code_count > 10) { /* enough, abandone */
279 LOG_DEBUG("execution abandoned, store pending: %d", store_pending);
280 return ERROR_JTAG_DEVICE_ERROR;
281 }
282 instr = MIPS32_NOP; /* shift out NOPs instructions */
283 code_count++;
284 }
285
286 /* Send instruction out */
287 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
288 mips_ejtag_drscan_32_out(ejtag_info, instr);
289 }
290 /* finish processor access, let the processor eat! */
291 mips32_pracc_finish(ejtag_info);
292
293 if (instr == MIPS32_DRET) /* after leaving debug mode nothing to do */
294 return jtag_execute_queue();
295
296 if (store_pending == 0 && pass) { /* store access done, but after passing pracc text */
297 LOG_DEBUG("warning: store access pass pracc text");
298 return ERROR_OK;
299 }
300 }
301 }
302
303 inline void pracc_queue_init(struct pracc_queue_info *ctx)
304 {
305 ctx->retval = ERROR_OK;
306 ctx->code_count = 0;
307 ctx->store_count = 0;
308
309 ctx->pracc_list = malloc(2 * ctx->max_code * sizeof(uint32_t));
310 if (ctx->pracc_list == NULL) {
311 LOG_ERROR("Out of memory");
312 ctx->retval = ERROR_FAIL;
313 }
314 }
315
316 inline void pracc_add(struct pracc_queue_info *ctx, uint32_t addr, uint32_t instr)
317 {
318 ctx->pracc_list[ctx->max_code + ctx->code_count] = addr;
319 ctx->pracc_list[ctx->code_count++] = instr;
320 if (addr)
321 ctx->store_count++;
322 }
323
324 inline void pracc_add_li32(struct pracc_queue_info *ctx, uint32_t reg_num, uint32_t data, bool optimize)
325 {
326 if (LOWER16(data) == 0 && optimize)
327 pracc_add(ctx, 0, MIPS32_LUI(reg_num, UPPER16(data))); /* load only upper value */
328 else if (UPPER16(data) == 0 && optimize)
329 pracc_add(ctx, 0, MIPS32_ORI(reg_num, 0, LOWER16(data))); /* load only lower */
330 else {
331 pracc_add(ctx, 0, MIPS32_LUI(reg_num, UPPER16(data))); /* load upper and lower */
332 pracc_add(ctx, 0, MIPS32_ORI(reg_num, reg_num, LOWER16(data)));
333 }
334 }
335
336 inline void pracc_queue_free(struct pracc_queue_info *ctx)
337 {
338 if (ctx->code_count > ctx->max_code) /* Only for internal check, will be erased */
339 LOG_ERROR("Internal error, code count: %d > max code: %d", ctx->code_count, ctx->max_code);
340 if (ctx->pracc_list != NULL)
341 free(ctx->pracc_list);
342 }
343
344 int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx, uint32_t *buf)
345 {
346 if (ejtag_info->mode == 0)
347 return mips32_pracc_exec(ejtag_info, ctx, buf);
348
349 union scan_in {
350 uint8_t scan_96[12];
351 struct {
352 uint8_t ctrl[4];
353 uint8_t data[4];
354 uint8_t addr[4];
355 } scan_32;
356
357 } *scan_in = malloc(sizeof(union scan_in) * (ctx->code_count + ctx->store_count));
358 if (scan_in == NULL) {
359 LOG_ERROR("Out of memory");
360 return ERROR_FAIL;
361 }
362
363 unsigned num_clocks =
364 ((uint64_t)(ejtag_info->scan_delay) * jtag_get_speed_khz() + 500000) / 1000000;
365
366 uint32_t ejtag_ctrl = ejtag_info->ejtag_ctrl & ~EJTAG_CTRL_PRACC;
367 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ALL);
368
369 int scan_count = 0;
370 for (int i = 0; i != 2 * ctx->code_count; i++) {
371 uint32_t data = 0;
372 if (i & 1u) { /* Check store address from previous instruction, if not the first */
373 if (i < 2 || 0 == ctx->pracc_list[ctx->max_code + (i / 2) - 1])
374 continue;
375 } else
376 data = ctx->pracc_list[i / 2];
377
378 jtag_add_clocks(num_clocks);
379 mips_ejtag_add_scan_96(ejtag_info, ejtag_ctrl, data, scan_in[scan_count++].scan_96);
380 }
381
382 int retval = jtag_execute_queue(); /* execute queued scans */
383 if (retval != ERROR_OK)
384 goto exit;
385
386 uint32_t fetch_addr = MIPS32_PRACC_TEXT; /* start address */
387 scan_count = 0;
388 for (int i = 0; i != 2 * ctx->code_count; i++) { /* verify every pracc access */
389 uint32_t store_addr = 0;
390 if (i & 1u) { /* Read store addres from previous instruction, if not the first */
391 store_addr = ctx->pracc_list[ctx->max_code + (i / 2) - 1];
392 if (i < 2 || 0 == store_addr)
393 continue;
394 }
395
396 ejtag_ctrl = buf_get_u32(scan_in[scan_count].scan_32.ctrl, 0, 32);
397 if (!(ejtag_ctrl & EJTAG_CTRL_PRACC)) {
398 LOG_ERROR("Error: access not pending count: %d", scan_count);
399 retval = ERROR_FAIL;
400 goto exit;
401 }
402
403 uint32_t addr = buf_get_u32(scan_in[scan_count].scan_32.addr, 0, 32);
404
405 if (store_addr != 0) {
406 if (!(ejtag_ctrl & EJTAG_CTRL_PRNW)) {
407 LOG_ERROR("Not a store/write access, count: %d", scan_count);
408 retval = ERROR_FAIL;
409 goto exit;
410 }
411 if (addr != store_addr) {
412 LOG_ERROR("Store address mismatch, read: %" PRIx32 " expected: %" PRIx32 " count: %d",
413 addr, store_addr, scan_count);
414 retval = ERROR_FAIL;
415 goto exit;
416 }
417 int buf_index = (addr - MIPS32_PRACC_PARAM_OUT) / 4;
418 buf[buf_index] = buf_get_u32(scan_in[scan_count].scan_32.data, 0, 32);
419
420 } else {
421 if (ejtag_ctrl & EJTAG_CTRL_PRNW) {
422 LOG_ERROR("Not a fetch/read access, count: %d", scan_count);
423 retval = ERROR_FAIL;
424 goto exit;
425 }
426 if (addr != fetch_addr) {
427 LOG_ERROR("Fetch addr mismatch, read: %" PRIx32 " expected: %" PRIx32 " count: %d",
428 addr, fetch_addr, scan_count);
429 retval = ERROR_FAIL;
430 goto exit;
431 }
432 fetch_addr += 4;
433 }
434 scan_count++;
435 }
436 exit:
437 free(scan_in);
438 return retval;
439 }
440
441 int mips32_pracc_read_u32(struct mips_ejtag *ejtag_info, uint32_t addr, uint32_t *buf)
442 {
443 struct pracc_queue_info ctx = {.max_code = 8};
444 pracc_queue_init(&ctx);
445 if (ctx.retval != ERROR_OK)
446 goto exit;
447
448 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
449 pracc_add(&ctx, 0, MIPS32_LUI(8, UPPER16((addr + 0x8000)))); /* load $8 with modified upper address */
450 pracc_add(&ctx, 0, MIPS32_LW(8, LOWER16(addr), 8)); /* lw $8, LOWER16(addr)($8) */
451 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
452 MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* sw $8,PRACC_OUT_OFFSET($15) */
453 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
454 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
455 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
456
457 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, buf);
458 exit:
459 pracc_queue_free(&ctx);
460 return ctx.retval;
461 }
462
463 int mips32_pracc_read_mem(struct mips_ejtag *ejtag_info, uint32_t addr, int size, int count, void *buf)
464 {
465 if (count == 1 && size == 4)
466 return mips32_pracc_read_u32(ejtag_info, addr, (uint32_t *)buf);
467
468 uint32_t *data = NULL;
469 struct pracc_queue_info ctx = {.max_code = 256 * 3 + 8 + 1}; /* alloc memory for the worst case */
470 pracc_queue_init(&ctx);
471 if (ctx.retval != ERROR_OK)
472 goto exit;
473
474 if (size != 4) {
475 data = malloc(256 * sizeof(uint32_t));
476 if (data == NULL) {
477 LOG_ERROR("Out of memory");
478 goto exit;
479 }
480 }
481
482 uint32_t *buf32 = buf;
483 uint16_t *buf16 = buf;
484 uint8_t *buf8 = buf;
485
486 while (count) {
487 ctx.code_count = 0;
488 ctx.store_count = 0;
489 int this_round_count = (count > 256) ? 256 : count;
490 uint32_t last_upper_base_addr = UPPER16((addr + 0x8000));
491
492 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
493 pracc_add(&ctx, 0, MIPS32_LUI(9, last_upper_base_addr)); /* load the upper memory address in $9 */
494
495 for (int i = 0; i != this_round_count; i++) { /* Main code loop */
496 uint32_t upper_base_addr = UPPER16((addr + 0x8000));
497 if (last_upper_base_addr != upper_base_addr) { /* if needed, change upper address in $9 */
498 pracc_add(&ctx, 0, MIPS32_LUI(9, upper_base_addr));
499 last_upper_base_addr = upper_base_addr;
500 }
501
502 if (size == 4)
503 pracc_add(&ctx, 0, MIPS32_LW(8, LOWER16(addr), 9)); /* load from memory to $8 */
504 else if (size == 2)
505 pracc_add(&ctx, 0, MIPS32_LHU(8, LOWER16(addr), 9));
506 else
507 pracc_add(&ctx, 0, MIPS32_LBU(8, LOWER16(addr), 9));
508
509 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + i * 4,
510 MIPS32_SW(8, PRACC_OUT_OFFSET + i * 4, 15)); /* store $8 at param out */
511 addr += size;
512 }
513 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
514 pracc_add_li32(&ctx, 9, ejtag_info->reg9, 0); /* restore $9 */
515
516 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
517 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
518
519 if (size == 4) {
520 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, buf32);
521 if (ctx.retval != ERROR_OK)
522 goto exit;
523 buf32 += this_round_count;
524 } else {
525 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, data);
526 if (ctx.retval != ERROR_OK)
527 goto exit;
528
529 uint32_t *data_p = data;
530 for (int i = 0; i != this_round_count; i++) {
531 if (size == 2)
532 *buf16++ = *data_p++;
533 else
534 *buf8++ = *data_p++;
535 }
536 }
537 count -= this_round_count;
538 }
539 exit:
540 pracc_queue_free(&ctx);
541 if (data != NULL)
542 free(data);
543 return ctx.retval;
544 }
545
546 int mips32_cp0_read(struct mips_ejtag *ejtag_info, uint32_t *val, uint32_t cp0_reg, uint32_t cp0_sel)
547 {
548 struct pracc_queue_info ctx = {.max_code = 7};
549 pracc_queue_init(&ctx);
550 if (ctx.retval != ERROR_OK)
551 goto exit;
552
553 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
554 pracc_add(&ctx, 0, MIPS32_MFC0(8, cp0_reg, cp0_sel)); /* move cp0 reg / sel to $8 */
555 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
556 MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
557 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
558 pracc_add(&ctx, 0, MIPS32_LUI(8, UPPER16(ejtag_info->reg8))); /* restore upper 16 bits of $8 */
559 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
560 pracc_add(&ctx, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info->reg8))); /* restore lower 16 bits of $8 */
561
562 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, val);
563 exit:
564 pracc_queue_free(&ctx);
565 return ctx.retval;
566 }
567
568 int mips32_cp0_write(struct mips_ejtag *ejtag_info, uint32_t val, uint32_t cp0_reg, uint32_t cp0_sel)
569 {
570 struct pracc_queue_info ctx = {.max_code = 6};
571 pracc_queue_init(&ctx);
572 if (ctx.retval != ERROR_OK)
573 goto exit;
574
575 pracc_add_li32(&ctx, 15, val, 0); /* Load val to $15 */
576
577 pracc_add(&ctx, 0, MIPS32_MTC0(15, cp0_reg, cp0_sel)); /* write $15 to cp0 reg / sel */
578 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
579 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
580
581 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
582 exit:
583 pracc_queue_free(&ctx);
584 return ctx.retval;
585 }
586
587 /**
588 * \b mips32_pracc_sync_cache
589 *
590 * Synchronize Caches to Make Instruction Writes Effective
591 * (ref. doc. MIPS32 Architecture For Programmers Volume II: The MIPS32 Instruction Set,
592 * Document Number: MD00086, Revision 2.00, June 9, 2003)
593 *
594 * When the instruction stream is written, the SYNCI instruction should be used
595 * in conjunction with other instructions to make the newly-written instructions effective.
596 *
597 * Explanation :
598 * A program that loads another program into memory is actually writing the D- side cache.
599 * The instructions it has loaded can't be executed until they reach the I-cache.
600 *
601 * After the instructions have been written, the loader should arrange
602 * to write back any containing D-cache line and invalidate any locations
603 * already in the I-cache.
604 *
605 * If the cache coherency attribute (CCA) is set to zero, it's a write through cache, there is no need
606 * to write back.
607 *
608 * In the latest MIPS32/64 CPUs, MIPS provides the synci instruction,
609 * which does the whole job for a cache-line-sized chunk of the memory you just loaded:
610 * That is, it arranges a D-cache write-back (if CCA = 3) and an I-cache invalidate.
611 *
612 * The line size is obtained with the rdhwr SYNCI_Step in release 2 or from cp0 config 1 register in release 1.
613 */
614 static int mips32_pracc_synchronize_cache(struct mips_ejtag *ejtag_info,
615 uint32_t start_addr, uint32_t end_addr, int cached, int rel)
616 {
617 struct pracc_queue_info ctx = {.max_code = 256 * 2 + 5};
618 pracc_queue_init(&ctx);
619 if (ctx.retval != ERROR_OK)
620 goto exit;
621 /** Find cache line size in bytes */
622 uint32_t clsiz;
623 if (rel) { /* Release 2 (rel = 1) */
624 pracc_add(&ctx, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
625
626 pracc_add(&ctx, 0, MIPS32_RDHWR(8, MIPS32_SYNCI_STEP)); /* load synci_step value to $8 */
627
628 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
629 MIPS32_SW(8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
630
631 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
632
633 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
634 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
635
636 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, &clsiz);
637 if (ctx.retval != ERROR_OK)
638 goto exit;
639
640 } else { /* Release 1 (rel = 0) */
641 uint32_t conf;
642 ctx.retval = mips32_cp0_read(ejtag_info, &conf, 16, 1);
643 if (ctx.retval != ERROR_OK)
644 goto exit;
645
646 uint32_t dl = (conf & MIPS32_CONFIG1_DL_MASK) >> MIPS32_CONFIG1_DL_SHIFT;
647
648 /* dl encoding : dl=1 => 4 bytes, dl=2 => 8 bytes, etc... max dl=6 => 128 bytes cache line size */
649 clsiz = 0x2 << dl;
650 if (dl == 0)
651 clsiz = 0;
652 }
653
654 if (clsiz == 0)
655 goto exit; /* Nothing to do */
656
657 /* make sure clsiz is power of 2 */
658 if (clsiz & (clsiz - 1)) {
659 LOG_DEBUG("clsiz must be power of 2");
660 ctx.retval = ERROR_FAIL;
661 goto exit;
662 }
663
664 /* make sure start_addr and end_addr have the same offset inside de cache line */
665 start_addr |= clsiz - 1;
666 end_addr |= clsiz - 1;
667
668 ctx.code_count = 0;
669 int count = 0;
670 uint32_t last_upper_base_addr = UPPER16((start_addr + 0x8000));
671
672 pracc_add(&ctx, 0, MIPS32_LUI(15, last_upper_base_addr)); /* load upper memory base address to $15 */
673
674 while (start_addr <= end_addr) { /* main loop */
675 uint32_t upper_base_addr = UPPER16((start_addr + 0x8000));
676 if (last_upper_base_addr != upper_base_addr) { /* if needed, change upper address in $15 */
677 pracc_add(&ctx, 0, MIPS32_LUI(15, upper_base_addr));
678 last_upper_base_addr = upper_base_addr;
679 }
680 if (rel)
681 pracc_add(&ctx, 0, MIPS32_SYNCI(LOWER16(start_addr), 15)); /* synci instruction, offset($15) */
682
683 else {
684 if (cached == 3)
685 pracc_add(&ctx, 0, MIPS32_CACHE(MIPS32_CACHE_D_HIT_WRITEBACK,
686 LOWER16(start_addr), 15)); /* cache Hit_Writeback_D, offset($15) */
687
688 pracc_add(&ctx, 0, MIPS32_CACHE(MIPS32_CACHE_I_HIT_INVALIDATE,
689 LOWER16(start_addr), 15)); /* cache Hit_Invalidate_I, offset($15) */
690 }
691 start_addr += clsiz;
692 count++;
693 if (count == 256 && start_addr <= end_addr) { /* more ?, then execute code list */
694 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
695 pracc_add(&ctx, 0, MIPS32_NOP); /* nop in delay slot */
696
697 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
698 if (ctx.retval != ERROR_OK)
699 goto exit;
700
701 ctx.code_count = 0;
702 count = 0;
703 }
704 }
705 pracc_add(&ctx, 0, MIPS32_SYNC);
706 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
707 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave*/
708
709 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
710 exit:
711 pracc_queue_free(&ctx);
712 return ctx.retval;
713 }
714
715 static int mips32_pracc_write_mem_generic(struct mips_ejtag *ejtag_info,
716 uint32_t addr, int size, int count, const void *buf)
717 {
718 struct pracc_queue_info ctx = {.max_code = 128 * 3 + 5 + 1}; /* alloc memory for the worst case */
719 pracc_queue_init(&ctx);
720 if (ctx.retval != ERROR_OK)
721 goto exit;
722
723 const uint32_t *buf32 = buf;
724 const uint16_t *buf16 = buf;
725 const uint8_t *buf8 = buf;
726
727 while (count) {
728 ctx.code_count = 0;
729 ctx.store_count = 0;
730 int this_round_count = (count > 128) ? 128 : count;
731 uint32_t last_upper_base_addr = UPPER16((addr + 0x8000));
732
733 pracc_add(&ctx, 0, MIPS32_LUI(15, last_upper_base_addr)); /* load $15 with memory base address */
734
735 for (int i = 0; i != this_round_count; i++) {
736 uint32_t upper_base_addr = UPPER16((addr + 0x8000));
737 if (last_upper_base_addr != upper_base_addr) {
738 pracc_add(&ctx, 0, MIPS32_LUI(15, upper_base_addr)); /* if needed, change upper address in $15*/
739 last_upper_base_addr = upper_base_addr;
740 }
741
742 if (size == 4) {
743 pracc_add_li32(&ctx, 8, *buf32, 1); /* load with li32, optimize */
744 pracc_add(&ctx, 0, MIPS32_SW(8, LOWER16(addr), 15)); /* store word to memory */
745 buf32++;
746
747 } else if (size == 2) {
748 pracc_add(&ctx, 0, MIPS32_ORI(8, 0, *buf16)); /* load lower value */
749 pracc_add(&ctx, 0, MIPS32_SH(8, LOWER16(addr), 15)); /* store half word to memory */
750 buf16++;
751
752 } else {
753 pracc_add(&ctx, 0, MIPS32_ORI(8, 0, *buf8)); /* load lower value */
754 pracc_add(&ctx, 0, MIPS32_SB(8, LOWER16(addr), 15)); /* store byte to memory */
755 buf8++;
756 }
757 addr += size;
758 }
759
760 pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
761
762 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
763 pracc_add(&ctx, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
764
765 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
766 if (ctx.retval != ERROR_OK)
767 goto exit;
768 count -= this_round_count;
769 }
770 exit:
771 pracc_queue_free(&ctx);
772 return ctx.retval;
773 }
774
775 int mips32_pracc_write_mem(struct mips_ejtag *ejtag_info, uint32_t addr, int size, int count, const void *buf)
776 {
777 int retval = mips32_pracc_write_mem_generic(ejtag_info, addr, size, count, buf);
778 if (retval != ERROR_OK)
779 return retval;
780
781 /**
782 * If we are in the cacheable region and cache is activated,
783 * we must clean D$ (if Cache Coherency Attribute is set to 3) + invalidate I$ after we did the write,
784 * so that changes do not continue to live only in D$ (if CCA = 3), but to be
785 * replicated in I$ also (maybe we wrote the istructions)
786 */
787 uint32_t conf = 0;
788 int cached = 0;
789
790 if ((KSEGX(addr) == KSEG1) || ((addr >= 0xff200000) && (addr <= 0xff3fffff)))
791 return retval; /*Nothing to do*/
792
793 mips32_cp0_read(ejtag_info, &conf, 16, 0);
794
795 switch (KSEGX(addr)) {
796 case KUSEG:
797 cached = (conf & MIPS32_CONFIG0_KU_MASK) >> MIPS32_CONFIG0_KU_SHIFT;
798 break;
799 case KSEG0:
800 cached = (conf & MIPS32_CONFIG0_K0_MASK) >> MIPS32_CONFIG0_K0_SHIFT;
801 break;
802 case KSEG2:
803 case KSEG3:
804 cached = (conf & MIPS32_CONFIG0_K23_MASK) >> MIPS32_CONFIG0_K23_SHIFT;
805 break;
806 default:
807 /* what ? */
808 break;
809 }
810
811 /**
812 * Check cachablitiy bits coherency algorithm
813 * is the region cacheable or uncached.
814 * If cacheable we have to synchronize the cache
815 */
816 if (cached == 3 || cached == 0) { /* Write back cache or write through cache */
817 uint32_t start_addr = addr;
818 uint32_t end_addr = addr + count * size;
819 uint32_t rel = (conf & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
820 if (rel > 1) {
821 LOG_DEBUG("Unknown release in cache code");
822 return ERROR_FAIL;
823 }
824 retval = mips32_pracc_synchronize_cache(ejtag_info, start_addr, end_addr, cached, rel);
825 }
826
827 return retval;
828 }
829
830 int mips32_pracc_write_regs(struct mips_ejtag *ejtag_info, uint32_t *regs)
831 {
832 static const uint32_t cp0_write_code[] = {
833 MIPS32_MTC0(1, 12, 0), /* move $1 to status */
834 MIPS32_MTLO(1), /* move $1 to lo */
835 MIPS32_MTHI(1), /* move $1 to hi */
836 MIPS32_MTC0(1, 8, 0), /* move $1 to badvaddr */
837 MIPS32_MTC0(1, 13, 0), /* move $1 to cause*/
838 MIPS32_MTC0(1, 24, 0), /* move $1 to depc (pc) */
839 };
840
841 struct pracc_queue_info ctx = {.max_code = 37 * 2 + 7 + 1};
842 pracc_queue_init(&ctx);
843 if (ctx.retval != ERROR_OK)
844 goto exit;
845
846 /* load registers 2 to 31 with li32, optimize */
847 for (int i = 2; i < 32; i++)
848 pracc_add_li32(&ctx, i, regs[i], 1);
849
850 for (int i = 0; i != 6; i++) {
851 pracc_add_li32(&ctx, 1, regs[i + 32], 0); /* load CPO value in $1 */
852 pracc_add(&ctx, 0, cp0_write_code[i]); /* write value from $1 to CPO register */
853 }
854 pracc_add(&ctx, 0, MIPS32_MTC0(15, 31, 0)); /* load $15 in DeSave */
855 pracc_add(&ctx, 0, MIPS32_LUI(1, UPPER16((regs[1])))); /* load upper half word in $1 */
856 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
857 pracc_add(&ctx, 0, MIPS32_ORI(1, 1, LOWER16((regs[1])))); /* load lower half word in $1 */
858
859 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL);
860
861 ejtag_info->reg8 = regs[8];
862 ejtag_info->reg9 = regs[9];
863 exit:
864 pracc_queue_free(&ctx);
865 return ctx.retval;
866 }
867
868 int mips32_pracc_read_regs(struct mips_ejtag *ejtag_info, uint32_t *regs)
869 {
870 static int cp0_read_code[] = {
871 MIPS32_MFC0(8, 12, 0), /* move status to $8 */
872 MIPS32_MFLO(8), /* move lo to $8 */
873 MIPS32_MFHI(8), /* move hi to $8 */
874 MIPS32_MFC0(8, 8, 0), /* move badvaddr to $8 */
875 MIPS32_MFC0(8, 13, 0), /* move cause to $8 */
876 MIPS32_MFC0(8, 24, 0), /* move depc (pc) to $8 */
877 };
878
879 struct pracc_queue_info ctx = {.max_code = 49};
880 pracc_queue_init(&ctx);
881 if (ctx.retval != ERROR_OK)
882 goto exit;
883
884 pracc_add(&ctx, 0, MIPS32_MTC0(1, 31, 0)); /* move $1 to COP0 DeSave */
885 pracc_add(&ctx, 0, MIPS32_LUI(1, PRACC_UPPER_BASE_ADDR)); /* $1 = MIP32_PRACC_BASE_ADDR */
886
887 for (int i = 2; i != 32; i++) /* store GPR's 2 to 31 */
888 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i * 4),
889 MIPS32_SW(i, PRACC_OUT_OFFSET + (i * 4), 1));
890
891 for (int i = 0; i != 6; i++) {
892 pracc_add(&ctx, 0, cp0_read_code[i]); /* load COP0 needed registers to $8 */
893 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i + 32) * 4, /* store $8 at PARAM OUT */
894 MIPS32_SW(8, PRACC_OUT_OFFSET + (i + 32) * 4, 1));
895 }
896 pracc_add(&ctx, 0, MIPS32_MFC0(8, 31, 0)); /* move DeSave to $8, reg1 value */
897 pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + 4, /* store reg1 value from $8 to param out */
898 MIPS32_SW(8, PRACC_OUT_OFFSET + 4, 1));
899
900 pracc_add(&ctx, 0, MIPS32_MFC0(1, 31, 0)); /* move COP0 DeSave to $1, restore reg1 */
901 pracc_add(&ctx, 0, MIPS32_B(NEG16(ctx.code_count + 1))); /* jump to start */
902 pracc_add(&ctx, 0, MIPS32_MTC0(15, 31, 0)); /* load $15 in DeSave */
903
904 ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, regs);
905
906 ejtag_info->reg8 = regs[8]; /* reg8 is saved but not restored, next called function should restore it */
907 ejtag_info->reg9 = regs[9];
908 exit:
909 pracc_queue_free(&ctx);
910 return ctx.retval;
911 }
912
913 /* fastdata upload/download requires an initialized working area
914 * to load the download code; it should not be called otherwise
915 * fetch order from the fastdata area
916 * 1. start addr
917 * 2. end addr
918 * 3. data ...
919 */
920 int mips32_pracc_fastdata_xfer(struct mips_ejtag *ejtag_info, struct working_area *source,
921 int write_t, uint32_t addr, int count, uint32_t *buf)
922 {
923 uint32_t handler_code[] = {
924 /* r15 points to the start of this code */
925 MIPS32_SW(8, MIPS32_FASTDATA_HANDLER_SIZE - 4, 15),
926 MIPS32_SW(9, MIPS32_FASTDATA_HANDLER_SIZE - 8, 15),
927 MIPS32_SW(10, MIPS32_FASTDATA_HANDLER_SIZE - 12, 15),
928 MIPS32_SW(11, MIPS32_FASTDATA_HANDLER_SIZE - 16, 15),
929 /* start of fastdata area in t0 */
930 MIPS32_LUI(8, UPPER16(MIPS32_PRACC_FASTDATA_AREA)),
931 MIPS32_ORI(8, 8, LOWER16(MIPS32_PRACC_FASTDATA_AREA)),
932 MIPS32_LW(9, 0, 8), /* start addr in t1 */
933 MIPS32_LW(10, 0, 8), /* end addr to t2 */
934 /* loop: */
935 write_t ? MIPS32_LW(11, 0, 8) : MIPS32_LW(11, 0, 9), /* from xfer area : from memory */
936 write_t ? MIPS32_SW(11, 0, 9) : MIPS32_SW(11, 0, 8), /* to memory : to xfer area */
937
938 MIPS32_BNE(10, 9, NEG16(3)), /* bne $t2,t1,loop */
939 MIPS32_ADDI(9, 9, 4), /* addi t1,t1,4 */
940
941 MIPS32_LW(8, MIPS32_FASTDATA_HANDLER_SIZE - 4, 15),
942 MIPS32_LW(9, MIPS32_FASTDATA_HANDLER_SIZE - 8, 15),
943 MIPS32_LW(10, MIPS32_FASTDATA_HANDLER_SIZE - 12, 15),
944 MIPS32_LW(11, MIPS32_FASTDATA_HANDLER_SIZE - 16, 15),
945
946 MIPS32_LUI(15, UPPER16(MIPS32_PRACC_TEXT)),
947 MIPS32_ORI(15, 15, LOWER16(MIPS32_PRACC_TEXT)),
948 MIPS32_JR(15), /* jr start */
949 MIPS32_MFC0(15, 31, 0), /* move COP0 DeSave to $15 */
950 };
951
952 if (source->size < MIPS32_FASTDATA_HANDLER_SIZE)
953 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
954
955 /* write program into RAM */
956 if (write_t != ejtag_info->fast_access_save) {
957 mips32_pracc_write_mem(ejtag_info, source->address, 4, ARRAY_SIZE(handler_code), handler_code);
958 /* save previous operation to speed to any consecutive read/writes */
959 ejtag_info->fast_access_save = write_t;
960 }
961
962 LOG_DEBUG("%s using 0x%.8" TARGET_PRIxADDR " for write handler", __func__, source->address);
963
964 uint32_t jmp_code[] = {
965 MIPS32_LUI(15, UPPER16(source->address)), /* load addr of jump in $15 */
966 MIPS32_ORI(15, 15, LOWER16(source->address)),
967 MIPS32_JR(15), /* jump to ram program */
968 MIPS32_NOP,
969 };
970
971 /* execute jump code, with no address check */
972 for (unsigned i = 0; i < ARRAY_SIZE(jmp_code); i++) {
973 int retval = wait_for_pracc_rw(ejtag_info);
974 if (retval != ERROR_OK)
975 return retval;
976
977 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_DATA);
978 mips_ejtag_drscan_32_out(ejtag_info, jmp_code[i]);
979
980 /* Clear the access pending bit (let the processor eat!) */
981 mips32_pracc_finish(ejtag_info);
982 }
983
984 /* wait PrAcc pending bit for FASTDATA write, read address */
985 int retval = mips32_pracc_read_ctrl_addr(ejtag_info);
986 if (retval != ERROR_OK)
987 return retval;
988
989 /* next fetch to dmseg should be in FASTDATA_AREA, check */
990 if (ejtag_info->pa_addr != MIPS32_PRACC_FASTDATA_AREA)
991 return ERROR_FAIL;
992
993 /* Send the load start address */
994 uint32_t val = addr;
995 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_FASTDATA);
996 mips_ejtag_fastdata_scan(ejtag_info, 1, &val);
997
998 retval = wait_for_pracc_rw(ejtag_info);
999 if (retval != ERROR_OK)
1000 return retval;
1001
1002 /* Send the load end address */
1003 val = addr + (count - 1) * 4;
1004 mips_ejtag_set_instr(ejtag_info, EJTAG_INST_FASTDATA);
1005 mips_ejtag_fastdata_scan(ejtag_info, 1, &val);
1006
1007 unsigned num_clocks = 0; /* like in legacy code */
1008 if (ejtag_info->mode != 0)
1009 num_clocks = ((uint64_t)(ejtag_info->scan_delay) * jtag_get_speed_khz() + 500000) / 1000000;
1010
1011 for (int i = 0; i < count; i++) {
1012 jtag_add_clocks(num_clocks);
1013 mips_ejtag_fastdata_scan(ejtag_info, write_t, buf++);
1014 }
1015
1016 retval = jtag_execute_queue();
1017 if (retval != ERROR_OK) {
1018 LOG_ERROR("fastdata load failed");
1019 return retval;
1020 }
1021
1022 retval = mips32_pracc_read_ctrl_addr(ejtag_info);
1023 if (retval != ERROR_OK)
1024 return retval;
1025
1026 if (ejtag_info->pa_addr != MIPS32_PRACC_TEXT)
1027 LOG_ERROR("mini program did not return to start");
1028
1029 return retval;
1030 }

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)