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

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)