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

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)