armv7a: ARMv7-A MMU tools
[openocd.git] / src / target / oocd_trace.c
1 /***************************************************************************
2 * Copyright (C) 2007 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "arm.h"
24 #include "etm.h"
25 #include "oocd_trace.h"
26
27 /*
28 * This is "proof of concept" code, for prototype hardware:
29 * https://lists.berlios.de/pipermail/openocd-development/2007-September/000336.html
30 */
31
32 static int oocd_trace_read_reg(struct oocd_trace *oocd_trace, int reg, uint32_t *value)
33 {
34 size_t bytes_written, bytes_read, bytes_to_read;
35 uint8_t cmd;
36
37 cmd = 0x10 | (reg & 0x7);
38 bytes_written = write(oocd_trace->tty_fd, &cmd, 1);
39 if (bytes_written < 1)
40 return ERROR_FAIL;
41
42 bytes_to_read = 4;
43 while (bytes_to_read > 0) {
44 bytes_read = read(oocd_trace->tty_fd, ((uint8_t *)value) + 4 - bytes_to_read, bytes_to_read);
45 bytes_to_read -= bytes_read;
46 }
47
48 LOG_DEBUG("reg #%i: 0x%8.8x", reg, *value);
49
50 return ERROR_OK;
51 }
52
53 static int oocd_trace_write_reg(struct oocd_trace *oocd_trace, int reg, uint32_t value)
54 {
55 size_t bytes_written;
56 uint8_t data[5];
57
58 data[0] = 0x18 | (reg & 0x7);
59 data[1] = value & 0xff;
60 data[2] = (value & 0xff00) >> 8;
61 data[3] = (value & 0xff0000) >> 16;
62 data[4] = (value & 0xff000000) >> 24;
63
64 bytes_written = write(oocd_trace->tty_fd, data, 5);
65 if (bytes_written < 5)
66 return ERROR_FAIL;
67
68 LOG_DEBUG("reg #%i: 0x%8.8x", reg, value);
69
70 return ERROR_OK;
71 }
72
73 static int oocd_trace_read_memory(struct oocd_trace *oocd_trace, uint8_t *data, uint32_t address, uint32_t size)
74 {
75 size_t bytes_written, bytes_to_read;
76 ssize_t bytes_read;
77 uint8_t cmd;
78
79 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, address);
80 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_SDRAM_COUNTER, size);
81
82 cmd = 0x20;
83 bytes_written = write(oocd_trace->tty_fd, &cmd, 1);
84 if (bytes_written < 1)
85 return ERROR_FAIL;
86
87 bytes_to_read = size * 16;
88 while (bytes_to_read > 0) {
89 bytes_read = read(oocd_trace->tty_fd,
90 ((uint8_t *)data) + (size * 16) - bytes_to_read, bytes_to_read);
91 if (bytes_read < 0)
92 LOG_DEBUG("read() returned %zi (%s)", bytes_read, strerror(errno));
93 else
94 bytes_to_read -= bytes_read;
95 }
96
97 return ERROR_OK;
98 }
99
100 static int oocd_trace_init(struct etm_context *etm_ctx)
101 {
102 uint8_t trash[256];
103 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
104 size_t bytes_read;
105
106 oocd_trace->tty_fd = open(oocd_trace->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
107
108 if (oocd_trace->tty_fd < 0) {
109 LOG_ERROR("can't open tty");
110 return ERROR_ETM_CAPTURE_INIT_FAILED;
111 }
112
113 /* clear input & output buffers, then switch to "blocking mode" */
114 tcflush(oocd_trace->tty_fd, TCOFLUSH);
115 tcflush(oocd_trace->tty_fd, TCIFLUSH);
116 fcntl(oocd_trace->tty_fd, F_SETFL, fcntl(oocd_trace->tty_fd, F_GETFL) & ~O_NONBLOCK);
117
118 tcgetattr(oocd_trace->tty_fd, &oocd_trace->oldtio); /* save current port settings */
119
120 bzero(&oocd_trace->newtio, sizeof(oocd_trace->newtio));
121 oocd_trace->newtio.c_cflag = CS8 | CLOCAL | CREAD | B2500000;
122
123 oocd_trace->newtio.c_iflag = IGNPAR | IGNBRK | IXON | IXOFF;
124 oocd_trace->newtio.c_oflag = 0;
125
126 /* set input mode (non-canonical, no echo,...) */
127 oocd_trace->newtio.c_lflag = 0;
128
129 cfmakeraw(&oocd_trace->newtio);
130 oocd_trace->newtio.c_cc[VTIME] = 1; /* inter-character timer used */
131 oocd_trace->newtio.c_cc[VMIN] = 0; /* blocking read until 0 chars received */
132
133 tcflush(oocd_trace->tty_fd, TCIFLUSH);
134 tcsetattr(oocd_trace->tty_fd, TCSANOW, &oocd_trace->newtio);
135
136 /* occasionally one bogus character is left in the input buffer
137 * read up any leftover characters to ensure communication is in sync */
138 do {
139 bytes_read = read(oocd_trace->tty_fd, trash, sizeof(trash));
140 if (bytes_read)
141 LOG_DEBUG("%zi bytes read", bytes_read);
142 } while (bytes_read > 0);
143
144 return ERROR_OK;
145 }
146
147 static trace_status_t oocd_trace_status(struct etm_context *etm_ctx)
148 {
149 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
150 uint32_t status;
151
152 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
153
154 /* if tracing is currently idle, return this information */
155 if (etm_ctx->capture_status == TRACE_IDLE)
156 return etm_ctx->capture_status;
157 else if (etm_ctx->capture_status & TRACE_RUNNING) {
158 /* check Full bit to identify an overflow */
159 if (status & 0x4)
160 etm_ctx->capture_status |= TRACE_OVERFLOWED;
161
162 /* check Triggered bit to identify trigger condition */
163 if (status & 0x2)
164 etm_ctx->capture_status |= TRACE_TRIGGERED;
165
166 if (status & 0x1) {
167 etm_ctx->capture_status &= ~TRACE_RUNNING;
168 etm_ctx->capture_status |= TRACE_COMPLETED;
169 }
170 }
171
172 return etm_ctx->capture_status;
173 }
174
175 static int oocd_trace_read_trace(struct etm_context *etm_ctx)
176 {
177 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
178 uint32_t status, address;
179 uint32_t first_frame = 0x0;
180 uint32_t num_frames = 1048576;
181 uint8_t *trace_data;
182 uint32_t i;
183
184 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
185 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_ADDRESS, &address);
186
187 /* check if we overflowed, and adjust first frame of the trace accordingly
188 * if we didn't overflow, read only up to the frame that would be written next,
189 * i.e. don't read invalid entries
190 */
191 if (status & 0x4)
192 first_frame = address;
193 else
194 num_frames = address;
195
196 /* read data into temporary array for unpacking
197 * one frame from OpenOCD + trace corresponds to 16 trace cycles
198 */
199 trace_data = malloc(sizeof(uint8_t) * num_frames * 16);
200 oocd_trace_read_memory(oocd_trace, trace_data, first_frame, num_frames);
201
202 if (etm_ctx->trace_depth > 0)
203 free(etm_ctx->trace_data);
204
205 etm_ctx->trace_depth = num_frames * 16;
206 etm_ctx->trace_data = malloc(sizeof(struct etmv1_trace_data) * etm_ctx->trace_depth);
207
208 for (i = 0; i < num_frames * 16; i++) {
209 etm_ctx->trace_data[i].pipestat = (trace_data[i] & 0x7);
210 etm_ctx->trace_data[i].packet = (trace_data[i] & 0x78) >> 3;
211 etm_ctx->trace_data[i].flags = 0;
212
213 if ((trace_data[i] & 0x80) >> 7)
214 etm_ctx->trace_data[i].flags |= ETMV1_TRACESYNC_CYCLE;
215
216 if (etm_ctx->trace_data[i].pipestat == STAT_TR) {
217 etm_ctx->trace_data[i].pipestat = etm_ctx->trace_data[i].packet & 0x7;
218 etm_ctx->trace_data[i].flags |= ETMV1_TRIGGER_CYCLE;
219 }
220 }
221
222 free(trace_data);
223
224 return ERROR_OK;
225 }
226
227 static int oocd_trace_start_capture(struct etm_context *etm_ctx)
228 {
229 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
230 uint32_t control = 0x1; /* 0x1: enabled */
231 uint32_t trigger_count;
232
233 if (((etm_ctx->control & ETM_PORT_MODE_MASK) != ETM_PORT_NORMAL)
234 || ((etm_ctx->control & ETM_PORT_WIDTH_MASK) != ETM_PORT_4BIT)) {
235 LOG_DEBUG("OpenOCD + trace only supports normal 4-bit ETM mode");
236 return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
237 }
238
239 if ((etm_ctx->control & ETM_PORT_CLOCK_MASK) == ETM_PORT_HALF_CLOCK)
240 control |= 0x2; /* half rate clock, capture at twice the clock rate */
241
242 /* OpenOCD + trace holds up to 16 million samples,
243 * but trigger counts is set in multiples of 16 */
244 trigger_count = (1048576 * /* trigger_percent */ 50) / 100;
245
246 /* capturing always starts at address zero */
247 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, 0x0);
248 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_TRIGGER_COUNTER, trigger_count);
249 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, control);
250
251 /* we're starting a new trace, initialize capture status */
252 etm_ctx->capture_status = TRACE_RUNNING;
253
254 return ERROR_OK;
255 }
256
257 static int oocd_trace_stop_capture(struct etm_context *etm_ctx)
258 {
259 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
260
261 /* trace stopped, just clear running flag, but preserve others */
262 etm_ctx->capture_status &= ~TRACE_RUNNING;
263
264 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, 0x0);
265
266 return ERROR_OK;
267 }
268
269 COMMAND_HANDLER(handle_oocd_trace_config_command)
270 {
271 struct target *target;
272 struct arm *arm;
273
274 if (CMD_ARGC != 2)
275 return ERROR_COMMAND_SYNTAX_ERROR;
276
277 target = get_current_target(CMD_CTX);
278 arm = target_to_arm(target);
279 if (!is_arm(arm)) {
280 command_print(CMD_CTX, "current target isn't an ARM");
281 return ERROR_FAIL;
282 }
283
284 if (arm->etm) {
285 struct oocd_trace *oocd_trace = malloc(sizeof(struct oocd_trace));
286
287 arm->etm->capture_driver_priv = oocd_trace;
288 oocd_trace->etm_ctx = arm->etm;
289
290 /* copy name of TTY device used to communicate with OpenOCD + trace */
291 oocd_trace->tty = strndup(CMD_ARGV[1], 256);
292 } else
293 LOG_ERROR("target has no ETM defined, OpenOCD + trace left unconfigured");
294
295 return ERROR_OK;
296 }
297
298 COMMAND_HANDLER(handle_oocd_trace_status_command)
299 {
300 struct target *target;
301 struct arm *arm;
302 struct oocd_trace *oocd_trace;
303 uint32_t status;
304
305 target = get_current_target(CMD_CTX);
306
307 arm = target_to_arm(target);
308 if (!is_arm(arm)) {
309 command_print(CMD_CTX, "current target isn't an ARM");
310 return ERROR_FAIL;
311 }
312
313 if (!arm->etm) {
314 command_print(CMD_CTX, "current target doesn't have an ETM configured");
315 return ERROR_FAIL;
316 }
317
318 if (strcmp(arm->etm->capture_driver->name, "oocd_trace") != 0) {
319 command_print(CMD_CTX, "current target's ETM capture driver isn't 'oocd_trace'");
320 return ERROR_FAIL;
321 }
322
323 oocd_trace = (struct oocd_trace *)arm->etm->capture_driver_priv;
324
325 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
326
327 if (status & 0x8)
328 command_print(CMD_CTX, "trace clock locked");
329 else
330 command_print(CMD_CTX, "no trace clock");
331
332 return ERROR_OK;
333 }
334
335 COMMAND_HANDLER(handle_oocd_trace_resync_command)
336 {
337 struct target *target;
338 struct arm *arm;
339 struct oocd_trace *oocd_trace;
340 size_t bytes_written;
341 uint8_t cmd_array[1];
342
343 target = get_current_target(CMD_CTX);
344
345 arm = target_to_arm(target);
346 if (!is_arm(arm)) {
347 command_print(CMD_CTX, "current target isn't an ARM");
348 return ERROR_FAIL;
349 }
350
351 if (!arm->etm) {
352 command_print(CMD_CTX, "current target doesn't have an ETM configured");
353 return ERROR_FAIL;
354 }
355
356 if (strcmp(arm->etm->capture_driver->name, "oocd_trace") != 0) {
357 command_print(CMD_CTX, "current target's ETM capture driver isn't 'oocd_trace'");
358 return ERROR_FAIL;
359 }
360
361 oocd_trace = (struct oocd_trace *)arm->etm->capture_driver_priv;
362
363 cmd_array[0] = 0xf0;
364
365 bytes_written = write(oocd_trace->tty_fd, cmd_array, 1);
366 if (bytes_written < 1)
367 return ERROR_FAIL;
368
369 command_print(CMD_CTX, "requesting traceclock resync");
370 LOG_DEBUG("resyncing traceclk pll");
371
372 return ERROR_OK;
373 }
374
375 static const struct command_registration oocd_trace_all_command_handlers[] = {
376 {
377 .name = "config",
378 .handler = handle_oocd_trace_config_command,
379 .mode = COMMAND_CONFIG,
380 .usage = "<target> <tty>",
381 },
382 {
383 .name = "status",
384 .handler = handle_oocd_trace_status_command,
385 .mode = COMMAND_EXEC,
386 .usage = "",
387 .help = "display OpenOCD + trace status",
388 },
389 {
390 .name = "resync",
391 .handler = handle_oocd_trace_resync_command,
392 .mode = COMMAND_EXEC,
393 .usage = "",
394 .help = "resync OpenOCD + trace capture clock",
395 },
396 COMMAND_REGISTRATION_DONE
397 };
398 static const struct command_registration oocd_trace_command_handlers[] = {
399 {
400 .name = "oocd_trace",
401 .mode = COMMAND_ANY,
402 .help = "OpenOCD trace capture driver command group",
403 .usage = "",
404 .chain = oocd_trace_all_command_handlers,
405 },
406 COMMAND_REGISTRATION_DONE
407 };
408
409 struct etm_capture_driver oocd_trace_capture_driver = {
410 .name = "oocd_trace",
411 .commands = oocd_trace_command_handlers,
412 .init = oocd_trace_init,
413 .status = oocd_trace_status,
414 .start_capture = oocd_trace_start_capture,
415 .stop_capture = oocd_trace_stop_capture,
416 .read_trace = oocd_trace_read_trace,
417 };

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)