kopia lustrzana https://github.com/stlink-org/stlink
650 wiersze
20 KiB
C
650 wiersze
20 KiB
C
#include <ctype.h>
|
|
#include <getopt.h>
|
|
#include <signal.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <stlink.h>
|
|
|
|
#include <chipid.h>
|
|
#include <logging.h>
|
|
#include <read_write.h>
|
|
#include <register.h>
|
|
#include <usb.h>
|
|
|
|
#define DEFAULT_LOGGING_LEVEL 50
|
|
#define DEBUG_LOGGING_LEVEL 100
|
|
|
|
#define APP_RESULT_SUCCESS 0
|
|
#define APP_RESULT_INVALID_PARAMS 1
|
|
#define APP_RESULT_STLINK_NOT_FOUND 2
|
|
#define APP_RESULT_STLINK_MISSING_DEVICE 3
|
|
#define APP_RESULT_STLINK_UNSUPPORTED_DEVICE 4
|
|
#define APP_RESULT_STLINK_UNSUPPORTED_LINK 5
|
|
#define APP_RESULT_UNSUPPORTED_TRACE_FREQUENCY 6
|
|
#define APP_RESULT_STLINK_STATE_ERROR 7
|
|
|
|
// See D4.2 of https://developer.arm.com/documentation/ddi0403/ed/
|
|
#define TRACE_OP_IS_OVERFLOW(c) ((c) == 0x70)
|
|
#define TRACE_OP_IS_LOCAL_TIME(c) (((c)&0x0f) == 0x00 && ((c)&0x70) != 0x00)
|
|
#define TRACE_OP_IS_EXTENSION(c) (((c)&0x0b) == 0x08)
|
|
#define TRACE_OP_IS_GLOBAL_TIME(c) (((c)&0xdf) == 0x94)
|
|
#define TRACE_OP_IS_SOURCE(c) (((c)&0x03) != 0x00)
|
|
#define TRACE_OP_IS_SW_SOURCE(c) (((c)&0x03) != 0x00 && ((c)&0x04) == 0x00)
|
|
#define TRACE_OP_IS_HW_SOURCE(c) (((c)&0x03) != 0x00 && ((c)&0x04) == 0x04)
|
|
#define TRACE_OP_IS_TARGET_SOURCE(c) ((c) == 0x01)
|
|
#define TRACE_OP_GET_CONTINUATION(c) ((c)&0x80)
|
|
#define TRACE_OP_GET_SOURCE_SIZE(c) ((c)&0x03)
|
|
#define TRACE_OP_GET_SW_SOURCE_ADDR(c) ((c) >> 3)
|
|
|
|
typedef struct {
|
|
bool show_help;
|
|
bool show_version;
|
|
int32_t logging_level;
|
|
uint32_t core_frequency;
|
|
uint32_t trace_frequency;
|
|
bool reset_board;
|
|
bool force;
|
|
char *serial_number;
|
|
} st_settings_t;
|
|
|
|
// We use a simple state machine to parse the trace data.
|
|
typedef enum {
|
|
TRACE_STATE_UNKNOWN,
|
|
TRACE_STATE_IDLE,
|
|
TRACE_STATE_TARGET_SOURCE,
|
|
TRACE_STATE_SKIP_FRAME,
|
|
TRACE_STATE_SKIP_4,
|
|
TRACE_STATE_SKIP_3,
|
|
TRACE_STATE_SKIP_2,
|
|
TRACE_STATE_SKIP_1,
|
|
} trace_state;
|
|
|
|
typedef struct {
|
|
time_t start_time;
|
|
bool configuration_checked;
|
|
|
|
trace_state state;
|
|
|
|
uint32_t count_raw_bytes;
|
|
uint32_t count_target_data;
|
|
uint32_t count_time_packets;
|
|
uint32_t count_hw_overflow;
|
|
uint32_t count_sw_overflow;
|
|
uint32_t count_error;
|
|
|
|
uint8_t unknown_opcodes[256 / 8];
|
|
uint32_t unknown_sources;
|
|
} st_trace_t;
|
|
|
|
// We use a global flag to allow communicating to the main thread from the
|
|
// signal handler.
|
|
static bool g_abort_trace = false;
|
|
|
|
static void abort_trace() { g_abort_trace = true; }
|
|
|
|
#if defined(_WIN32)
|
|
BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
|
|
(void)fdwCtrlType;
|
|
abort_trace();
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
static void usage(void) {
|
|
puts("st-trace - usage:");
|
|
puts(" -h, --help Print this help");
|
|
puts(" -V, --version Print this version");
|
|
puts(" -vXX, --verbose=XX Specify a specific verbosity level (0..99)");
|
|
puts(" -v, --verbose Specify a generally verbose logging");
|
|
puts(" -cXX, --clock=XX Specify the core frequency, optionally followed by");
|
|
puts(" k=kHz, m=MHz, or g=GHz (eg. --clock=180m)");
|
|
puts(" -tXX, --trace=XX Specify the trace frequency, optionally followed by");
|
|
puts(" k=kHz, m=MHz, or g=GHz (eg. --trace=2m)");
|
|
puts(" -n, --no-reset Do not reset board on connection");
|
|
puts(" -sXX, --serial=XX Use a specific serial number");
|
|
puts(" -f, --force Ignore most initialization errors");
|
|
}
|
|
|
|
static bool parse_frequency(char* text, uint32_t* result)
|
|
{
|
|
if (text == NULL) {
|
|
ELOG("Invalid frequency.\n");
|
|
return false;
|
|
}
|
|
|
|
char* suffix = text;
|
|
double value = strtod(text, &suffix);
|
|
|
|
if (value == 0.0) {
|
|
ELOG("Invalid frequency.\n");
|
|
return false;
|
|
}
|
|
|
|
double scale = 1.0;
|
|
if (*suffix == 'k')
|
|
scale = 1000;
|
|
else if (*suffix == 'm')
|
|
scale = 1000000;
|
|
else if (*suffix == 'g')
|
|
scale = 1000000000;
|
|
else if (*suffix != '\0') {
|
|
ELOG("Unknown frequency suffix '%s'.\n", suffix);
|
|
return false;
|
|
}
|
|
|
|
value *= scale;
|
|
if (value <= 0 || value > 0xFFFFFFFFul) {
|
|
ELOG("Frequency is out of valid range.\n");
|
|
return false;
|
|
}
|
|
|
|
*result = (uint32_t)value;
|
|
return true;
|
|
}
|
|
|
|
bool parse_options(int32_t argc, char **argv, st_settings_t *settings) {
|
|
|
|
static struct option long_options[] = {
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{"verbose", optional_argument, NULL, 'v'},
|
|
{"clock", required_argument, NULL, 'c'},
|
|
{"trace", required_argument, NULL, 't'},
|
|
{"no-reset", no_argument, NULL, 'n'},
|
|
{"serial", required_argument, NULL, 's'},
|
|
{"force", no_argument, NULL, 'f'},
|
|
{0, 0, 0, 0},
|
|
};
|
|
int32_t option_index = 0;
|
|
int32_t c;
|
|
bool error = false;
|
|
|
|
settings->show_help = false;
|
|
settings->show_version = false;
|
|
settings->logging_level = DEFAULT_LOGGING_LEVEL;
|
|
settings->core_frequency = 0;
|
|
settings->trace_frequency = 0;
|
|
settings->reset_board = true;
|
|
settings->force = false;
|
|
settings->serial_number = NULL;
|
|
ugly_init(settings->logging_level);
|
|
|
|
while ((c = getopt_long(argc, argv, "hVv::c:ns:f", long_options, &option_index)) != -1) {
|
|
switch (c) {
|
|
case 'h':
|
|
settings->show_help = true;
|
|
break;
|
|
case 'V':
|
|
settings->show_version = true;
|
|
break;
|
|
case 'v':
|
|
if (optarg) {
|
|
settings->logging_level = atoi(optarg);
|
|
} else {
|
|
settings->logging_level = DEBUG_LOGGING_LEVEL;
|
|
}
|
|
ugly_init(settings->logging_level);
|
|
break;
|
|
case 'c':
|
|
if (!parse_frequency(optarg, &settings->core_frequency))
|
|
error = true;
|
|
break;
|
|
case 't':
|
|
if (!parse_frequency(optarg, &settings->trace_frequency))
|
|
error = true;
|
|
break;
|
|
case 'n':
|
|
settings->reset_board = false;
|
|
break;
|
|
case 'f':
|
|
settings->force = true;
|
|
break;
|
|
case 's':
|
|
settings->serial_number = optarg;
|
|
break;
|
|
case '?':
|
|
error = true;
|
|
break;
|
|
default:
|
|
ELOG("Unknown command line option: '%c' (0x%02x)\n", c, c);
|
|
error = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind < argc) {
|
|
while (optind < argc) {
|
|
ELOG("Unknown command line argument: '%s'\n", argv[optind++]);
|
|
}
|
|
error = true;
|
|
}
|
|
|
|
if (error && !settings->force)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static stlink_t *stlink_connect(const st_settings_t *settings) {
|
|
return stlink_open_usb(settings->logging_level, false,
|
|
settings->serial_number, 0);
|
|
}
|
|
|
|
static bool enable_trace(stlink_t *stlink, const st_settings_t *settings,
|
|
uint32_t trace_frequency) {
|
|
|
|
if (stlink_force_debug(stlink)) {
|
|
ELOG("Unable to debug device\n");
|
|
if (!settings->force)
|
|
return false;
|
|
}
|
|
if (settings->reset_board && stlink_reset(stlink, RESET_SOFT_AND_HALT)) {
|
|
ELOG("Unable to reset device\n");
|
|
if (!settings->force)
|
|
return false;
|
|
}
|
|
|
|
stlink_write_debug32(stlink, STLINK_REG_DHCSR,
|
|
STLINK_REG_DHCSR_DBGKEY | STLINK_REG_DHCSR_C_DEBUGEN |
|
|
STLINK_REG_DHCSR_C_HALT);
|
|
stlink_write_debug32(stlink, STLINK_REG_DEMCR, STLINK_REG_DEMCR_TRCENA);
|
|
stlink_write_debug32(stlink, STLINK_REG_CM3_FP_CTRL,
|
|
STLINK_REG_CM3_FP_CTRL_KEY);
|
|
stlink_write_debug32(stlink, STLINK_REG_DWT_FUNCTION0, 0);
|
|
stlink_write_debug32(stlink, STLINK_REG_DWT_FUNCTION1, 0);
|
|
stlink_write_debug32(stlink, STLINK_REG_DWT_FUNCTION2, 0);
|
|
stlink_write_debug32(stlink, STLINK_REG_DWT_FUNCTION3, 0);
|
|
stlink_write_debug32(stlink, STLINK_REG_DWT_CTRL, 0);
|
|
stlink_write_debug32(
|
|
stlink, STLINK_REG_DBGMCU_CR,
|
|
STLINK_REG_DBGMCU_CR_DBG_SLEEP | STLINK_REG_DBGMCU_CR_DBG_STOP |
|
|
STLINK_REG_DBGMCU_CR_DBG_STANDBY | STLINK_REG_DBGMCU_CR_TRACE_IOEN |
|
|
STLINK_REG_DBGMCU_CR_TRACE_MODE_ASYNC);
|
|
|
|
if (stlink_trace_enable(stlink, trace_frequency)) {
|
|
ELOG("Unable to turn on tracing in stlink\n");
|
|
if (!settings->force)
|
|
return false;
|
|
}
|
|
|
|
stlink_write_debug32(stlink, STLINK_REG_TPI_CSPSR,
|
|
STLINK_REG_TPI_CSPSR_PORT_SIZE_1);
|
|
|
|
if (settings->core_frequency) {
|
|
uint32_t prescaler = settings->core_frequency / trace_frequency - 1;
|
|
if (prescaler > STLINK_REG_TPI_ACPR_MAX) {
|
|
ELOG("Trace frequency prescaler %d out of range. Try setting a faster "
|
|
"trace frequency.\n",
|
|
prescaler);
|
|
if (!settings->force)
|
|
return false;
|
|
}
|
|
stlink_write_debug32(stlink, STLINK_REG_TPI_ACPR,
|
|
prescaler); // Set TPIU_ACPR clock divisor
|
|
}
|
|
stlink_write_debug32(stlink, STLINK_REG_TPI_FFCR,
|
|
STLINK_REG_TPI_FFCR_TRIG_IN);
|
|
stlink_write_debug32(stlink, STLINK_REG_TPI_SPPR,
|
|
STLINK_REG_TPI_SPPR_SWO_NRZ);
|
|
stlink_write_debug32(stlink, STLINK_REG_ITM_LAR, STLINK_REG_ITM_LAR_KEY);
|
|
stlink_write_debug32(stlink, STLINK_REG_ITM_TCC,
|
|
0x00000400); // Set sync counter
|
|
stlink_write_debug32(stlink, STLINK_REG_ITM_TCR,
|
|
STLINK_REG_ITM_TCR_TRACE_BUS_ID_1 |
|
|
STLINK_REG_ITM_TCR_TS_ENA |
|
|
STLINK_REG_ITM_TCR_ITM_ENA);
|
|
stlink_write_debug32(stlink, STLINK_REG_ITM_TER,
|
|
STLINK_REG_ITM_TER_PORTS_ALL);
|
|
stlink_write_debug32(stlink, STLINK_REG_ITM_TPR,
|
|
STLINK_REG_ITM_TPR_PORTS_ALL);
|
|
stlink_write_debug32(stlink, STLINK_REG_DWT_CTRL,
|
|
4 * STLINK_REG_DWT_CTRL_NUM_COMP |
|
|
STLINK_REG_DWT_CTRL_CYC_TAP |
|
|
0xF * STLINK_REG_DWT_CTRL_POST_INIT |
|
|
0xF * STLINK_REG_DWT_CTRL_POST_PRESET |
|
|
STLINK_REG_DWT_CTRL_CYCCNT_ENA);
|
|
stlink_write_debug32(stlink, STLINK_REG_DEMCR, STLINK_REG_DEMCR_TRCENA);
|
|
|
|
uint32_t prescaler = 0;
|
|
stlink_read_debug32(stlink, STLINK_REG_TPI_ACPR, &prescaler);
|
|
if (prescaler) {
|
|
uint32_t system_clock_speed = (prescaler + 1) * trace_frequency;
|
|
ILOG("Trace Port Interface configured to expect a %d Hz system clock.\n",
|
|
system_clock_speed);
|
|
} else {
|
|
WLOG("Trace Port Interface not configured. Specify the system clock with "
|
|
"a --clock=XX command\n");
|
|
WLOG("line option or set it in your device's clock initialization routine, "
|
|
"such as with:\n");
|
|
WLOG(" TPI->ACPR = HAL_RCC_GetHCLKFreq() / %d - 1;\n", trace_frequency);
|
|
}
|
|
ILOG("Trace frequency set to %d Hz.\n", trace_frequency);
|
|
|
|
return true;
|
|
}
|
|
|
|
static trace_state update_trace_idle(st_trace_t *trace, uint8_t c) {
|
|
// Handle a trace byte when we are in the idle state.
|
|
|
|
if (TRACE_OP_IS_TARGET_SOURCE(c)) {
|
|
return TRACE_STATE_TARGET_SOURCE;
|
|
}
|
|
|
|
if (TRACE_OP_IS_SOURCE(c)) {
|
|
uint8_t size = TRACE_OP_GET_SOURCE_SIZE(c);
|
|
if (TRACE_OP_IS_SW_SOURCE(c)) {
|
|
uint8_t addr = TRACE_OP_GET_SW_SOURCE_ADDR(c);
|
|
if (!(trace->unknown_sources & (1 << addr)))
|
|
WLOG("Unsupported source 0x%x size %d\n", addr, size);
|
|
trace->unknown_sources |= (1 << addr);
|
|
}
|
|
if (size == 1)
|
|
return TRACE_STATE_SKIP_1;
|
|
if (size == 2)
|
|
return TRACE_STATE_SKIP_2;
|
|
if (size == 3)
|
|
return TRACE_STATE_SKIP_4;
|
|
}
|
|
|
|
if (TRACE_OP_IS_LOCAL_TIME(c) || TRACE_OP_IS_GLOBAL_TIME(c)) {
|
|
trace->count_time_packets++;
|
|
return TRACE_OP_GET_CONTINUATION(c) ? TRACE_STATE_SKIP_FRAME
|
|
: TRACE_STATE_IDLE;
|
|
}
|
|
|
|
if (TRACE_OP_IS_EXTENSION(c)) {
|
|
return TRACE_OP_GET_CONTINUATION(c) ? TRACE_STATE_SKIP_FRAME
|
|
: TRACE_STATE_IDLE;
|
|
}
|
|
|
|
if (TRACE_OP_IS_OVERFLOW(c)) {
|
|
trace->count_hw_overflow++;
|
|
}
|
|
|
|
if (!(trace->unknown_opcodes[c / 8] & (1 << c % 8)))
|
|
WLOG("Unknown opcode 0x%02x\n", c);
|
|
trace->unknown_opcodes[c / 8] |= (1 << c % 8);
|
|
|
|
trace->count_error++;
|
|
return TRACE_OP_GET_CONTINUATION(c) ? TRACE_STATE_SKIP_FRAME
|
|
: TRACE_STATE_IDLE;
|
|
}
|
|
|
|
static trace_state update_trace(st_trace_t *trace, uint8_t c) {
|
|
trace->count_raw_bytes++;
|
|
|
|
// Parse the input using a state machine.
|
|
|
|
if (trace->state == TRACE_STATE_UNKNOWN) {
|
|
if (TRACE_OP_IS_TARGET_SOURCE(c) || TRACE_OP_IS_LOCAL_TIME(c) ||
|
|
TRACE_OP_IS_GLOBAL_TIME(c))
|
|
trace->state = TRACE_STATE_IDLE;
|
|
}
|
|
|
|
switch (trace->state) {
|
|
case TRACE_STATE_IDLE:
|
|
return update_trace_idle(trace, c);
|
|
|
|
case TRACE_STATE_TARGET_SOURCE:
|
|
putchar(c);
|
|
if (c == '\n')
|
|
fflush(stdout);
|
|
trace->count_target_data++;
|
|
return TRACE_STATE_IDLE;
|
|
|
|
case TRACE_STATE_SKIP_FRAME:
|
|
return TRACE_OP_GET_CONTINUATION(c) ? TRACE_STATE_SKIP_FRAME
|
|
: TRACE_STATE_IDLE;
|
|
|
|
case TRACE_STATE_SKIP_4:
|
|
return TRACE_STATE_SKIP_3;
|
|
|
|
case TRACE_STATE_SKIP_3:
|
|
return TRACE_STATE_SKIP_2;
|
|
|
|
case TRACE_STATE_SKIP_2:
|
|
return TRACE_STATE_SKIP_1;
|
|
|
|
case TRACE_STATE_SKIP_1:
|
|
return TRACE_STATE_IDLE;
|
|
|
|
case TRACE_STATE_UNKNOWN:
|
|
return TRACE_STATE_UNKNOWN;
|
|
|
|
default:
|
|
ELOG("Invalid state %d. This should never happen\n", trace->state);
|
|
return TRACE_STATE_IDLE;
|
|
}
|
|
}
|
|
|
|
static bool read_trace(stlink_t *stlink, st_trace_t *trace) {
|
|
uint8_t buffer[STLINK_TRACE_BUF_LEN];
|
|
int32_t length = stlink_trace_read(stlink, buffer, sizeof(buffer));
|
|
|
|
if (length < 0) {
|
|
ELOG("Error reading trace (%d)\n", length);
|
|
return false;
|
|
}
|
|
|
|
if (length == 0) {
|
|
usleep(100);
|
|
return true;
|
|
}
|
|
|
|
if (length == sizeof(buffer)) {
|
|
if (trace->count_sw_overflow++)
|
|
DLOG("Buffer overflow.\n");
|
|
else
|
|
WLOG("Buffer overflow. Try using a slower trace frequency.\n");
|
|
trace->state = TRACE_STATE_UNKNOWN;
|
|
}
|
|
|
|
for (int32_t i = 0; i < length; i++) {
|
|
trace->state = update_trace(trace, buffer[i]);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void check_for_configuration_error(stlink_t *stlink, st_trace_t *trace,
|
|
uint32_t trace_frequency) {
|
|
// Only check configuration one time after the first 10 seconds of running.
|
|
time_t elapsed_time_s = time(NULL) - trace->start_time;
|
|
if (trace->configuration_checked || elapsed_time_s < 10) {
|
|
return;
|
|
}
|
|
trace->configuration_checked = true;
|
|
|
|
// Simple huristic to determine if we are configured poorly.
|
|
bool error_no_data = (trace->count_raw_bytes < 100);
|
|
bool error_low_data =
|
|
(trace->count_time_packets < 10 && trace->count_target_data < 1000);
|
|
bool error_bad_data = (trace->count_error > 1 || trace->unknown_sources > 0);
|
|
bool error_dropped_data = (trace->count_sw_overflow > 0);
|
|
|
|
if (!error_no_data && !error_low_data && !error_bad_data &&
|
|
!error_dropped_data)
|
|
return;
|
|
|
|
WLOG("****\n");
|
|
WLOG("We do not appear to be retrieving data from the stlink correctly.\n");
|
|
|
|
if (error_dropped_data) {
|
|
WLOG("Try setting a slower trace frequency with the --trace=%d command "
|
|
"line option.\n",
|
|
trace_frequency / 2);
|
|
}
|
|
|
|
if (error_no_data || error_low_data || error_bad_data) {
|
|
uint32_t prescaler = 0;
|
|
stlink_read_debug32(stlink, STLINK_REG_TPI_ACPR, &prescaler);
|
|
if (prescaler) {
|
|
uint32_t system_clock_speed = (prescaler + 1) * trace_frequency;
|
|
WLOG("Verify the system clock is running at %d Hz.\n",
|
|
system_clock_speed);
|
|
}
|
|
WLOG("Try specifying the system clock with the --clock=XX command line "
|
|
"option.\n");
|
|
WLOG("Try setting the trace speed in your device's clock initialization "
|
|
"routine:\n");
|
|
WLOG(" TPI->ACPR = HAL_RCC_GetHCLKFreq() / %d - 1;\n", trace_frequency);
|
|
}
|
|
|
|
WLOG("Diagnostic Information:\n");
|
|
WLOG("Raw Bytes: %d\n", trace->count_raw_bytes);
|
|
WLOG("Target Data: %d\n", trace->count_target_data);
|
|
WLOG("Time Packets: %d\n", trace->count_time_packets);
|
|
WLOG("Hardware Overflow Count: %d\n", trace->count_hw_overflow);
|
|
WLOG("Software Overflow Count: %d\n", trace->count_sw_overflow);
|
|
WLOG("Errors: %d\n", trace->count_error);
|
|
|
|
char buffer[1024];
|
|
memset(buffer, 0, sizeof(buffer));
|
|
uint32_t offset = 0;
|
|
for (uint32_t i = 0; i <= 0xFF; i++)
|
|
if (trace->unknown_opcodes[i / 8] & (1 << i % 8)) {
|
|
uint32_t n =
|
|
snprintf(buffer + offset, sizeof(buffer) - offset, "%02x, ", i);
|
|
if (n >= sizeof(buffer) - offset) {
|
|
break;
|
|
}
|
|
offset += n;
|
|
}
|
|
WLOG("Unknown Opcodes: %s\n", buffer);
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
offset = 0;
|
|
for (uint32_t i = 0; i < 32; i++)
|
|
if (trace->unknown_sources & (1 << i)) {
|
|
uint32_t n =
|
|
snprintf(buffer + offset, sizeof(buffer) - offset, "%d, ", i);
|
|
if (n >= sizeof(buffer) - offset) {
|
|
break;
|
|
}
|
|
offset += n;
|
|
}
|
|
WLOG("Unknown Sources: %s\n", buffer);
|
|
|
|
WLOG("Chip ID: 0x%04x\n", stlink->chip_id);
|
|
WLOG("****\n");
|
|
}
|
|
|
|
int32_t main(int32_t argc, char **argv) {
|
|
#if defined(_WIN32)
|
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE);
|
|
#else
|
|
signal(SIGINT, &abort_trace);
|
|
signal(SIGTERM, &abort_trace);
|
|
signal(SIGSEGV, &abort_trace);
|
|
signal(SIGPIPE, &abort_trace);
|
|
#endif
|
|
|
|
st_settings_t settings;
|
|
if (!parse_options(argc, argv, &settings)) {
|
|
usage();
|
|
return APP_RESULT_INVALID_PARAMS;
|
|
}
|
|
init_chipids (STLINK_CHIPS_DIR);
|
|
|
|
DLOG("show_help = %s\n", settings.show_help ? "true" : "false");
|
|
DLOG("show_version = %s\n", settings.show_version ? "true" : "false");
|
|
DLOG("logging_level = %d\n", settings.logging_level);
|
|
DLOG("core_frequency = %d Hz\n", settings.core_frequency);
|
|
DLOG("trace_frequency = %d Hz\n", settings.trace_frequency);
|
|
DLOG("reset_board = %s\n", settings.reset_board ? "true" : "false");
|
|
DLOG("force = %s\n", settings.force ? "true" : "false");
|
|
DLOG("serial_number = %s\n",
|
|
settings.serial_number ? settings.serial_number : "any");
|
|
|
|
if (settings.show_help) {
|
|
usage();
|
|
return APP_RESULT_SUCCESS;
|
|
}
|
|
|
|
if (settings.show_version) {
|
|
printf("v%s\n", STLINK_VERSION);
|
|
return APP_RESULT_SUCCESS;
|
|
}
|
|
|
|
stlink_t *stlink = stlink_connect(&settings);
|
|
if (!stlink) {
|
|
ELOG("Unable to locate an stlink\n");
|
|
return APP_RESULT_STLINK_NOT_FOUND;
|
|
}
|
|
|
|
stlink->verbose = settings.logging_level;
|
|
|
|
if (stlink->chip_id == STM32_CHIPID_UNKNOWN) {
|
|
ELOG("Your stlink is not connected to a device\n");
|
|
if (!settings.force)
|
|
return APP_RESULT_STLINK_MISSING_DEVICE;
|
|
}
|
|
|
|
if (!(stlink->version.flags & STLINK_F_HAS_TRACE)) {
|
|
ELOG("Your stlink does not support tracing\n");
|
|
if (!settings.force)
|
|
return APP_RESULT_STLINK_UNSUPPORTED_LINK;
|
|
}
|
|
|
|
if (!(stlink->chip_flags & CHIP_F_HAS_SWO_TRACING)) {
|
|
const struct stlink_chipid_params *params =
|
|
stlink_chipid_get_params(stlink->chip_id);
|
|
ELOG("We do not support SWO output for device '%s'\n",
|
|
params ? params->dev_type : "");
|
|
if (!settings.force)
|
|
return APP_RESULT_STLINK_UNSUPPORTED_DEVICE;
|
|
}
|
|
|
|
uint32_t trace_frequency = settings.trace_frequency;
|
|
if (!trace_frequency)
|
|
trace_frequency = STLINK_DEFAULT_TRACE_FREQUENCY;
|
|
uint32_t max_trace_freq = stlink->max_trace_freq;
|
|
uint32_t min_trace_freq = 0;
|
|
|
|
if (settings.core_frequency != 0) {
|
|
if (max_trace_freq > settings.core_frequency / 5)
|
|
max_trace_freq = settings.core_frequency / 5;
|
|
min_trace_freq = settings.core_frequency / (STLINK_REG_TPI_ACPR_MAX + 1);
|
|
}
|
|
if (trace_frequency > max_trace_freq ||
|
|
trace_frequency < min_trace_freq) {
|
|
ELOG("Invalid trace frequency %d (min %d max %d)\n", trace_frequency,
|
|
min_trace_freq, max_trace_freq);
|
|
if (!settings.force)
|
|
return APP_RESULT_UNSUPPORTED_TRACE_FREQUENCY;
|
|
}
|
|
|
|
if (!enable_trace(stlink, &settings, trace_frequency)) {
|
|
ELOG("Unable to enable trace mode\n");
|
|
if (!settings.force)
|
|
return APP_RESULT_STLINK_STATE_ERROR;
|
|
}
|
|
|
|
ILOG("Reading Trace\n");
|
|
st_trace_t trace;
|
|
memset(&trace, 0, sizeof(trace));
|
|
trace.start_time = time(NULL);
|
|
|
|
if (stlink_run(stlink, RUN_NORMAL)) {
|
|
ELOG("Unable to run device\n");
|
|
if (!settings.force)
|
|
return APP_RESULT_STLINK_STATE_ERROR;
|
|
}
|
|
|
|
while (!g_abort_trace && read_trace(stlink, &trace)) {
|
|
check_for_configuration_error(stlink, &trace, trace_frequency);
|
|
}
|
|
|
|
stlink_trace_disable(stlink);
|
|
stlink_close(stlink);
|
|
|
|
return APP_RESULT_SUCCESS;
|
|
}
|