target/armv7m_trace: Improve SWO frequency auto-detection 03/3903/5
authorMarc Schink <openocd-dev@marcschink.de>
Fri, 2 Dec 2016 14:39:23 +0000 (15:39 +0100)
committerPaul Fertser <fercerpav@gmail.com>
Sat, 21 Dec 2019 19:25:32 +0000 (19:25 +0000)
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>
src/jtag/core.c
src/jtag/drivers/jlink.c
src/jtag/drivers/stlink_usb.c
src/jtag/hla/hla_interface.c
src/jtag/hla/hla_layout.h
src/jtag/interface.h
src/target/armv7m_trace.c
src/target/cortex_m.h

index 144cf94f7d37582a63d6c190cc282ff71630613b..a498a8cf48f25e0334824e526e569ad9cd8e6264 100644 (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;
        }
index 09b3a858bcc1203d02c5144b2f59e994987e6737..d2a8712250fd1e9e638cd0ba4fa844a3750ce31a 100644 (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;
+
+       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.
index 12e1175f59473472a02908ae8b2afe7099a6233c..a168104137ee9f379ebc93ce558c9035579553f3 100644 (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);
index 2abed210d5b86a2ca70b0d2491a4f46f1dddc88b..7d9dea05e36f2ae44723b1ae5aeb62e75b89d30d 100644 (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;
index 9f41b59a42bc90efc5dca8e8ed88b7c8a79dd4e8..1d759e17dfaecb1706d00431928c435b8b202a1f 100644 (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
         *
index 905f1eb6267226ba4018c86c6d31a55cf291adea..ba3dea6d703d24b3fe6c34c4ea209bc92acec30d 100644 (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 */
index 6170119d9effc698daca348233bac183e70a48b8..853362f7eab610ca5cf724e36498ff8307b35424 100644 (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;
index 54d7a02287ea25dc672f90b955368509c22af58a..505a09b68e555394408d91b28fed7566e2804392 100644 (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)

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)