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

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)