present CM3 Trace agenda
authorDavid Brownell <david-b@pacbell.net>
Sun, 9 Jan 2011 02:11:12 +0000 (18:11 -0800)
committerØyvind Harboe <oyvind.harboe@zylin.com>
Sun, 9 Jan 2011 20:14:57 +0000 (21:14 +0100)
contrib/coresight-trace.txt [new file with mode: 0644]
contrib/itmdump.c [new file with mode: 0644]

diff --git a/contrib/coresight-trace.txt b/contrib/coresight-trace.txt
new file mode 100644 (file)
index 0000000..c093c20
--- /dev/null
@@ -0,0 +1,68 @@
++OpenOCD and CoreSight Tracing
++
+Many recent ARM chips  (Using e..g. Cortex-M3 and
+Cortex-M4 cores) support CoreSight debug/trace.
+This note sketches an approach currently planned for those cores
+with OpenOCD.
+
+ This tracing data can help debug and tune ARM software, but not
+all cores support tracing.  Some support more extensive tracing
+other cores with trace support +should be able to use the same
+approach and maybe some of the same analysis code.
+
++the Cortex-M3 is assumed here to be the
++core in use, for simplicity and to reflect current OpenOCD users.
+
+
+This note summarizes a software model to generate, collect, and
+analyze such trace data .  That is not fully implemented as of early
+January 2011, +and thus is not *yet* usable.
++
++
++Some microcontroller cores support a low pin-count Single-wire trace,
+with a mode where +trace data is emitted (usually to a UART.  To use
+this mode, +SWD must be in use.
++At this writing, OpenOCD SWD support is not yet complete either.
+
+(There are also multi-wire trace ports requiring more complex debug
+adapters than OpenOCD currently supports, and offering richer data.
++
++
++* ENABLING involves activating  SWD and (single wire) trace.
++
++current expectations are that OpenOCD itself will handle enabling;
+activating single wire trace involves a debug adapter interaction, and
+collecting that trace data requires particular (re)wiring.
++
++* CONFIGURATION involves setting up ITM  and/or ETM modules to emit the
++desired data from the Cortex core.  (This might include dumping
++event counters printf-style messages; code profiling; and more.  Not all
++cores offer the same trace capabilities.
++
++current expectations are that Tcl scripts will be used to configure these
++modules for the desired tracing, by direct writes to registers.  In some
++cases (as with RTOS event tracking and similar messaging, this might
++be  augmented or replaced by user code running on the ARM core.
++
++COLLECTION involves reading that trace data, probably through UART, and
++saving it in a useful format to analyse  For now, deferred analysis modes
+are assumed, not than real-time or interactive ones.
++
++
++current expectations are to to dump data in text using contrib/itmdump.c
++or derived tools, and to post-process it into reports.  Such reports might
++include program messaging (such as application data streams via ITM, maybe
++using printf type messaging; code coverage analysis or so forth.  Recent
++versions of CMSIS software reserve some ITM codespace for RTOS  event
+tracing and include ITM messaging support.
+Clearly some of that data would be valuable for interactive debugging.
++
++Should someone get ambitious, GUI reports should be possible.  GNU tools
++for simpler reports like gprof may be simpler to support at first.
++In any case, OpenOCD is not currently GUI-oriented.  Accordingly, we now
++expect any such graphics to come from postprocessing.
+
+ measurments for RTOS event timings should also be easy to collect.
++Examples include context and message switch times, as well as times
+for application interactions.
++
diff --git a/contrib/itmdump.c b/contrib/itmdump.c
new file mode 100644 (file)
index 0000000..1c484d0
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2010 by David Brownell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Simple utility to parse and dump ARM Cortex-M3 SWO trace output.  Once the
+ * mechanisms work right, this information can be used for various purposes
+ * including profiling (particularly easy for flat PC-sample profiles) and
+ * for debugging.
+ *
+ * SWO is the Single Wire Output found on some ARM cores, most notably on the
+ * Cortex-M3.  It combines data from several sources:
+ *
+ *  - Software trace (ITM):  so-called "printf-style" application messaging
+ *    using "ITM stimulus ports"; and differential timestamps.
+ *  - Hardware trace (DWT):  for profiling counters and comparator matches.
+ *  - TPIU may issue sync packets.
+ *
+ * The trace data format is defined in Appendix E, "Debug ITM and DWT packet
+ * protocol", of the ARMv7-M Architecture Reference Manual (DDI 0403C).  It
+ * is a superset of the ITM data format from the Coresight TRM.
+ *
+ * The trace data has two encodings.  The working assumption is that data
+ * gets into this program using the UART encoding.
+ */
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+
+/* Example ITM trace word (0xWWXXYYZZ) parsing for task events, sent
+ * on port 31 (Reserved for "the" RTOS in CMSIS v1.30)
+ *   WWXX: event code (0..3 pre-assigned, 4..15 reserved)
+ *   YY:   task priority
+ *   ZZ:   task number
+ *
+ * NOTE that this specific encoding could be space-optimized; and that
+ * trace data streams could also be history-sensitive.
+ */
+static void show_task(int port, unsigned data)
+{
+       unsigned code = data >> 16;
+       char buf[16];
+
+       switch (code) {
+       case 0:
+               strcpy(buf, "run");
+               break;
+       case 1:
+               strcpy(buf, "block");
+               break;
+       case 2:
+               strcpy(buf, "create");
+               break;
+       case 3:
+               strcpy(buf, "destroy");
+               break;
+       /* 4..15 reserved for other infrastructure ops */
+       default:
+               sprintf(buf, "code %d", code);
+               break;
+       }
+       printf("TASK %d, pri %d: %s",
+               (data >> 0) & 0xff,
+               (data >> 8) & 0xff,
+               buf);
+}
+
+static void show_reserved(FILE *f, char *label, int c)
+{
+       unsigned i;
+
+       printf("%s - %#02x", label, c);
+
+       for (i = 0; (c & 0x80) && i < 4; i++) {
+               c = fgetc(f);
+               if (c == EOF) {
+                       printf("(ERROR %d - %s) ", errno, strerror(errno));
+                       break;
+               }
+               printf(" %#02x", c);
+       }
+
+       printf("\n");
+}
+
+static bool read_varlen(FILE *f, int c, unsigned *value)
+{
+       unsigned size;
+       unsigned char buf[4];
+       unsigned i;
+
+       *value = 0;
+
+       switch (c & 3) {
+       case 3:
+               size = 4;
+               break;
+       case 2:
+               size = 2;
+               break;
+       case 1:
+               size = 1;
+               break;
+       default:
+               printf("INVALID SIZE\n");
+               return false;
+       }
+
+       memset(buf, 0, sizeof buf);
+       if (fread(buf, 1, size, f) != size)
+               goto err;
+
+       *value =  (buf[3] << 24)
+               + (buf[2] << 16)
+               + (buf[2] << 8)
+               + (buf[0] << 0);
+       return true;
+
+err:
+       printf("(ERROR %d - %s)\n", errno, strerror(errno));
+       return;
+}
+
+static void show_hard(FILE *f, int c)
+{
+       unsigned type = c >> 3;
+       unsigned value;
+       unsigned size;
+       char *label;
+
+       printf("DWT - ", type);
+
+       if (!read_varlen(f, c, &value))
+               return;
+       printf("%#x", value);
+
+       switch (type) {
+       case 0:                         /* event counter wrapping */
+               printf("overflow %s%s%s%s%s%s",
+                       (value & (1 << 5)) ? "cyc " : "",
+                       (value & (1 << 4)) ? "fold " : "",
+                       (value & (1 << 3)) ? "lsu " : "",
+                       (value & (1 << 2)) ? "slp " : "",
+                       (value & (1 << 1)) ? "exc " : "",
+                       (value & (1 << 0)) ? "cpi " : "");
+               break;
+       case 1:                         /* exception tracing */
+               switch (value >> 12) {
+               case 1:
+                       label = "entry to";
+                       break;
+               case 2:
+                       label = "exit from";
+                       break;
+               case 3:
+                       label = "return to";
+                       break;
+               default:
+                       label = "?";
+                       break;
+               }
+               printf("%s exception %d", label, value & 0x1ff);
+               break;
+       case 2:                         /* PC sampling */
+               if (c == 0x15)
+                       printf("PC - sleep");
+               else
+                       printf("PC - %#08x", value);
+               break;
+       case 8:                         /* data tracing, pc value */
+       case 10:
+       case 12:
+       case 14:
+               printf("Data trace %d, PC %#08x", (c >> 4) & 3, value);
+               /* optionally followed by data value */
+               break;
+       case 9:                         /* data tracing, address offset */
+       case 11:
+       case 13:
+       case 15:
+               printf("Data trace %d, address offset %#04x",
+                               (c >> 4) & 3, value);
+               /* always followed by data value */
+               break;
+       case 16 ... 23:                 /* data tracing, data value */
+               printf("Data trace %d, ", (c >> 4) & 3);
+               label = (c & 0x8) ? "write" : "read";
+               switch (c & 3) {
+               case 3:
+                       printf("word %s, value %#08x", label, value);
+                       break;
+               case 2:
+                       printf("halfword %s, value %#04x", label, value);
+                       break;
+               case 1:
+                       printf("byte %s, value %#02x", label, value);
+                       break;
+               }
+               break;
+       default:
+               printf("UNDEFINED");
+               break;
+       }
+
+       printf("\n");
+       return;
+}
+
+/*
+ * Table of SWIT (SoftWare InstrumentTation) message dump formats, for
+ * ITM port 0..31 application data.
+ *
+ * Eventually this should be customizable; all usage is application defined.
+ *
+ * REVISIT there can be up to 256 trace ports, via "ITM Extension" packets
+ */
+struct {
+       int port;
+       void (*show)(int port, unsigned data);
+} format[] = {
+       { .port = 31,  .show = show_task, },
+};
+
+static void show_swit(FILE *f, int c)
+{
+       unsigned size;
+       unsigned port = c >> 3;
+       unsigned char buf[4];
+       unsigned value = 0;
+       unsigned i;
+
+       printf("SWIT %u - ", port);
+
+       if (!read_varlen(f, c, &value))
+               return;
+       printf("%#08x", value);
+
+       for (i = 0; i <= sizeof(format) / sizeof(format[0]); i++) {
+               if (format[i].port == port) {
+                       printf(", ");
+                       format[i].show(port, value);
+                       break;
+               }
+       }
+
+       printf("\n");
+       return;
+
+err:
+       printf("(ERROR %d - %s)\n", errno, strerror(errno));
+       return;
+}
+
+static void show_timestamp(FILE *f, int c)
+{
+       unsigned counter = 0;
+       char *label = "";
+       bool delayed = false;
+
+       printf("TIMESTAMP - ");
+
+       /* Format 2: header only */
+       if (!(c & 0x80)) {
+               switch (c) {
+               case 0:         /* sync packet -- coding error! */
+               case 0x70:      /* overflow -- ditto! */
+                       printf("ERROR - %#02x\n", c);
+                       break;
+               default:
+                       /* synchronous to ITM */
+                       counter = c >> 4;
+                       goto done;
+               }
+               return;
+       }
+
+       /* Format 1:  one to four bytes of data too */
+       switch (c) {
+       default:
+               label = ", reserved control\n";
+               break;
+       case 0xc:
+               /* synchronous to ITM */
+               break;
+       case 0xd:
+               label = ", timestamp delayed";
+               delayed = true;
+               break;
+       case 0xe:
+               label = ", packet delayed";
+               delayed = true;
+               break;
+       case 0xf:
+               label = ", packet and timetamp delayed";
+               delayed = true;
+               break;
+       }
+
+       c = fgetc(f);
+       if (c == EOF)
+               goto err;
+       counter = c & 0x7f;
+       if (!(c & 0x80))
+               goto done;
+
+       c = fgetc(f);
+       if (c == EOF)
+               goto err;
+       counter |= (c & 0x7f) << 7;
+       if (!(c & 0x80))
+               goto done;
+
+       c = fgetc(f);
+       if (c == EOF)
+               goto err;
+       counter |= (c & 0x7f) << 14;
+       if (!(c & 0x80))
+               goto done;
+
+       c = fgetc(f);
+       if (c == EOF)
+               goto err;
+       counter |= (c & 0x7f) << 21;
+
+done:
+       /* REVISIT should we try to convert from delta values?  */
+       printf("+%u%s\n", counter, label);
+       return;
+
+err:
+       printf("(ERROR %d - %s) ", errno, strerror(errno));
+       goto done;
+}
+
+int main(int argc, char **argv)
+{
+       FILE *f = stdin;
+       int c;
+
+       /* parse arguments */
+       while ((c = getopt(argc, argv, "f:")) != EOF) {
+               switch (c) {
+               case 'f':
+                       /* e.g. from UART connected to /dev/ttyUSB0 */
+                       f = fopen(optarg, "r");
+                       if (!f) {
+                               perror(optarg);
+                               return 1;
+                       }
+                       break;
+               default:
+usage:
+                       fprintf(stderr, "usage: %s [-f input]",
+                               basename(argv[0]));
+                       return 1;
+               }
+       }
+
+       /* Parse data ... records have a header then data bytes.
+        * NOTE: we assume getc() deals in 8-bit bytes.
+        */
+       bool overflow = false;
+
+       while ((c = getc(f)) != EOF) {
+
+               /* Sync packet ... 7 zeroes, 0x80 */
+               if (c == 0) {
+                       int i;
+
+                       for (i = 0; i < 6; i++) {
+                               c = fgetc(f);
+                               if (c == EOF)
+                                       break;
+                               if (c != 0)
+                                       goto bad_sync;
+                       }
+                       c = fgetc(f);
+                       if (c == 0x80) {
+                               printf("SYNC\n");
+                               continue;
+                       }
+bad_sync:
+                       printf("BAD SYNC\n");
+                       continue;
+               }
+
+               /* Overflow packet */
+               if (c == 0x70) {
+                       /* REVISIT later, report just what overflowed!
+                        * Timestamp and SWIT can happen.  Non-ITM too?
+                        */
+                       overflow = true;
+                       printf("OVERFLOW ...\n");
+                       continue;
+               }
+               overflow = false;
+
+               switch (c & 0x0f) {
+               case 0x00:              /* Timestamp */
+                       show_timestamp(f, c);
+                       break;
+               case 0x04:              /* "Reserved" */
+                       show_reserved(f, "RESERVED", c);
+                       break;
+               case 0x08:              /* ITM Extension */
+                       /* FIXME someday, handle these ...  */
+                       show_reserved(f, "ITM EXT", c);
+                       break;
+               case 0x0c:              /* DWT Extension */
+                       show_reserved(f, "DWT EXT", c);
+                       break;
+               default:
+                       if (c & 4)
+                               show_hard(f, c);
+                       else
+                               show_swit(f, c);
+                       break;
+               }
+
+       }
+
+       return 0;
+}