diff --git a/src/jtag/core.c b/src/jtag/core.c index 144cf94f7..a498a8cf4 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 09b3a858b..d2a871225 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; - min = fabs(1.0 - (speed.freq / ((double)trace_freq * speed.min_div))); + 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; - for (divider = speed.min_div; divider < speed.max_div; divider++) { - deviation = fabs(1.0 - (speed.freq / ((double)trace_freq * divider))); + if (presc > TPIU_ACPR_MAX_SWOSCALER) + break; - if (deviation < 0.03) { - LOG_DEBUG("Found suitable frequency divider %u with deviation of " - "%.02f %%.", divider, deviation); + deviation = fabs(1.0 - ((double)*trace_freq * presc / traceclkin_freq)); + + 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 (!check_trace_freq(speed, *trace_freq)) - return ERROR_FAIL; + 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_DEBUG("Using %u bytes device memory for trace capturing.", buffer_size); + LOG_INFO("Given SWO frequency is not supported by the device, " + "using %u Hz instead.", *trace_freq); + } + + 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."); + + 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 12e1175f5..a16810413 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 2abed210d..7d9dea05e 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 9f41b59a4..1d759e17d 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 905f1eb62..ba3dea6d7 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 6170119d9..853362f7e 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 54d7a0228..505a09b68 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)