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

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)