Cortex-A8: better context restore
[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_register_commands(struct command_context *cmd_ctx);
34
35 static int oocd_trace_read_reg(struct oocd_trace *oocd_trace, int reg, uint32_t *value)
36 {
37 size_t bytes_written, bytes_read, bytes_to_read;
38 uint8_t cmd;
39
40 cmd = 0x10 | (reg & 0x7);
41 bytes_written = write(oocd_trace->tty_fd, &cmd, 1);
42
43 bytes_to_read = 4;
44 while (bytes_to_read > 0)
45 {
46 bytes_read = read(oocd_trace->tty_fd, ((uint8_t*)value) + 4 - bytes_to_read, bytes_to_read);
47 bytes_to_read -= bytes_read;
48 }
49
50 LOG_DEBUG("reg #%i: 0x%8.8x\n", reg, *value);
51
52 return ERROR_OK;
53 }
54
55 static int oocd_trace_write_reg(struct oocd_trace *oocd_trace, int reg, uint32_t value)
56 {
57 size_t bytes_written;
58 uint8_t data[5];
59
60 data[0] = 0x18 | (reg & 0x7);
61 data[1] = value & 0xff;
62 data[2] = (value & 0xff00) >> 8;
63 data[3] = (value & 0xff0000) >> 16;
64 data[4] = (value & 0xff000000) >> 24;
65
66 bytes_written = write(oocd_trace->tty_fd, data, 5);
67 LOG_DEBUG("reg #%i: 0x%8.8x\n", reg, value);
68
69 return ERROR_OK;
70 }
71
72 static int oocd_trace_read_memory(struct oocd_trace *oocd_trace, uint8_t *data, uint32_t address, uint32_t size)
73 {
74 size_t bytes_written, bytes_to_read;
75 ssize_t bytes_read;
76 uint8_t cmd;
77
78 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, address);
79 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_SDRAM_COUNTER, size);
80
81 cmd = 0x20;
82 bytes_written = write(oocd_trace->tty_fd, &cmd, 1);
83
84 bytes_to_read = size * 16;
85 while (bytes_to_read > 0)
86 {
87 if ((bytes_read = read(oocd_trace->tty_fd,
88 ((uint8_t*)data) + (size * 16) - bytes_to_read, bytes_to_read)) < 0)
89 {
90 LOG_DEBUG("read() returned %zi (%s)", bytes_read, strerror(errno));
91 }
92 else
93 bytes_to_read -= bytes_read;
94 }
95
96 return ERROR_OK;
97 }
98
99 static int oocd_trace_init(struct etm_context *etm_ctx)
100 {
101 uint8_t trash[256];
102 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
103 size_t bytes_read;
104
105 oocd_trace->tty_fd = open(oocd_trace->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
106
107 if (oocd_trace->tty_fd < 0)
108 {
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 while ((bytes_read = read(oocd_trace->tty_fd, trash, sizeof(trash))) > 0)
139 {
140 LOG_DEBUG("%zi bytes read\n", bytes_read);
141 };
142
143 return ERROR_OK;
144 }
145
146 static trace_status_t oocd_trace_status(struct etm_context *etm_ctx)
147 {
148 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
149 uint32_t status;
150
151 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
152
153 /* if tracing is currently idle, return this information */
154 if (etm_ctx->capture_status == TRACE_IDLE)
155 {
156 return etm_ctx->capture_status;
157 }
158 else if (etm_ctx->capture_status & TRACE_RUNNING)
159 {
160 /* check Full bit to identify an overflow */
161 if (status & 0x4)
162 etm_ctx->capture_status |= TRACE_OVERFLOWED;
163
164 /* check Triggered bit to identify trigger condition */
165 if (status & 0x2)
166 etm_ctx->capture_status |= TRACE_TRIGGERED;
167
168 if (status & 0x1)
169 {
170 etm_ctx->capture_status &= ~TRACE_RUNNING;
171 etm_ctx->capture_status |= TRACE_COMPLETED;
172 }
173 }
174
175 return etm_ctx->capture_status;
176 }
177
178 static int oocd_trace_read_trace(struct etm_context *etm_ctx)
179 {
180 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
181 uint32_t status, address;
182 uint32_t first_frame = 0x0;
183 uint32_t num_frames = 1048576;
184 uint8_t *trace_data;
185 uint32_t i;
186
187 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
188 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_ADDRESS, &address);
189
190 /* check if we overflowed, and adjust first frame of the trace accordingly
191 * if we didn't overflow, read only up to the frame that would be written next,
192 * i.e. don't read invalid entries
193 */
194 if (status & 0x4)
195 first_frame = address;
196 else
197 num_frames = address;
198
199 /* read data into temporary array for unpacking
200 * one frame from OpenOCD + trace corresponds to 16 trace cycles
201 */
202 trace_data = malloc(sizeof(uint8_t) * num_frames * 16);
203 oocd_trace_read_memory(oocd_trace, trace_data, first_frame, num_frames);
204
205 if (etm_ctx->trace_depth > 0)
206 {
207 free(etm_ctx->trace_data);
208 }
209
210 etm_ctx->trace_depth = num_frames * 16;
211 etm_ctx->trace_data = malloc(sizeof(struct etmv1_trace_data) * etm_ctx->trace_depth);
212
213 for (i = 0; i < num_frames * 16; i++)
214 {
215 etm_ctx->trace_data[i].pipestat = (trace_data[i] & 0x7);
216 etm_ctx->trace_data[i].packet = (trace_data[i] & 0x78) >> 3;
217 etm_ctx->trace_data[i].flags = 0;
218
219 if ((trace_data[i] & 0x80) >> 7)
220 {
221 etm_ctx->trace_data[i].flags |= ETMV1_TRACESYNC_CYCLE;
222 }
223
224 if (etm_ctx->trace_data[i].pipestat == STAT_TR)
225 {
226 etm_ctx->trace_data[i].pipestat = etm_ctx->trace_data[i].packet & 0x7;
227 etm_ctx->trace_data[i].flags |= ETMV1_TRIGGER_CYCLE;
228 }
229 }
230
231 free(trace_data);
232
233 return ERROR_OK;
234 }
235
236 static int oocd_trace_start_capture(struct etm_context *etm_ctx)
237 {
238 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
239 uint32_t control = 0x1; /* 0x1: enabled */
240 uint32_t trigger_count;
241
242 if (((etm_ctx->portmode & ETM_PORT_MODE_MASK) != ETM_PORT_NORMAL)
243 || ((etm_ctx->portmode & ETM_PORT_WIDTH_MASK) != ETM_PORT_4BIT))
244 {
245 LOG_DEBUG("OpenOCD + trace only supports normal 4-bit ETM mode");
246 return ERROR_ETM_PORTMODE_NOT_SUPPORTED;
247 }
248
249 if ((etm_ctx->portmode & ETM_PORT_CLOCK_MASK) == ETM_PORT_HALF_CLOCK)
250 {
251 control |= 0x2; /* half rate clock, capture at twice the clock rate */
252 }
253
254 /* OpenOCD + trace holds up to 16 million samples,
255 * but trigger counts is set in multiples of 16 */
256 trigger_count = (1048576 * etm_ctx->trigger_percent) / 100;
257
258 /* capturing always starts at address zero */
259 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_ADDRESS, 0x0);
260 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_TRIGGER_COUNTER, trigger_count);
261 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, control);
262
263 /* we're starting a new trace, initialize capture status */
264 etm_ctx->capture_status = TRACE_RUNNING;
265
266 return ERROR_OK;
267 }
268
269 static int oocd_trace_stop_capture(struct etm_context *etm_ctx)
270 {
271 struct oocd_trace *oocd_trace = etm_ctx->capture_driver_priv;
272
273 /* trace stopped, just clear running flag, but preserve others */
274 etm_ctx->capture_status &= ~TRACE_RUNNING;
275
276 oocd_trace_write_reg(oocd_trace, OOCD_TRACE_CONTROL, 0x0);
277
278 return ERROR_OK;
279 }
280
281 struct etm_capture_driver oocd_trace_capture_driver =
282 {
283 .name = "oocd_trace",
284 .register_commands = oocd_trace_register_commands,
285 .init = oocd_trace_init,
286 .status = oocd_trace_status,
287 .start_capture = oocd_trace_start_capture,
288 .stop_capture = oocd_trace_stop_capture,
289 .read_trace = oocd_trace_read_trace,
290 };
291
292 COMMAND_HANDLER(handle_oocd_trace_config_command)
293 {
294 struct target *target;
295 struct arm *arm;
296
297 if (CMD_ARGC != 2)
298 {
299 LOG_ERROR("incomplete 'oocd_trace config <target> <tty>' command");
300 return ERROR_FAIL;
301 }
302
303 target = get_current_target(CMD_CTX);
304 arm = target_to_arm(target);
305 if (!is_arm(arm))
306 {
307 command_print(CMD_CTX, "current target isn't an ARM");
308 return ERROR_FAIL;
309 }
310
311 if (arm->etm)
312 {
313 struct oocd_trace *oocd_trace = malloc(sizeof(struct oocd_trace));
314
315 arm->etm->capture_driver_priv = oocd_trace;
316 oocd_trace->etm_ctx = arm->etm;
317
318 /* copy name of TTY device used to communicate with OpenOCD + trace */
319 oocd_trace->tty = strndup(CMD_ARGV[1], 256);
320 }
321 else
322 {
323 LOG_ERROR("target has no ETM defined, OpenOCD + trace left unconfigured");
324 }
325
326 return ERROR_OK;
327 }
328
329 COMMAND_HANDLER(handle_oocd_trace_status_command)
330 {
331 struct target *target;
332 struct arm *arm;
333 struct oocd_trace *oocd_trace;
334 uint32_t status;
335
336 target = get_current_target(CMD_CTX);
337
338 arm = target_to_arm(target);
339 if (!is_arm(arm))
340 {
341 command_print(CMD_CTX, "current target isn't an ARM");
342 return ERROR_FAIL;
343 }
344
345 if (!arm->etm)
346 {
347 command_print(CMD_CTX, "current target doesn't have an ETM configured");
348 return ERROR_FAIL;
349 }
350
351 if (strcmp(arm->etm->capture_driver->name, "oocd_trace") != 0)
352 {
353 command_print(CMD_CTX, "current target's ETM capture driver isn't 'oocd_trace'");
354 return ERROR_FAIL;
355 }
356
357 oocd_trace = (struct oocd_trace*)arm->etm->capture_driver_priv;
358
359 oocd_trace_read_reg(oocd_trace, OOCD_TRACE_STATUS, &status);
360
361 if (status & 0x8)
362 command_print(CMD_CTX, "trace clock locked");
363 else
364 command_print(CMD_CTX, "no trace clock");
365
366 return ERROR_OK;
367 }
368
369 COMMAND_HANDLER(handle_oocd_trace_resync_command)
370 {
371 struct target *target;
372 struct arm *arm;
373 struct oocd_trace *oocd_trace;
374 size_t bytes_written;
375 uint8_t cmd_array[1];
376
377 target = get_current_target(CMD_CTX);
378
379 arm = target_to_arm(target);
380 if (!is_arm(arm))
381 {
382 command_print(CMD_CTX, "current target isn't an ARM");
383 return ERROR_FAIL;
384 }
385
386 if (!arm->etm)
387 {
388 command_print(CMD_CTX, "current target doesn't have an ETM configured");
389 return ERROR_FAIL;
390 }
391
392 if (strcmp(arm->etm->capture_driver->name, "oocd_trace") != 0)
393 {
394 command_print(CMD_CTX, "current target's ETM capture driver isn't 'oocd_trace'");
395 return ERROR_FAIL;
396 }
397
398 oocd_trace = (struct oocd_trace*)arm->etm->capture_driver_priv;
399
400 cmd_array[0] = 0xf0;
401
402 bytes_written = write(oocd_trace->tty_fd, cmd_array, 1);
403
404 command_print(CMD_CTX, "requesting traceclock resync");
405 LOG_DEBUG("resyncing traceclk pll");
406
407 return ERROR_OK;
408 }
409
410 int oocd_trace_register_commands(struct command_context *cmd_ctx)
411 {
412 struct command *oocd_trace_cmd;
413
414 oocd_trace_cmd = register_command(cmd_ctx, NULL, "oocd_trace", NULL, COMMAND_ANY, "OpenOCD + trace");
415
416 register_command(cmd_ctx, oocd_trace_cmd, "config", handle_oocd_trace_config_command, COMMAND_CONFIG, NULL);
417
418 register_command(cmd_ctx, oocd_trace_cmd, "status", handle_oocd_trace_status_command, COMMAND_EXEC, "display OpenOCD + trace status");
419 register_command(cmd_ctx, oocd_trace_cmd, "resync", handle_oocd_trace_resync_command, COMMAND_EXEC, "resync OpenOCD + trace capture clock");
420
421 return ERROR_OK;
422 }

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)