--- /dev/null
++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.
++
--- /dev/null
+/*
+ * 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;
+}