1 /***************************************************************************
2 * Copyright (C) 2008 by Spencer Oliver *
3 * spen@spen-soft.co.uk *
5 * Copyright (C) 2008 by David T.L. Wong *
7 * Copyright (C) 2009 by David N. Claffey <dnclaffey@gmail.com> *
9 * Copyright (C) 2011 by Drasko DRASKOVIC *
10 * drasko.draskovic@gmail.com *
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. *
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. *
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 ***************************************************************************/
27 * This version has optimized assembly routines for 32 bit operations:
30 * - write array of words
32 * One thing to be aware of is that the MIPS32 cpu will execute the
33 * instruction after a branch instruction (one delay slot).
40 * The LW $1, ($2 +100) instruction is also executed. If this is
41 * not wanted a NOP can be inserted:
48 * or the code can be changed to:
54 * The original code contained NOPs. I have removed these and moved
57 * These changes result in a 35% speed increase when programming an
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.
71 #include <helper/time_support.h>
74 #include "mips32_pracc.h"
76 struct mips32_pracc_context
{
77 uint32_t *local_oparam
;
83 struct mips_ejtag
*ejtag_info
;
86 static int wait_for_pracc_rw(struct mips_ejtag
*ejtag_info
, uint32_t *ctrl
)
89 int64_t then
= timeval_ms();
91 /* wait for the PrAcc to become "1" */
92 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_CONTROL
);
95 ejtag_ctrl
= ejtag_info
->ejtag_ctrl
;
96 int retval
= mips_ejtag_drscan_32(ejtag_info
, &ejtag_ctrl
);
97 if (retval
!= ERROR_OK
)
100 if (ejtag_ctrl
& EJTAG_CTRL_PRACC
)
103 int64_t timeout
= timeval_ms() - then
;
104 if (timeout
> 1000) {
105 LOG_DEBUG("DEBUGMODULE: No memory access in progress!");
106 return ERROR_JTAG_DEVICE_ERROR
;
114 /* Shift in control and address for a new processor access, save them in ejtag_info */
115 static int mips32_pracc_read_ctrl_addr(struct mips_ejtag
*ejtag_info
)
117 int retval
= wait_for_pracc_rw(ejtag_info
, &ejtag_info
->pa_ctrl
);
118 if (retval
!= ERROR_OK
)
121 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_ADDRESS
);
122 ejtag_info
->pa_addr
= 0;
123 retval
= mips_ejtag_drscan_32(ejtag_info
, &ejtag_info
->pa_addr
);
128 /* Finish processor access */
129 static int mips32_pracc_finish(struct mips_ejtag
*ejtag_info
)
131 uint32_t ctrl
= ejtag_info
->ejtag_ctrl
& ~EJTAG_CTRL_PRACC
;
132 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_CONTROL
);
133 mips_ejtag_drscan_32_out(ejtag_info
, ctrl
);
135 return jtag_execute_queue();
138 int mips32_pracc_clean_text_jump(struct mips_ejtag
*ejtag_info
)
140 uint32_t jt_code
= MIPS32_J((0x0FFFFFFF & MIPS32_PRACC_TEXT
) >> 2);
143 /* do 3 0/nops to clean pipeline before a jump to pracc text, NOP in delay slot */
144 for (int i
= 0; i
!= 5; i
++) {
146 retval
= wait_for_pracc_rw(ejtag_info
, &ejtag_info
->pa_ctrl
);
147 if (retval
!= ERROR_OK
)
150 /* Data or instruction out */
151 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_DATA
);
152 uint32_t data
= (i
== 3) ? jt_code
: MIPS32_NOP
;
153 mips_ejtag_drscan_32_out(ejtag_info
, data
);
156 retval
= mips32_pracc_finish(ejtag_info
);
157 if (retval
!= ERROR_OK
)
161 if (ejtag_info
->mode
!= 0) /* async mode support only for MIPS ... */
164 for (int i
= 0; i
!= 2; i
++) {
165 retval
= mips32_pracc_read_ctrl_addr(ejtag_info
);
166 if (retval
!= ERROR_OK
)
169 if (ejtag_info
->pa_addr
!= MIPS32_PRACC_TEXT
) { /* LEXRA/BMIPS ?, shift out another NOP, max 2 */
170 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_DATA
);
171 mips_ejtag_drscan_32_out(ejtag_info
, MIPS32_NOP
);
172 retval
= mips32_pracc_finish(ejtag_info
);
173 if (retval
!= ERROR_OK
)
182 int mips32_pracc_exec(struct mips_ejtag
*ejtag_info
, struct pracc_queue_info
*ctx
, uint32_t *param_out
)
185 int store_pending
= 0; /* increases with every store instruction at dmseg, decreases with every store pa */
186 uint32_t max_store_addr
= 0; /* for store pa address testing */
187 bool restart
= 0; /* restarting control */
188 int restart_count
= 0;
190 bool final_check
= 0; /* set to 1 if in final checks after function code shifted out */
191 bool pass
= 0; /* to check the pass through pracc text after function code sent */
196 if (restart_count
< 3) { /* max 3 restarts allowed */
197 retval
= mips32_pracc_clean_text_jump(ejtag_info
);
198 if (retval
!= ERROR_OK
)
201 return ERROR_JTAG_DEVICE_ERROR
;
205 LOG_DEBUG("restarting code");
208 retval
= mips32_pracc_read_ctrl_addr(ejtag_info
); /* update current pa info: control and address */
209 if (retval
!= ERROR_OK
)
212 /* Check for read or write access */
213 if (ejtag_info
->pa_ctrl
& EJTAG_CTRL_PRNW
) { /* write/store access */
214 /* Check for pending store from a previous store instruction at dmseg */
215 if (store_pending
== 0) {
216 LOG_DEBUG("unexpected write at address %" PRIx32
, ejtag_info
->pa_addr
);
217 if (code_count
< 2) { /* allow for restart */
221 return ERROR_JTAG_DEVICE_ERROR
;
224 if (ejtag_info
->pa_addr
< MIPS32_PRACC_PARAM_OUT
|| ejtag_info
->pa_addr
> max_store_addr
) {
226 LOG_DEBUG("writing at unexpected address %" PRIx32
, ejtag_info
->pa_addr
);
227 return ERROR_JTAG_DEVICE_ERROR
;
232 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_DATA
);
233 retval
= mips_ejtag_drscan_32(ejtag_info
, &data
);
234 if (retval
!= ERROR_OK
)
237 /* store data at param out, address based offset */
238 param_out
[(ejtag_info
->pa_addr
- MIPS32_PRACC_PARAM_OUT
) / 4] = data
;
241 } else { /* read/fetch access */
242 if (!final_check
) { /* executing function code */
244 if (ejtag_info
->pa_addr
!= (MIPS32_PRACC_TEXT
+ code_count
* 4)) {
245 LOG_DEBUG("reading at unexpected address %" PRIx32
", expected %x",
246 ejtag_info
->pa_addr
, MIPS32_PRACC_TEXT
+ code_count
* 4);
248 /* restart code execution only in some cases */
249 if (code_count
== 1 && ejtag_info
->pa_addr
== MIPS32_PRACC_TEXT
&& restart_count
== 0) {
250 LOG_DEBUG("restarting, without clean jump");
254 } else if (code_count
< 2) {
259 return ERROR_JTAG_DEVICE_ERROR
;
261 /* check for store instruction at dmseg */
262 uint32_t store_addr
= ctx
->pracc_list
[ctx
->max_code
+ code_count
];
263 if (store_addr
!= 0) {
264 if (store_addr
> max_store_addr
)
265 max_store_addr
= store_addr
;
269 instr
= ctx
->pracc_list
[code_count
++];
270 if (code_count
== ctx
->code_count
) /* last instruction, start final check */
273 } else { /* final check after function code shifted out */
275 if (ejtag_info
->pa_addr
== MIPS32_PRACC_TEXT
) {
276 if (!pass
) { /* first pass through pracc text */
277 if (store_pending
== 0) /* done, normal exit */
279 pass
= 1; /* pracc text passed */
280 code_count
= 0; /* restart code count */
282 LOG_DEBUG("unexpected second pass through pracc text");
283 return ERROR_JTAG_DEVICE_ERROR
;
286 if (ejtag_info
->pa_addr
!= (MIPS32_PRACC_TEXT
+ code_count
* 4)) {
287 LOG_DEBUG("unexpected read address in final check: %" PRIx32
", expected: %x",
288 ejtag_info
->pa_addr
, MIPS32_PRACC_TEXT
+ code_count
* 4);
289 return ERROR_JTAG_DEVICE_ERROR
;
293 if ((code_count
- ctx
->code_count
) > 1) { /* allow max 2 instruction delay slot */
294 LOG_DEBUG("failed to jump back to pracc text");
295 return ERROR_JTAG_DEVICE_ERROR
;
298 if (code_count
> 10) { /* enough, abandone */
299 LOG_DEBUG("execution abandoned, store pending: %d", store_pending
);
300 return ERROR_JTAG_DEVICE_ERROR
;
302 instr
= MIPS32_NOP
; /* shift out NOPs instructions */
306 /* Send instruction out */
307 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_DATA
);
308 mips_ejtag_drscan_32_out(ejtag_info
, instr
);
310 /* finish processor access, let the processor eat! */
311 retval
= mips32_pracc_finish(ejtag_info
);
312 if (retval
!= ERROR_OK
)
315 if (instr
== MIPS32_DRET
) /* after leaving debug mode nothing to do */
318 if (store_pending
== 0 && pass
) { /* store access done, but after passing pracc text */
319 LOG_DEBUG("warning: store access pass pracc text");
325 inline void pracc_queue_init(struct pracc_queue_info
*ctx
)
327 ctx
->retval
= ERROR_OK
;
329 ctx
->store_count
= 0;
331 ctx
->pracc_list
= malloc(2 * ctx
->max_code
* sizeof(uint32_t));
332 if (ctx
->pracc_list
== NULL
) {
333 LOG_ERROR("Out of memory");
334 ctx
->retval
= ERROR_FAIL
;
338 inline void pracc_add(struct pracc_queue_info
*ctx
, uint32_t addr
, uint32_t instr
)
340 ctx
->pracc_list
[ctx
->max_code
+ ctx
->code_count
] = addr
;
341 ctx
->pracc_list
[ctx
->code_count
++] = instr
;
346 inline void pracc_queue_free(struct pracc_queue_info
*ctx
)
348 if (ctx
->code_count
> ctx
->max_code
) /* Only for internal check, will be erased */
349 LOG_ERROR("Internal error, code count: %d > max code: %d", ctx
->code_count
, ctx
->max_code
);
350 if (ctx
->pracc_list
!= NULL
)
351 free(ctx
->pracc_list
);
354 int mips32_pracc_queue_exec(struct mips_ejtag
*ejtag_info
, struct pracc_queue_info
*ctx
, uint32_t *buf
)
356 if (ejtag_info
->mode
== 0)
357 return mips32_pracc_exec(ejtag_info
, ctx
, buf
);
367 } *scan_in
= malloc(sizeof(union scan_in
) * (ctx
->code_count
+ ctx
->store_count
));
368 if (scan_in
== NULL
) {
369 LOG_ERROR("Out of memory");
373 unsigned num_clocks
=
374 ((uint64_t)(ejtag_info
->scan_delay
) * jtag_get_speed_khz() + 500000) / 1000000;
376 uint32_t ejtag_ctrl
= ejtag_info
->ejtag_ctrl
& ~EJTAG_CTRL_PRACC
;
377 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_ALL
);
380 for (int i
= 0; i
!= 2 * ctx
->code_count
; i
++) {
382 if (i
& 1u) { /* Check store address from previous instruction, if not the first */
383 if (i
< 2 || 0 == ctx
->pracc_list
[ctx
->max_code
+ (i
/ 2) - 1])
386 data
= ctx
->pracc_list
[i
/ 2];
388 jtag_add_clocks(num_clocks
);
389 mips_ejtag_add_scan_96(ejtag_info
, ejtag_ctrl
, data
, scan_in
[scan_count
++].scan_96
);
392 int retval
= jtag_execute_queue(); /* execute queued scans */
393 if (retval
!= ERROR_OK
)
396 uint32_t fetch_addr
= MIPS32_PRACC_TEXT
; /* start address */
398 for (int i
= 0; i
!= 2 * ctx
->code_count
; i
++) { /* verify every pracc access */
399 uint32_t store_addr
= 0;
400 if (i
& 1u) { /* Read store addres from previous instruction, if not the first */
401 store_addr
= ctx
->pracc_list
[ctx
->max_code
+ (i
/ 2) - 1];
402 if (i
< 2 || 0 == store_addr
)
406 ejtag_ctrl
= buf_get_u32(scan_in
[scan_count
].scan_32
.ctrl
, 0, 32);
407 if (!(ejtag_ctrl
& EJTAG_CTRL_PRACC
)) {
408 LOG_ERROR("Error: access not pending count: %d", scan_count
);
413 uint32_t addr
= buf_get_u32(scan_in
[scan_count
].scan_32
.addr
, 0, 32);
415 if (store_addr
!= 0) {
416 if (!(ejtag_ctrl
& EJTAG_CTRL_PRNW
)) {
417 LOG_ERROR("Not a store/write access, count: %d", scan_count
);
421 if (addr
!= store_addr
) {
422 LOG_ERROR("Store address mismatch, read: %" PRIx32
" expected: %" PRIx32
" count: %d",
423 addr
, store_addr
, scan_count
);
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);
431 if (ejtag_ctrl
& EJTAG_CTRL_PRNW
) {
432 LOG_ERROR("Not a fetch/read access, count: %d", scan_count
);
436 if (addr
!= fetch_addr
) {
437 LOG_ERROR("Fetch addr mismatch, read: %" PRIx32
" expected: %" PRIx32
" count: %d",
438 addr
, fetch_addr
, scan_count
);
451 int mips32_pracc_read_u32(struct mips_ejtag
*ejtag_info
, uint32_t addr
, uint32_t *buf
)
453 struct pracc_queue_info ctx
= {.max_code
= 8};
454 pracc_queue_init(&ctx
);
455 if (ctx
.retval
!= ERROR_OK
)
458 pracc_add(&ctx
, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR
)); /* $15 = MIPS32_PRACC_BASE_ADDR */
459 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16((addr
+ 0x8000)))); /* load $8 with modified upper address */
460 pracc_add(&ctx
, 0, MIPS32_LW(8, LOWER16(addr
), 8)); /* lw $8, LOWER16(addr)($8) */
461 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
,
462 MIPS32_SW(8, PRACC_OUT_OFFSET
, 15)); /* sw $8,PRACC_OUT_OFFSET($15) */
463 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(ejtag_info
->reg8
))); /* restore upper 16 of $8 */
464 pracc_add(&ctx
, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info
->reg8
))); /* restore lower 16 of $8 */
465 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
466 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
468 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, buf
);
470 pracc_queue_free(&ctx
);
474 int mips32_pracc_read_mem(struct mips_ejtag
*ejtag_info
, uint32_t addr
, int size
, int count
, void *buf
)
476 if (count
== 1 && size
== 4)
477 return mips32_pracc_read_u32(ejtag_info
, addr
, (uint32_t *)buf
);
479 uint32_t *data
= NULL
;
480 struct pracc_queue_info ctx
= {.max_code
= 256 * 3 + 8 + 1}; /* alloc memory for the worst case */
481 pracc_queue_init(&ctx
);
482 if (ctx
.retval
!= ERROR_OK
)
486 data
= malloc(256 * sizeof(uint32_t));
488 LOG_ERROR("Out of memory");
493 uint32_t *buf32
= buf
;
494 uint16_t *buf16
= buf
;
500 int this_round_count
= (count
> 256) ? 256 : count
;
501 uint32_t last_upper_base_addr
= UPPER16((addr
+ 0x8000));
503 pracc_add(&ctx
, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR
)); /* $15 = MIPS32_PRACC_BASE_ADDR */
504 pracc_add(&ctx
, 0, MIPS32_LUI(9, last_upper_base_addr
)); /* load the upper memory address in $9 */
506 for (int i
= 0; i
!= this_round_count
; i
++) { /* Main code loop */
507 uint32_t upper_base_addr
= UPPER16((addr
+ 0x8000));
508 if (last_upper_base_addr
!= upper_base_addr
) { /* if needed, change upper address in $9 */
509 pracc_add(&ctx
, 0, MIPS32_LUI(9, upper_base_addr
));
510 last_upper_base_addr
= upper_base_addr
;
514 pracc_add(&ctx
, 0, MIPS32_LW(8, LOWER16(addr
), 9)); /* load from memory to $8 */
516 pracc_add(&ctx
, 0, MIPS32_LHU(8, LOWER16(addr
), 9));
518 pracc_add(&ctx
, 0, MIPS32_LBU(8, LOWER16(addr
), 9));
520 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
+ i
* 4,
521 MIPS32_SW(8, PRACC_OUT_OFFSET
+ i
* 4, 15)); /* store $8 at param out */
524 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(ejtag_info
->reg8
))); /* restore upper 16 bits of reg 8 */
525 pracc_add(&ctx
, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info
->reg8
))); /* restore lower 16 bits of reg 8 */
526 pracc_add(&ctx
, 0, MIPS32_LUI(9, UPPER16(ejtag_info
->reg9
))); /* restore upper 16 bits of reg 9 */
527 pracc_add(&ctx
, 0, MIPS32_ORI(9, 9, LOWER16(ejtag_info
->reg9
))); /* restore lower 16 bits of reg 9 */
529 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
530 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
533 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, buf32
);
534 if (ctx
.retval
!= ERROR_OK
)
536 buf32
+= this_round_count
;
538 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, data
);
539 if (ctx
.retval
!= ERROR_OK
)
542 uint32_t *data_p
= data
;
543 for (int i
= 0; i
!= this_round_count
; i
++) {
545 *buf16
++ = *data_p
++;
550 count
-= this_round_count
;
553 pracc_queue_free(&ctx
);
559 int mips32_cp0_read(struct mips_ejtag
*ejtag_info
, uint32_t *val
, uint32_t cp0_reg
, uint32_t cp0_sel
)
561 struct pracc_queue_info ctx
= {.max_code
= 7};
562 pracc_queue_init(&ctx
);
563 if (ctx
.retval
!= ERROR_OK
)
566 pracc_add(&ctx
, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR
)); /* $15 = MIPS32_PRACC_BASE_ADDR */
567 pracc_add(&ctx
, 0, MIPS32_MFC0(8, 0, 0) | (cp0_reg
<< 11) | cp0_sel
); /* move COP0 [cp0_reg select] to $8 */
568 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
,
569 MIPS32_SW(8, PRACC_OUT_OFFSET
, 15)); /* store $8 to pracc_out */
570 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
571 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(ejtag_info
->reg8
))); /* restore upper 16 bits of $8 */
572 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
573 pracc_add(&ctx
, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info
->reg8
))); /* restore lower 16 bits of $8 */
575 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, val
);
577 pracc_queue_free(&ctx
);
581 * Note that our input parametes cp0_reg and cp0_sel
582 * are numbers (not gprs) which make part of mfc0 instruction opcode.
584 * These are not fix, but can be different for each mips32_cp0_read() function call,
585 * and that is why we must insert them directly into opcode,
586 * i.e. we can not pass it on EJTAG microprogram stack (via param_in),
587 * and put them into the gprs later from MIPS32_PRACC_STACK
588 * because mfc0 do not use gpr as a parameter for the cp0_reg and select part,
589 * but plain (immediate) number.
591 * MIPS32_MTC0 is implemented via MIPS32_R_INST macro.
592 * In order to insert our parameters, we must change rd and funct fields.
594 * code[2] |= (cp0_reg << 11) | cp0_sel; change rd and funct of MIPS32_R_INST macro
598 int mips32_cp0_write(struct mips_ejtag
*ejtag_info
, uint32_t val
, uint32_t cp0_reg
, uint32_t cp0_sel
)
600 struct pracc_queue_info ctx
= {.max_code
= 6};
601 pracc_queue_init(&ctx
);
602 if (ctx
.retval
!= ERROR_OK
)
605 pracc_add(&ctx
, 0, MIPS32_LUI(15, UPPER16(val
))); /* Load val to $15 */
606 pracc_add(&ctx
, 0, MIPS32_ORI(15, 15, LOWER16(val
)));
608 pracc_add(&ctx
, 0, MIPS32_MTC0(15, 0, 0) | (cp0_reg
<< 11) | cp0_sel
); /* write cp0 reg / sel */
610 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
611 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
613 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, NULL
);
615 pracc_queue_free(&ctx
);
619 * Note that MIPS32_MTC0 macro is implemented via MIPS32_R_INST macro.
620 * In order to insert our parameters, we must change rd and funct fields.
621 * code[3] |= (cp0_reg << 11) | cp0_sel; change rd and funct fields of MIPS32_R_INST macro
626 * \b mips32_pracc_sync_cache
628 * Synchronize Caches to Make Instruction Writes Effective
629 * (ref. doc. MIPS32 Architecture For Programmers Volume II: The MIPS32 Instruction Set,
630 * Document Number: MD00086, Revision 2.00, June 9, 2003)
632 * When the instruction stream is written, the SYNCI instruction should be used
633 * in conjunction with other instructions to make the newly-written instructions effective.
636 * A program that loads another program into memory is actually writing the D- side cache.
637 * The instructions it has loaded can't be executed until they reach the I-cache.
639 * After the instructions have been written, the loader should arrange
640 * to write back any containing D-cache line and invalidate any locations
641 * already in the I-cache.
643 * If the cache coherency attribute (CCA) is set to zero, it's a write through cache, there is no need
646 * In the latest MIPS32/64 CPUs, MIPS provides the synci instruction,
647 * which does the whole job for a cache-line-sized chunk of the memory you just loaded:
648 * That is, it arranges a D-cache write-back (if CCA = 3) and an I-cache invalidate.
650 * The line size is obtained with the rdhwr SYNCI_Step in release 2 or from cp0 config 1 register in release 1.
652 static int mips32_pracc_synchronize_cache(struct mips_ejtag
*ejtag_info
,
653 uint32_t start_addr
, uint32_t end_addr
, int cached
, int rel
)
655 struct pracc_queue_info ctx
= {.max_code
= 256 * 2 + 5};
656 pracc_queue_init(&ctx
);
657 if (ctx
.retval
!= ERROR_OK
)
659 /** Find cache line size in bytes */
661 if (rel
) { /* Release 2 (rel = 1) */
662 pracc_add(&ctx
, 0, MIPS32_LUI(15, PRACC_UPPER_BASE_ADDR
)); /* $15 = MIPS32_PRACC_BASE_ADDR */
664 pracc_add(&ctx
, 0, MIPS32_RDHWR(8, MIPS32_SYNCI_STEP
)); /* load synci_step value to $8 */
666 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
,
667 MIPS32_SW(8, PRACC_OUT_OFFSET
, 15)); /* store $8 to pracc_out */
669 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(ejtag_info
->reg8
))); /* restore upper 16 bits of $8 */
670 pracc_add(&ctx
, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info
->reg8
))); /* restore lower 16 bits of $8 */
671 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
672 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* move COP0 DeSave to $15 */
674 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, &clsiz
);
675 if (ctx
.retval
!= ERROR_OK
)
678 } else { /* Release 1 (rel = 0) */
680 ctx
.retval
= mips32_cp0_read(ejtag_info
, &conf
, 16, 1);
681 if (ctx
.retval
!= ERROR_OK
)
684 uint32_t dl
= (conf
& MIPS32_CONFIG1_DL_MASK
) >> MIPS32_CONFIG1_DL_SHIFT
;
686 /* dl encoding : dl=1 => 4 bytes, dl=2 => 8 bytes, etc... max dl=6 => 128 bytes cache line size */
693 goto exit
; /* Nothing to do */
695 /* make sure clsiz is power of 2 */
696 if (clsiz
& (clsiz
- 1)) {
697 LOG_DEBUG("clsiz must be power of 2");
698 ctx
.retval
= ERROR_FAIL
;
702 /* make sure start_addr and end_addr have the same offset inside de cache line */
703 start_addr
|= clsiz
- 1;
704 end_addr
|= clsiz
- 1;
708 uint32_t last_upper_base_addr
= UPPER16((start_addr
+ 0x8000));
710 pracc_add(&ctx
, 0, MIPS32_LUI(15, last_upper_base_addr
)); /* load upper memory base address to $15 */
712 while (start_addr
<= end_addr
) { /* main loop */
713 uint32_t upper_base_addr
= UPPER16((start_addr
+ 0x8000));
714 if (last_upper_base_addr
!= upper_base_addr
) { /* if needed, change upper address in $15 */
715 pracc_add(&ctx
, 0, MIPS32_LUI(15, upper_base_addr
));
716 last_upper_base_addr
= upper_base_addr
;
719 pracc_add(&ctx
, 0, MIPS32_SYNCI(LOWER16(start_addr
), 15)); /* synci instruction, offset($15) */
723 pracc_add(&ctx
, 0, MIPS32_CACHE(MIPS32_CACHE_D_HIT_WRITEBACK
,
724 LOWER16(start_addr
), 15)); /* cache Hit_Writeback_D, offset($15) */
726 pracc_add(&ctx
, 0, MIPS32_CACHE(MIPS32_CACHE_I_HIT_INVALIDATE
,
727 LOWER16(start_addr
), 15)); /* cache Hit_Invalidate_I, offset($15) */
731 if (count
== 256 && start_addr
<= end_addr
) { /* more ?, then execute code list */
732 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
733 pracc_add(&ctx
, 0, MIPS32_NOP
); /* nop in delay slot */
735 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, NULL
);
736 if (ctx
.retval
!= ERROR_OK
)
743 pracc_add(&ctx
, 0, MIPS32_SYNC
);
744 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
745 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave*/
747 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, NULL
);
749 pracc_queue_free(&ctx
);
753 static int mips32_pracc_write_mem_generic(struct mips_ejtag
*ejtag_info
,
754 uint32_t addr
, int size
, int count
, const void *buf
)
756 struct pracc_queue_info ctx
= {.max_code
= 128 * 3 + 5 + 1}; /* alloc memory for the worst case */
757 pracc_queue_init(&ctx
);
758 if (ctx
.retval
!= ERROR_OK
)
761 const uint32_t *buf32
= buf
;
762 const uint16_t *buf16
= buf
;
763 const uint8_t *buf8
= buf
;
768 int this_round_count
= (count
> 128) ? 128 : count
;
769 uint32_t last_upper_base_addr
= UPPER16((addr
+ 0x8000));
771 pracc_add(&ctx
, 0, MIPS32_LUI(15, last_upper_base_addr
)); /* load $15 with memory base address */
773 for (int i
= 0; i
!= this_round_count
; i
++) {
774 uint32_t upper_base_addr
= UPPER16((addr
+ 0x8000));
775 if (last_upper_base_addr
!= upper_base_addr
) {
776 pracc_add(&ctx
, 0, MIPS32_LUI(15, upper_base_addr
)); /* if needed, change upper address in $15*/
777 last_upper_base_addr
= upper_base_addr
;
780 if (size
== 4) { /* for word writes check if one half word is 0 and load it accordingly */
781 if (LOWER16(*buf32
) == 0)
782 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(*buf32
))); /* load only upper value */
783 else if (UPPER16(*buf32
) == 0)
784 pracc_add(&ctx
, 0, MIPS32_ORI(8, 0, LOWER16(*buf32
))); /* load only lower */
786 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(*buf32
))); /* load upper and lower */
787 pracc_add(&ctx
, 0, MIPS32_ORI(8, 8, LOWER16(*buf32
)));
789 pracc_add(&ctx
, 0, MIPS32_SW(8, LOWER16(addr
), 15)); /* store word to memory */
792 } else if (size
== 2) {
793 pracc_add(&ctx
, 0, MIPS32_ORI(8, 0, *buf16
)); /* load lower value */
794 pracc_add(&ctx
, 0, MIPS32_SH(8, LOWER16(addr
), 15)); /* store half word to memory */
798 pracc_add(&ctx
, 0, MIPS32_ORI(8, 0, *buf8
)); /* load lower value */
799 pracc_add(&ctx
, 0, MIPS32_SB(8, LOWER16(addr
), 15)); /* store byte to memory */
805 pracc_add(&ctx
, 0, MIPS32_LUI(8, UPPER16(ejtag_info
->reg8
))); /* restore upper 16 bits of reg 8 */
806 pracc_add(&ctx
, 0, MIPS32_ORI(8, 8, LOWER16(ejtag_info
->reg8
))); /* restore lower 16 bits of reg 8 */
808 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
809 pracc_add(&ctx
, 0, MIPS32_MFC0(15, 31, 0)); /* restore $15 from DeSave */
811 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, NULL
);
812 if (ctx
.retval
!= ERROR_OK
)
814 count
-= this_round_count
;
817 pracc_queue_free(&ctx
);
821 int mips32_pracc_write_mem(struct mips_ejtag
*ejtag_info
, uint32_t addr
, int size
, int count
, const void *buf
)
823 int retval
= mips32_pracc_write_mem_generic(ejtag_info
, addr
, size
, count
, buf
);
824 if (retval
!= ERROR_OK
)
828 * If we are in the cacheable region and cache is activated,
829 * we must clean D$ (if Cache Coherency Attribute is set to 3) + invalidate I$ after we did the write,
830 * so that changes do not continue to live only in D$ (if CCA = 3), but to be
831 * replicated in I$ also (maybe we wrote the istructions)
836 if ((KSEGX(addr
) == KSEG1
) || ((addr
>= 0xff200000) && (addr
<= 0xff3fffff)))
837 return retval
; /*Nothing to do*/
839 mips32_cp0_read(ejtag_info
, &conf
, 16, 0);
841 switch (KSEGX(addr
)) {
843 cached
= (conf
& MIPS32_CONFIG0_KU_MASK
) >> MIPS32_CONFIG0_KU_SHIFT
;
846 cached
= (conf
& MIPS32_CONFIG0_K0_MASK
) >> MIPS32_CONFIG0_K0_SHIFT
;
850 cached
= (conf
& MIPS32_CONFIG0_K23_MASK
) >> MIPS32_CONFIG0_K23_SHIFT
;
858 * Check cachablitiy bits coherency algorithm
859 * is the region cacheable or uncached.
860 * If cacheable we have to synchronize the cache
862 if (cached
== 3 || cached
== 0) { /* Write back cache or write through cache */
863 uint32_t start_addr
= addr
;
864 uint32_t end_addr
= addr
+ count
* size
;
865 uint32_t rel
= (conf
& MIPS32_CONFIG0_AR_MASK
) >> MIPS32_CONFIG0_AR_SHIFT
;
867 LOG_DEBUG("Unknown release in cache code");
870 retval
= mips32_pracc_synchronize_cache(ejtag_info
, start_addr
, end_addr
, cached
, rel
);
876 int mips32_pracc_write_regs(struct mips_ejtag
*ejtag_info
, uint32_t *regs
)
878 static const uint32_t cp0_write_code
[] = {
879 MIPS32_MTC0(1, 12, 0), /* move $1 to status */
880 MIPS32_MTLO(1), /* move $1 to lo */
881 MIPS32_MTHI(1), /* move $1 to hi */
882 MIPS32_MTC0(1, 8, 0), /* move $1 to badvaddr */
883 MIPS32_MTC0(1, 13, 0), /* move $1 to cause*/
884 MIPS32_MTC0(1, 24, 0), /* move $1 to depc (pc) */
887 struct pracc_queue_info ctx
= {.max_code
= 37 * 2 + 7 + 1};
888 pracc_queue_init(&ctx
);
889 if (ctx
.retval
!= ERROR_OK
)
892 /* load registers 2 to 31 with lui and ori instructions, check if some instructions can be saved */
893 for (int i
= 2; i
< 32; i
++) {
894 if (LOWER16((regs
[i
])) == 0) /* if lower half word is 0, lui instruction only */
895 pracc_add(&ctx
, 0, MIPS32_LUI(i
, UPPER16((regs
[i
]))));
896 else if (UPPER16((regs
[i
])) == 0) /* if upper half word is 0, ori with $0 only*/
897 pracc_add(&ctx
, 0, MIPS32_ORI(i
, 0, LOWER16((regs
[i
]))));
898 else { /* default, load with lui and ori instructions */
899 pracc_add(&ctx
, 0, MIPS32_LUI(i
, UPPER16((regs
[i
]))));
900 pracc_add(&ctx
, 0, MIPS32_ORI(i
, i
, LOWER16((regs
[i
]))));
904 for (int i
= 0; i
!= 6; i
++) {
905 pracc_add(&ctx
, 0, MIPS32_LUI(1, UPPER16((regs
[i
+ 32])))); /* load CPO value in $1, with lui and ori */
906 pracc_add(&ctx
, 0, MIPS32_ORI(1, 1, LOWER16((regs
[i
+ 32]))));
907 pracc_add(&ctx
, 0, cp0_write_code
[i
]); /* write value from $1 to CPO register */
909 pracc_add(&ctx
, 0, MIPS32_MTC0(15, 31, 0)); /* load $15 in DeSave */
910 pracc_add(&ctx
, 0, MIPS32_LUI(1, UPPER16((regs
[1])))); /* load upper half word in $1 */
911 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
912 pracc_add(&ctx
, 0, MIPS32_ORI(1, 1, LOWER16((regs
[1])))); /* load lower half word in $1 */
914 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, NULL
);
916 ejtag_info
->reg8
= regs
[8];
917 ejtag_info
->reg9
= regs
[9];
919 pracc_queue_free(&ctx
);
923 int mips32_pracc_read_regs(struct mips_ejtag
*ejtag_info
, uint32_t *regs
)
925 static int cp0_read_code
[] = {
926 MIPS32_MFC0(8, 12, 0), /* move status to $8 */
927 MIPS32_MFLO(8), /* move lo to $8 */
928 MIPS32_MFHI(8), /* move hi to $8 */
929 MIPS32_MFC0(8, 8, 0), /* move badvaddr to $8 */
930 MIPS32_MFC0(8, 13, 0), /* move cause to $8 */
931 MIPS32_MFC0(8, 24, 0), /* move depc (pc) to $8 */
934 struct pracc_queue_info ctx
= {.max_code
= 49};
935 pracc_queue_init(&ctx
);
936 if (ctx
.retval
!= ERROR_OK
)
939 pracc_add(&ctx
, 0, MIPS32_MTC0(1, 31, 0)); /* move $1 to COP0 DeSave */
940 pracc_add(&ctx
, 0, MIPS32_LUI(1, PRACC_UPPER_BASE_ADDR
)); /* $1 = MIP32_PRACC_BASE_ADDR */
942 for (int i
= 2; i
!= 32; i
++) /* store GPR's 2 to 31 */
943 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
+ (i
* 4),
944 MIPS32_SW(i
, PRACC_OUT_OFFSET
+ (i
* 4), 1));
946 for (int i
= 0; i
!= 6; i
++) {
947 pracc_add(&ctx
, 0, cp0_read_code
[i
]); /* load COP0 needed registers to $8 */
948 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
+ (i
+ 32) * 4, /* store $8 at PARAM OUT */
949 MIPS32_SW(8, PRACC_OUT_OFFSET
+ (i
+ 32) * 4, 1));
951 pracc_add(&ctx
, 0, MIPS32_MFC0(8, 31, 0)); /* move DeSave to $8, reg1 value */
952 pracc_add(&ctx
, MIPS32_PRACC_PARAM_OUT
+ 4, /* store reg1 value from $8 to param out */
953 MIPS32_SW(8, PRACC_OUT_OFFSET
+ 4, 1));
955 pracc_add(&ctx
, 0, MIPS32_MFC0(1, 31, 0)); /* move COP0 DeSave to $1, restore reg1 */
956 pracc_add(&ctx
, 0, MIPS32_B(NEG16(ctx
.code_count
+ 1))); /* jump to start */
957 pracc_add(&ctx
, 0, MIPS32_MTC0(15, 31, 0)); /* load $15 in DeSave */
959 if (ejtag_info
->mode
== 0)
960 ctx
.store_count
++; /* Needed by legacy code, due to offset from reg0 */
962 ctx
.retval
= mips32_pracc_queue_exec(ejtag_info
, &ctx
, regs
);
964 ejtag_info
->reg8
= regs
[8]; /* reg8 is saved but not restored, next called function should restore it */
965 ejtag_info
->reg9
= regs
[9];
967 pracc_queue_free(&ctx
);
971 /* fastdata upload/download requires an initialized working area
972 * to load the download code; it should not be called otherwise
973 * fetch order from the fastdata area
978 int mips32_pracc_fastdata_xfer(struct mips_ejtag
*ejtag_info
, struct working_area
*source
,
979 int write_t
, uint32_t addr
, int count
, uint32_t *buf
)
981 uint32_t handler_code
[] = {
982 /* caution when editing, table is modified below */
983 /* r15 points to the start of this code */
984 MIPS32_SW(8, MIPS32_FASTDATA_HANDLER_SIZE
- 4, 15),
985 MIPS32_SW(9, MIPS32_FASTDATA_HANDLER_SIZE
- 8, 15),
986 MIPS32_SW(10, MIPS32_FASTDATA_HANDLER_SIZE
- 12, 15),
987 MIPS32_SW(11, MIPS32_FASTDATA_HANDLER_SIZE
- 16, 15),
988 /* start of fastdata area in t0 */
989 MIPS32_LUI(8, UPPER16(MIPS32_PRACC_FASTDATA_AREA
)),
990 MIPS32_ORI(8, 8, LOWER16(MIPS32_PRACC_FASTDATA_AREA
)),
991 MIPS32_LW(9, 0, 8), /* start addr in t1 */
992 MIPS32_LW(10, 0, 8), /* end addr to t2 */
994 /* 8 */ MIPS32_LW(11, 0, 0), /* lw t3,[t8 | r9] */
995 /* 9 */ MIPS32_SW(11, 0, 0), /* sw t3,[r9 | r8] */
996 MIPS32_BNE(10, 9, NEG16(3)), /* bne $t2,t1,loop */
997 MIPS32_ADDI(9, 9, 4), /* addi t1,t1,4 */
999 MIPS32_LW(8, MIPS32_FASTDATA_HANDLER_SIZE
- 4, 15),
1000 MIPS32_LW(9, MIPS32_FASTDATA_HANDLER_SIZE
- 8, 15),
1001 MIPS32_LW(10, MIPS32_FASTDATA_HANDLER_SIZE
- 12, 15),
1002 MIPS32_LW(11, MIPS32_FASTDATA_HANDLER_SIZE
- 16, 15),
1004 MIPS32_LUI(15, UPPER16(MIPS32_PRACC_TEXT
)),
1005 MIPS32_ORI(15, 15, LOWER16(MIPS32_PRACC_TEXT
)),
1006 MIPS32_JR(15), /* jr start */
1007 MIPS32_MFC0(15, 31, 0), /* move COP0 DeSave to $15 */
1010 uint32_t jmp_code
[] = {
1011 /* 0 */ MIPS32_LUI(15, 0), /* addr of working area added below */
1012 /* 1 */ MIPS32_ORI(15, 15, 0), /* addr of working area added below */
1013 MIPS32_JR(15), /* jump to ram program */
1018 uint32_t val
, ejtag_ctrl
, address
;
1020 if (source
->size
< MIPS32_FASTDATA_HANDLER_SIZE
)
1021 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE
;
1024 handler_code
[8] = MIPS32_LW(11, 0, 8); /* load data from probe at fastdata area */
1025 handler_code
[9] = MIPS32_SW(11, 0, 9); /* store data to RAM @ r9 */
1027 handler_code
[8] = MIPS32_LW(11, 0, 9); /* load data from RAM @ r9 */
1028 handler_code
[9] = MIPS32_SW(11, 0, 8); /* store data to probe at fastdata area */
1031 /* write program into RAM */
1032 if (write_t
!= ejtag_info
->fast_access_save
) {
1033 mips32_pracc_write_mem(ejtag_info
, source
->address
, 4, ARRAY_SIZE(handler_code
), handler_code
);
1034 /* save previous operation to speed to any consecutive read/writes */
1035 ejtag_info
->fast_access_save
= write_t
;
1038 LOG_DEBUG("%s using 0x%.8" PRIx32
" for write handler", __func__
, source
->address
);
1040 jmp_code
[0] |= UPPER16(source
->address
);
1041 jmp_code
[1] |= LOWER16(source
->address
);
1043 for (i
= 0; i
< (int) ARRAY_SIZE(jmp_code
); i
++) {
1044 retval
= wait_for_pracc_rw(ejtag_info
, &ejtag_ctrl
);
1045 if (retval
!= ERROR_OK
)
1048 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_DATA
);
1049 mips_ejtag_drscan_32_out(ejtag_info
, jmp_code
[i
]);
1051 /* Clear the access pending bit (let the processor eat!) */
1052 ejtag_ctrl
= ejtag_info
->ejtag_ctrl
& ~EJTAG_CTRL_PRACC
;
1053 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_CONTROL
);
1054 mips_ejtag_drscan_32_out(ejtag_info
, ejtag_ctrl
);
1057 /* wait PrAcc pending bit for FASTDATA write */
1058 retval
= wait_for_pracc_rw(ejtag_info
, &ejtag_ctrl
);
1059 if (retval
!= ERROR_OK
)
1062 /* next fetch to dmseg should be in FASTDATA_AREA, check */
1064 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_ADDRESS
);
1065 retval
= mips_ejtag_drscan_32(ejtag_info
, &address
);
1066 if (retval
!= ERROR_OK
)
1069 if (address
!= MIPS32_PRACC_FASTDATA_AREA
)
1072 /* Send the load start address */
1074 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_FASTDATA
);
1075 mips_ejtag_fastdata_scan(ejtag_info
, 1, &val
);
1077 retval
= wait_for_pracc_rw(ejtag_info
, &ejtag_ctrl
);
1078 if (retval
!= ERROR_OK
)
1081 /* Send the load end address */
1082 val
= addr
+ (count
- 1) * 4;
1083 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_FASTDATA
);
1084 mips_ejtag_fastdata_scan(ejtag_info
, 1, &val
);
1086 unsigned num_clocks
= 0; /* like in legacy code */
1087 if (ejtag_info
->mode
!= 0)
1088 num_clocks
= ((uint64_t)(ejtag_info
->scan_delay
) * jtag_get_speed_khz() + 500000) / 1000000;
1090 for (i
= 0; i
< count
; i
++) {
1091 jtag_add_clocks(num_clocks
);
1092 retval
= mips_ejtag_fastdata_scan(ejtag_info
, write_t
, buf
++);
1093 if (retval
!= ERROR_OK
)
1097 retval
= jtag_execute_queue();
1098 if (retval
!= ERROR_OK
) {
1099 LOG_ERROR("fastdata load failed");
1103 retval
= wait_for_pracc_rw(ejtag_info
, &ejtag_ctrl
);
1104 if (retval
!= ERROR_OK
)
1108 mips_ejtag_set_instr(ejtag_info
, EJTAG_INST_ADDRESS
);
1109 retval
= mips_ejtag_drscan_32(ejtag_info
, &address
);
1110 if (retval
!= ERROR_OK
)
1113 if (address
!= MIPS32_PRACC_TEXT
)
1114 LOG_ERROR("mini program did not return to start");
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)