1 /***************************************************************************
2 * Copyright (C) 2007 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
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. *
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. *
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 ***************************************************************************/
27 #include "oocd_trace.h"
30 * This is "proof of concept" code, for prototype hardware:
31 * https://lists.berlios.de/pipermail/openocd-development/2007-September/000336.html
34 static int oocd_trace_read_reg(struct oocd_trace
*oocd_trace
, int reg
, uint32_t *value
)
36 size_t bytes_written
, bytes_read
, bytes_to_read
;
39 cmd
= 0x10 | (reg
& 0x7);
40 bytes_written
= write(oocd_trace
->tty_fd
, &cmd
, 1);
41 if (bytes_written
< 1)
45 while (bytes_to_read
> 0) {
46 bytes_read
= read(oocd_trace
->tty_fd
, ((uint8_t *)value
) + 4 - bytes_to_read
, bytes_to_read
);
47 bytes_to_read
-= bytes_read
;
50 LOG_DEBUG("reg #%i: 0x%8.8x", reg
, *value
);
55 static int oocd_trace_write_reg(struct oocd_trace
*oocd_trace
, int reg
, uint32_t value
)
60 data
[0] = 0x18 | (reg
& 0x7);
61 data
[1] = value
& 0xff;
62 data
[2] = (value
& 0xff00) >> 8;
63 data
[3] = (value
& 0xff0000) >> 16;
64 data
[4] = (value
& 0xff000000) >> 24;
66 bytes_written
= write(oocd_trace
->tty_fd
, data
, 5);
67 if (bytes_written
< 5)
70 LOG_DEBUG("reg #%i: 0x%8.8x", reg
, value
);
75 static int oocd_trace_read_memory(struct oocd_trace
*oocd_trace
, uint8_t *data
, uint32_t address
, uint32_t size
)
77 size_t bytes_written
, bytes_to_read
;
81 oocd_trace_write_reg(oocd_trace
, OOCD_TRACE_ADDRESS
, address
);
82 oocd_trace_write_reg(oocd_trace
, OOCD_TRACE_SDRAM_COUNTER
, size
);
85 bytes_written
= write(oocd_trace
->tty_fd
, &cmd
, 1);
86 if (bytes_written
< 1)
89 bytes_to_read
= size
* 16;
90 while (bytes_to_read
> 0) {
91 bytes_read
= read(oocd_trace
->tty_fd
,
92 ((uint8_t *)data
) + (size
* 16) - bytes_to_read
, bytes_to_read
);
94 LOG_DEBUG("read() returned %zi (%s)", bytes_read
, strerror(errno
));
96 bytes_to_read
-= bytes_read
;
102 static int oocd_trace_init(struct etm_context
*etm_ctx
)
105 struct oocd_trace
*oocd_trace
= etm_ctx
->capture_driver_priv
;
108 oocd_trace
->tty_fd
= open(oocd_trace
->tty
, O_RDWR
| O_NOCTTY
| O_NONBLOCK
);
110 if (oocd_trace
->tty_fd
< 0) {
111 LOG_ERROR("can't open tty");
112 return ERROR_ETM_CAPTURE_INIT_FAILED
;
115 /* clear input & output buffers, then switch to "blocking mode" */
116 tcflush(oocd_trace
->tty_fd
, TCOFLUSH
);
117 tcflush(oocd_trace
->tty_fd
, TCIFLUSH
);
118 fcntl(oocd_trace
->tty_fd
, F_SETFL
, fcntl(oocd_trace
->tty_fd
, F_GETFL
) & ~O_NONBLOCK
);
120 tcgetattr(oocd_trace
->tty_fd
, &oocd_trace
->oldtio
); /* save current port settings */
122 bzero(&oocd_trace
->newtio
, sizeof(oocd_trace
->newtio
));
123 oocd_trace
->newtio
.c_cflag
= CS8
| CLOCAL
| CREAD
| B2500000
;
125 oocd_trace
->newtio
.c_iflag
= IGNPAR
| IGNBRK
| IXON
| IXOFF
;
126 oocd_trace
->newtio
.c_oflag
= 0;
128 /* set input mode (non-canonical, no echo,...) */
129 oocd_trace
->newtio
.c_lflag
= 0;
131 cfmakeraw(&oocd_trace
->newtio
);
132 oocd_trace
->newtio
.c_cc
[VTIME
] = 1; /* inter-character timer used */
133 oocd_trace
->newtio
.c_cc
[VMIN
] = 0; /* blocking read until 0 chars received */
135 tcflush(oocd_trace
->tty_fd
, TCIFLUSH
);
136 tcsetattr(oocd_trace
->tty_fd
, TCSANOW
, &oocd_trace
->newtio
);
138 /* occasionally one bogus character is left in the input buffer
139 * read up any leftover characters to ensure communication is in sync */
141 bytes_read
= read(oocd_trace
->tty_fd
, trash
, sizeof(trash
));
143 LOG_DEBUG("%zi bytes read", bytes_read
);
144 } while (bytes_read
> 0);
149 static trace_status_t
oocd_trace_status(struct etm_context
*etm_ctx
)
151 struct oocd_trace
*oocd_trace
= etm_ctx
->capture_driver_priv
;
154 oocd_trace_read_reg(oocd_trace
, OOCD_TRACE_STATUS
, &status
);
156 /* if tracing is currently idle, return this information */
157 if (etm_ctx
->capture_status
== TRACE_IDLE
)
158 return etm_ctx
->capture_status
;
159 else if (etm_ctx
->capture_status
& TRACE_RUNNING
) {
160 /* check Full bit to identify an overflow */
162 etm_ctx
->capture_status
|= TRACE_OVERFLOWED
;
164 /* check Triggered bit to identify trigger condition */
166 etm_ctx
->capture_status
|= TRACE_TRIGGERED
;
169 etm_ctx
->capture_status
&= ~TRACE_RUNNING
;
170 etm_ctx
->capture_status
|= TRACE_COMPLETED
;
174 return etm_ctx
->capture_status
;
177 static int oocd_trace_read_trace(struct etm_context
*etm_ctx
)
179 struct oocd_trace
*oocd_trace
= etm_ctx
->capture_driver_priv
;
180 uint32_t status
, address
;
181 uint32_t first_frame
= 0x0;
182 uint32_t num_frames
= 1048576;
186 oocd_trace_read_reg(oocd_trace
, OOCD_TRACE_STATUS
, &status
);
187 oocd_trace_read_reg(oocd_trace
, OOCD_TRACE_ADDRESS
, &address
);
189 /* check if we overflowed, and adjust first frame of the trace accordingly
190 * if we didn't overflow, read only up to the frame that would be written next,
191 * i.e. don't read invalid entries
194 first_frame
= address
;
196 num_frames
= address
;
198 /* read data into temporary array for unpacking
199 * one frame from OpenOCD + trace corresponds to 16 trace cycles
201 trace_data
= malloc(sizeof(uint8_t) * num_frames
* 16);
202 oocd_trace_read_memory(oocd_trace
, trace_data
, first_frame
, num_frames
);
204 if (etm_ctx
->trace_depth
> 0)
205 free(etm_ctx
->trace_data
);
207 etm_ctx
->trace_depth
= num_frames
* 16;
208 etm_ctx
->trace_data
= malloc(sizeof(struct etmv1_trace_data
) * etm_ctx
->trace_depth
);
210 for (i
= 0; i
< num_frames
* 16; i
++) {
211 etm_ctx
->trace_data
[i
].pipestat
= (trace_data
[i
] & 0x7);
212 etm_ctx
->trace_data
[i
].packet
= (trace_data
[i
] & 0x78) >> 3;
213 etm_ctx
->trace_data
[i
].flags
= 0;
215 if ((trace_data
[i
] & 0x80) >> 7)
216 etm_ctx
->trace_data
[i
].flags
|= ETMV1_TRACESYNC_CYCLE
;
218 if (etm_ctx
->trace_data
[i
].pipestat
== STAT_TR
) {
219 etm_ctx
->trace_data
[i
].pipestat
= etm_ctx
->trace_data
[i
].packet
& 0x7;
220 etm_ctx
->trace_data
[i
].flags
|= ETMV1_TRIGGER_CYCLE
;
229 static int oocd_trace_start_capture(struct etm_context
*etm_ctx
)
231 struct oocd_trace
*oocd_trace
= etm_ctx
->capture_driver_priv
;
232 uint32_t control
= 0x1; /* 0x1: enabled */
233 uint32_t trigger_count
;
235 if (((etm_ctx
->control
& ETM_PORT_MODE_MASK
) != ETM_PORT_NORMAL
)
236 || ((etm_ctx
->control
& ETM_PORT_WIDTH_MASK
) != ETM_PORT_4BIT
)) {
237 LOG_DEBUG("OpenOCD + trace only supports normal 4-bit ETM mode");
238 return ERROR_ETM_PORTMODE_NOT_SUPPORTED
;
241 if ((etm_ctx
->control
& ETM_PORT_CLOCK_MASK
) == ETM_PORT_HALF_CLOCK
)
242 control
|= 0x2; /* half rate clock, capture at twice the clock rate */
244 /* OpenOCD + trace holds up to 16 million samples,
245 * but trigger counts is set in multiples of 16 */
246 trigger_count
= (1048576 * /* trigger_percent */ 50) / 100;
248 /* capturing always starts at address zero */
249 oocd_trace_write_reg(oocd_trace
, OOCD_TRACE_ADDRESS
, 0x0);
250 oocd_trace_write_reg(oocd_trace
, OOCD_TRACE_TRIGGER_COUNTER
, trigger_count
);
251 oocd_trace_write_reg(oocd_trace
, OOCD_TRACE_CONTROL
, control
);
253 /* we're starting a new trace, initialize capture status */
254 etm_ctx
->capture_status
= TRACE_RUNNING
;
259 static int oocd_trace_stop_capture(struct etm_context
*etm_ctx
)
261 struct oocd_trace
*oocd_trace
= etm_ctx
->capture_driver_priv
;
263 /* trace stopped, just clear running flag, but preserve others */
264 etm_ctx
->capture_status
&= ~TRACE_RUNNING
;
266 oocd_trace_write_reg(oocd_trace
, OOCD_TRACE_CONTROL
, 0x0);
271 COMMAND_HANDLER(handle_oocd_trace_config_command
)
273 struct target
*target
;
277 return ERROR_COMMAND_SYNTAX_ERROR
;
279 target
= get_current_target(CMD_CTX
);
280 arm
= target_to_arm(target
);
282 command_print(CMD_CTX
, "current target isn't an ARM");
287 struct oocd_trace
*oocd_trace
= malloc(sizeof(struct oocd_trace
));
289 arm
->etm
->capture_driver_priv
= oocd_trace
;
290 oocd_trace
->etm_ctx
= arm
->etm
;
292 /* copy name of TTY device used to communicate with OpenOCD + trace */
293 oocd_trace
->tty
= strndup(CMD_ARGV
[1], 256);
295 LOG_ERROR("target has no ETM defined, OpenOCD + trace left unconfigured");
300 COMMAND_HANDLER(handle_oocd_trace_status_command
)
302 struct target
*target
;
304 struct oocd_trace
*oocd_trace
;
307 target
= get_current_target(CMD_CTX
);
309 arm
= target_to_arm(target
);
311 command_print(CMD_CTX
, "current target isn't an ARM");
316 command_print(CMD_CTX
, "current target doesn't have an ETM configured");
320 if (strcmp(arm
->etm
->capture_driver
->name
, "oocd_trace") != 0) {
321 command_print(CMD_CTX
, "current target's ETM capture driver isn't 'oocd_trace'");
325 oocd_trace
= (struct oocd_trace
*)arm
->etm
->capture_driver_priv
;
327 oocd_trace_read_reg(oocd_trace
, OOCD_TRACE_STATUS
, &status
);
330 command_print(CMD_CTX
, "trace clock locked");
332 command_print(CMD_CTX
, "no trace clock");
337 COMMAND_HANDLER(handle_oocd_trace_resync_command
)
339 struct target
*target
;
341 struct oocd_trace
*oocd_trace
;
342 size_t bytes_written
;
343 uint8_t cmd_array
[1];
345 target
= get_current_target(CMD_CTX
);
347 arm
= target_to_arm(target
);
349 command_print(CMD_CTX
, "current target isn't an ARM");
354 command_print(CMD_CTX
, "current target doesn't have an ETM configured");
358 if (strcmp(arm
->etm
->capture_driver
->name
, "oocd_trace") != 0) {
359 command_print(CMD_CTX
, "current target's ETM capture driver isn't 'oocd_trace'");
363 oocd_trace
= (struct oocd_trace
*)arm
->etm
->capture_driver_priv
;
367 bytes_written
= write(oocd_trace
->tty_fd
, cmd_array
, 1);
368 if (bytes_written
< 1)
371 command_print(CMD_CTX
, "requesting traceclock resync");
372 LOG_DEBUG("resyncing traceclk pll");
377 static const struct command_registration oocd_trace_all_command_handlers
[] = {
380 .handler
= handle_oocd_trace_config_command
,
381 .mode
= COMMAND_CONFIG
,
382 .usage
= "<target> <tty>",
386 .handler
= handle_oocd_trace_status_command
,
387 .mode
= COMMAND_EXEC
,
389 .help
= "display OpenOCD + trace status",
393 .handler
= handle_oocd_trace_resync_command
,
394 .mode
= COMMAND_EXEC
,
396 .help
= "resync OpenOCD + trace capture clock",
398 COMMAND_REGISTRATION_DONE
400 static const struct command_registration oocd_trace_command_handlers
[] = {
402 .name
= "oocd_trace",
404 .help
= "OpenOCD trace capture driver command group",
406 .chain
= oocd_trace_all_command_handlers
,
408 COMMAND_REGISTRATION_DONE
411 struct etm_capture_driver oocd_trace_capture_driver
= {
412 .name
= "oocd_trace",
413 .commands
= oocd_trace_command_handlers
,
414 .init
= oocd_trace_init
,
415 .status
= oocd_trace_status
,
416 .start_capture
= oocd_trace_start_capture
,
417 .stop_capture
= oocd_trace_stop_capture
,
418 .read_trace
= oocd_trace_read_trace
,