armv7m: add generic trace support (TPIU, ITM, etc.)
This provides support for various trace-related subsystems in a generic and expandable way. Change-Id: I3a27fa7b8cfb111753088bb8c3d760dd12d1395f Signed-off-by: Paul Fertser <fercerpav@gmail.com> Reviewed-on: http://openocd.zylin.com/2538 Tested-by: jenkins__archive__
parent
3e1dfdcb85
commit
a09a75653d
|
@ -4641,6 +4641,8 @@ when reset disables PLLs needed to use a fast clock.
|
||||||
@* After all targets have resumed
|
@* After all targets have resumed
|
||||||
@item @b{resumed}
|
@item @b{resumed}
|
||||||
@* Target has resumed
|
@* Target has resumed
|
||||||
|
@item @b{trace-config}
|
||||||
|
@* After target hardware trace configuration was changed
|
||||||
@end itemize
|
@end itemize
|
||||||
|
|
||||||
@node Flash Commands
|
@node Flash Commands
|
||||||
|
@ -7636,6 +7638,93 @@ fix CSW_SPROT from register AP_REG_CSW on selected dap.
|
||||||
Defaulting to 0.
|
Defaulting to 0.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@subsection ARMv7-M specific commands
|
||||||
|
@cindex tracing
|
||||||
|
@cindex SWO
|
||||||
|
@cindex SWV
|
||||||
|
@cindex TPIU
|
||||||
|
@cindex ITM
|
||||||
|
@cindex ETM
|
||||||
|
|
||||||
|
@deffn Command {tpiu config} (@option{disable} | ((@option{external} | @option{internal @var{filename}}) @
|
||||||
|
(@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
|
||||||
|
@var{TRACECLKIN_freq} [@var{trace_freq}]))
|
||||||
|
|
||||||
|
ARMv7-M architecture provides several modules to generate debugging
|
||||||
|
information internally (ITM, DWT and ETM). Their output is directed
|
||||||
|
through TPIU to be captured externally either on an SWO pin (this
|
||||||
|
configuration is called SWV) or on a synchronous parallel trace port.
|
||||||
|
|
||||||
|
This command configures the TPIU module of the target and, if internal
|
||||||
|
capture mode is selected, starts to capture trace output by using the
|
||||||
|
debugger adapter features.
|
||||||
|
|
||||||
|
Some targets require additional actions to be performed in the
|
||||||
|
@b{trace-config} handler for trace port to be activated.
|
||||||
|
|
||||||
|
Command options:
|
||||||
|
@itemize @minus
|
||||||
|
@item @option{disable} disable TPIU handling;
|
||||||
|
@item @option{external} configure TPIU to let user capture trace
|
||||||
|
output externally (with an additional UART or logic analyzer hardware);
|
||||||
|
@item @option{internal @var{filename}} configure TPIU and debug adapter to
|
||||||
|
gather trace data and append it to @var{filename} (which can be
|
||||||
|
either a regular file or a named pipe);
|
||||||
|
@item @option{sync @var{port_width}} use synchronous parallel trace output
|
||||||
|
mode, and set port width to @var{port_width};
|
||||||
|
@item @option{manchester} use asynchronous SWO mode with Manchester
|
||||||
|
coding;
|
||||||
|
@item @option{uart} use asynchronous SWO mode with NRZ (same as
|
||||||
|
regular UART 8N1) coding;
|
||||||
|
@item @var{formatter_enable} is @option{on} or @option{off} to enable
|
||||||
|
or disable TPIU formatter which needs to be used when both ITM and ETM
|
||||||
|
data is to be output via SWO;
|
||||||
|
@item @var{TRACECLKIN_freq} this should be specified to match target's
|
||||||
|
current TRACECLKIN frequency (usually the same as HCLK);
|
||||||
|
@item @var{trace_freq} trace port frequency. Can be omitted in
|
||||||
|
internal mode to let the adapter driver select the maximum supported
|
||||||
|
rate automatically.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
@enumerate
|
||||||
|
@item STM32L152 board is programmed with an application that configures
|
||||||
|
PLL to provide core clock with 24MHz frequency; to use ITM output it's
|
||||||
|
enough to:
|
||||||
|
@example
|
||||||
|
#include <libopencm3/cm3/itm.h>
|
||||||
|
...
|
||||||
|
ITM_STIM8(0) = c;
|
||||||
|
...
|
||||||
|
@end example
|
||||||
|
(the most obvious way is to use the first stimulus port for printf,
|
||||||
|
for that this ITM_STIM8 assignment can be used inside _write(); to make it
|
||||||
|
blocking to avoid data loss, add @code{while (!(ITM_STIM8(0) &
|
||||||
|
ITM_STIM_FIFOREADY));});
|
||||||
|
@item An FT2232H UART is connected to the SWO pin of the board;
|
||||||
|
@item Commands to configure UART for 12MHz baud rate:
|
||||||
|
@example
|
||||||
|
$ setserial /dev/ttyUSB1 spd_cust divisor 5
|
||||||
|
$ stty -F /dev/ttyUSB1 38400
|
||||||
|
@end example
|
||||||
|
(FT2232H's base frequency is 60MHz, spd_cust allows to alias 38400
|
||||||
|
baud with our custom divisor to get 12MHz)
|
||||||
|
@item @code{itmdump -f /dev/ttyUSB1 -d1}
|
||||||
|
@item @code{openocd -f interface/stlink-v2-1.cfg -c "transport select
|
||||||
|
hla_swd" -f target/stm32l1.cfg -c "tpiu config external uart off
|
||||||
|
24000000 12000000"}
|
||||||
|
@end enumerate
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {itm port} @var{port} (@option{0}|@option{1}|@option{on}|@option{off})
|
||||||
|
Enable or disable trace output for ITM stimulus @var{port} (counting
|
||||||
|
from 0). Port 0 is enabled on target creation automatically.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Command {itm ports} (@option{0}|@option{1}|@option{on}|@option{off})
|
||||||
|
Enable or disable trace output for all ITM stimulus ports.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@subsection Cortex-M specific commands
|
@subsection Cortex-M specific commands
|
||||||
@cindex Cortex-M
|
@cindex Cortex-M
|
||||||
|
|
||||||
|
|
|
@ -1031,16 +1031,16 @@ static int stlink_configure_target_trace_port(void *handle)
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto out;
|
goto out;
|
||||||
/* set the TPI clock prescaler */
|
/* set the TPI clock prescaler */
|
||||||
res = stlink_usb_write_debug_reg(handle, TPI_ACPR, h->trace.prescale);
|
res = stlink_usb_write_debug_reg(handle, TPIU_ACPR, h->trace.prescale);
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto out;
|
goto out;
|
||||||
/* select the pin protocol. The STLinkv2 only supports asynchronous
|
/* select the pin protocol. The STLinkv2 only supports asynchronous
|
||||||
* UART emulation (NRZ) mode, so that's what we pick. */
|
* UART emulation (NRZ) mode, so that's what we pick. */
|
||||||
res = stlink_usb_write_debug_reg(handle, TPI_SPPR, 0x02);
|
res = stlink_usb_write_debug_reg(handle, TPIU_SPPR, 0x02);
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto out;
|
goto out;
|
||||||
/* disable continuous formatting */
|
/* disable continuous formatting */
|
||||||
res = stlink_usb_write_debug_reg(handle, TPI_FFCR, (1<<8));
|
res = stlink_usb_write_debug_reg(handle, TPIU_FFCR, (1<<8));
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -1059,7 +1059,7 @@ static int stlink_configure_target_trace_port(void *handle)
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto out;
|
goto out;
|
||||||
/* trace port enable (port 0) */
|
/* trace port enable (port 0) */
|
||||||
res = stlink_usb_write_debug_reg(handle, ITM_TER, (1<<0));
|
res = stlink_usb_write_debug_reg(handle, ITM_TER0, (1<<0));
|
||||||
if (res != ERROR_OK)
|
if (res != ERROR_OK)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ ARMV6_SRC = \
|
||||||
|
|
||||||
ARMV7_SRC = \
|
ARMV7_SRC = \
|
||||||
armv7m.c \
|
armv7m.c \
|
||||||
|
armv7m_trace.c \
|
||||||
cortex_m.c \
|
cortex_m.c \
|
||||||
armv7a.c \
|
armv7a.c \
|
||||||
cortex_a.c
|
cortex_a.c
|
||||||
|
@ -155,6 +156,7 @@ noinst_HEADERS = \
|
||||||
armv4_5_cache.h \
|
armv4_5_cache.h \
|
||||||
armv7a.h \
|
armv7a.h \
|
||||||
armv7m.h \
|
armv7m.h \
|
||||||
|
armv7m_trace.h \
|
||||||
avrt.h \
|
avrt.h \
|
||||||
dsp563xx.h \
|
dsp563xx.h \
|
||||||
dsp563xx_once.h \
|
dsp563xx_once.h \
|
||||||
|
|
|
@ -635,6 +635,9 @@ int armv7m_init_arch_info(struct target *target, struct armv7m_common *armv7m)
|
||||||
|
|
||||||
armv7m->common_magic = ARMV7M_COMMON_MAGIC;
|
armv7m->common_magic = ARMV7M_COMMON_MAGIC;
|
||||||
armv7m->fp_feature = FP_NONE;
|
armv7m->fp_feature = FP_NONE;
|
||||||
|
armv7m->trace_config.trace_bus_id = 1;
|
||||||
|
/* Enable stimulus port #0 by default */
|
||||||
|
armv7m->trace_config.itm_ter[0] = 1;
|
||||||
|
|
||||||
arm->core_type = ARM_MODE_THREAD;
|
arm->core_type = ARM_MODE_THREAD;
|
||||||
arm->arch_info = armv7m;
|
arm->arch_info = armv7m;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include "arm_adi_v5.h"
|
#include "arm_adi_v5.h"
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
|
#include "armv7m_trace.h"
|
||||||
|
|
||||||
extern const int armv7m_psp_reg_map[];
|
extern const int armv7m_psp_reg_map[];
|
||||||
extern const int armv7m_msp_reg_map[];
|
extern const int armv7m_msp_reg_map[];
|
||||||
|
@ -153,6 +154,8 @@ struct armv7m_common {
|
||||||
/* stlink is a high level adapter, does not support all functions */
|
/* stlink is a high level adapter, does not support all functions */
|
||||||
bool stlink;
|
bool stlink;
|
||||||
|
|
||||||
|
struct armv7m_trace_config trace_config;
|
||||||
|
|
||||||
/* Direct processor core register read and writes */
|
/* Direct processor core register read and writes */
|
||||||
int (*load_core_reg_u32)(struct target *target, uint32_t num, uint32_t *value);
|
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);
|
int (*store_core_reg_u32)(struct target *target, uint32_t num, uint32_t value);
|
||||||
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015 Paul Fertser <fercerpav@gmail.com> *
|
||||||
|
* *
|
||||||
|
* 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 2 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. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <target/target.h>
|
||||||
|
#include <target/armv7m.h>
|
||||||
|
#include <target/cortex_m.h>
|
||||||
|
#include <target/armv7m_trace.h>
|
||||||
|
|
||||||
|
int armv7m_trace_tpiu_config(struct target *target)
|
||||||
|
{
|
||||||
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
|
struct armv7m_trace_config *trace_config = &armv7m->trace_config;
|
||||||
|
int prescaler;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if (!trace_config->trace_freq) {
|
||||||
|
LOG_ERROR("Trace port frequency is 0, can't enable TPIU");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace_config->traceclkin_freq % trace_config->trace_freq) {
|
||||||
|
LOG_ERROR("Can not calculate an integer divisor to get %u trace port frequency from %u TRACECLKIN frequency",
|
||||||
|
trace_config->trace_freq, trace_config->traceclkin_freq);
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
prescaler = trace_config->traceclkin_freq / trace_config->trace_freq;
|
||||||
|
|
||||||
|
retval = target_write_u32(target, TPIU_CSPSR, 1 << trace_config->port_size);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = target_write_u32(target, TPIU_ACPR, prescaler - 1);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = target_write_u32(target, TPIU_SPPR, trace_config->pin_protocol);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
uint32_t ffcr;
|
||||||
|
retval = target_read_u32(target, TPIU_FFCR, &ffcr);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
if (trace_config->formatter)
|
||||||
|
ffcr |= (1 << 1);
|
||||||
|
else
|
||||||
|
ffcr &= ~(1 << 1);
|
||||||
|
retval = target_write_u32(target, TPIU_FFCR, ffcr);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
target_call_event_callbacks(target, TARGET_EVENT_TRACE_CONFIG);
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int armv7m_trace_itm_config(struct target *target)
|
||||||
|
{
|
||||||
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
|
struct armv7m_trace_config *trace_config = &armv7m->trace_config;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = target_write_u32(target, ITM_LAR, ITM_LAR_KEY);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* Enable ITM, TXENA, set TraceBusID and other parameters */
|
||||||
|
retval = target_write_u32(target, ITM_TCR, (1 << 0) | (1 << 3) |
|
||||||
|
(trace_config->itm_diff_timestamps << 1) |
|
||||||
|
(trace_config->itm_synchro_packets << 2) |
|
||||||
|
(trace_config->itm_async_timestamps << 4) |
|
||||||
|
(trace_config->itm_ts_prescale << 8) |
|
||||||
|
(trace_config->trace_bus_id << 16));
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < 8; i++) {
|
||||||
|
retval = target_write_u32(target, ITM_TER0 + i * 4,
|
||||||
|
trace_config->itm_ter[i]);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_trace_file(struct armv7m_common *armv7m)
|
||||||
|
{
|
||||||
|
if (armv7m->trace_config.trace_file)
|
||||||
|
fclose(armv7m->trace_config.trace_file);
|
||||||
|
armv7m->trace_config.trace_file = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(handle_tpiu_config_command)
|
||||||
|
{
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
|
|
||||||
|
unsigned int cmd_idx = 0;
|
||||||
|
|
||||||
|
if (CMD_ARGC == cmd_idx)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
|
||||||
|
if (CMD_ARGC == cmd_idx + 1) {
|
||||||
|
close_trace_file(armv7m);
|
||||||
|
|
||||||
|
armv7m->trace_config.config_type = DISABLED;
|
||||||
|
if (CMD_CTX->mode == COMMAND_EXEC)
|
||||||
|
return armv7m_trace_tpiu_config(target);
|
||||||
|
else
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(CMD_ARGV[cmd_idx], "external") ||
|
||||||
|
!strcmp(CMD_ARGV[cmd_idx], "internal")) {
|
||||||
|
close_trace_file(armv7m);
|
||||||
|
|
||||||
|
armv7m->trace_config.config_type = EXTERNAL;
|
||||||
|
if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
|
||||||
|
cmd_idx++;
|
||||||
|
if (CMD_ARGC == cmd_idx)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
armv7m->trace_config.config_type = INTERNAL;
|
||||||
|
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
|
||||||
|
if (!armv7m->trace_config.trace_file) {
|
||||||
|
LOG_ERROR("Can't open trace destination file");
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd_idx++;
|
||||||
|
if (CMD_ARGC == cmd_idx)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
if (!strcmp(CMD_ARGV[cmd_idx], "sync")) {
|
||||||
|
armv7m->trace_config.pin_protocol = SYNC;
|
||||||
|
|
||||||
|
cmd_idx++;
|
||||||
|
if (CMD_ARGC == cmd_idx)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[cmd_idx], armv7m->trace_config.port_size);
|
||||||
|
} else {
|
||||||
|
if (!strcmp(CMD_ARGV[cmd_idx], "manchester"))
|
||||||
|
armv7m->trace_config.pin_protocol = ASYNC_MANCHESTER;
|
||||||
|
else if (!strcmp(CMD_ARGV[cmd_idx], "uart"))
|
||||||
|
armv7m->trace_config.pin_protocol = ASYNC_UART;
|
||||||
|
else
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
cmd_idx++;
|
||||||
|
if (CMD_ARGC == cmd_idx)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_ON_OFF(CMD_ARGV[cmd_idx], armv7m->trace_config.formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_idx++;
|
||||||
|
if (CMD_ARGC == cmd_idx)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.traceclkin_freq);
|
||||||
|
|
||||||
|
cmd_idx++;
|
||||||
|
if (CMD_ARGC != cmd_idx) {
|
||||||
|
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[cmd_idx], armv7m->trace_config.trace_freq);
|
||||||
|
cmd_idx++;
|
||||||
|
} else {
|
||||||
|
if (armv7m->trace_config.config_type != INTERNAL) {
|
||||||
|
LOG_ERROR("Trace port frequency can't be omitted in external capture mode");
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
armv7m->trace_config.trace_freq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CMD_ARGC == cmd_idx) {
|
||||||
|
if (CMD_CTX->mode == COMMAND_EXEC)
|
||||||
|
return armv7m_trace_tpiu_config(target);
|
||||||
|
else
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(handle_itm_port_command)
|
||||||
|
{
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
|
unsigned int reg_idx;
|
||||||
|
uint8_t port;
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
if (CMD_ARGC != 2)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], port);
|
||||||
|
COMMAND_PARSE_ON_OFF(CMD_ARGV[1], enable);
|
||||||
|
reg_idx = port / 32;
|
||||||
|
port = port % 32;
|
||||||
|
if (enable)
|
||||||
|
armv7m->trace_config.itm_ter[reg_idx] |= (1 << port);
|
||||||
|
else
|
||||||
|
armv7m->trace_config.itm_ter[reg_idx] &= ~(1 << port);
|
||||||
|
|
||||||
|
if (CMD_CTX->mode == COMMAND_EXEC)
|
||||||
|
return armv7m_trace_itm_config(target);
|
||||||
|
else
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMAND_HANDLER(handle_itm_ports_command)
|
||||||
|
{
|
||||||
|
struct target *target = get_current_target(CMD_CTX);
|
||||||
|
struct armv7m_common *armv7m = target_to_armv7m(target);
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
if (CMD_ARGC != 1)
|
||||||
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
||||||
|
|
||||||
|
COMMAND_PARSE_ON_OFF(CMD_ARGV[0], enable);
|
||||||
|
memset(armv7m->trace_config.itm_ter, enable ? 0xff : 0,
|
||||||
|
sizeof(armv7m->trace_config.itm_ter));
|
||||||
|
|
||||||
|
if (CMD_CTX->mode == COMMAND_EXEC)
|
||||||
|
return armv7m_trace_itm_config(target);
|
||||||
|
else
|
||||||
|
return ERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct command_registration tpiu_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "config",
|
||||||
|
.handler = handle_tpiu_config_command,
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "Configure TPIU features",
|
||||||
|
.usage = "(disable | "
|
||||||
|
"((external | internal <filename>) "
|
||||||
|
"(sync <port width> | ((manchester | uart) <formatter enable>)) "
|
||||||
|
"<TRACECLKIN freq> [<trace freq>]))",
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct command_registration itm_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "port",
|
||||||
|
.handler = handle_itm_port_command,
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "Enable or disable ITM stimulus port",
|
||||||
|
.usage = "<port> (0|1|on|off)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "ports",
|
||||||
|
.handler = handle_itm_ports_command,
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "Enable or disable all ITM stimulus ports",
|
||||||
|
.usage = "(0|1|on|off)",
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct command_registration armv7m_trace_command_handlers[] = {
|
||||||
|
{
|
||||||
|
.name = "tpiu",
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "tpiu command group",
|
||||||
|
.usage = "",
|
||||||
|
.chain = tpiu_command_handlers,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "itm",
|
||||||
|
.mode = COMMAND_ANY,
|
||||||
|
.help = "itm command group",
|
||||||
|
.usage = "",
|
||||||
|
.chain = itm_command_handlers,
|
||||||
|
},
|
||||||
|
COMMAND_REGISTRATION_DONE
|
||||||
|
};
|
|
@ -0,0 +1,87 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2015 Paul Fertser <fercerpav@gmail.com> *
|
||||||
|
* *
|
||||||
|
* 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 2 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. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ARMV7M_TRACE_H
|
||||||
|
#define ARMV7M_TRACE_H
|
||||||
|
|
||||||
|
#include <command.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Holds the interface to TPIU, ITM and DWT configuration functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum trace_config_type {
|
||||||
|
DISABLED, /**< tracing is disabled */
|
||||||
|
EXTERNAL, /**< trace output is captured externally */
|
||||||
|
INTERNAL /**< trace output is handled by OpenOCD adapter driver */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum tpio_pin_protocol {
|
||||||
|
SYNC, /**< synchronous trace output */
|
||||||
|
ASYNC_MANCHESTER, /**< asynchronous output with Manchester coding */
|
||||||
|
ASYNC_UART /**< asynchronous output with NRZ coding */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum itm_ts_prescaler {
|
||||||
|
ITM_TS_PRESCALE1, /**< no prescaling for the timestamp counter */
|
||||||
|
ITM_TS_PRESCALE4, /**< refclock divided by 4 for the timestamp counter */
|
||||||
|
ITM_TS_PRESCALE16, /**< refclock divided by 16 for the timestamp counter */
|
||||||
|
ITM_TS_PRESCALE64, /**< refclock divided by 64 for the timestamp counter */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct armv7m_trace_config {
|
||||||
|
/** Currently active trace capture mode */
|
||||||
|
enum trace_config_type config_type;
|
||||||
|
|
||||||
|
/** Currently active trace output mode */
|
||||||
|
enum tpio_pin_protocol pin_protocol;
|
||||||
|
/** TPIU formatter enable/disable (in async mode) */
|
||||||
|
bool formatter;
|
||||||
|
/** Synchronous output port width */
|
||||||
|
uint32_t port_size;
|
||||||
|
|
||||||
|
/** Bitmask of currenty enabled ITM stimuli */
|
||||||
|
uint32_t itm_ter[8];
|
||||||
|
/** Identifier for multi-source trace stream formatting */
|
||||||
|
unsigned int trace_bus_id;
|
||||||
|
/** Prescaler for the timestamp counter */
|
||||||
|
enum itm_ts_prescaler itm_ts_prescale;
|
||||||
|
/** Enable differential timestamps */
|
||||||
|
bool itm_diff_timestamps;
|
||||||
|
/** Enable async timestamps model */
|
||||||
|
bool itm_async_timestamps;
|
||||||
|
/** Enable synchronisation packet transmission (for sync port only) */
|
||||||
|
bool itm_synchro_packets;
|
||||||
|
|
||||||
|
/** Current frequency of TRACECLKIN (usually matches HCLK) */
|
||||||
|
unsigned int traceclkin_freq;
|
||||||
|
/** Current frequency of trace port */
|
||||||
|
unsigned int trace_freq;
|
||||||
|
/** Handle to output trace data in INTERNAL capture mode */
|
||||||
|
FILE *trace_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct command_registration armv7m_trace_command_handlers[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure hardware accordingly to the current TPIU target settings
|
||||||
|
*/
|
||||||
|
int armv7m_trace_tpiu_config(struct target *target);
|
||||||
|
/**
|
||||||
|
* Configure hardware accordingly to the current ITM target settings
|
||||||
|
*/
|
||||||
|
int armv7m_trace_itm_config(struct target *target);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1932,6 +1932,16 @@ int cortex_m_examine(struct target *target)
|
||||||
armv7m->dap.tar_autoincr_block = (1 << 12);
|
armv7m->dap.tar_autoincr_block = (1 << 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure trace modules */
|
||||||
|
retval = target_write_u32(target, DCB_DEMCR, TRCENA | armv7m->demcr);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (armv7m->trace_config.config_type != DISABLED) {
|
||||||
|
armv7m_trace_tpiu_config(target);
|
||||||
|
armv7m_trace_itm_config(target);
|
||||||
|
}
|
||||||
|
|
||||||
/* NOTE: FPB and DWT are both optional. */
|
/* NOTE: FPB and DWT are both optional. */
|
||||||
|
|
||||||
/* Setup FPB */
|
/* Setup FPB */
|
||||||
|
@ -2324,6 +2334,9 @@ static const struct command_registration cortex_m_command_handlers[] = {
|
||||||
{
|
{
|
||||||
.chain = armv7m_command_handlers,
|
.chain = armv7m_command_handlers,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.chain = armv7m_trace_command_handlers,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "cortex_m",
|
.name = "cortex_m",
|
||||||
.mode = COMMAND_EXEC,
|
.mode = COMMAND_EXEC,
|
||||||
|
|
|
@ -33,10 +33,11 @@
|
||||||
|
|
||||||
#define SYSTEM_CONTROL_BASE 0x400FE000
|
#define SYSTEM_CONTROL_BASE 0x400FE000
|
||||||
|
|
||||||
#define ITM_TER 0xE0000E00
|
#define ITM_TER0 0xE0000E00
|
||||||
#define ITM_TPR 0xE0000E40
|
#define ITM_TPR 0xE0000E40
|
||||||
#define ITM_TCR 0xE0000E80
|
#define ITM_TCR 0xE0000E80
|
||||||
#define ITM_LAR 0xE0000FB0
|
#define ITM_LAR 0xE0000FB0
|
||||||
|
#define ITM_LAR_KEY 0xC5ACCE55
|
||||||
|
|
||||||
#define CPUID 0xE000ED00
|
#define CPUID 0xE000ED00
|
||||||
/* Debug Control Block */
|
/* Debug Control Block */
|
||||||
|
@ -69,13 +70,13 @@
|
||||||
#define FPU_FPCAR 0xE000EF38
|
#define FPU_FPCAR 0xE000EF38
|
||||||
#define FPU_FPDSCR 0xE000EF3C
|
#define FPU_FPDSCR 0xE000EF3C
|
||||||
|
|
||||||
#define TPI_SSPSR 0xE0040000
|
#define TPIU_SSPSR 0xE0040000
|
||||||
#define TPI_CSPSR 0xE0040004
|
#define TPIU_CSPSR 0xE0040004
|
||||||
#define TPI_ACPR 0xE0040010
|
#define TPIU_ACPR 0xE0040010
|
||||||
#define TPI_SPPR 0xE00400F0
|
#define TPIU_SPPR 0xE00400F0
|
||||||
#define TPI_FFSR 0xE0040300
|
#define TPIU_FFSR 0xE0040300
|
||||||
#define TPI_FFCR 0xE0040304
|
#define TPIU_FFCR 0xE0040304
|
||||||
#define TPI_FSCR 0xE0040308
|
#define TPIU_FSCR 0xE0040308
|
||||||
|
|
||||||
/* DCB_DHCSR bit and field definitions */
|
/* DCB_DHCSR bit and field definitions */
|
||||||
#define DBGKEY (0xA05F << 16)
|
#define DBGKEY (0xA05F << 16)
|
||||||
|
|
|
@ -772,6 +772,9 @@ static const struct command_registration adapter_command_handlers[] = {
|
||||||
{
|
{
|
||||||
.chain = arm_command_handlers,
|
.chain = arm_command_handlers,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.chain = armv7m_trace_command_handlers,
|
||||||
|
},
|
||||||
COMMAND_REGISTRATION_DONE
|
COMMAND_REGISTRATION_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -217,6 +217,8 @@ static const Jim_Nvp nvp_target_event[] = {
|
||||||
{ .value = TARGET_EVENT_GDB_FLASH_ERASE_START, .name = "gdb-flash-erase-start" },
|
{ .value = TARGET_EVENT_GDB_FLASH_ERASE_START, .name = "gdb-flash-erase-start" },
|
||||||
{ .value = TARGET_EVENT_GDB_FLASH_ERASE_END , .name = "gdb-flash-erase-end" },
|
{ .value = TARGET_EVENT_GDB_FLASH_ERASE_END , .name = "gdb-flash-erase-end" },
|
||||||
|
|
||||||
|
{ .value = TARGET_EVENT_TRACE_CONFIG, .name = "trace-config" },
|
||||||
|
|
||||||
{ .name = NULL, .value = -1 }
|
{ .name = NULL, .value = -1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -266,6 +266,8 @@ enum target_event {
|
||||||
TARGET_EVENT_GDB_FLASH_ERASE_END,
|
TARGET_EVENT_GDB_FLASH_ERASE_END,
|
||||||
TARGET_EVENT_GDB_FLASH_WRITE_START,
|
TARGET_EVENT_GDB_FLASH_WRITE_START,
|
||||||
TARGET_EVENT_GDB_FLASH_WRITE_END,
|
TARGET_EVENT_GDB_FLASH_WRITE_END,
|
||||||
|
|
||||||
|
TARGET_EVENT_TRACE_CONFIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct target_event_action {
|
struct target_event_action {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# stm32 devices support both JTAG and SWD transports.
|
# stm32 devices support both JTAG and SWD transports.
|
||||||
#
|
#
|
||||||
source [find target/swj-dp.tcl]
|
source [find target/swj-dp.tcl]
|
||||||
|
source [find mem_helper.tcl]
|
||||||
|
|
||||||
if { [info exists CHIPNAME] } {
|
if { [info exists CHIPNAME] } {
|
||||||
set _CHIPNAME $CHIPNAME
|
set _CHIPNAME $CHIPNAME
|
||||||
|
@ -93,3 +94,16 @@ if {![using_hla]} {
|
||||||
# perform a soft reset
|
# perform a soft reset
|
||||||
cortex_m reset_config sysresetreq
|
cortex_m reset_config sysresetreq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event examine-end {
|
||||||
|
# DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP |
|
||||||
|
# DBG_STANDBY | DBG_STOP | DBG_SLEEP
|
||||||
|
mmw 0xE0042004 0x00000307 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event trace-config {
|
||||||
|
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
|
||||||
|
# change this value accordingly to configure trace pins
|
||||||
|
# assignment
|
||||||
|
mmw 0xE0042004 0x00000020 0
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# stm32 devices support both JTAG and SWD transports.
|
# stm32 devices support both JTAG and SWD transports.
|
||||||
#
|
#
|
||||||
source [find target/swj-dp.tcl]
|
source [find target/swj-dp.tcl]
|
||||||
|
source [find mem_helper.tcl]
|
||||||
|
|
||||||
if { [info exists CHIPNAME] } {
|
if { [info exists CHIPNAME] } {
|
||||||
set _CHIPNAME $CHIPNAME
|
set _CHIPNAME $CHIPNAME
|
||||||
|
@ -77,3 +78,19 @@ if {![using_hla]} {
|
||||||
# perform a soft reset
|
# perform a soft reset
|
||||||
cortex_m reset_config sysresetreq
|
cortex_m reset_config sysresetreq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event examine-end {
|
||||||
|
# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
|
||||||
|
mmw 0xE0042004 0x00000007 0
|
||||||
|
|
||||||
|
# Stop watchdog counters during halt
|
||||||
|
# DBGMCU_APB1_FZ = DBG_IWDG_STOP | DBG_WWDG_STOP
|
||||||
|
mww 0xE0042008 0x00001800
|
||||||
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event trace-config {
|
||||||
|
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
|
||||||
|
# change this value accordingly to configure trace pins
|
||||||
|
# assignment
|
||||||
|
mmw 0xE0042004 0x00000020 0
|
||||||
|
}
|
||||||
|
|
|
@ -118,3 +118,10 @@ proc stm32f3x_default_reset_init {} {
|
||||||
$_TARGETNAME configure -event examine-end { stm32f3x_default_examine_end }
|
$_TARGETNAME configure -event examine-end { stm32f3x_default_examine_end }
|
||||||
$_TARGETNAME configure -event reset-start { stm32f3x_default_reset_start }
|
$_TARGETNAME configure -event reset-start { stm32f3x_default_reset_start }
|
||||||
$_TARGETNAME configure -event reset-init { stm32f3x_default_reset_init }
|
$_TARGETNAME configure -event reset-init { stm32f3x_default_reset_init }
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event trace-config {
|
||||||
|
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
|
||||||
|
# change this value accordingly to configure trace pins
|
||||||
|
# assignment
|
||||||
|
mmw 0xe0042004 0x00000020 0
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
# stm32 devices support both JTAG and SWD transports.
|
# stm32 devices support both JTAG and SWD transports.
|
||||||
#
|
#
|
||||||
source [find target/swj-dp.tcl]
|
source [find target/swj-dp.tcl]
|
||||||
|
source [find mem_helper.tcl]
|
||||||
|
|
||||||
if { [info exists CHIPNAME] } {
|
if { [info exists CHIPNAME] } {
|
||||||
set _CHIPNAME $CHIPNAME
|
set _CHIPNAME $CHIPNAME
|
||||||
|
@ -89,3 +90,19 @@ if {![using_hla]} {
|
||||||
# perform a soft reset
|
# perform a soft reset
|
||||||
cortex_m reset_config sysresetreq
|
cortex_m reset_config sysresetreq
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event examine-end {
|
||||||
|
# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
|
||||||
|
mmw 0xE0042004 0x00000007 0
|
||||||
|
|
||||||
|
# Stop watchdog counters during halt
|
||||||
|
# DBGMCU_APB1_FZ = DBG_IWDG_STOP | DBG_WWDG_STOP
|
||||||
|
mww 0xE0042008 0x00001800
|
||||||
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event trace-config {
|
||||||
|
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
|
||||||
|
# change this value accordingly to configure trace pins
|
||||||
|
# assignment
|
||||||
|
mmw 0xE0042004 0x00000020 0
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
source [find target/swj-dp.tcl]
|
source [find target/swj-dp.tcl]
|
||||||
|
source [find mem_helper.tcl]
|
||||||
|
|
||||||
if { [info exists CHIPNAME] } {
|
if { [info exists CHIPNAME] } {
|
||||||
set _CHIPNAME $CHIPNAME
|
set _CHIPNAME $CHIPNAME
|
||||||
|
@ -107,3 +108,19 @@ $_TARGETNAME configure -event reset-init {
|
||||||
$_TARGETNAME configure -event reset-start {
|
$_TARGETNAME configure -event reset-start {
|
||||||
adapter_khz 300
|
adapter_khz 300
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event examine-end {
|
||||||
|
# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
|
||||||
|
mmw 0xE0042004 0x00000007 0
|
||||||
|
|
||||||
|
# Stop watchdog counters during halt
|
||||||
|
# DBGMCU_APB1_FZ = DBG_IWDG_STOP | DBG_WWDG_STOP
|
||||||
|
mww 0xE0042008 0x00001800
|
||||||
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event trace-config {
|
||||||
|
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
|
||||||
|
# change this value accordingly to configure trace pins
|
||||||
|
# assignment
|
||||||
|
mmw 0xE0042004 0x00000020 0
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue