drivers/jtag/jlink: support SWD mode 41/2141/8
authorPaul Fertser <fercerpav@gmail.com>
Wed, 14 May 2014 07:42:16 +0000 (11:42 +0400)
committerAndreas Fritiofson <andreas.fritiofson@gmail.com>
Sat, 2 Aug 2014 13:53:33 +0000 (13:53 +0000)
Quick attempt at SWD support, closely modelled after ftdi.

Change-Id: I25140d80c5be7b2f8f0e2ef722a4ba4df0da4cf3
Signed-off-by: Brian Campbell <Brian.Campbell@ed.ac.uk>
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Reviewed-on: http://openocd.zylin.com/2141
Tested-by: jenkins
Reviewed-by: Nemui Trinomius <nemuisan_kawausogasuki@live.jp>
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>
src/jtag/drivers/jlink.c

index 632dcee4f072650dbafaf9a7e22b55fa98d03eb1..871bf24c613ef8be43d5b4bd3ff1053ad1562600 100644 (file)
@@ -29,6 +29,7 @@
 #endif
 
 #include <jtag/interface.h>
+#include <jtag/swd.h>
 #include <jtag/commands.h>
 #include "libusb_common.h"
 
@@ -201,6 +202,11 @@ static char *jlink_hw_type_str[] = {
        "J-Link Pro",
 };
 
+#define JLINK_TIF_JTAG         0
+#define JLINK_TIF_SWD          1
+#define JLINK_SWD_DIR_IN       0
+#define JLINK_SWD_DIR_OUT      1
+
 /* Queue command functions */
 static void jlink_end_state(tap_state_t state);
 static void jlink_state_move(void);
@@ -211,6 +217,9 @@ static void jlink_scan(bool ir_scan, enum scan_type type, uint8_t *buffer,
 static void jlink_reset(int trst, int srst);
 static void jlink_simple_command(uint8_t command);
 static int jlink_get_status(void);
+static int jlink_swd_run_queue(struct adiv5_dap *dap);
+static void jlink_swd_queue_cmd(struct adiv5_dap *dap, uint8_t cmd, uint32_t *dst, uint32_t data);
+static int jlink_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq);
 
 /* J-Link tap buffer functions */
 static void jlink_tap_init(void);
@@ -254,6 +263,9 @@ static uint16_t pids[] = { 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0 };
 static uint32_t jlink_caps;
 static uint32_t jlink_hw_type;
 
+static int queued_retval;
+static bool swd_mode;
+
 /* 256 byte non-volatile memory */
 struct jlink_config {
        uint8_t usb_address;
@@ -529,11 +541,17 @@ static int jlink_init(void)
         *
         * Segger recommends to select interface necessarily as a part of init process,
         * in case any previous session leaves improper interface selected.
-        *
-        * Until SWD implemented, select only JTAG interface here.
         */
+       int retval;
        if (jlink_caps & (1<<EMU_CAP_SELECT_IF))
-               jlink_select_interface(0);
+               retval = jlink_select_interface(swd_mode ? JLINK_TIF_SWD : JLINK_TIF_JTAG);
+       else
+               retval = swd_mode ? ERROR_JTAG_DEVICE_ERROR : ERROR_OK;
+
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Selected transport mode is not supported.");
+               return ERROR_JTAG_INIT_FAILED;
+       }
 
        LOG_INFO("J-Link JTAG Interface ready");
 
@@ -541,11 +559,19 @@ static int jlink_init(void)
        jtag_sleep(3000);
        jlink_tap_init();
 
-       /* v5/6 jlink seems to have an issue if the first tap move
-        * is not divisible by 8, so we send a TLR on first power up */
-       for (i = 0; i < 8; i++)
-               jlink_tap_append_step(1, 0);
-       jlink_tap_execute();
+       if (!swd_mode) {
+               /* v5/6 jlink seems to have an issue if the first tap move
+                * is not divisible by 8, so we send a TLR on first power up */
+               for (i = 0; i < 8; i++)
+                       jlink_tap_append_step(1, 0);
+               jlink_tap_execute();
+       }
+
+       if (swd_mode)
+               jlink_swd_switch_seq(NULL, JTAG_TO_SWD);
+       else
+               jlink_swd_switch_seq(NULL, SWD_TO_JTAG);
+       jlink_swd_run_queue(NULL);
 
        return ERROR_OK;
 }
@@ -1294,10 +1320,50 @@ static const struct command_registration jlink_command_handlers[] = {
        COMMAND_REGISTRATION_DONE
 };
 
+static int jlink_swd_init(void)
+{
+       LOG_INFO("JLink SWD mode enabled");
+       swd_mode = true;
+
+       return ERROR_OK;
+}
+
+static void jlink_swd_write_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t value)
+{
+       assert(!(cmd & SWD_CMD_RnW));
+       jlink_swd_queue_cmd(dap, cmd, NULL, value);
+}
+
+static void jlink_swd_read_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t *value)
+{
+       assert(cmd & SWD_CMD_RnW);
+       jlink_swd_queue_cmd(dap, cmd, value, 0);
+}
+
+static int_least32_t jlink_swd_frequency(struct adiv5_dap *dap, int_least32_t hz)
+{
+       if (hz > 0)
+               jlink_speed(hz);
+
+       return hz;
+}
+
+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,
+       .run = jlink_swd_run_queue,
+};
+
+static const char * const jlink_transports[] = { "jtag", "swd", NULL };
+
 struct jtag_interface jlink_interface = {
        .name = "jlink",
        .commands = jlink_command_handlers,
-       .transports = jtag_only,
+       .transports = jlink_transports,
+       .swd = &jlink_swd,
 
        .execute_queue = jlink_execute_queue,
        .speed = jlink_speed,
@@ -1312,6 +1378,7 @@ struct jtag_interface jlink_interface = {
 
 
 static unsigned tap_length;
+/* In SWD mode use tms buffer for direction control */
 static uint8_t tms_buffer[JLINK_TAP_BUFFER_SIZE];
 static uint8_t tdi_buffer[JLINK_TAP_BUFFER_SIZE];
 static uint8_t tdo_buffer[JLINK_TAP_BUFFER_SIZE];
@@ -1320,7 +1387,7 @@ struct pending_scan_result {
        int first;      /* First bit position in tdo_buffer to read */
        int length; /* Number of bits to read */
        struct scan_command *command; /* Corresponding scan command */
-       uint8_t *buffer;
+       void *buffer;
 };
 
 #define MAX_PENDING_SCAN_RESULTS 256
@@ -1465,6 +1532,184 @@ static int jlink_tap_execute(void)
        return ERROR_OK;
 }
 
+static void fill_buffer(uint8_t *buf, uint32_t val, uint32_t len)
+{
+       unsigned int tap_pos = tap_length;
+
+       while (len > 32) {
+               buf_set_u32(buf, tap_pos, 32, val);
+               len -= 32;
+               tap_pos += 32;
+       }
+       if (len)
+               buf_set_u32(buf, tap_pos, len, val);
+}
+
+static void jlink_queue_data_out(const uint8_t *data, uint32_t len)
+{
+       const uint32_t dir_out = 0xffffffff;
+
+       if (data)
+               bit_copy(tdi_buffer, tap_length, data, 0, len);
+       else
+               fill_buffer(tdi_buffer, 0, len);
+       fill_buffer(tms_buffer, dir_out, len);
+       tap_length += len;
+}
+
+static void jlink_queue_data_in(uint32_t len)
+{
+       const uint32_t dir_in = 0;
+
+       fill_buffer(tms_buffer, dir_in, len);
+       tap_length += len;
+}
+
+static int jlink_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq)
+{
+       const uint8_t *s;
+       unsigned int s_len;
+
+       switch (seq) {
+       case LINE_RESET:
+               LOG_DEBUG("SWD line reset");
+               s = swd_seq_line_reset;
+               s_len = swd_seq_line_reset_len;
+               break;
+       case JTAG_TO_SWD:
+               LOG_DEBUG("JTAG-to-SWD");
+               s = swd_seq_jtag_to_swd;
+               s_len = swd_seq_jtag_to_swd_len;
+               break;
+       case SWD_TO_JTAG:
+               LOG_DEBUG("SWD-to-JTAG");
+               s = swd_seq_swd_to_jtag;
+               s_len = swd_seq_swd_to_jtag_len;
+               break;
+       default:
+               LOG_ERROR("Sequence %d not supported", seq);
+               return ERROR_FAIL;
+       }
+
+       jlink_queue_data_out(s, s_len);
+
+       return ERROR_OK;
+}
+
+static int jlink_swd_run_queue(struct adiv5_dap *dap)
+{
+       LOG_DEBUG("Executing %d queued transactions", pending_scan_results_length);
+       int retval;
+
+       if (queued_retval != ERROR_OK) {
+               LOG_DEBUG("Skipping due to previous errors: %d", queued_retval);
+               goto skip;
+       }
+
+       /* A transaction must be followed by another transaction or at least 8 idle cycles to
+        * ensure that data is clocked through the AP. */
+       jlink_queue_data_out(NULL, 8);
+
+       size_t byte_length = DIV_ROUND_UP(tap_length, 8);
+
+       /* There's a comment in jlink_tap_execute saying JLink returns
+        * an extra NULL in packet when size of incoming message is a
+        * multiple of 64. Someone should verify if that's still the
+        * case with the current jlink firmware */
+
+       usb_out_buffer[0] = EMU_CMD_HW_JTAG3;
+       usb_out_buffer[1] = 0;
+       usb_out_buffer[2] = (tap_length >> 0) & 0xff;
+       usb_out_buffer[3] = (tap_length >> 8) & 0xff;
+       memcpy(usb_out_buffer + 4, tms_buffer, byte_length);
+       memcpy(usb_out_buffer + 4 + byte_length, tdi_buffer, byte_length);
+
+       retval = jlink_usb_message(jlink_handle, 4 + 2 * byte_length,
+                                  byte_length + 1);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("jlink_swd_run_queue failed USB io (%d)", retval);
+               goto skip;
+       }
+
+       retval = usb_in_buffer[byte_length];
+       if (retval) {
+               LOG_ERROR("jlink_swd_run_queue failed, result %d", retval);
+               goto skip;
+       }
+
+       for (int i = 0; i < pending_scan_results_length; i++) {
+               int ack = buf_get_u32(usb_in_buffer, pending_scan_results_buffer[i].first, 3);
+
+               if (ack != SWD_ACK_OK) {
+                       LOG_ERROR("SWD ack not OK: %d %s", ack,
+                                 ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK");
+                       queued_retval = ack;
+                       goto skip;
+               } else if (pending_scan_results_buffer[i].length) {
+                       uint32_t data = buf_get_u32(usb_in_buffer, 3 + pending_scan_results_buffer[i].first, 32);
+                       int parity = buf_get_u32(usb_in_buffer, 3 + 32 + pending_scan_results_buffer[i].first, 1);
+
+                       if (parity != parity_u32(data)) {
+                               LOG_ERROR("SWD Read data parity mismatch");
+                               queued_retval = ERROR_FAIL;
+                               goto skip;
+                       }
+
+                       if (pending_scan_results_buffer[i].buffer)
+                               *(uint32_t *)pending_scan_results_buffer[i].buffer = data;
+               }
+       }
+
+skip:
+       jlink_tap_init();
+       retval = queued_retval;
+       queued_retval = ERROR_OK;
+
+       return retval;
+}
+
+static void jlink_swd_queue_cmd(struct adiv5_dap *dap, uint8_t cmd, uint32_t *dst, uint32_t data)
+{
+       uint8_t data_parity_trn[DIV_ROUND_UP(32 + 1, 8)];
+       if (tap_length + 46 + 8 + dap->memaccess_tck >= sizeof(tdi_buffer) * 8 ||
+           pending_scan_results_length == MAX_PENDING_SCAN_RESULTS) {
+               /* Not enough room in the queue. Run the queue. */
+               queued_retval = jlink_swd_run_queue(dap);
+       }
+
+       if (queued_retval != ERROR_OK)
+               return;
+
+       cmd |= SWD_CMD_START | SWD_CMD_PARK;
+
+       jlink_queue_data_out(&cmd, 8);
+
+       pending_scan_results_buffer[pending_scan_results_length].first = tap_length;
+
+       if (cmd & SWD_CMD_RnW) {
+               /* Queue a read transaction */
+               pending_scan_results_buffer[pending_scan_results_length].length = 32;
+               pending_scan_results_buffer[pending_scan_results_length].buffer = dst;
+
+               jlink_queue_data_in(1 + 3 + 32 + 1 + 1);
+       } else {
+               /* Queue a write transaction */
+               pending_scan_results_buffer[pending_scan_results_length].length = 0;
+               jlink_queue_data_in(1 + 3 + 1);
+
+               buf_set_u32(data_parity_trn, 0, 32, data);
+               buf_set_u32(data_parity_trn, 32, 1, parity_u32(data));
+
+               jlink_queue_data_out(data_parity_trn, 32 + 1);
+       }
+
+       pending_scan_results_length++;
+
+       /* Insert idle cycles after AP accesses to avoid WAIT */
+       if (cmd & SWD_CMD_APnDP)
+               jlink_queue_data_out(NULL, dap->memaccess_tck);
+}
+
 /*****************************************************************************/
 /* JLink USB low-level functions */
 

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)