target/armv7m_trace: Improve SWO frequency auto-detection
[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
28 #define TRACE_BUF_SIZE 4096
29
30 static int armv7m_poll_trace(void *target)
31 {
32 struct armv7m_common *armv7m = target_to_armv7m(target);
33 uint8_t buf[TRACE_BUF_SIZE];
34 size_t size = sizeof(buf);
35 int retval;
36
37 retval = adapter_poll_trace(buf, &size);
38 if (retval != ERROR_OK || !size)
39 return retval;
40
41 target_call_trace_callbacks(target, size, buf);
42
43 if (armv7m->trace_config.trace_file != NULL) {
44 if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size)
45 fflush(armv7m->trace_config.trace_file);
46 else {
47 LOG_ERROR("Error writing to the trace destination file");
48 return ERROR_FAIL;
49 }
50 }
51
52 return ERROR_OK;
53 }
54
55 int armv7m_trace_tpiu_config(struct target *target)
56 {
57 struct armv7m_common *armv7m = target_to_armv7m(target);
58 struct armv7m_trace_config *trace_config = &armv7m->trace_config;
59 uint16_t prescaler;
60 int retval;
61
62 target_unregister_timer_callback(armv7m_poll_trace, target);
63
64 retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL,
65 trace_config->pin_protocol, trace_config->port_size,
66 &trace_config->trace_freq, trace_config->traceclkin_freq, &prescaler);
67
68 if (retval != ERROR_OK)
69 return retval;
70
71 if (!trace_config->trace_freq) {
72 LOG_ERROR("Trace port frequency is 0, can't enable TPIU");
73 return ERROR_FAIL;
74 }
75
76 retval = target_write_u32(target, TPIU_CSPSR, 1 << trace_config->port_size);
77 if (retval != ERROR_OK)
78 return retval;
79
80 retval = target_write_u32(target, TPIU_ACPR, prescaler - 1);
81 if (retval != ERROR_OK)
82 return retval;
83
84 retval = target_write_u32(target, TPIU_SPPR, trace_config->pin_protocol);
85 if (retval != ERROR_OK)
86 return retval;
87
88 uint32_t ffcr;
89 retval = target_read_u32(target, TPIU_FFCR, &ffcr);
90 if (retval != ERROR_OK)
91 return retval;
92 if (trace_config->formatter)
93 ffcr |= (1 << 1);
94 else
95 ffcr &= ~(1 << 1);
96 retval = target_write_u32(target, TPIU_FFCR, ffcr);
97 if (retval != ERROR_OK)
98 return retval;
99
100 if (trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL)
101 target_register_timer_callback(armv7m_poll_trace, 1,
102 TARGET_TIMER_TYPE_PERIODIC, target);
103
104 target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
105
106 return ERROR_OK;
107 }
108
109 int armv7m_trace_itm_config(struct target *target)
110 {
111 struct armv7m_common *armv7m = target_to_armv7m(target);
112 struct armv7m_trace_config *trace_config = &armv7m->trace_config;
113 int retval;
114
115 retval = target_write_u32(target, ITM_LAR, ITM_LAR_KEY);
116 if (retval != ERROR_OK)
117 return retval;
118
119 /* Enable ITM, TXENA, set TraceBusID and other parameters */
120 retval = target_write_u32(target, ITM_TCR, (1 << 0) | (1 << 3) |
121 (trace_config->itm_diff_timestamps << 1) |
122 (trace_config->itm_synchro_packets << 2) |
123 (trace_config->itm_async_timestamps << 4) |
124 (trace_config->itm_ts_prescale << 8) |
125 (trace_config->trace_bus_id << 16));
126 if (retval != ERROR_OK)
127 return retval;
128
129 for (unsigned int i = 0; i < 8; i++) {
130 retval = target_write_u32(target, ITM_TER0 + i * 4,
131 trace_config->itm_ter[i]);
132 if (retval != ERROR_OK)
133 return retval;
134 }
135
136 return ERROR_OK;
137 }
138
139 static void close_trace_file(struct armv7m_common *armv7m)
140 {
141 if (armv7m->trace_config.trace_file)
142 fclose(armv7m->trace_config.trace_file);
143 armv7m->trace_config.trace_file = NULL;
144 }
145
146 COMMAND_HANDLER(handle_tpiu_config_command)
147 {
148 struct target *target = get_current_target(CMD_CTX);
149 struct armv7m_common *armv7m = target_to_armv7m(target);
150
151 unsigned int cmd_idx = 0;
152
153 if (CMD_ARGC == cmd_idx)
154 return ERROR_COMMAND_SYNTAX_ERROR;
155 if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
156 if (CMD_ARGC == cmd_idx + 1) {
157 close_trace_file(armv7m);
158
159 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
160 if (CMD_CTX->mode == COMMAND_EXEC)
161 return armv7m_trace_tpiu_config(target);
162 else
163 return ERROR_OK;
164 }
165 } else if (!strcmp(CMD_ARGV[cmd_idx], "external") ||
166 !strcmp(CMD_ARGV[cmd_idx], "internal")) {
167 close_trace_file(armv7m);
168
169 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
170 if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
171 cmd_idx++;
172 if (CMD_ARGC == cmd_idx)
173 return ERROR_COMMAND_SYNTAX_ERROR;
174
175 armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL;
176
177 if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
178 armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
179 if (!armv7m->trace_config.trace_file) {
180 LOG_ERROR("Can't open trace destination file");
181 return ERROR_FAIL;
182 }
183 }
184 }
185 cmd_idx++;
186 if (CMD_ARGC == cmd_idx)
187 return ERROR_COMMAND_SYNTAX_ERROR;
188
189 if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
190 armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_SYNC;
191
192 cmd_idx++;
193 if (CMD_ARGC == cmd_idx)
194 return ERROR_COMMAND_SYNTAX_ERROR;
195
196 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
197 } else {
198 if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
199 armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER;
200 else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
201 armv7m->trace_config.pin_protocol = TPIU_PIN_PROTOCOL_ASYNC_UART;
202 else
203 return ERROR_COMMAND_SYNTAX_ERROR;
204
205 cmd_idx++;
206 if (CMD_ARGC == cmd_idx)
207 return ERROR_COMMAND_SYNTAX_ERROR;
208
209 COMMAND_PARSE_ON_OFF(CMD_ARGV[cmd_idx], armv7m->trace_config.formatter);
210 }
211
212 cmd_idx++;
213 if (CMD_ARGC == cmd_idx)
214 return ERROR_COMMAND_SYNTAX_ERROR;
215
216 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.traceclkin_freq);
217
218 cmd_idx++;
219 if (CMD_ARGC != cmd_idx) {
220 COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
221 cmd_idx++;
222 } else {
223 if (armv7m->trace_config.config_type != TRACE_CONFIG_TYPE_INTERNAL) {
224 LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
225 return ERROR_COMMAND_SYNTAX_ERROR;
226 }
227 armv7m->trace_config.trace_freq = 0;
228 }
229
230 if (CMD_ARGC == cmd_idx) {
231 if (CMD_CTX->mode == COMMAND_EXEC)
232 return armv7m_trace_tpiu_config(target);
233 else
234 return ERROR_OK;
235 }
236 }
237
238 return ERROR_COMMAND_SYNTAX_ERROR;
239 }
240
241 COMMAND_HANDLER(handle_itm_port_command)
242 {
243 struct target *target = get_current_target(CMD_CTX);
244 struct armv7m_common *armv7m = target_to_armv7m(target);
245 unsigned int reg_idx;
246 uint8_t port;
247 bool enable;
248
249 if (CMD_ARGC != 2)
250 return ERROR_COMMAND_SYNTAX_ERROR;
251
252 COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], port);
253 COMMAND_PARSE_ON_OFF(CMD_ARGV[1], enable);
254 reg_idx = port / 32;
255 port = port % 32;
256 if (enable)
257 armv7m->trace_config.itm_ter[reg_idx] |= (1 << port);
258 else
259 armv7m->trace_config.itm_ter[reg_idx] &= ~(1 << port);
260
261 if (CMD_CTX->mode == COMMAND_EXEC)
262 return armv7m_trace_itm_config(target);
263 else
264 return ERROR_OK;
265 }
266
267 COMMAND_HANDLER(handle_itm_ports_command)
268 {
269 struct target *target = get_current_target(CMD_CTX);
270 struct armv7m_common *armv7m = target_to_armv7m(target);
271 bool enable;
272
273 if (CMD_ARGC != 1)
274 return ERROR_COMMAND_SYNTAX_ERROR;
275
276 COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
277 memset(armv7m->trace_config.itm_ter, enable ? 0xff : 0,
278 sizeof(armv7m->trace_config.itm_ter));
279
280 if (CMD_CTX->mode == COMMAND_EXEC)
281 return armv7m_trace_itm_config(target);
282 else
283 return ERROR_OK;
284 }
285
286 static const struct command_registration tpiu_command_handlers[] = {
287 {
288 .name = "config",
289 .handler = handle_tpiu_config_command,
290 .mode = COMMAND_ANY,
291 .help = "Configure TPIU features",
292 .usage = "(disable | "
293 "((external | internal <filename>) "
294 "(sync <port width> | ((manchester | uart) <formatter enable>)) "
295 "<TRACECLKIN freq> [<trace freq>]))",
296 },
297 COMMAND_REGISTRATION_DONE
298 };
299
300 static const struct command_registration itm_command_handlers[] = {
301 {
302 .name = "port",
303 .handler = handle_itm_port_command,
304 .mode = COMMAND_ANY,
305 .help = "Enable or disable ITM stimulus port",
306 .usage = "<port> (0|1|on|off)",
307 },
308 {
309 .name = "ports",
310 .handler = handle_itm_ports_command,
311 .mode = COMMAND_ANY,
312 .help = "Enable or disable all ITM stimulus ports",
313 .usage = "(0|1|on|off)",
314 },
315 COMMAND_REGISTRATION_DONE
316 };
317
318 const struct command_registration armv7m_trace_command_handlers[] = {
319 {
320 .name = "tpiu",
321 .mode = COMMAND_ANY,
322 .help = "tpiu command group",
323 .usage = "",
324 .chain = tpiu_command_handlers,
325 },
326 {
327 .name = "itm",
328 .mode = COMMAND_ANY,
329 .help = "itm command group",
330 .usage = "",
331 .chain = itm_command_handlers,
332 },
333 COMMAND_REGISTRATION_DONE
334 };

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)