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

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)