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

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)