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

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)