From 2dc88e1479f29ef0141b05bfcd907ad9a3e2d54c Mon Sep 17 00:00:00 2001 From: Marc Schink Date: Fri, 2 Dec 2016 15:39:23 +0100 Subject: [PATCH] target/armv7m_trace: Improve SWO frequency auto-detection The SWO frequency auto-detection with J-Link adapters does not work properly in the current implementation. This is because the trace layer has only information about the highest possible SWO frequency supported by the adapter. With that the trace layer calculates the SWO prescaler which usually leads to a frequency deviation greater than what is permitted by J-Link adapters. Move the calculation of the SWO prescaler from the trace layer into the trace configuration of the adapter to overcome this problem. The adapter has the necessary information to choose a suitable SWO frequency and calculate the corresponding prescaler that complies with the maximum allowed frequency deviation. Tested with: - STM32L152RC Discovery Kit (ST-Link) - EFM32GG-STK3700 (J-Link) Change-Id: I38ff2b89d32f0a92c597989b590afe5c75cf4902 Signed-off-by: Marc Schink Reviewed-on: http://openocd.zylin.com/3903 Tested-by: jenkins Reviewed-by: Paul Fertser --- src/jtag/core.c | 11 ++-- src/jtag/drivers/jlink.c | 106 ++++++++++++++++++++++++++-------- src/jtag/drivers/stlink_usb.c | 20 ++++++- src/jtag/hla/hla_interface.c | 7 ++- src/jtag/hla/hla_layout.h | 6 +- src/jtag/interface.h | 9 ++- src/target/armv7m_trace.c | 26 ++------- src/target/cortex_m.h | 3 + 8 files changed, 129 insertions(+), 59 deletions(-) diff --git a/src/jtag/core.c b/src/jtag/core.c index 144cf94f7d..a498a8cf48 100644 --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -1918,12 +1918,13 @@ void adapter_deassert_reset(void) } int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq) + uint32_t port_size, unsigned int *trace_freq, + unsigned int traceclkin_freq, uint16_t *prescaler) { - if (jtag->config_trace) - return jtag->config_trace(enabled, pin_protocol, port_size, - trace_freq); - else if (enabled) { + if (jtag->config_trace) { + return jtag->config_trace(enabled, pin_protocol, port_size, trace_freq, + traceclkin_freq, prescaler); + } else if (enabled) { LOG_ERROR("The selected interface does not support tracing"); return ERROR_FAIL; } diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index 09b3a858bc..d2a8712250 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -62,6 +63,9 @@ static bool trace_enabled; static unsigned int swd_buffer_size = JLINK_TAP_BUFFER_SIZE; +/* Maximum SWO frequency deviation. */ +#define SWO_MAX_FREQ_DEV 0.03 + /* 256 byte non-volatile memory */ struct device_config { uint8_t usb_address; @@ -1267,42 +1271,63 @@ static uint32_t calculate_trace_buffer_size(void) return tmp & 0xffffff00; } -static bool check_trace_freq(struct jaylink_swo_speed speed, - uint32_t trace_freq) +static bool calculate_swo_prescaler(unsigned int traceclkin_freq, + uint32_t trace_freq, uint16_t *prescaler) { - double min; + unsigned int presc; double deviation; + + presc = ((1.0 - SWO_MAX_FREQ_DEV) * traceclkin_freq) / trace_freq + 1; + + if (presc > TPIU_ACPR_MAX_SWOSCALER) + return false; + + deviation = fabs(1.0 - ((double)trace_freq * presc / traceclkin_freq)); + + if (deviation > SWO_MAX_FREQ_DEV) + return false; + + *prescaler = presc; + + return true; +} + +static bool detect_swo_freq_and_prescaler(struct jaylink_swo_speed speed, + unsigned int traceclkin_freq, uint32_t *trace_freq, + uint16_t *prescaler) +{ uint32_t divider; + unsigned int presc; + double deviation; + + for (divider = speed.min_div; divider <= speed.max_div; divider++) { + *trace_freq = speed.freq / divider; + presc = ((1.0 - SWO_MAX_FREQ_DEV) * traceclkin_freq) / *trace_freq + 1; - min = fabs(1.0 - (speed.freq / ((double)trace_freq * speed.min_div))); + if (presc > TPIU_ACPR_MAX_SWOSCALER) + break; - for (divider = speed.min_div; divider < speed.max_div; divider++) { - deviation = fabs(1.0 - (speed.freq / ((double)trace_freq * divider))); + deviation = fabs(1.0 - ((double)*trace_freq * presc / traceclkin_freq)); - if (deviation < 0.03) { - LOG_DEBUG("Found suitable frequency divider %u with deviation of " - "%.02f %%.", divider, deviation); + if (deviation <= SWO_MAX_FREQ_DEV) { + *prescaler = presc; return true; } - - if (deviation < min) - min = deviation; } - LOG_ERROR("Selected trace frequency is not supported by the device. " - "Please choose a different trace frequency."); - LOG_ERROR("Maximum permitted deviation is 3.00 %%, but only %.02f %% " - "could be achieved.", min * 100); - return false; } static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq) + uint32_t port_size, unsigned int *trace_freq, + unsigned int traceclkin_freq, uint16_t *prescaler) { int ret; uint32_t buffer_size; struct jaylink_swo_speed speed; + uint32_t divider; + uint32_t min_freq; + uint32_t max_freq; if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SWO)) { LOG_ERROR("Trace capturing is not supported by the device."); @@ -1349,13 +1374,45 @@ static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, return ERROR_FAIL; } - if (!*trace_freq) - *trace_freq = speed.freq / speed.min_div; + if (*trace_freq > 0) { + divider = speed.freq / *trace_freq; + min_freq = speed.freq / speed.max_div; + max_freq = speed.freq / speed.min_div; + + if (*trace_freq > max_freq) { + LOG_INFO("Given SWO frequency too high, using %u Hz instead.", + max_freq); + *trace_freq = max_freq; + } else if (*trace_freq < min_freq) { + LOG_INFO("Given SWO frequency too low, using %u Hz instead.", + min_freq); + *trace_freq = min_freq; + } else if (*trace_freq != speed.freq / divider) { + *trace_freq = speed.freq / divider; + + LOG_INFO("Given SWO frequency is not supported by the device, " + "using %u Hz instead.", *trace_freq); + } - if (!check_trace_freq(speed, *trace_freq)) - return ERROR_FAIL; + if (!calculate_swo_prescaler(traceclkin_freq, *trace_freq, + prescaler)) { + LOG_ERROR("SWO frequency is not suitable. Please choose a " + "different frequency or use auto-detection."); + return ERROR_FAIL; + } + } else { + LOG_INFO("Trying to auto-detect SWO frequency."); - LOG_DEBUG("Using %u bytes device memory for trace capturing.", buffer_size); + if (!detect_swo_freq_and_prescaler(speed, traceclkin_freq, trace_freq, + prescaler)) { + LOG_ERROR("Maximum permitted frequency deviation of %.02f %% " + "could not be achieved.", SWO_MAX_FREQ_DEV); + LOG_ERROR("Auto-detection of SWO frequency failed."); + return ERROR_FAIL; + } + + LOG_INFO("Using SWO frequency of %u Hz.", *trace_freq); + } ret = jaylink_swo_start(devh, JAYLINK_SWO_MODE_UART, *trace_freq, buffer_size); @@ -1365,6 +1422,9 @@ static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, return ERROR_FAIL; } + LOG_DEBUG("Using %u bytes device memory for trace capturing.", + buffer_size); + /* * Adjust the SWD transaction buffer size as starting SWO capturing * allocates device internal memory. diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 12e1175f59..a168104137 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -2883,10 +2883,13 @@ error_open: return ERROR_FAIL; } -int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq) +int stlink_config_trace(void *handle, bool enabled, + enum tpiu_pin_protocol pin_protocol, uint32_t port_size, + unsigned int *trace_freq, unsigned int traceclkin_freq, + uint16_t *prescaler) { struct stlink_usb_handle_s *h = handle; + uint16_t presc; if (enabled && (!(h->version.flags & STLINK_F_HAS_TRACE) || pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART)) { @@ -2909,6 +2912,19 @@ int stlink_config_trace(void *handle, bool enabled, enum tpiu_pin_protocol pin_p if (!*trace_freq) *trace_freq = STLINK_TRACE_MAX_HZ; + + presc = traceclkin_freq / *trace_freq; + + if (traceclkin_freq % *trace_freq > 0) + presc++; + + if (presc > TPIU_ACPR_MAX_SWOSCALER) { + LOG_ERROR("SWO frequency is not suitable. Please choose a different " + "frequency."); + return ERROR_FAIL; + } + + *prescaler = presc; h->trace.source_hz = *trace_freq; return stlink_usb_trace_enable(h); diff --git a/src/jtag/hla/hla_interface.c b/src/jtag/hla/hla_interface.c index 2abed210d5..7d9dea05e3 100644 --- a/src/jtag/hla/hla_interface.c +++ b/src/jtag/hla/hla_interface.c @@ -192,11 +192,12 @@ int hl_interface_override_target(const char **targetname) } int hl_interface_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq) + uint32_t port_size, unsigned int *trace_freq, + unsigned int traceclkin_freq, uint16_t *prescaler) { if (hl_if.layout->api->config_trace) - return hl_if.layout->api->config_trace(hl_if.handle, enabled, pin_protocol, - port_size, trace_freq); + return hl_if.layout->api->config_trace(hl_if.handle, enabled, + pin_protocol, port_size, trace_freq, traceclkin_freq, prescaler); else if (enabled) { LOG_ERROR("The selected interface does not support tracing"); return ERROR_FAIL; diff --git a/src/jtag/hla/hla_layout.h b/src/jtag/hla/hla_layout.h index 9f41b59a42..1d759e17df 100644 --- a/src/jtag/hla/hla_layout.h +++ b/src/jtag/hla/hla_layout.h @@ -91,8 +91,10 @@ struct hl_layout_api_s { * its maximum supported rate there * @returns ERROR_OK on success, an error code on failure. */ - int (*config_trace)(void *handle, bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq); + int (*config_trace)(void *handle, bool enabled, + enum tpiu_pin_protocol pin_protocol, uint32_t port_size, + unsigned int *trace_freq, unsigned int traceclkin_freq, + uint16_t *prescaler); /** * Poll for new trace data * diff --git a/src/jtag/interface.h b/src/jtag/interface.h index 905f1eb626..ba3dea6d70 100644 --- a/src/jtag/interface.h +++ b/src/jtag/interface.h @@ -303,10 +303,14 @@ struct jtag_interface { * @param trace_freq A pointer to the configured trace * frequency; if it points to 0, the adapter driver must write * its maximum supported rate there + * @param traceclkin_freq TRACECLKIN frequency provided to the TPIU in Hz + * @param prescaler Pointer to the SWO prescaler calculated by the + * adapter * @returns ERROR_OK on success, an error code on failure. */ int (*config_trace)(bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq); + uint32_t port_size, unsigned int *trace_freq, + unsigned int traceclkin_freq, uint16_t *prescaler); /** * Poll for new trace data @@ -325,7 +329,8 @@ extern const char * const jtag_only[]; void adapter_assert_reset(void); void adapter_deassert_reset(void); int adapter_config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol, - uint32_t port_size, unsigned int *trace_freq); + uint32_t port_size, unsigned int *trace_freq, + unsigned int traceclkin_freq, uint16_t *prescaler); int adapter_poll_trace(uint8_t *buf, size_t *size); #endif /* OPENOCD_JTAG_INTERFACE_H */ diff --git a/src/target/armv7m_trace.c b/src/target/armv7m_trace.c index 6170119d9e..853362f7ea 100644 --- a/src/target/armv7m_trace.c +++ b/src/target/armv7m_trace.c @@ -56,16 +56,15 @@ 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; + uint16_t prescaler; int retval; target_unregister_timer_callback(armv7m_poll_trace, target); - retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL, - trace_config->pin_protocol, - trace_config->port_size, - &trace_config->trace_freq); + trace_config->pin_protocol, trace_config->port_size, + &trace_config->trace_freq, trace_config->traceclkin_freq, &prescaler); + if (retval != ERROR_OK) return retval; @@ -74,23 +73,6 @@ int armv7m_trace_tpiu_config(struct target *target) return ERROR_FAIL; } - prescaler = trace_config->traceclkin_freq / trace_config->trace_freq; - - if (trace_config->traceclkin_freq % trace_config->trace_freq) { - prescaler++; - int trace_freq = trace_config->traceclkin_freq / prescaler; - LOG_INFO("Can not obtain %u trace port frequency from %u TRACECLKIN frequency, using %u instead", - trace_config->trace_freq, trace_config->traceclkin_freq, - trace_freq); - trace_config->trace_freq = trace_freq; - retval = adapter_config_trace(trace_config->config_type == TRACE_CONFIG_TYPE_INTERNAL, - trace_config->pin_protocol, - trace_config->port_size, - &trace_config->trace_freq); - if (retval != ERROR_OK) - return retval; - } - retval = target_write_u32(target, TPIU_CSPSR, 1 << trace_config->port_size); if (retval != ERROR_OK) return retval; diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h index 54d7a02287..505a09b68e 100644 --- a/src/target/cortex_m.h +++ b/src/target/cortex_m.h @@ -86,6 +86,9 @@ #define TPIU_FFCR 0xE0040304 #define TPIU_FSCR 0xE0040308 +/* Maximum SWO prescaler value. */ +#define TPIU_ACPR_MAX_SWOSCALER 0x1fff + /* DCB_DHCSR bit and field definitions */ #define DBGKEY (0xA05F << 16) #define C_DEBUGEN (1 << 0) -- 2.30.2