746ab393ee0acca0070468da6a1f49cccb5a7791
[openocd.git] / src / target / arm_tpiu_swo.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /**
4 * @file
5 * This file implements support for the ARM CoreSight components Trace Port
6 * Interface Unit (TPIU) and Serial Wire Output (SWO). It also supports the
7 * CoreSight TPIU-Lite and the special TPIU version present with Cortex-M3
8 * and Cortex-M4 (that includes SWO).
9 */
10
11 /*
12 * Relevant specifications from ARM include:
13 *
14 * CoreSight(tm) Components Technical Reference Manual ARM DDI 0314H
15 * CoreSight(tm) TPIU-Lite Technical Reference Manual ARM DDI 0317A
16 * Cortex(tm)-M3 Technical Reference Manual ARM DDI 0337G
17 * Cortex(tm)-M4 Technical Reference Manual ARM DDI 0439B
18 * CoreSight(tm) SoC-400 Technical Reference Manual ARM DDI 0480F
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdlib.h>
26 #include <jim.h>
27
28 #include <helper/bits.h>
29 #include <helper/command.h>
30 #include <helper/jim-nvp.h>
31 #include <helper/list.h>
32 #include <helper/log.h>
33 #include <helper/types.h>
34 #include <jtag/interface.h>
35 #include <server/server.h>
36 #include <target/arm_adi_v5.h>
37 #include <target/target.h>
38 #include <transport/transport.h>
39 #include "arm_tpiu_swo.h"
40
41 /* START_DEPRECATED_TPIU */
42 #include <target/cortex_m.h>
43 #include <target/target_type.h>
44 #define MSG "DEPRECATED \'tpiu config\' command: "
45 /* END_DEPRECATED_TPIU */
46
47 #define TCP_SERVICE_NAME "tpiu_swo_trace"
48
49 /* default for Cortex-M3 and Cortex-M4 specific TPIU */
50 #define TPIU_SWO_DEFAULT_BASE 0xE0040000
51
52 #define TPIU_SSPSR_OFFSET 0x000
53 #define TPIU_CSPSR_OFFSET 0x004
54 #define TPIU_ACPR_OFFSET 0x010
55 #define TPIU_SPPR_OFFSET 0x0F0
56 #define TPIU_FFSR_OFFSET 0x300
57 #define TPIU_FFCR_OFFSET 0x304
58 #define TPIU_FSCR_OFFSET 0x308
59 #define TPIU_DEVID_OFFSET 0xfc8
60
61 #define TPIU_ACPR_MAX_PRESCALER 0x1fff
62 #define TPIU_SPPR_PROTOCOL_SYNC (TPIU_PIN_PROTOCOL_SYNC)
63 #define TPIU_SPPR_PROTOCOL_MANCHESTER (TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER)
64 #define TPIU_SPPR_PROTOCOL_UART (TPIU_PIN_PROTOCOL_ASYNC_UART)
65 #define TPIU_DEVID_NOSUPPORT_SYNC BIT(9)
66 #define TPIU_DEVID_SUPPORT_MANCHESTER BIT(10)
67 #define TPIU_DEVID_SUPPORT_UART BIT(11)
68
69 enum arm_tpiu_swo_event {
70 TPIU_SWO_EVENT_PRE_ENABLE,
71 TPIU_SWO_EVENT_POST_ENABLE,
72 TPIU_SWO_EVENT_PRE_DISABLE,
73 TPIU_SWO_EVENT_POST_DISABLE,
74 };
75
76 static const struct jim_nvp nvp_arm_tpiu_swo_event[] = {
77 { .value = TPIU_SWO_EVENT_PRE_ENABLE, .name = "pre-enable" },
78 { .value = TPIU_SWO_EVENT_POST_ENABLE, .name = "post-enable" },
79 { .value = TPIU_SWO_EVENT_PRE_DISABLE, .name = "pre-disable" },
80 { .value = TPIU_SWO_EVENT_POST_DISABLE, .name = "post-disable" },
81 };
82
83 struct arm_tpiu_swo_event_action {
84 enum arm_tpiu_swo_event event;
85 Jim_Interp *interp;
86 Jim_Obj *body;
87 struct arm_tpiu_swo_event_action *next;
88 };
89
90 struct arm_tpiu_swo_object {
91 struct list_head lh;
92 struct adiv5_mem_ap_spot spot;
93 char *name;
94 struct arm_tpiu_swo_event_action *event_action;
95 /* record enable before init */
96 bool deferred_enable;
97 bool enabled;
98 bool en_capture;
99 /** Handle to output trace data in INTERNAL capture mode */
100 /** Synchronous output port width */
101 uint32_t port_width;
102 FILE *file;
103 /** output mode */
104 unsigned int pin_protocol;
105 /** Enable formatter */
106 bool en_formatter;
107 /** frequency of TRACECLKIN (usually matches HCLK) */
108 unsigned int traceclkin_freq;
109 /** SWO pin frequency */
110 unsigned int swo_pin_freq;
111 /** where to dump the captured output trace data */
112 char *out_filename;
113 /** track TCP connections */
114 struct list_head connections;
115 /* START_DEPRECATED_TPIU */
116 bool recheck_ap_cur_target;
117 /* END_DEPRECATED_TPIU */
118 };
119
120 struct arm_tpiu_swo_connection {
121 struct list_head lh;
122 struct connection *connection;
123 };
124
125 struct arm_tpiu_swo_priv_connection {
126 struct arm_tpiu_swo_object *obj;
127 };
128
129 static LIST_HEAD(all_tpiu_swo);
130
131 #define ARM_TPIU_SWO_TRACE_BUF_SIZE 4096
132
133 static int arm_tpiu_swo_poll_trace(void *priv)
134 {
135 struct arm_tpiu_swo_object *obj = priv;
136 uint8_t buf[ARM_TPIU_SWO_TRACE_BUF_SIZE];
137 size_t size = sizeof(buf);
138 struct arm_tpiu_swo_connection *c;
139
140 int retval = adapter_poll_trace(buf, &size);
141 if (retval != ERROR_OK || !size)
142 return retval;
143
144 target_call_trace_callbacks(/*target*/NULL, size, buf);
145
146 if (obj->file) {
147 if (fwrite(buf, 1, size, obj->file) == size) {
148 fflush(obj->file);
149 } else {
150 LOG_ERROR("Error writing to the SWO trace destination file");
151 return ERROR_FAIL;
152 }
153 }
154
155 if (obj->out_filename && obj->out_filename[0] == ':')
156 list_for_each_entry(c, &obj->connections, lh)
157 if (connection_write(c->connection, buf, size) != (int)size)
158 retval = ERROR_FAIL;
159
160 return ERROR_OK;
161 }
162
163 static void arm_tpiu_swo_handle_event(struct arm_tpiu_swo_object *obj, enum arm_tpiu_swo_event event)
164 {
165 for (struct arm_tpiu_swo_event_action *ea = obj->event_action; ea; ea = ea->next) {
166 if (ea->event != event)
167 continue;
168
169 LOG_DEBUG("TPIU/SWO: %s event: %s (%d) action : %s",
170 obj->name,
171 jim_nvp_value2name_simple(nvp_arm_tpiu_swo_event, event)->name,
172 event,
173 Jim_GetString(ea->body, NULL));
174
175 /* prevent event execution to change current target */
176 struct command_context *cmd_ctx = current_command_context(ea->interp);
177 struct target *saved_target = cmd_ctx->current_target;
178 int retval = Jim_EvalObj(ea->interp, ea->body);
179 cmd_ctx->current_target = saved_target;
180
181 if (retval == JIM_RETURN)
182 retval = ea->interp->returnCode;
183 if (retval == JIM_OK || retval == ERROR_COMMAND_CLOSE_CONNECTION)
184 return;
185
186 Jim_MakeErrorMessage(ea->interp);
187 LOG_USER("Error executing event %s on TPIU/SWO %s:\n%s",
188 jim_nvp_value2name_simple(nvp_arm_tpiu_swo_event, event)->name,
189 obj->name,
190 Jim_GetString(Jim_GetResult(ea->interp), NULL));
191 /* clean both error code and stacktrace before return */
192 Jim_Eval(ea->interp, "error \"\" \"\"");
193 return;
194 }
195 }
196
197 static void arm_tpiu_swo_close_output(struct arm_tpiu_swo_object *obj)
198 {
199 if (obj->file) {
200 fclose(obj->file);
201 obj->file = NULL;
202 }
203 if (obj->out_filename && obj->out_filename[0] == ':')
204 remove_service(TCP_SERVICE_NAME, &obj->out_filename[1]);
205 }
206
207 int arm_tpiu_swo_cleanup_all(void)
208 {
209 struct arm_tpiu_swo_object *obj, *tmp;
210
211 list_for_each_entry_safe(obj, tmp, &all_tpiu_swo, lh) {
212 if (obj->enabled)
213 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_DISABLE);
214
215 arm_tpiu_swo_close_output(obj);
216
217 if (obj->en_capture) {
218 target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
219
220 int retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
221 if (retval != ERROR_OK)
222 LOG_ERROR("Failed to stop adapter's trace");
223 }
224
225 if (obj->enabled)
226 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_DISABLE);
227
228 struct arm_tpiu_swo_event_action *ea = obj->event_action;
229 while (ea) {
230 struct arm_tpiu_swo_event_action *next = ea->next;
231 Jim_DecrRefCount(ea->interp, ea->body);
232 free(ea);
233 ea = next;
234 }
235
236 free(obj->name);
237 free(obj->out_filename);
238 free(obj);
239 }
240
241 return ERROR_OK;
242 }
243
244 static int arm_tpiu_swo_service_new_connection(struct connection *connection)
245 {
246 struct arm_tpiu_swo_priv_connection *priv = connection->service->priv;
247 struct arm_tpiu_swo_object *obj = priv->obj;
248 struct arm_tpiu_swo_connection *c = malloc(sizeof(*c));
249 if (!c) {
250 LOG_ERROR("Out of memory");
251 return ERROR_FAIL;
252 }
253 c->connection = connection;
254 list_add(&c->lh, &obj->connections);
255 return ERROR_OK;
256 }
257
258 static int arm_tpiu_swo_service_input(struct connection *connection)
259 {
260 /* read a dummy buffer to check if the connection is still active */
261 long dummy;
262 int bytes_read = connection_read(connection, &dummy, sizeof(dummy));
263
264 if (bytes_read == 0) {
265 return ERROR_SERVER_REMOTE_CLOSED;
266 } else if (bytes_read == -1) {
267 LOG_ERROR("error during read: %s", strerror(errno));
268 return ERROR_SERVER_REMOTE_CLOSED;
269 }
270
271 return ERROR_OK;
272 }
273
274 static int arm_tpiu_swo_service_connection_closed(struct connection *connection)
275 {
276 struct arm_tpiu_swo_priv_connection *priv = connection->service->priv;
277 struct arm_tpiu_swo_object *obj = priv->obj;
278 struct arm_tpiu_swo_connection *c, *tmp;
279
280 list_for_each_entry_safe(c, tmp, &obj->connections, lh)
281 if (c->connection == connection) {
282 list_del(&c->lh);
283 free(c);
284 return ERROR_OK;
285 }
286 LOG_ERROR("Failed to find connection to close!");
287 return ERROR_FAIL;
288 }
289
290 COMMAND_HANDLER(handle_arm_tpiu_swo_event_list)
291 {
292 struct arm_tpiu_swo_object *obj = CMD_DATA;
293
294 command_print(CMD, "Event actions for TPIU/SWO %s\n", obj->name);
295 command_print(CMD, "%-25s | Body", "Event");
296 command_print(CMD, "------------------------- | "
297 "----------------------------------------");
298
299 for (struct arm_tpiu_swo_event_action *ea = obj->event_action; ea; ea = ea->next) {
300 struct jim_nvp *opt = jim_nvp_value2name_simple(nvp_arm_tpiu_swo_event, ea->event);
301 command_print(CMD, "%-25s | %s",
302 opt->name, Jim_GetString(ea->body, NULL));
303 }
304 command_print(CMD, "***END***");
305 return ERROR_OK;
306 }
307
308 enum arm_tpiu_swo_cfg_param {
309 CFG_PORT_WIDTH,
310 CFG_PROTOCOL,
311 CFG_FORMATTER,
312 CFG_TRACECLKIN,
313 CFG_BITRATE,
314 CFG_OUTFILE,
315 CFG_EVENT,
316 };
317
318 static const struct jim_nvp nvp_arm_tpiu_swo_config_opts[] = {
319 { .name = "-port-width", .value = CFG_PORT_WIDTH },
320 { .name = "-protocol", .value = CFG_PROTOCOL },
321 { .name = "-formatter", .value = CFG_FORMATTER },
322 { .name = "-traceclk", .value = CFG_TRACECLKIN },
323 { .name = "-pin-freq", .value = CFG_BITRATE },
324 { .name = "-output", .value = CFG_OUTFILE },
325 { .name = "-event", .value = CFG_EVENT },
326 /* handled by mem_ap_spot, added for jim_getopt_nvp_unknown() */
327 { .name = "-dap", .value = -1 },
328 { .name = "-ap-num", .value = -1 },
329 { .name = "-baseaddr", .value = -1 },
330 { .name = NULL, .value = -1 },
331 };
332
333 static const struct jim_nvp nvp_arm_tpiu_swo_protocol_opts[] = {
334 { .name = "sync", .value = TPIU_SPPR_PROTOCOL_SYNC },
335 { .name = "uart", .value = TPIU_SPPR_PROTOCOL_UART },
336 { .name = "manchester", .value = TPIU_SPPR_PROTOCOL_MANCHESTER },
337 { .name = NULL, .value = -1 },
338 };
339
340 static const struct jim_nvp nvp_arm_tpiu_swo_bool_opts[] = {
341 { .name = "on", .value = 1 },
342 { .name = "yes", .value = 1 },
343 { .name = "1", .value = 1 },
344 { .name = "true", .value = 1 },
345 { .name = "off", .value = 0 },
346 { .name = "no", .value = 0 },
347 { .name = "0", .value = 0 },
348 { .name = "false", .value = 0 },
349 { .name = NULL, .value = -1 },
350 };
351
352 static int arm_tpiu_swo_configure(struct jim_getopt_info *goi, struct arm_tpiu_swo_object *obj)
353 {
354 assert(obj);
355
356 if (goi->isconfigure && obj->enabled) {
357 Jim_SetResultFormatted(goi->interp, "Cannot configure TPIU/SWO; %s is enabled!", obj->name);
358 return JIM_ERR;
359 }
360
361 /* parse config or cget options ... */
362 while (goi->argc > 0) {
363 Jim_SetEmptyResult(goi->interp);
364
365 int e = adiv5_jim_mem_ap_spot_configure(&obj->spot, goi);
366 if (e == JIM_OK)
367 continue;
368 if (e == JIM_ERR)
369 return e;
370
371 struct jim_nvp *n;
372 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_config_opts, &n);
373 if (e != JIM_OK) {
374 jim_getopt_nvp_unknown(goi, nvp_arm_tpiu_swo_config_opts, 0);
375 return e;
376 }
377
378 switch (n->value) {
379 case CFG_PORT_WIDTH:
380 if (goi->isconfigure) {
381 jim_wide port_width;
382 e = jim_getopt_wide(goi, &port_width);
383 if (e != JIM_OK)
384 return e;
385 if (port_width < 1 || port_width > 32) {
386 Jim_SetResultString(goi->interp, "Invalid port width!", -1);
387 return JIM_ERR;
388 }
389 obj->port_width = (uint32_t)port_width;
390 } else {
391 if (goi->argc)
392 goto err_no_params;
393 Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->port_width));
394 }
395 break;
396 case CFG_PROTOCOL:
397 if (goi->isconfigure) {
398 struct jim_nvp *p;
399 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_protocol_opts, &p);
400 if (e != JIM_OK)
401 return e;
402 obj->pin_protocol = p->value;
403 } else {
404 if (goi->argc)
405 goto err_no_params;
406 struct jim_nvp *p;
407 e = jim_nvp_value2name(goi->interp, nvp_arm_tpiu_swo_protocol_opts, obj->pin_protocol, &p);
408 if (e != JIM_OK) {
409 Jim_SetResultString(goi->interp, "protocol error", -1);
410 return JIM_ERR;
411 }
412 Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, p->name, -1));
413 }
414 break;
415 case CFG_FORMATTER:
416 if (goi->isconfigure) {
417 struct jim_nvp *p;
418 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_bool_opts, &p);
419 if (e != JIM_OK)
420 return e;
421 obj->en_formatter = p->value;
422 } else {
423 if (goi->argc)
424 goto err_no_params;
425 struct jim_nvp *p;
426 e = jim_nvp_value2name(goi->interp, nvp_arm_tpiu_swo_bool_opts, obj->en_formatter, &p);
427 if (e != JIM_OK) {
428 Jim_SetResultString(goi->interp, "formatter error", -1);
429 return JIM_ERR;
430 }
431 Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, p->name, -1));
432 }
433 break;
434 case CFG_TRACECLKIN:
435 if (goi->isconfigure) {
436 jim_wide clk;
437 e = jim_getopt_wide(goi, &clk);
438 if (e != JIM_OK)
439 return e;
440 obj->traceclkin_freq = clk;
441 } else {
442 if (goi->argc)
443 goto err_no_params;
444 Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->traceclkin_freq));
445 }
446 break;
447 case CFG_BITRATE:
448 if (goi->isconfigure) {
449 jim_wide clk;
450 e = jim_getopt_wide(goi, &clk);
451 if (e != JIM_OK)
452 return e;
453 obj->swo_pin_freq = clk;
454 } else {
455 if (goi->argc)
456 goto err_no_params;
457 Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, obj->swo_pin_freq));
458 }
459 break;
460 case CFG_OUTFILE:
461 if (goi->isconfigure) {
462 const char *s;
463 e = jim_getopt_string(goi, &s, NULL);
464 if (e != JIM_OK)
465 return e;
466 if (s[0] == ':') {
467 char *end;
468 long port = strtol(s + 1, &end, 0);
469 if (port <= 0 || port > UINT16_MAX || *end != '\0') {
470 Jim_SetResultFormatted(goi->interp, "Invalid TCP port \'%s\'", s + 1);
471 return JIM_ERR;
472 }
473 }
474 free(obj->out_filename);
475 obj->out_filename = strdup(s);
476 if (!obj->out_filename) {
477 LOG_ERROR("Out of memory");
478 return JIM_ERR;
479 }
480 } else {
481 if (goi->argc)
482 goto err_no_params;
483 if (obj->out_filename)
484 Jim_SetResult(goi->interp, Jim_NewStringObj(goi->interp, obj->out_filename, -1));
485 }
486 break;
487 case CFG_EVENT:
488 if (goi->isconfigure) {
489 if (goi->argc < 2) {
490 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name? ?EVENT-BODY?");
491 return JIM_ERR;
492 }
493 } else {
494 if (goi->argc != 1) {
495 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "-event ?event-name?");
496 return JIM_ERR;
497 }
498 }
499
500 {
501 struct jim_nvp *p;
502 Jim_Obj *o;
503 struct arm_tpiu_swo_event_action *ea = obj->event_action;
504
505 e = jim_getopt_nvp(goi, nvp_arm_tpiu_swo_event, &p);
506 if (e != JIM_OK) {
507 jim_getopt_nvp_unknown(goi, nvp_arm_tpiu_swo_event, 1);
508 return e;
509 }
510
511 while (ea) {
512 /* replace existing? */
513 if (ea->event == (enum arm_tpiu_swo_event)p->value)
514 break;
515 ea = ea->next;
516 }
517
518 if (goi->isconfigure) {
519 if (!ea) {
520 ea = calloc(1, sizeof(*ea));
521 if (!ea) {
522 LOG_ERROR("Out of memory");
523 return JIM_ERR;
524 }
525 ea->next = obj->event_action;
526 obj->event_action = ea;
527 }
528 if (ea->body)
529 Jim_DecrRefCount(ea->interp, ea->body);
530 ea->event = p->value;
531 ea->interp = goi->interp;
532 jim_getopt_obj(goi, &o);
533 ea->body = Jim_DuplicateObj(goi->interp, o);
534 Jim_IncrRefCount(ea->body);
535 } else {
536 if (ea)
537 Jim_SetResult(goi->interp, Jim_DuplicateObj(goi->interp, ea->body));
538 }
539 }
540 break;
541 }
542 }
543
544 return JIM_OK;
545
546 err_no_params:
547 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS");
548 return JIM_ERR;
549 }
550
551 static int jim_arm_tpiu_swo_configure(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
552 {
553 struct command *c = jim_to_command(interp);
554 struct jim_getopt_info goi;
555
556 jim_getopt_setup(&goi, interp, argc - 1, argv + 1);
557 goi.isconfigure = !strcmp(c->name, "configure");
558 if (goi.argc < 1) {
559 Jim_WrongNumArgs(goi.interp, goi.argc, goi.argv,
560 "missing: -option ...");
561 return JIM_ERR;
562 }
563 struct arm_tpiu_swo_object *obj = c->jim_handler_data;
564 return arm_tpiu_swo_configure(&goi, obj);
565 }
566
567 static int wrap_write_u32(struct target *target, struct adiv5_ap *tpiu_ap,
568 target_addr_t address, uint32_t value)
569 {
570 if (transport_is_hla())
571 return target_write_u32(target, address, value);
572 else
573 return mem_ap_write_atomic_u32(tpiu_ap, address, value);
574 }
575
576 static int wrap_read_u32(struct target *target, struct adiv5_ap *tpiu_ap,
577 target_addr_t address, uint32_t *value)
578 {
579 if (transport_is_hla())
580 return target_read_u32(target, address, value);
581 else
582 return mem_ap_read_atomic_u32(tpiu_ap, address, value);
583 }
584
585 static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
586 {
587 struct command *c = jim_to_command(interp);
588 struct arm_tpiu_swo_object *obj = c->jim_handler_data;
589 struct command_context *cmd_ctx = current_command_context(interp);
590 struct adiv5_ap *tpiu_ap = dap_ap(obj->spot.dap, obj->spot.ap_num);
591 uint32_t value;
592 int retval;
593
594 if (argc != 1) {
595 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
596 return JIM_ERR;
597 }
598
599 if (cmd_ctx->mode == COMMAND_CONFIG) {
600 LOG_DEBUG("%s: enable deferred", obj->name);
601 obj->deferred_enable = true;
602 return JIM_OK;
603 }
604
605 if (obj->enabled)
606 return JIM_OK;
607
608 if (transport_is_hla() && obj->spot.ap_num > 0) {
609 LOG_ERROR("Invalid access port %d. Only AP#0 allowed with hla transport", obj->spot.ap_num);
610 return JIM_ERR;
611 }
612
613 if (!obj->traceclkin_freq) {
614 LOG_ERROR("Trace clock-in frequency not set");
615 return JIM_ERR;
616 }
617
618 if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART)
619 if (!obj->swo_pin_freq) {
620 LOG_ERROR("SWO pin frequency not set");
621 return JIM_ERR;
622 }
623
624 struct target *target = get_current_target(cmd_ctx);
625
626 /* START_DEPRECATED_TPIU */
627 if (obj->recheck_ap_cur_target) {
628 if (strcmp(target->type->name, "cortex_m") &&
629 strcmp(target->type->name, "hla_target")) {
630 LOG_ERROR(MSG "Current target is not a Cortex-M nor a HLA");
631 return JIM_ERR;
632 }
633 if (!target_was_examined(target)) {
634 LOG_ERROR(MSG "Current target not examined yet");
635 return JIM_ERR;
636 }
637 struct cortex_m_common *cm = target_to_cm(target);
638 obj->recheck_ap_cur_target = false;
639 obj->spot.ap_num = cm->armv7m.debug_ap->ap_num;
640 tpiu_ap = dap_ap(obj->spot.dap, obj->spot.ap_num);
641 if (obj->spot.ap_num == 0)
642 LOG_INFO(MSG "Confirmed TPIU %s is on AP 0", obj->name);
643 else
644 LOG_INFO(MSG "Target %s is on AP %d. Revised command is "
645 "\'tpiu create %s -dap %s -ap-num %d\'",
646 target_name(target), obj->spot.ap_num,
647 obj->name, adiv5_dap_name(obj->spot.dap), obj->spot.ap_num);
648 }
649 /* END_DEPRECATED_TPIU */
650
651 /* trigger the event before any attempt to R/W in the TPIU/SWO */
652 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_ENABLE);
653
654 retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_DEVID_OFFSET, &value);
655 if (retval != ERROR_OK) {
656 LOG_ERROR("Unable to read %s", obj->name);
657 return JIM_ERR;
658 }
659 switch (obj->pin_protocol) {
660 case TPIU_SPPR_PROTOCOL_SYNC:
661 value = !(value & TPIU_DEVID_NOSUPPORT_SYNC);
662 break;
663 case TPIU_SPPR_PROTOCOL_UART:
664 value &= TPIU_DEVID_SUPPORT_UART;
665 break;
666 case TPIU_SPPR_PROTOCOL_MANCHESTER:
667 value &= TPIU_DEVID_SUPPORT_MANCHESTER;
668 break;
669 default:
670 value = 0;
671 }
672 if (!value) {
673 struct jim_nvp *p;
674 jim_nvp_value2name(interp, nvp_arm_tpiu_swo_protocol_opts, obj->pin_protocol, &p);
675 LOG_ERROR("%s does not support protocol %s", obj->name, p->name);
676 return JIM_ERR;
677 }
678
679 if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_SYNC) {
680 retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_SSPSR_OFFSET, &value);
681 if (!(value & BIT(obj->port_width - 1))) {
682 LOG_ERROR("TPIU does not support port-width of %d bits", obj->port_width);
683 return JIM_ERR;
684 }
685 }
686
687 uint16_t prescaler = 1; /* dummy value */
688 unsigned int swo_pin_freq = obj->swo_pin_freq; /* could be replaced */
689
690 if (obj->out_filename && strcmp(obj->out_filename, "external") && obj->out_filename[0]) {
691 if (obj->out_filename[0] == ':') {
692 struct arm_tpiu_swo_priv_connection *priv = malloc(sizeof(*priv));
693 if (!priv) {
694 LOG_ERROR("Out of memory");
695 return JIM_ERR;
696 }
697 priv->obj = obj;
698 LOG_INFO("starting trace server for %s on %s", obj->name, &obj->out_filename[1]);
699 retval = add_service("tpiu_swo_trace", &obj->out_filename[1],
700 CONNECTION_LIMIT_UNLIMITED, arm_tpiu_swo_service_new_connection,
701 arm_tpiu_swo_service_input, arm_tpiu_swo_service_connection_closed,
702 priv);
703 if (retval != ERROR_OK) {
704 LOG_ERROR("Can't configure trace TCP port %s", &obj->out_filename[1]);
705 return JIM_ERR;
706 }
707 } else if (strcmp(obj->out_filename, "-")) {
708 obj->file = fopen(obj->out_filename, "ab");
709 if (!obj->file) {
710 LOG_ERROR("Can't open trace destination file \"%s\"", obj->out_filename);
711 return JIM_ERR;
712 }
713 }
714
715 retval = adapter_config_trace(true, obj->pin_protocol, obj->port_width,
716 &swo_pin_freq, obj->traceclkin_freq, &prescaler);
717 if (retval != ERROR_OK) {
718 LOG_ERROR("Failed to start adapter's trace");
719 arm_tpiu_swo_close_output(obj);
720 return JIM_ERR;
721 }
722
723 if (obj->swo_pin_freq != swo_pin_freq)
724 LOG_INFO("SWO pin data rate adjusted by adapter to %d Hz", swo_pin_freq);
725 obj->swo_pin_freq = swo_pin_freq;
726
727 target_register_timer_callback(arm_tpiu_swo_poll_trace, 1,
728 TARGET_TIMER_TYPE_PERIODIC, obj);
729
730 obj->en_capture = true;
731 } else if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART) {
732 prescaler = (obj->traceclkin_freq + obj->swo_pin_freq / 2) / obj->swo_pin_freq;
733 if (prescaler > TPIU_ACPR_MAX_PRESCALER)
734 prescaler = TPIU_ACPR_MAX_PRESCALER;
735 swo_pin_freq = obj->traceclkin_freq / prescaler;
736
737 if (obj->swo_pin_freq != swo_pin_freq)
738 LOG_INFO("SWO pin data rate adjusted to %d Hz", swo_pin_freq);
739 obj->swo_pin_freq = swo_pin_freq;
740 }
741
742 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_CSPSR_OFFSET, BIT(obj->port_width - 1));
743 if (retval != ERROR_OK)
744 goto error_exit;
745
746 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_ACPR_OFFSET, prescaler - 1);
747 if (retval != ERROR_OK)
748 goto error_exit;
749
750 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_SPPR_OFFSET, obj->pin_protocol);
751 if (retval != ERROR_OK)
752 goto error_exit;
753
754 retval = wrap_read_u32(target, tpiu_ap, obj->spot.base + TPIU_FFCR_OFFSET, &value);
755 if (retval != ERROR_OK)
756 goto error_exit;
757 if (obj->en_formatter)
758 value |= BIT(1);
759 else
760 value &= ~BIT(1);
761 retval = wrap_write_u32(target, tpiu_ap, obj->spot.base + TPIU_FFCR_OFFSET, value);
762 if (retval != ERROR_OK)
763 goto error_exit;
764
765 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_ENABLE);
766
767 obj->enabled = true;
768 return JIM_OK;
769
770 error_exit:
771 LOG_ERROR("Error!");
772
773 if (obj->en_capture) {
774 obj->en_capture = false;
775
776 arm_tpiu_swo_close_output(obj);
777
778 target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
779
780 retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
781 if (retval != ERROR_OK) {
782 LOG_ERROR("Failed to stop adapter's trace");
783 return JIM_ERR;
784 }
785 }
786 return JIM_ERR;
787 }
788
789 static int jim_arm_tpiu_swo_disable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
790 {
791 struct command *c = jim_to_command(interp);
792 struct arm_tpiu_swo_object *obj = c->jim_handler_data;
793
794 if (argc != 1) {
795 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
796 return JIM_ERR;
797 }
798
799 if (!obj->enabled)
800 return JIM_OK;
801 obj->enabled = false;
802
803 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_PRE_DISABLE);
804
805 if (obj->en_capture) {
806 obj->en_capture = false;
807
808 arm_tpiu_swo_close_output(obj);
809
810 target_unregister_timer_callback(arm_tpiu_swo_poll_trace, obj);
811
812 int retval = adapter_config_trace(false, 0, 0, NULL, 0, NULL);
813 if (retval != ERROR_OK) {
814 LOG_ERROR("Failed to stop adapter's trace");
815 return JIM_ERR;
816 }
817 }
818
819 arm_tpiu_swo_handle_event(obj, TPIU_SWO_EVENT_POST_DISABLE);
820 return JIM_OK;
821 }
822
823 static const struct command_registration arm_tpiu_swo_instance_command_handlers[] = {
824 {
825 .name = "configure",
826 .mode = COMMAND_ANY,
827 .jim_handler = jim_arm_tpiu_swo_configure,
828 .help = "configure a new TPIU/SWO for use",
829 .usage = "[attribute value ...]",
830 },
831 {
832 .name = "cget",
833 .mode = COMMAND_ANY,
834 .jim_handler = jim_arm_tpiu_swo_configure,
835 .help = "returns the specified TPIU/SWO attribute",
836 .usage = "attribute",
837 },
838 {
839 .name = "eventlist",
840 .mode = COMMAND_ANY,
841 .handler = handle_arm_tpiu_swo_event_list,
842 .help = "displays a table of events defined for this TPIU/SWO",
843 .usage = "",
844 },
845 {
846 .name = "enable",
847 .mode = COMMAND_ANY,
848 .jim_handler = jim_arm_tpiu_swo_enable,
849 .usage = "",
850 .help = "Enables the TPIU/SWO output",
851 },
852 {
853 .name = "disable",
854 .mode = COMMAND_EXEC,
855 .jim_handler = jim_arm_tpiu_swo_disable,
856 .usage = "",
857 .help = "Disables the TPIU/SWO output",
858 },
859 COMMAND_REGISTRATION_DONE
860 };
861
862 static int arm_tpiu_swo_create(Jim_Interp *interp, struct arm_tpiu_swo_object *obj)
863 {
864 struct command_context *cmd_ctx;
865 Jim_Cmd *cmd;
866 int e;
867
868 cmd_ctx = current_command_context(interp);
869 assert(cmd_ctx);
870
871 /* does this command exist? */
872 cmd = Jim_GetCommand(interp, Jim_NewStringObj(interp, obj->name, -1), JIM_ERRMSG);
873 if (cmd) {
874 Jim_SetResultFormatted(interp, "Command: %s Exists", obj->name);
875 return JIM_ERR;
876 }
877
878 /* now - create the new tpiu/swo name command */
879 const struct command_registration obj_commands[] = {
880 {
881 .name = obj->name,
882 .mode = COMMAND_ANY,
883 .help = "tpiu/swo instance command group",
884 .usage = "",
885 .chain = arm_tpiu_swo_instance_command_handlers,
886 },
887 COMMAND_REGISTRATION_DONE
888 };
889 e = register_commands_with_data(cmd_ctx, NULL, obj_commands, obj);
890 if (e != ERROR_OK)
891 return JIM_ERR;
892
893 list_add_tail(&obj->lh, &all_tpiu_swo);
894
895 return JIM_OK;
896 }
897
898 static int jim_arm_tpiu_swo_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
899 {
900 struct jim_getopt_info goi;
901 jim_getopt_setup(&goi, interp, argc - 1, argv + 1);
902 if (goi.argc < 1) {
903 Jim_WrongNumArgs(goi.interp, 1, goi.argv, "?name? ..options...");
904 return JIM_ERR;
905 }
906
907 struct arm_tpiu_swo_object *obj = calloc(1, sizeof(struct arm_tpiu_swo_object));
908 if (!obj) {
909 LOG_ERROR("Out of memory");
910 return JIM_ERR;
911 }
912 INIT_LIST_HEAD(&obj->connections);
913 adiv5_mem_ap_spot_init(&obj->spot);
914 obj->spot.base = TPIU_SWO_DEFAULT_BASE;
915 obj->port_width = 1;
916
917 Jim_Obj *n;
918 jim_getopt_obj(&goi, &n);
919 obj->name = strdup(Jim_GetString(n, NULL));
920 if (!obj->name) {
921 LOG_ERROR("Out of memory");
922 free(obj);
923 return JIM_ERR;
924 }
925
926 /* Do the rest as "configure" options */
927 goi.isconfigure = 1;
928 int e = arm_tpiu_swo_configure(&goi, obj);
929 if (e != JIM_OK)
930 goto err_exit;
931
932 if (!obj->spot.dap || obj->spot.ap_num == DP_APSEL_INVALID) {
933 Jim_SetResultString(goi.interp, "-dap and -ap-num required when creating TPIU", -1);
934 goto err_exit;
935 }
936
937 e = arm_tpiu_swo_create(goi.interp, obj);
938 if (e != JIM_OK)
939 goto err_exit;
940
941 return JIM_OK;
942
943 err_exit:
944 free(obj->name);
945 free(obj->out_filename);
946 free(obj);
947 return JIM_ERR;
948 }
949
950 static int jim_arm_tpiu_swo_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
951 {
952 struct arm_tpiu_swo_object *obj;
953
954 if (argc != 1) {
955 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
956 return JIM_ERR;
957 }
958 Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
959 list_for_each_entry(obj, &all_tpiu_swo, lh) {
960 Jim_ListAppendElement(interp, Jim_GetResult(interp),
961 Jim_NewStringObj(interp, obj->name, -1));
962 }
963 return JIM_OK;
964 }
965
966 static int jim_arm_tpiu_swo_init(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
967 {
968 struct command_context *cmd_ctx = current_command_context(interp);
969 struct arm_tpiu_swo_object *obj;
970 int retval = JIM_OK;
971
972 if (argc != 1) {
973 Jim_WrongNumArgs(interp, 1, argv, "Too many parameters");
974 return JIM_ERR;
975 }
976 list_for_each_entry(obj, &all_tpiu_swo, lh) {
977 if (!obj->deferred_enable)
978 continue;
979 LOG_DEBUG("%s: running enable during init", obj->name);
980 int retval2 = command_run_linef(cmd_ctx, "%s enable", obj->name);
981 if (retval2 != ERROR_OK)
982 retval = JIM_ERR;
983 }
984 return retval;
985 }
986
987 /* START_DEPRECATED_TPIU */
988 /* DEPRECATED: emulation of old command 'tpiu config' */
989 COMMAND_HANDLER(handle_tpiu_deprecated_config_command)
990 {
991 struct target *target = get_current_target(CMD_CTX);
992 struct arm_tpiu_swo_object *obj = NULL;
993 int retval;
994
995 if (strcmp(target->type->name, "cortex_m") &&
996 strcmp(target->type->name, "hla_target")) {
997 LOG_ERROR(MSG "Current target is not a Cortex-M nor a HLA");
998 return ERROR_FAIL;
999 }
1000
1001 if (!list_empty(&all_tpiu_swo)) {
1002 obj = list_first_entry(&all_tpiu_swo, typeof(*obj), lh);
1003 LOG_INFO(MSG "Using %s", obj->name);
1004 } else {
1005 struct cortex_m_common *cm = target_to_cm(target);
1006 struct adiv5_private_config *pc = target->private_config;
1007 struct adiv5_dap *dap = pc->dap;
1008 int ap_num = pc->ap_num;
1009 bool set_recheck_ap_cur_target = false;
1010
1011 LOG_INFO(MSG "Adding a TPIU \'%s.tpiu\' in the configuration", target_name(target));
1012
1013 if (ap_num == DP_APSEL_INVALID && transport_is_hla())
1014 ap_num = 0; /* HLA should only support AP 0 */
1015
1016 if (ap_num == DP_APSEL_INVALID && target_was_examined(target))
1017 ap_num = cm->armv7m.debug_ap->ap_num;
1018
1019 if (ap_num == DP_APSEL_INVALID) {
1020 LOG_INFO(MSG "Target %s uses AP autodetection. Adding TPIU on AP 0; can be revised later",
1021 target_name(target));
1022 ap_num = 0;
1023 set_recheck_ap_cur_target = true;
1024 }
1025
1026 LOG_INFO(MSG "Running: \'tpiu create %s.tpiu -dap %s -ap-num %d\'",
1027 target_name(target), adiv5_dap_name(dap), ap_num);
1028
1029 retval = command_run_linef(CMD_CTX, "tpiu create %s.tpiu -dap %s -ap-num %d",
1030 target_name(target), adiv5_dap_name(dap), ap_num);
1031 if (retval != ERROR_OK)
1032 return retval;
1033
1034 obj = list_first_entry(&all_tpiu_swo, typeof(*obj), lh);
1035 if (set_recheck_ap_cur_target)
1036 obj->recheck_ap_cur_target = true;
1037 }
1038
1039 unsigned int cmd_idx = 0;
1040 if (cmd_idx == CMD_ARGC)
1041 return ERROR_COMMAND_SYNTAX_ERROR;
1042
1043 if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
1044 if (CMD_ARGC != cmd_idx + 1)
1045 return ERROR_COMMAND_SYNTAX_ERROR;
1046 LOG_INFO(MSG "Running: \'%s disable\'", obj->name);
1047 return command_run_linef(CMD_CTX, "%s disable", obj->name);
1048 }
1049
1050 const char *output = NULL;
1051 const char *protocol;
1052 const char *formatter = NULL;
1053 const char *port_width = NULL;
1054 const char *trace_clk;
1055 const char *pin_clk = NULL;
1056 if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
1057 cmd_idx++;
1058 if (cmd_idx == CMD_ARGC)
1059 return ERROR_COMMAND_SYNTAX_ERROR;
1060 output = CMD_ARGV[cmd_idx];
1061 } else if (strcmp(CMD_ARGV[cmd_idx], "external"))
1062 return ERROR_COMMAND_SYNTAX_ERROR;
1063 cmd_idx++;
1064 if (cmd_idx == CMD_ARGC)
1065 return ERROR_COMMAND_SYNTAX_ERROR;
1066 if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
1067 protocol = CMD_ARGV[cmd_idx];
1068 cmd_idx++;
1069 if (cmd_idx == CMD_ARGC)
1070 return ERROR_COMMAND_SYNTAX_ERROR;
1071 port_width = CMD_ARGV[cmd_idx];
1072 } else {
1073 if (strcmp(CMD_ARGV[cmd_idx], "manchester") && strcmp(CMD_ARGV[cmd_idx], "uart"))
1074 return ERROR_COMMAND_SYNTAX_ERROR;
1075 protocol = CMD_ARGV[cmd_idx];
1076 cmd_idx++;
1077 if (cmd_idx == CMD_ARGC)
1078 return ERROR_COMMAND_SYNTAX_ERROR;
1079 formatter = CMD_ARGV[cmd_idx];
1080 }
1081 cmd_idx++;
1082 if (cmd_idx == CMD_ARGC)
1083 return ERROR_COMMAND_SYNTAX_ERROR;
1084 trace_clk = CMD_ARGV[cmd_idx];
1085 cmd_idx++;
1086 if (cmd_idx != CMD_ARGC) {
1087 pin_clk = CMD_ARGV[cmd_idx];
1088 cmd_idx++;
1089 }
1090 if (cmd_idx != CMD_ARGC)
1091 return ERROR_COMMAND_SYNTAX_ERROR;
1092
1093 LOG_INFO(MSG "Running: \'%s configure -protocol %s -traceclk %s" "%s%s" "%s%s" "%s%s" "%s%s\'",
1094 obj->name, protocol, trace_clk,
1095 pin_clk ? " -pin-freq " : "", pin_clk ? pin_clk : "",
1096 output ? " -output " : "", output ? output : "",
1097 formatter ? " -formatter " : "", formatter ? formatter : "",
1098 port_width ? " -port-width " : "", port_width ? port_width : "");
1099
1100 retval = command_run_linef(CMD_CTX,
1101 "%s configure -protocol %s -traceclk %s" "%s%s" "%s%s" "%s%s" "%s%s",
1102 obj->name, protocol, trace_clk,
1103 pin_clk ? " -pin-freq " : "", pin_clk ? pin_clk : "",
1104 output ? " -output " : "", output ? output : "",
1105 formatter ? " -formatter " : "", formatter ? formatter : "",
1106 port_width ? " -port-width " : "", port_width ? port_width : "");
1107 if (retval != ERROR_OK)
1108 return retval;
1109
1110 LOG_INFO(MSG "Running: \'%s enable\'", obj->name);
1111 retval = command_run_linef(CMD_CTX, "%s enable", obj->name);
1112 if (retval != ERROR_OK)
1113 return retval;
1114
1115 target_handle_event(target, TARGET_EVENT_TRACE_CONFIG);
1116 return ERROR_OK;
1117 }
1118
1119 static const struct command_registration arm_tpiu_deprecated_subcommand_handlers[] = {
1120 {
1121 .name = "config",
1122 .handler = handle_tpiu_deprecated_config_command,
1123 .mode = COMMAND_ANY,
1124 .help = "Configure TPIU features, DEPRECATED, use \'tpiu create\'",
1125 .usage = "(disable | "
1126 "((external | internal (<filename> | <:port> | -)) "
1127 "(sync <port width> | ((manchester | uart) <formatter enable>)) "
1128 "<TRACECLKIN freq> [<trace freq>]))",
1129 },
1130 COMMAND_REGISTRATION_DONE
1131 };
1132
1133 const struct command_registration arm_tpiu_deprecated_command_handlers[] = {
1134 {
1135 .name = "tpiu",
1136 .chain = arm_tpiu_deprecated_subcommand_handlers,
1137 .usage = "",
1138 .help = "tpiu command group",
1139 },
1140 COMMAND_REGISTRATION_DONE
1141 };
1142 /* END_DEPRECATED_TPIU */
1143
1144 static const struct command_registration arm_tpiu_swo_subcommand_handlers[] = {
1145 {
1146 .name = "create",
1147 .mode = COMMAND_ANY,
1148 .jim_handler = jim_arm_tpiu_swo_create,
1149 .usage = "name [-dap dap] [-ap-num num] [-address baseaddr]",
1150 .help = "Creates a new TPIU or SWO object",
1151 },
1152 {
1153 .name = "names",
1154 .mode = COMMAND_ANY,
1155 .jim_handler = jim_arm_tpiu_swo_names,
1156 .usage = "",
1157 .help = "Lists all registered TPIU and SWO objects by name",
1158 },
1159 {
1160 .name = "init",
1161 .mode = COMMAND_EXEC,
1162 .jim_handler = jim_arm_tpiu_swo_init,
1163 .usage = "",
1164 .help = "Initialize TPIU and SWO",
1165 },
1166 COMMAND_REGISTRATION_DONE
1167 };
1168
1169 static const struct command_registration arm_tpiu_swo_command_handlers[] = {
1170 {
1171 .name = "tpiu",
1172 .chain = arm_tpiu_swo_subcommand_handlers,
1173 .usage = "",
1174 .help = "tpiu command group",
1175 },
1176 {
1177 .name = "swo",
1178 .chain = arm_tpiu_swo_subcommand_handlers,
1179 .usage = "",
1180 .help = "swo command group",
1181 },
1182 COMMAND_REGISTRATION_DONE
1183 };
1184
1185 int arm_tpiu_swo_register_commands(struct command_context *cmd_ctx)
1186 {
1187 return register_commands(cmd_ctx, NULL, arm_tpiu_swo_command_handlers);
1188 }

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)