]> git.gag.com Git - fw/openocd/blob - src/target/armv7m_trace.c
tcl/target: start using the new TPIU/SWO support
[fw/openocd] / 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
412         armv7m->trace_config.itm_deferred_config = true;
413         return ERROR_OK;
414 }
415
416 COMMAND_HANDLER(handle_itm_ports_command)
417 {
418         struct target *target = get_current_target(CMD_CTX);
419         struct armv7m_common *armv7m = target_to_armv7m(target);
420         bool enable;
421
422         if (CMD_ARGC != 1)
423                 return ERROR_COMMAND_SYNTAX_ERROR;
424
425         COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
426         memset(armv7m->trace_config.itm_ter, enable ? 0xff : 0,
427                sizeof(armv7m->trace_config.itm_ter));
428
429         if (CMD_CTX->mode == COMMAND_EXEC)
430                 return armv7m_trace_itm_config(target);
431
432         armv7m->trace_config.itm_deferred_config = true;
433         return ERROR_OK;
434 }
435
436 static const struct command_registration tpiu_command_handlers[] = {
437         {
438                 .name = "config",
439                 .handler = handle_tpiu_config_command,
440                 .mode = COMMAND_ANY,
441                 .help = "Configure TPIU features",
442                 .usage = "(disable | "
443                 "((external | internal (<filename> | <:port> | -)) "
444                 "(sync <port width> | ((manchester | uart) <formatter enable>)) "
445                 "<TRACECLKIN freq> [<trace freq>]))",
446         },
447         COMMAND_REGISTRATION_DONE
448 };
449
450 static const struct command_registration itm_command_handlers[] = {
451         {
452                 .name = "port",
453                 .handler = handle_itm_port_command,
454                 .mode = COMMAND_ANY,
455                 .help = "Enable or disable ITM stimulus port",
456                 .usage = "<port> (0|1|on|off)",
457         },
458         {
459                 .name = "ports",
460                 .handler = handle_itm_ports_command,
461                 .mode = COMMAND_ANY,
462                 .help = "Enable or disable all ITM stimulus ports",
463                 .usage = "(0|1|on|off)",
464         },
465         COMMAND_REGISTRATION_DONE
466 };
467
468 const struct command_registration armv7m_trace_command_handlers[] = {
469         {
470                 .name = "tpiu",
471                 .mode = COMMAND_ANY,
472                 .help = "tpiu command group",
473                 .usage = "",
474                 .chain = tpiu_command_handlers,
475         },
476         {
477                 .name = "itm",
478                 .mode = COMMAND_ANY,
479                 .help = "itm command group",
480                 .usage = "",
481                 .chain = itm_command_handlers,
482         },
483         COMMAND_REGISTRATION_DONE
484 };