stlink: fix execution order in stlink_config_trace()
[openocd.git] / src / target / armv7m_trace.c
1 /***************************************************************************
2 * Copyright (C) 2015 Paul Fertser <fercerpav@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <target/target.h>
23 #include <target/armv7m.h>
24 #include <target/cortex_m.h>
25 #include <target/armv7m_trace.h>
26 #include <jtag/interface.h>
27 #include <helper/time_support.h>
28
29 #define TRACE_BUF_SIZE 4096
30
31 static int armv7m_poll_trace(void *target)
32 {
33 struct armv7m_common *armv7m = target_to_armv7m(target);
34 uint8_t buf[TRACE_BUF_SIZE];
35 size_t size = sizeof(buf);
36 int retval;
37
38 retval = adapter_poll_trace(buf, &size);
39 if (retval != ERROR_OK || !size)
40 return retval;
41
42 target_call_trace_callbacks(target, size, buf);
43
44 switch (armv7m->trace_config.internal_channel) {
45 case TRACE_INTERNAL_CHANNEL_FILE:
46 if (armv7m->trace_config.trace_file != NULL) {
47 if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size)
48 fflush(armv7m->trace_config.trace_file);
49 else {
50 LOG_ERROR("Error writing to the trace destination file");
51 return ERROR_FAIL;
52 }
53 }
54 break;
55 case TRACE_INTERNAL_CHANNEL_TCP:
56 if (armv7m->trace_config.trace_service != NULL) {
57 /* broadcast to all service connections */
58 struct connection *connection = armv7m->trace_config.trace_service->connections;
59 retval = ERROR_OK;
60 while (connection) {
61 if (connection_write(connection, buf, size) != (int) size)
62 retval = ERROR_FAIL;
63
64 connection = connection->next;
65 }
66
67 if (retval != ERROR_OK) {
68 LOG_ERROR("Error streaming the trace to TCP/IP port");
69 return ERROR_FAIL;
70 }
71 }
72 break;
73 case TRACE_INTERNAL_CHANNEL_TCL_ONLY:
74 /* nothing to do :
75 * the trace data is sent to TCL by calling the target_call_trace_callbacks
76 **/
77 break;
78 default:
79 LOG_ERROR("unsupported trace internal channel");
80 return ERROR_FAIL;
81 }
82
83 return ERROR_OK;
84 }
85
86 int armv7m_trace_tpiu_config(struct target *target)
87 {
88 struct armv7m_common *armv7m = target_to_armv7m(target);
89 struct armv7m_trace_config *trace_config = &armv7m->trace_config;
90 uint16_t prescaler = TPIU_ACPR_MAX_SWOSCALER + 1;
91 int retval;
92
93 target_unregister_timer_callback(armv7m_poll_trace, target);
94
95 retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
96 trace_config->pin_protocol, trace_config->port_size,
97 &trace_config->trace_freq, trace_config->traceclkin_freq, &prescaler);
98
99 if (retval != ERROR_OK)
100 return retval;
101
102 if (trace_config->config_type == TRACE_CONFIG_TYPE_EXTERNAL) {
103 prescaler = trace_config->traceclkin_freq / trace_config->trace_freq;
104
105 if (trace_config->traceclkin_freq % trace_config->trace_freq) {
106 prescaler++;
107
108 int trace_freq = trace_config->traceclkin_freq / prescaler;
109 LOG_INFO("Can not obtain %u trace port frequency from %u "
110 "TRACECLKIN frequency, using %u instead",
111 trace_config->trace_freq, trace_config->traceclkin_freq,
112 trace_freq);
113
114 trace_config->trace_freq = trace_freq;
115 }
116 }
117
118 if (!trace_config->trace_freq) {
119 LOG_ERROR("Trace port frequency is 0, can't enable TPIU");
120 return ERROR_FAIL;
121 }
122
123 retval = target_write_u32(target, TPIU_CSPSR, 1 << trace_config->port_size);
124 if (retval != ERROR_OK)
125 return retval;
126
127 retval = target_write_u32(target, TPIU_ACPR, prescaler - 1);
128 if (retval != ERROR_OK)
129 return retval;
130
131 retval = target_write_u32(target, TPIU_SPPR, trace_config->pin_protocol);
132 if (retval != ERROR_OK)
133 return retval;
134
135 uint32_t ffcr;
136 retval = target_read_u32(target, TPIU_FFCR, &ffcr);
137 if (retval != ERROR_OK)
138 return retval;
139 if (trace_config->formatter)
140 ffcr |= (1 << 1);
141 else
142 ffcr &= ~(1 << 1);
143 retval = target_write_u32(target, TPIU_FFCR, ffcr);
144 if (retval != ERROR_OK)
145 return retval;
146
147 if (trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL)
148 target_register_timer_callback(armv7m_poll_trace, 1,
149 TARGET_TIMER_TYPE_PERIODIC, target);
150
151 target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
152
153 return ERROR_OK;
154 }
155
156 int armv7m_trace_itm_config(struct target *target)
157 {
158 struct armv7m_common *armv7m = target_to_armv7m(target);
159 struct armv7m_trace_config *trace_config = &armv7m->trace_config;
160 int retval;
161
162 retval = target_write_u32(target, ITM_LAR, ITM_LAR_KEY);
163 if (retval != ERROR_OK)
164 return retval;
165
166 /* pg315 of CoreSight Components
167 * It is recommended that the ITMEn bit is cleared and waits for the
168 * ITMBusy bit to be cleared, before changing any fields in the
169 * Control Register, otherwise the behavior can be unpredictable.
170 */
171 uint32_t itm_tcr;
172 retval = target_read_u32(target, ITM_TCR, &itm_tcr);
173 if (retval != ERROR_OK)
174 return retval;
175 retval = target_write_u32(target,
176 ITM_TCR,
177 itm_tcr & ~ITM_TCR_ITMENA_BIT
178 );
179 if (retval != ERROR_OK)
180 return retval;
181
182 int64_t then = timeval_ms() + 1000;
183 do {
184 retval = target_read_u32(target, ITM_TCR, &itm_tcr);
185 if (retval != ERROR_OK)
186 return retval;
187 if (timeval_ms() > then) {
188 LOG_ERROR("timeout waiting for ITM_TCR_BUSY_BIT");
189 return ERROR_FAIL;
190 }
191 } while (itm_tcr & ITM_TCR_BUSY_BIT);
192
193 /* Enable ITM, TXENA, set TraceBusID and other parameters */
194 retval = target_write_u32(target, ITM_TCR, (1 << 0) | (1 << 3) |
195 (trace_config->itm_diff_timestamps << 1) |
196 (trace_config->itm_synchro_packets << 2) |
197 (trace_config->itm_async_timestamps << 4) |
198 (trace_config->itm_ts_prescale << 8) |
199 (trace_config->trace_bus_id << 16));
200 if (retval != ERROR_OK)
201 return retval;
202
203 for (unsigned int i = 0; i < 8; i++) {
204 retval = target_write_u32(target, ITM_TER0 + i * 4,
205 trace_config->itm_ter[i]);
206 if (retval != ERROR_OK)
207 return retval;
208 }
209
210 return ERROR_OK;
211 }
212
213 static void close_trace_channel(struct armv7m_common *armv7m)
214 {
215 switch (armv7m->trace_config.internal_channel) {
216 case TRACE_INTERNAL_CHANNEL_FILE:
217 if (armv7m->trace_config.trace_file)
218 fclose(armv7m->trace_config.trace_file);
219 armv7m->trace_config.trace_file = NULL;
220 break;
221 case TRACE_INTERNAL_CHANNEL_TCP:
222 if (armv7m->trace_config.trace_service)
223 remove_service(armv7m->trace_config.trace_service->name, armv7m->trace_config.trace_service->port);
224 armv7m->trace_config.trace_service = NULL;
225 break;
226 case TRACE_INTERNAL_CHANNEL_TCL_ONLY:
227 /* nothing to do:
228 * the trace polling is disabled in the beginning of armv7m_trace_tpiu_config
229 **/
230 break;
231 default:
232 LOG_ERROR("unsupported trace internal channel");
233 }
234 }
235
236 static int trace_new_connection(struct connection *connection)
237 {
238 /* nothing to do */
239 return ERROR_OK;
240 }
241
242 static int trace_input(struct connection *connection)
243 {
244 /* create a dummy buffer to check if the connection is still active */
245 const int buf_len = 100;
246 unsigned char buf[buf_len];
247 int bytes_read = connection_read(connection, buf, buf_len);
248
249 if (bytes_read == 0)
250 return ERROR_SERVER_REMOTE_CLOSED;
251 else if (bytes_read == -1) {
252 LOG_ERROR("error during read: %s", strerror(errno));
253 return ERROR_SERVER_REMOTE_CLOSED;
254 }
255
256 return ERROR_OK;
257 }
258
259 static int trace_connection_closed(struct connection *connection)
260 {
261 /* nothing to do, no connection->priv to free */
262 return ERROR_OK;
263 }
264
265 extern struct command_context *global_cmd_ctx;
266
267 int armv7m_trace_tpiu_exit(struct target *target)
268 {
269 struct armv7m_common *armv7m = target_to_armv7m(target);
270
271 if (global_cmd_ctx->mode == COMMAND_CONFIG ||
272 armv7m->trace_config.config_type == TRACE_CONFIG_TYPE_DISABLED)
273 return ERROR_OK;
274
275 close_trace_channel(armv7m);
276 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
277 return armv7m_trace_tpiu_config(target);
278 }
279
280 COMMAND_HANDLER(handle_tpiu_config_command)
281 {
282 struct target *target = get_current_target(CMD_CTX);
283 struct armv7m_common *armv7m = target_to_armv7m(target);
284
285 unsigned int cmd_idx = 0;
286
287 if (CMD_ARGC == cmd_idx)
288 return ERROR_COMMAND_SYNTAX_ERROR;
289 if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
290 if (CMD_ARGC == cmd_idx + 1) {
291 close_trace_channel(armv7m);
292
293 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
294 if (CMD_CTX->mode == COMMAND_EXEC)
295 return armv7m_trace_tpiu_config(target);
296 else
297 return ERROR_OK;
298 }
299 } else if (!strcmp(CMD_ARGV[cmd_idx], "external") ||
300 !strcmp(CMD_ARGV[cmd_idx], "internal")) {
301 close_trace_channel(armv7m);
302
303 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
304 if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
305 cmd_idx++;
306 if (CMD_ARGC == cmd_idx)
307 return ERROR_COMMAND_SYNTAX_ERROR;
308
309 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL;
310 armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCL_ONLY;
311
312 if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
313 if (CMD_ARGV[cmd_idx][0] == ':') {
314 armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCP;
315
316 int ret = add_service("armv7m_trace", &(CMD_ARGV[cmd_idx][1]),
317 CONNECTION_LIMIT_UNLIMITED, trace_new_connection, trace_input,
318 trace_connection_closed, NULL, &armv7m->trace_config.trace_service);
319 if (ret != ERROR_OK) {
320 LOG_ERROR("Can't configure trace TCP port");
321 return ERROR_FAIL;
322 }
323 } else {
324 armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_FILE;
325 armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
326 if (!armv7m->trace_config.trace_file) {
327 LOG_ERROR("Can't open trace destination file");
328 return ERROR_FAIL;
329 }
330 }
331 }
332 }
333 cmd_idx++;
334 if (CMD_ARGC == cmd_idx)
335 return ERROR_COMMAND_SYNTAX_ERROR;
336
337 if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
338 armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_SYNC;
339
340 cmd_idx++;
341 if (CMD_ARGC == cmd_idx)
342 return ERROR_COMMAND_SYNTAX_ERROR;
343
344 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
345 } else {
346 if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
347 armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER;
348 else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
349 armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_UART;
350 else
351 return ERROR_COMMAND_SYNTAX_ERROR;
352
353 cmd_idx++;
354 if (CMD_ARGC == cmd_idx)
355 return ERROR_COMMAND_SYNTAX_ERROR;
356
357 COMMAND_PARSE_ON_OFF(CMD_ARGV[cmd_idx], armv7m->trace_config.formatter);
358 }
359
360 cmd_idx++;
361 if (CMD_ARGC == cmd_idx)
362 return ERROR_COMMAND_SYNTAX_ERROR;
363
364 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.traceclkin_freq);
365
366 cmd_idx++;
367 if (CMD_ARGC != cmd_idx) {
368 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
369 cmd_idx++;
370 } else {
371 if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_INTERNAL) {
372 LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
373 return ERROR_COMMAND_SYNTAX_ERROR;
374 }
375 armv7m->trace_config.trace_freq = 0;
376 }
377
378 if (CMD_ARGC == cmd_idx) {
379 if (CMD_CTX->mode == COMMAND_EXEC)
380 return armv7m_trace_tpiu_config(target);
381 else
382 return ERROR_OK;
383 }
384 }
385
386 return ERROR_COMMAND_SYNTAX_ERROR;
387 }
388
389 COMMAND_HANDLER(handle_itm_port_command)
390 {
391 struct target *target = get_current_target(CMD_CTX);
392 struct armv7m_common *armv7m = target_to_armv7m(target);
393 unsigned int reg_idx;
394 uint8_t port;
395 bool enable;
396
397 if (CMD_ARGC != 2)
398 return ERROR_COMMAND_SYNTAX_ERROR;
399
400 COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], port);
401 COMMAND_PARSE_ON_OFF(CMD_ARGV[1], enable);
402 reg_idx = port / 32;
403 port = port % 32;
404 if (enable)
405 armv7m->trace_config.itm_ter[reg_idx] |= (1 << port);
406 else
407 armv7m->trace_config.itm_ter[reg_idx] &= ~(1 << port);
408
409 if (CMD_CTX->mode == COMMAND_EXEC)
410 return armv7m_trace_itm_config(target);
411 else
412 return ERROR_OK;
413 }
414
415 COMMAND_HANDLER(handle_itm_ports_command)
416 {
417 struct target *target = get_current_target(CMD_CTX);
418 struct armv7m_common *armv7m = target_to_armv7m(target);
419 bool enable;
420
421 if (CMD_ARGC != 1)
422 return ERROR_COMMAND_SYNTAX_ERROR;
423
424 COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
425 memset(armv7m->trace_config.itm_ter, enable ? 0xff : 0,
426 sizeof(armv7m->trace_config.itm_ter));
427
428 if (CMD_CTX->mode == COMMAND_EXEC)
429 return armv7m_trace_itm_config(target);
430 else
431 return ERROR_OK;
432 }
433
434 static const struct command_registration tpiu_command_handlers[] = {
435 {
436 .name = "config",
437 .handler = handle_tpiu_config_command,
438 .mode = COMMAND_ANY,
439 .help = "Configure TPIU features",
440 .usage = "(disable | "
441 "((external | internal (<filename> | <:port> | -)) "
442 "(sync <port width> | ((manchester | uart) <formatter enable>)) "
443 "<TRACECLKIN freq> [<trace freq>]))",
444 },
445 COMMAND_REGISTRATION_DONE
446 };
447
448 static const struct command_registration itm_command_handlers[] = {
449 {
450 .name = "port",
451 .handler = handle_itm_port_command,
452 .mode = COMMAND_ANY,
453 .help = "Enable or disable ITM stimulus port",
454 .usage = "<port> (0|1|on|off)",
455 },
456 {
457 .name = "ports",
458 .handler = handle_itm_ports_command,
459 .mode = COMMAND_ANY,
460 .help = "Enable or disable all ITM stimulus ports",
461 .usage = "(0|1|on|off)",
462 },
463 COMMAND_REGISTRATION_DONE
464 };
465
466 const struct command_registration armv7m_trace_command_handlers[] = {
467 {
468 .name = "tpiu",
469 .mode = COMMAND_ANY,
470 .help = "tpiu command group",
471 .usage = "",
472 .chain = tpiu_command_handlers,
473 },
474 {
475 .name = "itm",
476 .mode = COMMAND_ANY,
477 .help = "itm command group",
478 .usage = "",
479 .chain = itm_command_handlers,
480 },
481 COMMAND_REGISTRATION_DONE
482 };

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)