drivers/jlink: fix check for max prescaler
[openocd.git] / src / jtag / drivers / jlink.c
index cc04953cd690d15fec1b6b3c0f9841f4111ad969..b915707eca9f301fa7149ce5ff450c140239189b 100644 (file)
@@ -38,6 +38,8 @@
 #include <jtag/interface.h>
 #include <jtag/swd.h>
 #include <jtag/commands.h>
+#include <jtag/drivers/jtag_usb_common.h>
+#include <target/cortex_m.h>
 
 #include <libjaylink/libjaylink.h>
 
@@ -50,6 +52,7 @@ static uint8_t caps[JAYLINK_DEV_EXT_CAPS_SIZE];
 
 static uint32_t serial_number;
 static bool use_serial_number;
+static bool use_usb_location;
 static enum jaylink_usb_address usb_address;
 static bool use_usb_address;
 static enum jaylink_target_interface iface = JAYLINK_TIF_JTAG;
@@ -60,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;
@@ -88,6 +94,7 @@ static void jlink_path_move(int num_states, tap_state_t *path);
 static void jlink_stableclocks(int num_cycles);
 static void jlink_runtest(int num_cycles);
 static void jlink_reset(int trst, int srst);
+static int jlink_reset_safe(int trst, int srst);
 static int jlink_swd_run_queue(void);
 static void jlink_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint32_t ap_delay_clk);
 static int jlink_swd_switch_seq(enum swd_special_seq seq);
@@ -122,13 +129,13 @@ static int queued_retval;
 
 static void jlink_execute_stableclocks(struct jtag_command *cmd)
 {
-       DEBUG_JTAG_IO("stableclocks %i cycles", cmd->cmd.runtest->num_cycles);
+       LOG_DEBUG_IO("stableclocks %i cycles", cmd->cmd.runtest->num_cycles);
        jlink_stableclocks(cmd->cmd.runtest->num_cycles);
 }
 
 static void jlink_execute_runtest(struct jtag_command *cmd)
 {
-       DEBUG_JTAG_IO("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles,
+       LOG_DEBUG_IO("runtest %i cycles, end in %i", cmd->cmd.runtest->num_cycles,
                cmd->cmd.runtest->end_state);
 
        jlink_end_state(cmd->cmd.runtest->end_state);
@@ -137,7 +144,7 @@ static void jlink_execute_runtest(struct jtag_command *cmd)
 
 static void jlink_execute_statemove(struct jtag_command *cmd)
 {
-       DEBUG_JTAG_IO("statemove end in %i", cmd->cmd.statemove->end_state);
+       LOG_DEBUG_IO("statemove end in %i", cmd->cmd.statemove->end_state);
 
        jlink_end_state(cmd->cmd.statemove->end_state);
        jlink_state_move();
@@ -145,7 +152,7 @@ static void jlink_execute_statemove(struct jtag_command *cmd)
 
 static void jlink_execute_pathmove(struct jtag_command *cmd)
 {
-       DEBUG_JTAG_IO("pathmove: %i states, end in %i",
+       LOG_DEBUG_IO("pathmove: %i states, end in %i",
                cmd->cmd.pathmove->num_states,
                cmd->cmd.pathmove->path[cmd->cmd.pathmove->num_states - 1]);
 
@@ -154,7 +161,7 @@ static void jlink_execute_pathmove(struct jtag_command *cmd)
 
 static void jlink_execute_scan(struct jtag_command *cmd)
 {
-       DEBUG_JTAG_IO("%s type:%d", cmd->cmd.scan->ir_scan ? "IRSCAN" : "DRSCAN",
+       LOG_DEBUG_IO("%s type:%d", cmd->cmd.scan->ir_scan ? "IRSCAN" : "DRSCAN",
                jtag_scan_type(cmd->cmd.scan));
 
        /* Make sure there are no trailing fields with num_bits == 0, or the logic below will fail. */
@@ -188,7 +195,7 @@ static void jlink_execute_scan(struct jtag_command *cmd)
 
        for (int i = 0; i < cmd->cmd.scan->num_fields; i++, field++) {
                scan_size += field->num_bits;
-               DEBUG_JTAG_IO("%s%s field %d/%d %d bits",
+               LOG_DEBUG_IO("%s%s field %d/%d %d bits",
                        field->in_value ? "in" : "",
                        field->out_value ? "out" : "",
                        i,
@@ -240,24 +247,14 @@ static void jlink_execute_scan(struct jtag_command *cmd)
                jlink_state_move();
        }
 
-       DEBUG_JTAG_IO("%s scan, %i bits, end in %s",
+       LOG_DEBUG_IO("%s scan, %i bits, end in %s",
                (cmd->cmd.scan->ir_scan) ? "IR" : "DR", scan_size,
                tap_state_name(tap_get_end_state()));
 }
 
-static void jlink_execute_reset(struct jtag_command *cmd)
-{
-       DEBUG_JTAG_IO("reset trst: %i srst %i", cmd->cmd.reset->trst,
-               cmd->cmd.reset->srst);
-
-       jlink_flush();
-       jlink_reset(cmd->cmd.reset->trst, cmd->cmd.reset->srst);
-       jlink_flush();
-}
-
 static void jlink_execute_sleep(struct jtag_command *cmd)
 {
-       DEBUG_JTAG_IO("sleep %" PRIi32 "", cmd->cmd.sleep->us);
+       LOG_DEBUG_IO("sleep %" PRIu32 "", cmd->cmd.sleep->us);
        jlink_flush();
        jtag_sleep(cmd->cmd.sleep->us);
 }
@@ -280,9 +277,6 @@ static int jlink_execute_command(struct jtag_command *cmd)
                case JTAG_SCAN:
                        jlink_execute_scan(cmd);
                        break;
-               case JTAG_RESET:
-                       jlink_execute_reset(cmd);
-                       break;
                case JTAG_SLEEP:
                        jlink_execute_sleep(cmd);
                        break;
@@ -486,7 +480,7 @@ static bool adjust_swd_buffer_size(void)
        }
 
        if (tmp < 143) {
-               LOG_ERROR("Not enough free device internal memory: %u bytes.", tmp);
+               LOG_ERROR("Not enough free device internal memory: %" PRIu32 " bytes.", tmp);
                return false;
        }
 
@@ -534,6 +528,31 @@ static int jaylink_log_handler(const struct jaylink_context *ctx,
        return 0;
 }
 
+static bool jlink_usb_location_equal(struct jaylink_device *dev)
+{
+       int retval;
+       uint8_t bus;
+       uint8_t *ports;
+       size_t num_ports;
+       bool equal = false;
+
+       retval = jaylink_device_get_usb_bus_ports(dev, &bus, &ports, &num_ports);
+
+       if (retval == JAYLINK_ERR_NOT_SUPPORTED) {
+               return false;
+       } else if (retval != JAYLINK_OK) {
+               LOG_WARNING("jaylink_device_get_usb_bus_ports() failed: %s.",
+                       jaylink_strerror(retval));
+               return false;
+       }
+
+       equal = jtag_usb_location_equal(bus, ports,     num_ports);
+       free(ports);
+
+       return equal;
+}
+
+
 static int jlink_init(void)
 {
        int ret;
@@ -595,7 +614,9 @@ static int jlink_init(void)
                return ERROR_JTAG_INIT_FAILED;
        }
 
-       if (!use_serial_number && !use_usb_address && num_devices > 1) {
+       use_usb_location = (jtag_usb_get_location() != NULL);
+
+       if (!use_serial_number && !use_usb_address && !use_usb_location && num_devices > 1) {
                LOG_ERROR("Multiple devices found, specify the desired device.");
                jaylink_free_devices(devs, true);
                jaylink_exit(jayctx);
@@ -605,8 +626,10 @@ static int jlink_init(void)
        found_device = false;
 
        for (i = 0; devs[i]; i++) {
+               struct jaylink_device *dev = devs[i];
+
                if (use_serial_number) {
-                       ret = jaylink_device_get_serial_number(devs[i], &tmp);
+                       ret = jaylink_device_get_serial_number(dev, &tmp);
 
                        if (ret == JAYLINK_ERR_NOT_AVAILABLE) {
                                continue;
@@ -621,7 +644,7 @@ static int jlink_init(void)
                }
 
                if (use_usb_address) {
-                       ret = jaylink_device_get_usb_address(devs[i], &address);
+                       ret = jaylink_device_get_usb_address(dev, &address);
 
                        if (ret == JAYLINK_ERR_NOT_SUPPORTED) {
                                continue;
@@ -635,7 +658,10 @@ static int jlink_init(void)
                                continue;
                }
 
-               ret = jaylink_open(devs[i], &devh);
+               if (use_usb_location && !jlink_usb_location_equal(dev))
+                       continue;
+
+               ret = jaylink_open(dev, &devh);
 
                if (ret == JAYLINK_OK) {
                        found_device = true;
@@ -918,6 +944,13 @@ static void jlink_reset(int trst, int srst)
                jaylink_jtag_set_trst(devh);
 }
 
+static int jlink_reset_safe(int trst, int srst)
+{
+       jlink_flush();
+       jlink_reset(trst, srst);
+       return jlink_flush();
+}
+
 COMMAND_HANDLER(jlink_usb_command)
 {
        int tmp;
@@ -1016,7 +1049,7 @@ COMMAND_HANDLER(jlink_handle_free_memory_command)
                return ERROR_FAIL;
        }
 
-       command_print(CMD, "Device has %u bytes of free memory.", tmp);
+       command_print(CMD, "Device has %" PRIu32 " bytes of free memory.", tmp);
 
        return ERROR_OK;
 }
@@ -1233,55 +1266,71 @@ 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)
+{
+       unsigned int presc = (traceclkin_freq + trace_freq / 2) / trace_freq;
+       if (presc == 0 || presc > TPIU_ACPR_MAX_SWOSCALER + 1)
+               return false;
+
+       /* Probe's UART speed must be within 3% of the TPIU's SWO baud rate. */
+       unsigned int max_deviation = (traceclkin_freq * 3) / 100;
+       if (presc * trace_freq < traceclkin_freq - max_deviation ||
+           presc * trace_freq > traceclkin_freq + max_deviation)
+               return false;
+
+       *prescaler = presc;
+
+       return true;
+}
+
+static bool detect_swo_freq_and_prescaler(struct jaylink_swo_speed speed,
+               unsigned int traceclkin_freq, unsigned int *trace_freq,
+               uint16_t *prescaler)
 {
-       double min;
-       double deviation;
        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 + 1)
+                       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;
+
+       trace_enabled = enabled;
 
        if (!jaylink_has_cap(caps, JAYLINK_DEV_CAP_SWO)) {
-               LOG_ERROR("Trace capturing is not supported by the device.");
-               return ERROR_FAIL;
-       }
+               if (!enabled)
+                       return ERROR_OK;
 
-       if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) {
-               LOG_ERROR("Selected pin protocol is not supported.");
+               LOG_ERROR("Trace capturing is not supported by the device.");
                return ERROR_FAIL;
        }
 
-       trace_enabled = enabled;
-
        ret = jaylink_swo_stop(devh);
 
        if (ret != JAYLINK_OK) {
@@ -1300,6 +1349,11 @@ static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
                return ERROR_OK;
        }
 
+       if (pin_protocol != TPIU_PIN_PROTOCOL_ASYNC_UART) {
+               LOG_ERROR("Selected pin protocol is not supported.");
+               return ERROR_FAIL;
+       }
+
        buffer_size = calculate_trace_buffer_size();
 
        if (!buffer_size) {
@@ -1315,13 +1369,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 %" PRIu32 " Hz instead.",
+                               max_freq);
+                       *trace_freq = max_freq;
+               } else if (*trace_freq < min_freq) {
+                       LOG_INFO("Given SWO frequency too low, using %" PRIu32 " 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);
@@ -1331,6 +1417,9 @@ static int config_trace(bool enabled, enum tpiu_pin_protocol pin_protocol,
                return ERROR_FAIL;
        }
 
+       LOG_DEBUG("Using %" PRIu32 " bytes device memory for trace capturing.",
+               buffer_size);
+
        /*
         * Adjust the SWD transaction buffer size as starting SWO capturing
         * allocates device internal memory.
@@ -1436,7 +1525,7 @@ COMMAND_HANDLER(jlink_handle_config_mac_address_command)
        } else if (CMD_ARGC == 1) {
                str = CMD_ARGV[0];
 
-               if ((strlen(str) != 17) || (str[2] != ':' || str[5] != ':' || \
+               if ((strlen(str) != 17) || (str[2] != ':' || str[5] != ':' ||
                                str[8] != ':' || str[11] != ':' || str[14] != ':')) {
                        command_print(CMD, "Invalid MAC address format.");
                        return ERROR_COMMAND_SYNTAX_ERROR;
@@ -1715,7 +1804,7 @@ COMMAND_HANDLER(jlink_handle_emucom_read_command)
                return ERROR_FAIL;
        } else if (ret == JAYLINK_ERR_DEV_NOT_AVAILABLE) {
                LOG_ERROR("Channel is not available for the requested amount of data. "
-                       "%" PRIu32 " bytes are avilable.", length);
+                       "%" PRIu32 " bytes are available.", length);
                free(buf);
                return ERROR_FAIL;
        } else if (ret != JAYLINK_OK) {
@@ -1895,14 +1984,6 @@ static void jlink_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_c
        jlink_swd_queue_cmd(cmd, value, 0, ap_delay_clk);
 }
 
-static int_least32_t jlink_swd_frequency(int_least32_t hz)
-{
-       if (hz > 0)
-               jlink_speed(hz / 1000);
-
-       return hz;
-}
-
 /***************************************************************************/
 /* J-Link tap functions */
 
@@ -2004,7 +2085,7 @@ static int jlink_flush(void)
                buf_set_buf(tdo_buffer, p->first, p->buffer,
                            p->buffer_offset, p->length);
 
-               DEBUG_JTAG_IO("Pending scan result, length = %d.", p->length);
+               LOG_DEBUG_IO("Pending scan result, length = %d.", p->length);
        }
 
        jlink_tap_init();
@@ -2178,7 +2259,6 @@ static void jlink_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint3
 
 static const struct swd_driver jlink_swd = {
        .init = &jlink_swd_init,
-       .frequency = &jlink_swd_frequency,
        .switch_seq = &jlink_swd_switch_seq,
        .read_reg = &jlink_swd_read_reg,
        .write_reg = &jlink_swd_write_reg,
@@ -2187,17 +2267,24 @@ static const struct swd_driver jlink_swd = {
 
 static const char * const jlink_transports[] = { "jtag", "swd", NULL };
 
-struct jtag_interface jlink_interface = {
+static struct jtag_interface jlink_interface = {
+       .execute_queue = &jlink_execute_queue,
+};
+
+struct adapter_driver jlink_adapter_driver = {
        .name = "jlink",
-       .commands = jlink_command_handlers,
        .transports = jlink_transports,
-       .swd = &jlink_swd,
-       .execute_queue = &jlink_execute_queue,
-       .speed = &jlink_speed,
-       .speed_div = &jlink_speed_div,
-       .khz = &jlink_khz,
+       .commands = jlink_command_handlers,
+
        .init = &jlink_init,
        .quit = &jlink_quit,
+       .reset = &jlink_reset_safe,
+       .speed = &jlink_speed,
+       .khz = &jlink_khz,
+       .speed_div = &jlink_speed_div,
        .config_trace = &config_trace,
        .poll_trace = &poll_trace,
+
+       .jtag_ops = &jlink_interface,
+       .swd_ops = &jlink_swd,
 };

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)