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

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)