flash/nor/stm32: Report errors in wait_status_busy
[openocd.git] / contrib / itmdump.c
1 /*
2 * Copyright (C) 2010 by David Brownell
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or (at
7 * your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Simple utility to parse and dump ARM Cortex-M3 SWO trace output. Once the
20 * mechanisms work right, this information can be used for various purposes
21 * including profiling (particularly easy for flat PC-sample profiles) and
22 * for debugging.
23 *
24 * SWO is the Single Wire Output found on some ARM cores, most notably on the
25 * Cortex-M3. It combines data from several sources:
26 *
27 * - Software trace (ITM): so-called "printf-style" application messaging
28 * using "ITM stimulus ports"; and differential timestamps.
29 * - Hardware trace (DWT): for profiling counters and comparator matches.
30 * - TPIU may issue sync packets.
31 *
32 * The trace data format is defined in Appendix E, "Debug ITM and DWT packet
33 * protocol", of the ARMv7-M Architecture Reference Manual (DDI 0403C). It
34 * is a superset of the ITM data format from the Coresight TRM.
35 *
36 * The trace data has two encodings. The working assumption is that data
37 * gets into this program using the UART encoding.
38 */
39
40 #include <errno.h>
41 #include <libgen.h>
42 #include <stdio.h>
43 #include <stdbool.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 unsigned int dump_swit;
48
49 /* Example ITM trace word (0xWWXXYYZZ) parsing for task events, sent
50 * on port 31 (Reserved for "the" RTOS in CMSIS v1.30)
51 * WWXX: event code (0..3 pre-assigned, 4..15 reserved)
52 * YY: task priority
53 * ZZ: task number
54 *
55 * NOTE that this specific encoding could be space-optimized; and that
56 * trace data streams could also be history-sensitive.
57 */
58 static void show_task(int port, unsigned data)
59 {
60 unsigned code = data >> 16;
61 char buf[16];
62
63 if (dump_swit)
64 return;
65
66 switch (code) {
67 case 0:
68 strcpy(buf, "run");
69 break;
70 case 1:
71 strcpy(buf, "block");
72 break;
73 case 2:
74 strcpy(buf, "create");
75 break;
76 case 3:
77 strcpy(buf, "destroy");
78 break;
79 /* 4..15 reserved for other infrastructure ops */
80 default:
81 sprintf(buf, "code %d", code);
82 break;
83 }
84 printf("TASK %d, pri %d: %s",
85 (data >> 0) & 0xff,
86 (data >> 8) & 0xff,
87 buf);
88 }
89
90 static void show_reserved(FILE *f, char *label, int c)
91 {
92 unsigned i;
93
94 if (dump_swit)
95 return;
96
97 printf("%s - %#02x", label, c);
98
99 for (i = 0; (c & 0x80) && i < 4; i++) {
100 c = fgetc(f);
101 if (c == EOF) {
102 printf("(ERROR %d - %s) ", errno, strerror(errno));
103 break;
104 }
105 printf(" %#02x", c);
106 }
107
108 printf("\n");
109 }
110
111 static bool read_varlen(FILE *f, int c, unsigned *value)
112 {
113 unsigned size;
114 unsigned char buf[4];
115
116 *value = 0;
117
118 switch (c & 3) {
119 case 3:
120 size = 4;
121 break;
122 case 2:
123 size = 2;
124 break;
125 case 1:
126 size = 1;
127 break;
128 default:
129 printf("INVALID SIZE\n");
130 return false;
131 }
132
133 memset(buf, 0, sizeof buf);
134 if (fread(buf, 1, size, f) != size)
135 goto err;
136
137 *value = (buf[3] << 24)
138 + (buf[2] << 16)
139 + (buf[1] << 8)
140 + (buf[0] << 0);
141 return true;
142
143 err:
144 printf("(ERROR %d - %s)\n", errno, strerror(errno));
145 return false;
146 }
147
148 static void show_hard(FILE *f, int c)
149 {
150 unsigned type = c >> 3;
151 unsigned value;
152 char *label;
153
154 if (dump_swit)
155 return;
156
157 printf("DWT - ");
158
159 if (!read_varlen(f, c, &value))
160 return;
161 printf("%#x", value);
162
163 switch (type) {
164 case 0: /* event counter wrapping */
165 printf("overflow %s%s%s%s%s%s",
166 (value & (1 << 5)) ? "cyc " : "",
167 (value & (1 << 4)) ? "fold " : "",
168 (value & (1 << 3)) ? "lsu " : "",
169 (value & (1 << 2)) ? "slp " : "",
170 (value & (1 << 1)) ? "exc " : "",
171 (value & (1 << 0)) ? "cpi " : "");
172 break;
173 case 1: /* exception tracing */
174 switch (value >> 12) {
175 case 1:
176 label = "entry to";
177 break;
178 case 2:
179 label = "exit from";
180 break;
181 case 3:
182 label = "return to";
183 break;
184 default:
185 label = "?";
186 break;
187 }
188 printf("%s exception %d", label, value & 0x1ff);
189 break;
190 case 2: /* PC sampling */
191 if (c == 0x15)
192 printf("PC - sleep");
193 else
194 printf("PC - %#08x", value);
195 break;
196 case 8: /* data tracing, pc value */
197 case 10:
198 case 12:
199 case 14:
200 printf("Data trace %d, PC %#08x", (c >> 4) & 3, value);
201 /* optionally followed by data value */
202 break;
203 case 9: /* data tracing, address offset */
204 case 11:
205 case 13:
206 case 15:
207 printf("Data trace %d, address offset %#04x",
208 (c >> 4) & 3, value);
209 /* always followed by data value */
210 break;
211 case 16 ... 23: /* data tracing, data value */
212 printf("Data trace %d, ", (c >> 4) & 3);
213 label = (c & 0x8) ? "write" : "read";
214 switch (c & 3) {
215 case 3:
216 printf("word %s, value %#08x", label, value);
217 break;
218 case 2:
219 printf("halfword %s, value %#04x", label, value);
220 break;
221 case 1:
222 printf("byte %s, value %#02x", label, value);
223 break;
224 }
225 break;
226 default:
227 printf("UNDEFINED, rawtype: %x", type);
228 break;
229 }
230
231 printf("\n");
232 return;
233 }
234
235 /*
236 * Table of SWIT (SoftWare InstrumentTation) message dump formats, for
237 * ITM port 0..31 application data.
238 *
239 * Eventually this should be customizable; all usage is application defined.
240 *
241 * REVISIT there can be up to 256 trace ports, via "ITM Extension" packets
242 */
243 struct {
244 int port;
245 void (*show)(int port, unsigned data);
246 } format[] = {
247 { .port = 31, .show = show_task, },
248 };
249
250 static void show_swit(FILE *f, int c)
251 {
252 unsigned port = c >> 3;
253 unsigned value = 0;
254 unsigned i;
255
256 if (port + 1 == dump_swit) {
257 if (!read_varlen(f, c, &value))
258 return;
259 printf("%c", value);
260 return;
261 }
262
263 if (!read_varlen(f, c, &value))
264 return;
265
266 if (dump_swit)
267 return;
268
269 printf("SWIT %u - ", port);
270
271 printf("%#08x", value);
272
273 for (i = 0; i < sizeof(format) / sizeof(format[0]); i++) {
274 if (format[i].port == port) {
275 printf(", ");
276 format[i].show(port, value);
277 break;
278 }
279 }
280
281 printf("\n");
282 return;
283 }
284
285 static void show_timestamp(FILE *f, int c)
286 {
287 unsigned counter = 0;
288 char *label = "";
289 bool delayed = false;
290
291 if (dump_swit)
292 return;
293
294 printf("TIMESTAMP - ");
295
296 /* Format 2: header only */
297 if (!(c & 0x80)) {
298 switch (c) {
299 case 0: /* sync packet -- coding error! */
300 case 0x70: /* overflow -- ditto! */
301 printf("ERROR - %#02x\n", c);
302 break;
303 default:
304 /* synchronous to ITM */
305 counter = c >> 4;
306 goto done;
307 }
308 return;
309 }
310
311 /* Format 1: one to four bytes of data too */
312 switch (c >> 4) {
313 default:
314 label = ", reserved control\n";
315 break;
316 case 0xc:
317 /* synchronous to ITM */
318 break;
319 case 0xd:
320 label = ", timestamp delayed";
321 delayed = true;
322 break;
323 case 0xe:
324 label = ", packet delayed";
325 delayed = true;
326 break;
327 case 0xf:
328 label = ", packet and timetamp delayed";
329 delayed = true;
330 break;
331 }
332
333 c = fgetc(f);
334 if (c == EOF)
335 goto err;
336 counter = c & 0x7f;
337 if (!(c & 0x80))
338 goto done;
339
340 c = fgetc(f);
341 if (c == EOF)
342 goto err;
343 counter |= (c & 0x7f) << 7;
344 if (!(c & 0x80))
345 goto done;
346
347 c = fgetc(f);
348 if (c == EOF)
349 goto err;
350 counter |= (c & 0x7f) << 14;
351 if (!(c & 0x80))
352 goto done;
353
354 c = fgetc(f);
355 if (c == EOF)
356 goto err;
357 counter |= (c & 0x7f) << 21;
358
359 done:
360 /* REVISIT should we try to convert from delta values? */
361 printf("+%u%s\n", counter, label);
362 return;
363
364 err:
365 printf("(ERROR %d - %s) ", errno, strerror(errno));
366 goto done;
367 }
368
369 int main(int argc, char **argv)
370 {
371 FILE *f = stdin;
372 int c;
373
374 /* parse arguments */
375 while ((c = getopt(argc, argv, "f:d:")) != EOF) {
376 switch (c) {
377 case 'f':
378 /* e.g. from UART connected to /dev/ttyUSB0 */
379 f = fopen(optarg, "r");
380 if (!f) {
381 perror(optarg);
382 return 1;
383 }
384 break;
385 case 'd':
386 dump_swit = atoi(optarg);
387 break;
388 default:
389 fprintf(stderr, "usage: %s [-f input]",
390 basename(argv[0]));
391 return 1;
392 }
393 }
394
395 /* Parse data ... records have a header then data bytes.
396 * NOTE: we assume getc() deals in 8-bit bytes.
397 */
398 bool overflow = false;
399
400 while ((c = getc(f)) != EOF) {
401
402 /* Sync packet ... 7 zeroes, 0x80 */
403 if (c == 0) {
404 int i;
405
406 for (i = 0; i < 6; i++) {
407 c = fgetc(f);
408 if (c == EOF)
409 break;
410 if (c != 0)
411 goto bad_sync;
412 }
413 c = fgetc(f);
414 if (c == 0x80) {
415 printf("SYNC\n");
416 continue;
417 }
418 bad_sync:
419 printf("BAD SYNC\n");
420 continue;
421 }
422
423 /* Overflow packet */
424 if (c == 0x70) {
425 /* REVISIT later, report just what overflowed!
426 * Timestamp and SWIT can happen. Non-ITM too?
427 */
428 overflow = true;
429 printf("OVERFLOW ...\n");
430 continue;
431 }
432 overflow = false;
433
434 switch (c & 0x0f) {
435 case 0x00: /* Timestamp */
436 show_timestamp(f, c);
437 break;
438 case 0x04: /* "Reserved" */
439 show_reserved(f, "RESERVED", c);
440 break;
441 case 0x08: /* ITM Extension */
442 /* FIXME someday, handle these ... */
443 show_reserved(f, "ITM EXT", c);
444 break;
445 case 0x0c: /* DWT Extension */
446 show_reserved(f, "DWT EXT", c);
447 break;
448 default:
449 if (c & 4)
450 show_hard(f, c);
451 else
452 show_swit(f, c);
453 break;
454 }
455
456 }
457
458 return 0;
459 }