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 <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/3903
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
This commit is contained in:
Marc Schink 2016-12-02 15:39:23 +01:00 committed by Paul Fertser
parent 90bd7d1482
commit 2dc88e1479
8 changed files with 129 additions and 59 deletions

View File

@ -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;
}

View File

@ -39,6 +39,7 @@
#include <jtag/swd.h>
#include <jtag/commands.h>
#include <jtag/drivers/jtag_usb_common.h>
#include <target/cortex_m.h>
#include <libjaylink/libjaylink.h>
@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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
*

View File

@ -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 */

View File

@ -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;

View File

@ -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)