This is an automated email from Gerrit. Mateusz Manowiecki ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/2444
-- gerrit commit fe7c2f5944893b92b12c27bbfd827859c0f62901 Author: Mateusz Manowiecki <[email protected]> Date: Mon Jan 5 18:00:35 2015 +0100 Add SWD protocol support to buspirate This work is based on Andreas Fritiofson ftdi implementation and was heavily influenced by Python version provided by Will Donnelly. Change-Id: I29d52b1b39a44d59f96b20824aef2942f572f766 Signed-off-by: Mateusz Manowiecki <[email protected]> diff --git a/src/jtag/drivers/buspirate.c b/src/jtag/drivers/buspirate.c index 12d34b9..7d297c7 100644 --- a/src/jtag/drivers/buspirate.c +++ b/src/jtag/drivers/buspirate.c @@ -24,6 +24,7 @@ #endif #include <jtag/interface.h> +#include <jtag/swd.h> #include <jtag/commands.h> #include <termios.h> @@ -42,6 +43,8 @@ static void buspirate_path_move(int num_states, tap_state_t *path); static void buspirate_runtest(int num_cycles); static void buspirate_scan(bool ir_scan, enum scan_type type, uint8_t *buffer, int scan_size, struct scan_command *command); +static int buspirate_swd_switch_seq(struct adiv5_dap *dap, + enum swd_special_seq seq); #define CMD_UNKNOWN 0x00 #define CMD_PORT_MODE 0x01 @@ -49,9 +52,20 @@ static void buspirate_scan(bool ir_scan, enum scan_type type, #define CMD_READ_ADCS 0x03 /*#define CMD_TAP_SHIFT 0x04 // old protocol */ #define CMD_TAP_SHIFT 0x05 +#define CMD_ENTER_RWIRE 0x05 #define CMD_ENTER_OOCD 0x06 #define CMD_UART_SPEED 0x07 #define CMD_JTAG_SPEED 0x08 +#define CMD_RAW_SPEED 0x60 +#define CMD_RAW_CONFIG 0x80 + +/* raw-wire mode configuration */ +#define CMD_RAW_CONFIG_HIZ 0x00 +#define CMD_RAW_CONFIG_3V3 0x08 +#define CMD_RAW_CONFIG_2W 0x00 +#define CMD_RAW_CONFIG_3W 0x04 +#define CMD_RAW_CONFIG_MSB 0x00 +#define CMD_RAW_CONFIG_LSB 0x02 /* Not all OSes have this speed defined */ #if !defined(B1000000) @@ -82,6 +96,29 @@ enum { SERIAL_FAST = 1 }; +enum { + SPEED_RAW_5_KHZ = 0x0, + SPEED_RAW_50_KHZ = 0x1, + SPEED_RAW_100_KHZ = 0x2, + SPEED_RAW_400_KHZ = 0x3 +}; + +/* SWD mode specific */ +static bool swd_mode; + +/* FIXME: Where to store per-instance data? We need an SWD context. */ +/* copied from ftdi.c driver */ +static struct swd_cmd_queue_entry { + uint8_t cmd; + uint32_t *dst; + uint32_t data; + uint8_t parity; + uint8_t ack; +} *swd_cmd_queue; +static size_t swd_cmd_queue_length; +static size_t swd_cmd_queue_alloced; +static int queued_retval; + static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */ static const cc_t NORMAL_TIMEOUT = 10; @@ -94,6 +131,8 @@ static char *buspirate_port; static enum tap_state last_tap_state = TAP_RESET; +/* SWD interface */ +static int buspirate_swd_run_queue(struct adiv5_dap *dap); /* TAP interface */ static void buspirate_tap_init(void); @@ -106,14 +145,17 @@ static void buspirate_tap_make_space(int scan, int bits); static void buspirate_reset(int trst, int srst); /* low level interface */ +static void buspirate_bbio_enable(int); static void buspirate_jtag_reset(int); -static void buspirate_jtag_enable(int); static unsigned char buspirate_jtag_command(int, char *, int); static void buspirate_jtag_set_speed(int, char); static void buspirate_jtag_set_mode(int, char); static void buspirate_jtag_set_feature(int, char, char); static void buspirate_jtag_get_adcs(int); +/* low level two-wire interface */ +static void buspirate_swd_set_params(int fd); + /* low level HW communication interface */ static int buspirate_serial_open(char *port); static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout); @@ -292,20 +334,26 @@ static int buspirate_init(void) return ERROR_JTAG_INIT_FAILED; } - buspirate_jtag_enable(buspirate_fd); + buspirate_bbio_enable(buspirate_fd); - if (buspirate_baudrate != SERIAL_NORMAL) + if (swd_mode) + buspirate_swd_set_params(buspirate_fd); + else if (buspirate_baudrate != SERIAL_NORMAL) buspirate_jtag_set_speed(buspirate_fd, SERIAL_FAST); - LOG_INFO("Buspirate Interface ready!"); + LOG_INFO("Buspirate %s Interface ready!", swd_mode ? "SWD" : "JTAG"); - buspirate_tap_init(); - buspirate_jtag_set_mode(buspirate_fd, buspirate_pinmode); - buspirate_jtag_set_feature(buspirate_fd, FEATURE_VREG, - (buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE); - buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP, - (buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE); - buspirate_reset(0, 0); + if (swd_mode) { + buspirate_swd_switch_seq(NULL, JTAG_TO_SWD); + } else { + buspirate_tap_init(); + buspirate_jtag_set_mode(buspirate_fd, buspirate_pinmode); + buspirate_jtag_set_feature(buspirate_fd, FEATURE_VREG, + (buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE); + buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP, + (buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE); + buspirate_reset(0, 0); + } return ERROR_OK; } @@ -313,9 +361,11 @@ static int buspirate_init(void) static int buspirate_quit(void) { LOG_INFO("Shutting down buspirate."); - buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ); + if (!swd_mode) { + buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ); + buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL); + } - buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL); buspirate_jtag_reset(buspirate_fd); buspirate_serial_close(buspirate_fd); @@ -437,6 +487,7 @@ COMMAND_HANDLER(buspirate_handle_port_command) } +/* all commands except buspirate_port won't work in SWD mode */ static const struct command_registration buspirate_command_handlers[] = { { .name = "buspirate_adc", @@ -489,10 +540,240 @@ static const struct command_registration buspirate_command_handlers[] = { COMMAND_REGISTRATION_DONE }; +static int buspirate_swd_init(void) +{ + LOG_INFO("Buspirate SWD mode enabled"); + swd_mode = true; + + swd_cmd_queue_alloced = 10; + swd_cmd_queue = malloc(swd_cmd_queue_alloced * sizeof(*swd_cmd_queue)); + + return swd_cmd_queue != NULL ? ERROR_OK : ERROR_FAIL; +} + +static int buspirate_swd_switch_seq(struct adiv5_dap *dap, enum swd_special_seq seq) +{ + const uint8_t *sequence; + int sequence_len; + char tmp[64]; + + switch (seq) { + case LINE_RESET: + LOG_DEBUG("SWD line reset"); + sequence = swd_seq_line_reset; + sequence_len = DIV_ROUND_UP(swd_seq_line_reset_len, 8); + break; + case JTAG_TO_SWD: + LOG_DEBUG("JTAG-to-SWD"); + sequence = swd_seq_jtag_to_swd; + sequence_len = DIV_ROUND_UP(swd_seq_jtag_to_swd_len, 8); + break; + case SWD_TO_JTAG: + LOG_DEBUG("SWD-to-JTAG"); + sequence = swd_seq_swd_to_jtag; + sequence_len = DIV_ROUND_UP(swd_seq_swd_to_jtag_len, 8); + break; + default: + LOG_ERROR("Sequence %d not supported", seq); + return ERROR_FAIL; + } + + /* FIXME: all above sequences fit into one pirate command for now + * but it may cause trouble later + */ + + tmp[0] = 0x10 + ((sequence_len - 1) & 0x0F); + memcpy(tmp + 1, sequence, sequence_len); + + buspirate_serial_write(buspirate_fd, tmp, sequence_len + 1); + buspirate_serial_read(buspirate_fd, tmp, sequence_len + 1); + + return ERROR_OK; +} + +static void buspirate_swd_queue_cmd(struct adiv5_dap *dap, uint8_t cmd, uint32_t *dst, uint32_t data) +{ + if (swd_cmd_queue_length >= swd_cmd_queue_alloced) { + /* Not enough room in the queue. Run the queue and increase its size for next time. + * Note that it's not possible to avoid running the queue here, because mpsse contains + * pointers into the queue which may be invalid after the realloc. */ + queued_retval = buspirate_swd_run_queue(dap); + struct swd_cmd_queue_entry *q = realloc(swd_cmd_queue, swd_cmd_queue_alloced * 2 * sizeof(*swd_cmd_queue)); + if (q != NULL) { + swd_cmd_queue = q; + swd_cmd_queue_alloced *= 2; + LOG_DEBUG("Increased SWD command queue to %zu elements", swd_cmd_queue_alloced); + } + } + + if (queued_retval != ERROR_OK) + return; + + size_t i = swd_cmd_queue_length++; + swd_cmd_queue[i].cmd = cmd | SWD_CMD_START | SWD_CMD_PARK; + + if (swd_cmd_queue[i].cmd & SWD_CMD_RnW) { + /* Queue a read transaction */ + swd_cmd_queue[i].dst = dst; + } else { + /* Queue a write transaction */ + swd_cmd_queue[i].data = data; + swd_cmd_queue[i].parity = parity_u32(data) ? 0x01 : 0x00; + } +} + +static void buspirate_swd_read_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t *value) +{ + assert(cmd & SWD_CMD_RnW); + buspirate_swd_queue_cmd(dap, cmd, value, 0); +} + +static void buspirate_swd_write_reg(struct adiv5_dap *dap, uint8_t cmd, uint32_t value) +{ + assert(!(cmd & SWD_CMD_RnW)); + buspirate_swd_queue_cmd(dap, cmd, NULL, value); +} + +static void buspirate_swd_idle_bytes(uint8_t count) +{ + char tmp[20]; + + tmp[0] = 0x10 + ((count - 1) & 0x0F); + memset(tmp + 1, 0x00, sizeof(tmp) - 1); + + buspirate_serial_write(buspirate_fd, tmp, count + 1); + buspirate_serial_read(buspirate_fd, tmp, count + 1); +} + +static int buspirate_run_cmd(struct adiv5_dap *dap, size_t queue_item) +{ + char tmp[16]; + int to_send; + + tmp[0] = 0x10; /* bus pirate: send 1 byte */ + tmp[1] = swd_cmd_queue[queue_item].cmd; /* swd cmd */ + tmp[2] = 0x07; /* ack __x */ + tmp[3] = 0x07; /* ack _x_ */ + tmp[4] = 0x07; /* ack x__ */ + tmp[5] = 0x07; /* write mode trn_1 */ + tmp[6] = 0x07; /* write mode trn_2 */ + + to_send = (((swd_cmd_queue[queue_item].cmd & SWD_CMD_RnW) == 0) ? 7 : 5); + buspirate_serial_write(buspirate_fd, tmp, to_send); + + /* read ack */ + buspirate_serial_read(buspirate_fd, tmp, 2); /* drop pirate command ret vals */ + buspirate_serial_read(buspirate_fd, tmp, to_send - 2); /* ack bits */ + + swd_cmd_queue[queue_item].ack = tmp[2] << 2 | tmp[1] << 1 | tmp[0]; + + if (swd_cmd_queue[queue_item].cmd & SWD_CMD_RnW) { + /* do a read transaction */ + tmp[0] = 0x06; /* 4 data bytes */ + tmp[1] = 0x06; + tmp[2] = 0x06; + tmp[3] = 0x06; + tmp[4] = 0x07; /* parity bit */ + tmp[5] = 0x21; /* 2 turnaround clocks */ + + buspirate_serial_write(buspirate_fd, tmp, 6); + buspirate_serial_read(buspirate_fd, tmp, 6); + + /* store the data and parity */ + swd_cmd_queue[queue_item].data = (uint8_t) tmp[0]; + swd_cmd_queue[queue_item].data |= (uint8_t) tmp[1] << 8; + swd_cmd_queue[queue_item].data |= (uint8_t) tmp[2] << 16; + swd_cmd_queue[queue_item].data |= (uint8_t) tmp[3] << 24; + swd_cmd_queue[queue_item].parity = tmp[4] ? 0x01 : 0x00; + } else { + /* do a write transaction */ + tmp[0] = 0x10 + ((4 + 1 - 1) & 0xF); /* bus pirate: send 4+1 bytes */ + buf_set_u32(tmp + 1, 0, 32, swd_cmd_queue[queue_item].data); + /* write sequence ends with parity bit and 7 idle ticks */ + tmp[5] = swd_cmd_queue[queue_item].parity; + + buspirate_serial_write(buspirate_fd, tmp, 6); + buspirate_serial_read(buspirate_fd, tmp, 6); + } + + /* Insert idle cycles after AP accesses to avoid WAIT */ + if (swd_cmd_queue[queue_item].cmd & SWD_CMD_APnDP) + buspirate_swd_idle_bytes(1); + + return 0; +} + +static int buspirate_swd_run_queue(struct adiv5_dap *dap) +{ + LOG_DEBUG("Executing %zu queued transactions", swd_cmd_queue_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. */ + buspirate_swd_idle_bytes(1); + + for (size_t i = 0; i < swd_cmd_queue_length; i++) + buspirate_run_cmd(dap, i); + + for (size_t i = 0; i < swd_cmd_queue_length; i++) { + int ack = swd_cmd_queue[i].ack; + + LOG_DEBUG("%s %s %s reg %X = %08"PRIx32, + ack == SWD_ACK_OK ? "OK" : ack == SWD_ACK_WAIT ? "WAIT" : ack == SWD_ACK_FAULT ? "FAULT" : "JUNK", + swd_cmd_queue[i].cmd & SWD_CMD_APnDP ? "AP" : "DP", + swd_cmd_queue[i].cmd & SWD_CMD_RnW ? "read" : "write", + (swd_cmd_queue[i].cmd & SWD_CMD_A32) >> 1, + swd_cmd_queue[i].data); + + if (ack != SWD_ACK_OK) { + queued_retval = ack; + goto skip; + + } else if (swd_cmd_queue[i].cmd & SWD_CMD_RnW) { + uint32_t data = swd_cmd_queue[i].data; + int parity = swd_cmd_queue[i].parity; + + if (parity != parity_u32(data)) { + LOG_ERROR("SWD Read data parity mismatch %x %x", parity, parity_u32(data)); + queued_retval = ERROR_FAIL; + goto skip; + } + + if (swd_cmd_queue[i].dst != NULL) + *swd_cmd_queue[i].dst = data; + } + } + +skip: + swd_cmd_queue_length = 0; + retval = queued_retval; + queued_retval = ERROR_OK; + + return retval; +} + +static const struct swd_driver buspirate_swd = { + .init = buspirate_swd_init, + .switch_seq = buspirate_swd_switch_seq, + .read_reg = buspirate_swd_read_reg, + .write_reg = buspirate_swd_write_reg, + .run = buspirate_swd_run_queue, +}; + +static const char * const buspirate_transports[] = { "jtag", "swd", NULL }; + struct jtag_interface buspirate_interface = { .name = "buspirate", .execute_queue = buspirate_execute_queue, .commands = buspirate_command_handlers, + .transports = buspirate_transports, + .swd = &buspirate_swd, + .init = buspirate_init, .quit = buspirate_quit }; @@ -793,6 +1074,13 @@ static void buspirate_reset(int trst, int srst) { LOG_DEBUG("trst: %i, srst: %i", trst, srst); + /* software reset only in swd mode, add: + * "reset_config none" + * to your configuration + */ + if (swd_mode) + return; + if (trst) buspirate_jtag_set_feature(buspirate_fd, FEATURE_TRST, ACTION_DISABLE); @@ -809,18 +1097,29 @@ static void buspirate_reset(int trst, int srst) } /*************** jtag lowlevel functions ********************/ -static void buspirate_jtag_enable(int fd) +static void buspirate_bbio_enable(int fd) { int ret; + char command; + const char *mode_answers[2] = { "OCD1", "RAW1" }; + const char *correct_ans = NULL; char tmp[21] = { [0 ... 20] = 0x00 }; int done = 0; int cmd_sent = 0; - LOG_DEBUG("Entering binary mode"); + if (swd_mode) { + command = CMD_ENTER_RWIRE; + correct_ans = mode_answers[1]; + } else { + command = CMD_ENTER_OOCD; + correct_ans = mode_answers[0]; + } + + LOG_DEBUG("Entering binary mode, that is %s", correct_ans); buspirate_serial_write(fd, tmp, 20); usleep(10000); - /* reads 1 to n "BBIO1"s and one "OCD1" */ + /* reads 1 to n "BBIO1"s and one "OCD1" or "RAW1" */ while (!done) { ret = buspirate_serial_read(fd, tmp, 4); if (ret != 4) { @@ -841,14 +1140,14 @@ static void buspirate_jtag_enable(int fd) } if (cmd_sent == 0) { cmd_sent = 1; - tmp[0] = CMD_ENTER_OOCD; + tmp[0] = command; ret = buspirate_serial_write(fd, tmp, 1); if (ret != 1) { LOG_ERROR("error reading"); exit(-1); } } - } else if (strncmp(tmp, "OCD1", 4) == 0) + } else if (strncmp(tmp, correct_ans, 4) == 0) done = 1; else { LOG_ERROR("Buspirate did not answer correctly! " @@ -863,7 +1162,7 @@ static void buspirate_jtag_reset(int fd) { char tmp[5]; - tmp[0] = 0x00; /* exit OCD1 mode */ + tmp[0] = 0x00; /* exit binary mode */ buspirate_serial_write(fd, tmp, 1); usleep(10000); /* We ignore the return value here purposly, nothing we can do */ @@ -875,6 +1174,39 @@ static void buspirate_jtag_reset(int fd) LOG_ERROR("Unable to restart buspirate!"); } +static void buspirate_swd_set_params(int fd) +{ + int ret; + char tmp[1]; + + /* raw-wire mode configuration */ + tmp[0] = CMD_RAW_CONFIG | CMD_RAW_CONFIG_3V3 | CMD_RAW_CONFIG_LSB; + buspirate_serial_write(fd, tmp, 1); + ret = buspirate_serial_read(fd, tmp, 1); + if (ret != 1) { + LOG_ERROR("Buspirate did not answer correctly"); + exit(-1); + } + if (tmp[0] != 1) { + LOG_ERROR("Buspirate did not reply as expected to the configure command"); + exit(-1); + } + + /* speed settings */ + tmp[0] = CMD_RAW_SPEED | SPEED_RAW_400_KHZ; + buspirate_serial_write(fd, tmp, 1); + ret = buspirate_serial_read(fd, tmp, 1); + if (ret != 1) { + LOG_ERROR("Buspirate did not answer correctly"); + exit(-1); + } + if (tmp[0] != 1) { + LOG_ERROR("Buspirate did not reply as expected to the speed change command"); + exit(-1); + } + LOG_INFO("Buspirate two-wire mode configured correctly"); +} + static void buspirate_jtag_set_speed(int fd, char speed) { int ret; diff --git a/tcl/interface/buspirate.cfg b/tcl/interface/buspirate.cfg index 2b68538..e70f4d3 100644 --- a/tcl/interface/buspirate.cfg +++ b/tcl/interface/buspirate.cfg @@ -9,6 +9,13 @@ interface buspirate # you need to specify port on which BP lives #buspirate_port /dev/ttyUSB0 +# this depends on the cable, you are safe with this option +# should be overwritten with "none" for SWD mode! +reset_config srst_only + + +# the following settings won't work in SWD mode! + # communication speed setting buspirate_speed normal ;# or fast @@ -21,6 +28,3 @@ buspirate_speed normal ;# or fast # pullup state Enabled = 1 Disabled = 0 #buspirate_pullup 0 -# this depends on the cable, you are safe with this option -reset_config srst_only - -- ------------------------------------------------------------------------------ Dive into the World of Parallel Programming! The Go Parallel Website, sponsored by Intel and developed in partnership with Slashdot Media, is your hub for all things parallel software development, from weekly thought leadership blogs to news, videos, case studies, tutorials and more. Take a look and join the conversation now. http://goparallel.sourceforge.net _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
