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

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)