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

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)