This is an automated email from Gerrit.

James G. Smith ([email protected]) just uploaded a new patch set to 
Gerrit, which you can find at http://openocd.zylin.com/1662

-- gerrit

commit 321a74734cefcc7d0d366367bb228b0898eaae93
Author: James G. Smith <[email protected]>
Date:   Fri Sep 27 09:49:26 2013 +0100

    cortex_m: TPIU ITM/DWT processing and telnet delivery
    
    New feature. Add ability to route data from ITM/DWT to configured
    telnet ports.
    
    This patch is far from being ready for mainline inclusion, since (as
    commented inline) most of the work is being done at the wrong point in
    the source tree. The major parts of this new feature really needs a
    discussion with the OpenOCD maintainers and wider community about
    "how", "where" and "if". If the fundamental concept is sound, I expect
    many patch revisions before it can be accepted, but just wanted my
    "work-in-progress" to be available for discussion. Where in the
    target/adapter/driver world this needs to be situated is one of the
    first questions. It would be easy to extend the recently added "trace"
    support and limit the functionality purely to the "stlink_usb.c"
    world, but most of the functionality is actually generic for all
    ARM/Cortex parts capable of providing ITM and DWT output. How the
    driver can access the target state cleanly needs a design
    decision. This working patch gets around that by holding a handle onto
    the target, but this is not clean. In reality the "target_type"
    structure (and "hl_layout_api_s" in the case of ST-LINK/V2) should
    really provide an API that higher-level generic TPIU/ITM/DWT support
    can use (I originally had that in an earlier patch prior to noticing and
    integrating with the "trace" support that is in the mainline).
    
    This patch fundamentally adds a new optional initialisation command
    "itm", which can be used to route specific ITM stimulus ports to
    specific telnet ports. For example, this can be used to provide the
    diagnostic "printf()" style output to appear at a known network
    location, without the need for post-execution parsing of a simple raw
    binary TPIU capture.
    
    NOTE: This patch also tweaks the ST-LINK/V2 TPIU setup to allow for
    the correct stimulus ports to be enabled based on the configuration.
    
    To use, for example, the STM32F4DISCOVERY configuration file I have uses:
      trace 168000000
      # By default all stimulus ports are disabled, so we need to explicitly
      # enable the ports we wish to capture/monitor:
      itm stimport 31 4601
      itm stimport 24 4602 capture
      # The following is an example of how a port could be disabled (if it
      # was enabled by default):
      # itm stimport 12 disabled
      ...
      proc local_attach_proc { } {
        reset halt
        # STM32Fxx specific enable:
        mww 0xE0042004 0x00000027
      }
    
    A simple "telnet <ip> 4601" directed at the host running OpenOCD can
    then be left running to capture diagnostic output over the lifetime of
    the OpenOCD session.
    
    Background: We use this support within our company since we have a
    remote test infrastructure, and it is easier dealing with gathering
    trace and diagnostic information in real-time via network ports. It
    also makes it easier to integrate diagnostic and trace capture with
    3rd-party tools (e.g. Eclipse) during application debugging.
    
    Change-Id: I2c2c7eb2782518ca0b83f0ba1448dd9e75dbdcdb
    Signed-off-by: James G. Smith <[email protected]>

diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c
index 070afa3..b111988 100644
--- a/src/jtag/drivers/stlink_usb.c
+++ b/src/jtag/drivers/stlink_usb.c
@@ -101,6 +101,8 @@ struct stlink_usb_handle_s {
        uint16_t pid;
        /** this is the currently used jtag api */
        enum stlink_jtag_api_version jtag_api;
+       /** hook for target supplied trace processing */
+       struct target *target;
        /** */
        struct {
                /** whether SWO tracing is enabled or not */
@@ -860,6 +862,11 @@ static int stlink_usb_trace_read(void *handle)
                                                if (fwrite(buf, 1, size, 
h->trace.output_f) > 0)
                                                        
fflush(h->trace.output_f);
                                        }
+                                       /* Pass data to local ITM processing: */
+                                       {
+                                               extern void 
trace_process(struct target *target, uint8_t *buf, size_t size);
+                                               trace_process(h->target, buf, 
size);
+                                       }
                                }
                        }
                }
@@ -1018,15 +1025,41 @@ static int stlink_configure_target_trace_port(void 
*handle)
        if (res != ERROR_OK)
                goto out;
        /* enable trace with ATB ID 1 */
-       res = stlink_usb_write_debug_reg(handle, ITM_TCR, 
(1<<16)|(1<<0)|(1<<2));
+       uint32_t tcrval = ((1<<16) | (1<<0));
+       /* TODO:FIXME: Ideally we should not need to be passing in references to
+        * the target or armv7m structures. However we need to reference the
+        * top-level (dynamic) TPIU/ITM/DWT configuration. */
+       struct armv7m_common *armv7m = target_to_armv7m(h->target);
+       assert(armv7m != NULL);
+       if (armv7m->tpiusync)
+               tcrval |= (1 << 2);
+       if (armv7m->dwt)
+               tcrval |= (1 << 3);
+       res = stlink_usb_write_debug_reg(handle, ITM_TCR, tcrval);
        if (res != ERROR_OK)
                goto out;
-       /* trace privilege */
-       res = stlink_usb_write_debug_reg(handle, ITM_TPR, 1);
+       /* NOTE: The ITM stimulus world can support upto 256 ports, but this
+        * implementation is currently limited to the initial 32 ports. */
+       uint32_t portmask = 0x00000000;
+       unsigned int idx;
+       for (idx = 0; (idx < 32); idx++) {
+               if (armv7m->itm_stimport_service[idx])
+                       portmask |= (1 << idx);
+       }
+       /* If no "itm stimport" configurations specified then default to only
+        * enabling stimulus port 0. */
+       if (portmask == 0x00000000)
+               portmask = (1 << 0);
+       /* trace privilege - currently only for ports 0..31 */
+       uint32_t privmask = 0x00000000;
+       for (idx = 0; (idx < 4); idx++) {
+               if (portmask & (0xFF << (8 * idx)))
+                       privmask |= (1 << idx);
+       }
+       res = stlink_usb_write_debug_reg(handle, ITM_TPR, privmask);
        if (res != ERROR_OK)
                goto out;
-       /* trace port enable (port 0) */
-       res = stlink_usb_write_debug_reg(handle, ITM_TER, (1<<0));
+       res = stlink_usb_write_debug_reg(handle, ITM_TER, portmask);
        if (res != ERROR_OK)
                goto out;
 
@@ -1072,6 +1105,12 @@ static int stlink_usb_trace_enable(void *handle)
        if (h->version.jtag >= STLINK_TRACE_MIN_VERSION) {
                uint32_t trace_hz;
 
+               /* TODO: This "stlink_configure_target_trace_port()" function
+                * should really be provided by the higher level TPIU/ITM/DWT
+                * support code using generic target reg read/write support (and
+                * it should also attach the periodic callback (done below) to
+                * the higher level code). However, as before, we would need a
+                * handle onto the relevant context. */
                res = stlink_configure_target_trace_port(handle);
                if (res != ERROR_OK)
                        LOG_ERROR("Unable to configure tracing on target\n");
@@ -1094,6 +1133,7 @@ static int stlink_usb_trace_enable(void *handle)
                if (res == ERROR_OK)  {
                        h->trace.enabled = true;
                        LOG_DEBUG("Tracing: recording at %uHz\n", trace_hz);
+
                        /* We need the trace read function to be called at a
                         * high-enough frequency to ensure reasonable
                         * "timeliness" in processing ITM/DWT data. */
@@ -1746,6 +1786,7 @@ static int stlink_usb_open(struct hl_interface_param_s 
*param, void **fd)
                prescale = param->trace_source_hz > STLINK_TRACE_MAX_HZ ?
                        (param->trace_source_hz / STLINK_TRACE_MAX_HZ) - 1 : 0;
 
+               h->target = param->target;
                h->trace.output_f = param->trace_f;
                h->trace.source_hz = param->trace_source_hz;
                h->trace.prescale = prescale;
diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c
index 50ce47d..ad42307 100644
--- a/src/jtag/hla/hla_interface.c
+++ b/src/jtag/hla/hla_interface.c
@@ -37,14 +37,16 @@
 
 #include <target/target.h>
 
-static struct hl_interface_s hl_if = { {0, 0, 0, 0, 0, HL_TRANSPORT_UNKNOWN, 
false, NULL, 0}, 0, 0 };
+static struct hl_interface_s hl_if = { {0, 0, 0, 0, 0, HL_TRANSPORT_UNKNOWN, 
false, NULL, 0, NULL}, 0, 0 };
 
-int hl_interface_open(enum hl_transports tr)
+int hl_interface_open(enum hl_transports tr, struct target *t)
 {
        LOG_DEBUG("hl_interface_open");
 
        enum reset_types jtag_reset_config = jtag_get_reset_config();
 
+       hl_if.param.target = t;
+
        if (jtag_reset_config & RESET_CNCT_UNDER_SRST) {
                if (jtag_reset_config & RESET_SRST_NO_GATING)
                        hl_if.param.connect_under_reset = true;
diff --git a/src/jtag/hla/hla_interface.h b/src/jtag/hla/hla_interface.h
index fcf1d9e..3c2980a 100644
--- a/src/jtag/hla/hla_interface.h
+++ b/src/jtag/hla/hla_interface.h
@@ -50,6 +50,8 @@ struct hl_interface_param_s {
        FILE *trace_f;
        /** Trace module source clock rate */
        uint32_t trace_source_hz;
+       /** Reference target for local trace processing */
+       struct target *target;
 };
 
 struct hl_interface_s {
@@ -62,7 +64,7 @@ struct hl_interface_s {
 };
 
 /** */
-int hl_interface_open(enum hl_transports tr);
+int hl_interface_open(enum hl_transports tr, struct target *t);
 /** */
 
 int hl_interface_init_target(struct target *t);
diff --git a/src/jtag/hla/hla_transport.c b/src/jtag/hla/hla_transport.c
index e3c003d..35b4d28 100644
--- a/src/jtag/hla/hla_transport.c
+++ b/src/jtag/hla/hla_transport.c
@@ -174,7 +174,7 @@ static int hl_transport_init(struct command_context 
*cmd_ctx)
        else if (strcmp(transport->name, "stlink_swim") == 0)
                tr = HL_TRANSPORT_SWIM;
 
-       int retval = hl_interface_open(tr);
+       int retval = hl_interface_open(tr, t);
 
        if (retval != ERROR_OK)
                return retval;
diff --git a/src/target/armv7m.h b/src/target/armv7m.h
index 92bada0..9371056 100644
--- a/src/target/armv7m.h
+++ b/src/target/armv7m.h
@@ -137,6 +137,54 @@ enum {
 
 #define ARMV7M_COMMON_MAGIC 0x2A452A45
 
+#define ITM_MAX_STIMPORT (256)
+
+#define BIT_(__n) (1<<(__n))
+#define MASK_(__n, __s) (((1<<(__s))-1)<<(__n))
+#define VALUE_(__n, __v) ((__v)<<(__n))
+
+#define ITMDWTPP_SYNC (0x00) /* Terminated by single set bit after at least 47 
bits. */
+#define ITMDWTPP_OVERFLOW (0x70) /* PROTOCOL byte with 0-bytes of payload */
+
+/* Least-significant 2-bits decode PROTOCOL or SOURCE packets */
+#define ITMDWTPP_TYPE_MASK MASK_(0, 2)
+#define ITMDWTPP_TYPE_PROTOCOL VALUE_(0, 0)
+#define ITMDWTPP_TYPE_SOURCE1 VALUE_(0, 1)
+#define ITMDWTPP_TYPE_SOURCE2 VALUE_(0, 2)
+#define ITMDWTPP_TYPE_SOURCE4 VALUE_(0, 3)
+
+#define ITMDWTPP_PROTOCOL_EXTENSION BIT_(3) /* extension */
+#define ITMDWTPP_SOURCE_SELECTION BIT_(2)
+#define ITMDWTPP_SOURCE_SHIFT (3)
+#define ITMDWTPP_SOURCE_MASK MASK_(ITMDWTPP_SOURCE_SHIFT, 5)
+
+#define ITMDWTPP_PROTOCOL_EXT_ITM_PAGE_SHIFT (4)
+#define ITMDWTPP_PROTOCOL_EXT_ITM_PAGE_MASK 
MASK_(ITMDWTPP_PROTOCOL_EXT_ITM_PAGE_SHIFT, 3)
+
+typedef enum {
+       /* Waiting for header byte. This MUST be zero for initial starting
+        * state: */
+       PKTFSM_HDR = 0,
+       /* ITM (instrumentation) */
+       PKTFSM_ITM1,
+       PKTFSM_ITM2,
+       PKTFSM_ITM3,
+       PKTFSM_ITM4,
+       /* DWT (hardware) */
+       PKTFSM_DWT1,
+       PKTFSM_DWT2,
+       PKTFSM_DWT3,
+       PKTFSM_DWT4,
+} tpiu_pktfsm_e;
+
+/* Context for decoding TPIU stream split across multiple "capture" buffers. */
+struct armv7m_tpiu_pktstate {
+       tpiu_pktfsm_e fsm; /* next state to be processed */
+       unsigned int pcode; /* private packet type data which depends on "fsm" 
being processed */
+       unsigned int pdata; /* private packet type data which depends on "fsm" 
being processed */
+       unsigned char size; /* non-zero indicates number of packet data bytes 
expected */
+};
+
 struct armv7m_common {
        struct arm      arm;
 
@@ -150,6 +198,25 @@ struct armv7m_common {
        /* stlink is a high level adapter, does not support all functions */
        bool stlink;
 
+       /* TODO:DECIDE: This may not be the best place to hang the service
+        * references; but it will suffice for initial testing. We should
+        * probably add a new source file "itm.c" to encapsulate the generic
+        * parts of ITM support. The issue is then referencing the relevant
+        * holding structure from within the interface handling code. At the
+        * moment the "target" is not available by default.
+        *
+        * NOTES: If BUILD_OOCD_TRACE is defined then the oocd_trace.[ch]
+        * support is included by "etm.c". */
+       /* We just have a simple 256 entry vector (on this resource rich host)
+        * since it is easier to manage the services attached, rather than
+        * maintaining a dynamic chain that would need to be scanned when
+        * processing. */
+       void *itm_stimport_service[ITM_MAX_STIMPORT];
+       void *tpiu_packet_service;
+       struct armv7m_tpiu_pktstate tpiu_pktstate;
+       bool tpiusync;
+       bool dwt;
+
        /* Direct processor core register read and writes */
        int (*load_core_reg_u32)(struct target *target, uint32_t num, uint32_t 
*value);
        int (*store_core_reg_u32)(struct target *target, uint32_t num, uint32_t 
value);
diff --git a/src/target/hla_target.c b/src/target/hla_target.c
index a65ba80..f55b655 100644
--- a/src/target/hla_target.c
+++ b/src/target/hla_target.c
@@ -39,6 +39,7 @@
 #include "armv7m.h"
 #include "cortex_m.h"
 #include "arm_semihosting.h"
+#include "server/server.h"
 #include "target_request.h"
 
 #define savedDCRDR  dbgbase  /* FIXME: using target->dbgbase to preserve DCRDR 
*/
@@ -351,6 +352,628 @@ static int adapter_init_arch_info(struct target *target,
        return ERROR_OK;
 }
 
+/* TODO:CONSIDER: In reality this ITM stimulus port "console" support should be
+ * generic (part of armv7m maybe) and not tied to this HLA interface support. 
In
+ * fact the "telnet" connection support source should probably be somewhere 
like
+ * "src/server/itm_console.c" or even a generic "console" support file, since
+ * there is NOTHING ITM specific about it as regards being given characters to
+ * output. The aim is that "nc <ipaddr> <port>" should be useable to capture
+ * binary data output on a "console" stream. */
+
+static const char *itm_packet_port;
+static const char *itm_stimport_network[ITM_MAX_STIMPORT];
+#if ((ITM_MAX_STIMPORT % 32) != 0)
+# error ITM maximum stimulus port count should be a multiple of 32
+#endif
+/* Bitmap of whether we are capturing the specific stimport output to a file: 
*/
+static uint32_t itm_stimport_capture[ITM_MAX_STIMPORT / 32];
+
+struct itm_stimport_connection {
+       struct connection *connection; /* back reference to parent */
+       int closed;
+};
+
+struct itm_stimport_service {
+       struct itm_stimport_connection *cc;
+       uint8_t stimport;
+       int do_capture;
+       FILE *cFile;
+       /* CONSIDER:IMPLEMENT: We could hold a buffer of data received since the
+        * "last target TPIU reset", where we can supply buffered data to any
+        * new connection. We do not actually need this since we can just rely
+        * on the user attaching to the relevant telnet port prior to executing
+        * their application/test. We already have support for saving all of the
+        * captured trace output to a file for later processing if needed. */
+};
+
+static int itm_stimport_write(struct connection *connection, const void *data, 
int len)
+{
+       struct itm_stimport_connection *cc = connection->priv;
+
+       if (cc) {
+               if (cc->closed)
+                       return ERROR_SERVER_REMOTE_CLOSED;
+
+               if (connection_write(connection, data, len) == len)
+                       return ERROR_OK;
+
+               cc->closed = 1;
+       } else {
+               LOG_ERROR("BUG: ITM console connection->priv == NULL");
+       }
+
+       return ERROR_SERVER_REMOTE_CLOSED;
+}
+
+int itm_stimport_output(void *priv, unsigned char db)
+{
+       struct itm_stimport_service *cs = priv;
+
+       assert(cs != NULL);
+
+       if (cs->do_capture) {
+               if (cs->cFile == NULL) {
+                       char fnbuffer[256];
+                       char *filename = "capture.bin";
+                       time_t now = time(NULL);
+#if defined(__USE_MINGW_ANSI_STDIO) /* mingw32 FC14 does not seem to have 
localtime_r() */
+                       struct tm *pln = localtime(&now);
+                       snprintf(fnbuffer, 256, 
"%04d%02d%02d_%02d%02d%02d_stimport-%u_capture.bin", \
+                                (1900 + pln->tm_year), (pln->tm_mon + 1), 
pln->tm_mday, \
+                                pln->tm_hour, pln->tm_min, pln->tm_sec, 
cs->stimport);
+                       filename = fnbuffer;
+#else /* !MINGW */
+                       struct tm ln;
+                       if (localtime_r(&now, &ln)) {
+                               snprintf(fnbuffer, 256, 
"%04d%02d%02d_%02d%02d%02d_stimport-%u_capture.bin", \
+                                        (1900 + ln.tm_year), (ln.tm_mon + 1), 
ln.tm_mday, \
+                                        ln.tm_hour, ln.tm_min, ln.tm_sec, 
cs->stimport);
+                               filename = fnbuffer;
+                       }
+#endif /* !MINGW */
+                       cs->cFile = fopen(filename, "w+"); /* create or 
overwrite */
+                       setvbuf(cs->cFile, NULL, _IONBF, 0);
+               }
+               if (cs->cFile) {
+                       unsigned char lc = db;
+                       if (fwrite(&lc, 1, 1, cs->cFile) != 1) {
+                               LOG_ERROR("Failed to write to capture file 
(disabling capture)");
+                               cs->do_capture = 0;
+                               (void)fclose(cs->cFile);
+                               cs->cFile = NULL;
+                       }
+               } else {
+                       LOG_ERROR("Failed to create capture file (disabling 
capture)");
+                       cs->do_capture = 0;
+               }
+       }
+
+       struct itm_stimport_connection *cc = cs->cc;
+
+       if (cc) {
+               unsigned char lc = db;
+               assert(cc->connection != NULL);
+               return itm_stimport_write(cc->connection, &lc, sizeof(unsigned 
char));
+       } else {
+               /* CONSIDER: As commented above, we could be buffering this for
+                * later "telnet" console connection. We can either have a large
+                * pre-allocated buffer, that we realloc() occasionally; or just
+                * drop old data (have a wrapping buffer). */
+       }
+
+       return ERROR_OK;
+}
+
+static int itm_stimport_new_connection(struct connection *connection)
+{
+       struct itm_stimport_connection *cc = calloc(1, sizeof(struct 
itm_stimport_connection));
+
+       connection->priv = cc;
+       if (cc) {
+               struct itm_stimport_service *cs = connection->service->priv;
+               assert(cs != NULL);
+
+               cc->connection = connection;
+               cs->cc = cc;
+
+               /* CONSIDER: If we do add pre-connection buffering then we
+                * should output the buffer at this point by calling
+                * "itm_stimport_write(connection,...)". */
+       }
+
+       return ERROR_OK;
+}
+
+/* Arbitrary size buffer to cope with characters being received on the telnet
+ * connection. At the moment input data is not processed, but if support is
+ * required then the buffering can be tuned to match the expected usage. */
+#define ITM_STIMPORT_BUFFER_SIZE (32)
+
+static int itm_stimport_input(struct connection *connection)
+{
+       /* struct itm_stimport_connection *cc = connection->priv; */
+       unsigned char buffer[ITM_STIMPORT_BUFFER_SIZE];
+       int bytes_read;
+
+       bytes_read = connection_read(connection, buffer, 
ITM_STIMPORT_BUFFER_SIZE);
+       if (bytes_read == 0) {
+               return ERROR_SERVER_REMOTE_CLOSED;
+       } else if (bytes_read == -1) {
+               LOG_ERROR("error during ITM console read: %s", strerror(errno));
+               return ERROR_SERVER_REMOTE_CLOSED;
+       }
+
+       LOG_OUTPUT("TODO: process input of %d bytes", bytes_read);
+       /* We could use the "standard" approach of writing explicit patterns to
+        * known target locations to provide a back-channel to the target if
+        * bi-directional communication is required. For the moment we just drop
+        * input, since we are adding the support initially for unidirectional
+        * trace and diagnostic output from the target. */
+
+       return ERROR_OK;
+}
+
+static int itm_stimport_connection_closed(struct connection *connection)
+{
+       struct itm_stimport_connection *cc = connection->priv;
+
+       if (cc) {
+               free(cc);
+               connection->priv = NULL;
+       } else {
+               LOG_ERROR("BUG: ITM console connection->priv == NULL");
+       }
+
+       return ERROR_OK;
+}
+
+static int itm_stimport_init(struct target *target)
+{
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       int res;
+       unsigned int idx;
+
+       assert(armv7m != NULL);
+
+       if (strcmp(itm_packet_port, "disabled") != 0) {
+               struct itm_stimport_service *tpiu_packet_service = calloc(1, 
sizeof(struct itm_stimport_service));
+               if (tpiu_packet_service) {
+                       armv7m->tpiu_packet_service = tpiu_packet_service;
+                       res = add_service("itm_packets", itm_packet_port, 1, 
itm_stimport_new_connection, \
+                                         itm_stimport_input, 
itm_stimport_connection_closed, tpiu_packet_service);
+                       if (ERROR_OK != res)
+                               LOG_ERROR("Failed to add ITM packet service 
(res %d)", res);
+               }
+       }
+
+       res = ERROR_OK;
+       for (idx = 0; (idx < ITM_MAX_STIMPORT); idx++) {
+               if (strcmp(itm_stimport_network[idx], "disabled") != 0) {
+                       struct itm_stimport_service *itm_stimport_service = 
calloc(1, sizeof(struct itm_stimport_service));
+                       if (itm_stimport_service) {
+                               itm_stimport_service->stimport = idx;
+
+                               if (itm_stimport_capture[idx / 32] & (1 << (idx 
% 32)))
+                                       itm_stimport_service->do_capture = -1;
+
+                               /* hook to allow ITM trace to output */
+                               armv7m->itm_stimport_service[idx] = 
itm_stimport_service;
+                               res = add_service("itm_stimport", 
itm_stimport_network[idx], 1, itm_stimport_new_connection, \
+                                                 itm_stimport_input, 
itm_stimport_connection_closed, itm_stimport_service);
+                               if (ERROR_OK != res)
+                                       break;
+
+                               LOG_INFO("ITM stimulus port %i available on 
server port %s%s", idx, \
+                                        itm_stimport_network[idx], 
(itm_stimport_service->do_capture ? " (capturing to file)" : ""));
+                       }
+               }
+       }
+
+       return res;
+}
+
+COMMAND_HANDLER(handle_cortex_m_itm_stimport_command)
+{
+       if (CMD_ARGC < 2)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       unsigned spnum;
+       COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], spnum);
+
+       if (spnum >= ITM_MAX_STIMPORT) {
+               command_print(CMD_CTX, "Stimulus port %i is out of bounds", 
spnum);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       if (strcmp(CMD_ARGV[1], "disabled") == 0) {
+               if (itm_stimport_network[spnum])
+                       free((char *)itm_stimport_network[spnum]);
+               itm_stimport_network[spnum] = strdup("disabled");
+               return ERROR_OK;
+       }
+
+       unsigned npnum;
+       COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], npnum);
+
+       if ((npnum < 1000) || (npnum > 65535)) {
+               command_print(CMD_CTX, "Stimulus port %i network port %i is out 
of bounds", spnum, npnum);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       /* We could check if npnum is already in use by other stimulus port, tcl
+        * or telnet configurations. However, if a clash is configured then a
+        * "port in use" error will be raised so we can just leave that code to
+        * catch duplication errors. */
+       itm_stimport_network[spnum] = malloc(5 + 1);
+       sprintf((char *)itm_stimport_network[spnum], "%i", npnum);
+
+       if (CMD_ARGC > 2) {
+               if (strcmp(CMD_ARGV[2], "capture") == 0) {
+                       itm_stimport_capture[spnum / 32] |= (1 << (spnum % 32));
+               } else {
+                       command_print(CMD_CTX, "Stimulus port %i network port 
%i unrecognised modifier", spnum, npnum);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_cortex_m_itm_packets_command)
+{
+       return CALL_COMMAND_HANDLER(server_pipe_command, &itm_packet_port);
+}
+
+#define TPIU_BOOL_WRAPPER(name, print_name)    \
+       COMMAND_HANDLER(handle_cortex_m_itm_ ## name) \
+       { \
+               struct target *target = get_current_target(CMD_CTX); \
+               assert(target != NULL); \
+               struct armv7m_common *armv7m = target_to_armv7m(target); \
+               assert(armv7m != NULL); \
+               return CALL_COMMAND_HANDLER(handle_command_parse_bool, \
+                       &armv7m->name, print_name); \
+       }
+
+TPIU_BOOL_WRAPPER(tpiusync, "TPIO sync packets")
+TPIU_BOOL_WRAPPER(dwt, "DWT packets")
+
+static const struct command_registration cortex_m_itm_any_command_handlers[] = 
{
+       {
+               .name = "stimport",
+               .handler = handle_cortex_m_itm_stimport_command,
+               .mode = COMMAND_ANY,
+               .help = "Specify stimulus port to capture "
+                       "and network TCP port on which to listen "
+                       "for incoming network connections.",
+               .usage = "stimport_num tcp_port|'disabled' ['capture']",
+       },
+       {
+               .name = "packets",
+               .handler = handle_cortex_m_itm_packets_command,
+               .mode = COMMAND_ANY,
+               .help = "Specify network TCP port on which to listen "
+                       "for incoming network connections. The port "
+                       "will provide the raw (unprocessed) TPIU packets.",
+               .usage = "tcp_port",
+       },
+       {
+               .name = "tpiusync",
+               .handler = handle_cortex_m_itm_tpiusync,
+               .mode = COMMAND_ANY,
+               .help = "Display or modify flag controlling generation "
+                       "of TPIU sync packets.",
+               .usage = "['enable'|'disable']",
+       },
+       {
+               .name = "dwt",
+               .handler = handle_cortex_m_itm_dwt,
+               .mode = COMMAND_ANY,
+               .help = "Display or modify flag controlling generation "
+                       "of DWT packets.",
+               .usage = "['enable'|'disable']",
+       },
+       COMMAND_REGISTRATION_DONE
+};
+
+/* NOTE: Ideally we should NEVER generate info/debug/logging output in these 
ITM
+ * and DWT processing routines, since slow processing of the trace capture can
+ * result in OVERFLOW packets being seen if the trace is not emptied quickly
+ * enough. */
+
+static void itm_process_data(void *priv, struct armv7m_tpiu_pktstate *ctx)
+{
+       struct target *target = priv;
+       assert(target != NULL);
+
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       assert(armv7m != NULL);
+
+       /* Check if connection for this stimulus port: */
+       if (armv7m->itm_stimport_service[ctx->pcode]) {
+               unsigned int idx;
+               for (idx = 0; (idx < ctx->size); idx++) {
+                       unsigned char db = ((ctx->pdata >> (idx * 8)) & 0xFF);
+                       
itm_stimport_output(armv7m->itm_stimport_service[ctx->pcode], db);
+               }
+       }
+
+       return;
+}
+
+static void dwt_process_data(void *priv, struct armv7m_tpiu_pktstate *ctx)
+{
+       /* DWT discriminator IDs:
+        *     0 - Event counter wrapping
+        *         1-byte with bitmask of counter overflow marker bits.
+        *     1 - Exception tracing
+        *         2-byte exception number and event description.
+        *     2 - PC sample
+        *         1-byte for WFI/WFE (CPU asleep) or 4-byte for PC sample.
+        * 8..23 - Data tracing
+        *
+        * NOTE: The DWT_SLEEPCNT counts the number of "power saving" cycles
+        * initiated by WFI or WFE. */
+
+       /* CONSIDER: We provide a mechanism for external tools to get the raw
+        * TPIU stream. However, we could provide "profiling" support within
+        * OpenOCD by processing PC sampling (if enabled).
+        *
+        * Other SWD systems provide profiling by keeping counts of CPU
+        * addresses seen. We should only ever see 16bit aligned PC values when
+        * dealing with ARM/Thumb CPUs and some simple profiles use that to
+        * provide a simple 16-bit profile count. If we wanted to leave the CPU
+        * running for longer then we would ideally want deeper counts
+        * (e.g. 64-bit). We would need command line functionality to allow the
+        * profile dump gathered so far to be output. This could either be via
+        * the gdb "monitor" interface, or directly via the telnet
+        * console. Ideally we would have the option to wrap the data gathered
+        * in a gprof "gmon.out" compatible form for direct processing. We would
+        * need to know "address+length" pairs for areas of the target 32-bit
+        * memory space to track PC samples for. If the PC is outside of the
+        * configured ranges then we should track that count as "other" so that
+        * some information is held (much like we track how many samples were in
+        * "WFI/WFE" state). It should not be an issue for resource rich hosts
+        * (PCs with lots of memory) to set aside buffers for the memory sizes
+        * normally available on Cortex-M SoC designs (e.g. 256K). */
+
+       if (ctx->pcode == 2) {
+               if (ctx->size == 1) {
+                       if (ctx->pdata == 0x00) {
+                               static uint32_t total_idle;
+                               total_idle++;
+                       } /* else DWT PC sample size 1 but non-zero */
+               } else if (ctx->size == 4) {
+                       /* IMPLEMENT: Track sample for PC
+                        * "ctx->pdata". Increment count in relevant "pot" for
+                        * sampled address. */
+               } /* else DWT PC sample size unrecognised */
+       }
+
+       return;
+}
+
+/* NOTE: Issues were seen receiving trace data when using ST-LINK/V2 firmware
+ * reporting JTAG v14. The ST-LINK/V2 JTAG v17 firmware seems to work MUCH
+ * better. */
+
+/* NOTE: We do this FSM-driven byte-at-a-time processing since we seem to get
+ * partial packets returned by the trace data fetch, which means we need to 
keep
+ * state between data reads from the USB ST-LINK/V2 interface. */
+
+static void itmdwt_process_byte(void *priv, struct armv7m_tpiu_pktstate *ctx, 
unsigned char db)
+{
+       struct target *target = priv;
+       assert(target != NULL);
+
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       assert(armv7m != NULL);
+
+       switch (ctx->fsm) {
+       case PKTFSM_HDR:
+               switch (db) {
+               case ITMDWTPP_SYNC:
+               case ITMDWTPP_OVERFLOW:
+                       /* stay at PKTFSM_HDR */
+                       break;
+
+               default:
+                       {
+                               unsigned char source = (db & 
ITMDWTPP_TYPE_MASK);
+                               unsigned char size = 0;
+
+                               switch (source) {
+                               case ITMDWTPP_TYPE_PROTOCOL:
+                                       if (db & ITMDWTPP_PROTOCOL_EXTENSION) {
+                                               /* EX[2:0] in bits 4..6 with C 
in bit 7 */
+                                               if (db & 
ITMDWTPP_SOURCE_SELECTION)
+                                                       /* DWT EXTENSION "db" 
*/;
+                                               else
+                                                       /* ITM EXTENSION page:
+                                                        * 
((db&ITMDWTPP_PROTOCOL_EXT_ITM_PAGE_MASK)>>ITMDWTPP_PROTOCOL_EXT_ITM_PAGE_SHIFT)
+                                                        */;
+                                       } else {
+                                               if (db & 
ITMDWTPP_SOURCE_SELECTION) {
+                                                       if ((db & 0x94) == 0x94)
+                                                               /* GLOBAL 
timestamp "db" */;
+                                                       else
+                                                               /* RESERVED 
"db" */;
+                                               } else {
+                                                       /* LOCAL timestamp "db" 
*/
+                                               }
+                                       }
+                                       break;
+
+                               case ITMDWTPP_TYPE_SOURCE1:
+                                       size = 1;
+                               case ITMDWTPP_TYPE_SOURCE2:
+                                       if (size == 0)
+                                               size = 2;
+                               case ITMDWTPP_TYPE_SOURCE4:
+                                       if (size == 0)
+                                               size = 4;
+
+                                       /* Common source processing */
+                                       if (db & ITMDWTPP_SOURCE_SELECTION) {
+                                               unsigned char id = ((db & 
ITMDWTPP_SOURCE_MASK) >> ITMDWTPP_SOURCE_SHIFT);
+                                               ctx->fsm = PKTFSM_DWT1;
+                                               ctx->pcode = id;
+                                               ctx->pdata = 0;
+                                       } else {
+                                               unsigned char port = ((db & 
ITMDWTPP_SOURCE_MASK) >> ITMDWTPP_SOURCE_SHIFT);
+                                               ctx->fsm = PKTFSM_ITM1;
+                                               ctx->pcode = port;
+                                               ctx->pdata = 0;
+                                       }
+                                       break;
+                               }
+
+                               ctx->size = size;
+                       }
+                       break;
+               }
+               break;
+
+       case PKTFSM_ITM1:
+               ctx->pdata = db;
+               if (ctx->size == 1) {
+                       itm_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       ctx->fsm = PKTFSM_ITM2;
+               }
+               break;
+
+       case PKTFSM_ITM2:
+               ctx->pdata |= (db << 8);
+               if (ctx->size == 2) {
+                       itm_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       ctx->fsm = PKTFSM_ITM3;
+               }
+               break;
+
+       case PKTFSM_ITM3:
+               ctx->pdata |= (db << 16);
+               if (ctx->size == 3) {
+                       itm_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       ctx->fsm = PKTFSM_ITM4;
+               }
+               break;
+
+       case PKTFSM_ITM4:
+               ctx->pdata |= (db << 24);
+               if (ctx->size == 4) {
+                       itm_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       /* Unexpected size */
+                       ctx->fsm = PKTFSM_HDR;
+               }
+               break;
+
+       case PKTFSM_DWT1:
+               ctx->pdata = db;
+               if (ctx->size == 1) {
+                       dwt_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       ctx->fsm = PKTFSM_DWT2;
+               }
+               break;
+
+       case PKTFSM_DWT2:
+               ctx->pdata |= (db << 8);
+               if (ctx->size == 2) {
+                       dwt_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       ctx->fsm = PKTFSM_DWT3;
+               }
+               break;
+
+       case PKTFSM_DWT3:
+               ctx->pdata |= (db << 16);
+               if (ctx->size == 3) {
+                       dwt_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       ctx->fsm = PKTFSM_DWT4;
+               }
+               break;
+
+       case PKTFSM_DWT4:
+               ctx->pdata |= (db << 24);
+               if (ctx->size == 4) {
+                       dwt_process_data(priv, ctx);
+                       ctx->fsm = PKTFSM_HDR;
+               } else {
+                       /* Unexpected size */
+                       ctx->fsm = PKTFSM_HDR;
+               }
+               break;
+       }
+
+       return;
+}
+
+void trace_process(struct target *target, uint8_t *buf, size_t size)
+{
+       /* If configured for raw packet capture then write raw trace data as
+        * received to the open connection: */
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       assert(armv7m != NULL);
+
+       if (armv7m->tpiu_packet_service) {
+               struct itm_stimport_service *cs = armv7m->tpiu_packet_service;
+               struct itm_stimport_connection *cc = cs->cc;
+               /* This can be used by external tools to reconstruct the trace
+                * data (and will have the console and instrumentation
+                * interleaved along with any other enabled trace data). */
+               /* NOTE: In reality we could leave all TPIU processing to
+                * external tools if we wanted to avoid OpenOCD having to parse
+                * the packet format; and deal with stimulus ports, PC samples,
+                * etc. However, it is useful having built-in support for at
+                * least diagnostic output when using OpenOCD in a test server
+                * environment. */
+               if (cc) {
+                       assert(cc->connection != NULL);
+                       itm_stimport_write(cc->connection, buf, (int)size);
+               }
+       }
+
+       unsigned int idx;
+       for (idx = 0; (idx < size); idx++)
+               itmdwt_process_byte(target, &(armv7m->tpiu_pktstate), buf[idx]);
+
+       return;
+}
+
+static int tpiu_reset(struct target *target)
+{
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       assert(armv7m != NULL);
+
+       /* Close any active capture files: */
+       unsigned int idx;
+       for (idx = 0; (idx < ITM_MAX_STIMPORT); idx++) {
+               struct itm_stimport_service *cs = 
armv7m->itm_stimport_service[idx];
+               if (cs) {
+                       if (cs->cFile) {
+                               (void)fclose(cs->cFile);
+                               cs->cFile = NULL;
+                       }
+               }
+       }
+
+       memset(&(armv7m->tpiu_pktstate), '\0', sizeof(struct 
armv7m_tpiu_pktstate));
+
+       return ERROR_OK;
+}
+
 static int adapter_init_target(struct command_context *cmd_ctx,
                                    struct target *target)
 {
@@ -358,6 +981,26 @@ static int adapter_init_target(struct command_context 
*cmd_ctx,
 
        armv7m_build_reg_cache(target);
 
+       /* TODO:CONSIDER: This is not necessarily the best place for this
+        * initialisation, but it will suffice for initial testing. */
+       if (itm_packet_port == NULL)
+               itm_packet_port = strdup("disabled");
+
+       unsigned int idx;
+       for (idx = 0; (idx < ITM_MAX_STIMPORT); idx++) {
+               if (itm_stimport_network[idx] == NULL) {
+                       /* If we wanted we could check for explicit "idx" 
values and
+                        * enable services for those stimulus ports by default 
by
+                        * setting a valid network port value. e.g.
+                               if (idx == 0) {
+                                       itm_stimport_network[idx] = 
strdup("4445");
+                               }
+                        * At the moment we provide no default port mappings. */
+                       itm_stimport_network[idx] = strdup("disabled");
+               }
+       }
+       itm_stimport_init(target);
+
        return ERROR_OK;
 }
 
@@ -373,6 +1016,11 @@ static int adapter_target_create(struct target *target,
 
        adapter_init_arch_info(target, cortex_m3, target->tap);
 
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       assert(armv7m != NULL);
+       armv7m->tpiusync = true;
+       armv7m->dwt = false;
+
        return ERROR_OK;
 }
 
@@ -568,6 +1216,11 @@ static int adapter_deassert_reset(struct target *target)
         */
        jtag_add_reset(0, 0);
 
+       if (tpiu_reset(target) != ERROR_OK) {
+               LOG_ERROR("Failed to initialise ITM state");
+               /* continue since we do not treat this as fatal */
+       }
+
        target->savedDCRDR = 0;  /* clear both DCC busy bits on initial resume 
*/
 
        return target->reset_halt ? ERROR_OK : target_resume(target, 1, 0, 0, 
0);
@@ -785,6 +1438,17 @@ static const struct command_registration 
adapter_command_handlers[] = {
        {
                .chain = arm_command_handlers,
        },
+       /* This "itm" command support should really be dependant on a Cortex-M
+        * CPU being detected, or the target interface explicitly supporting a
+        * Cortex-M CPU. Se need to find the relevant source tree point for
+        * adding this "extension". */
+       {
+               .name = "itm",
+               .mode = COMMAND_ANY,
+               .help = "Cortex-M ITM specific commands",
+               .usage = "",
+               .chain = cortex_m_itm_any_command_handlers,
+       },
        COMMAND_REGISTRATION_DONE
 };
 

-- 

------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60133471&iu=/4140/ostg.clktrk
_______________________________________________
OpenOCD-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openocd-devel

Reply via email to