From: Mateusz Manowiecki Date: Thu, 26 Jan 2017 00:12:18 +0000 (+0100) Subject: Add SWD protocol support to buspirate (2nd try) X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=6a4f5a4a6707520b7e12ec4199cd77129ee6436e;p=openocd Add SWD protocol support to buspirate (2nd try) This is a second try for this patch. I removed the queues from the previous version. I made it compatible with SRST reset and added support for those features that could be supported in raw binary mode. Change-Id: I96fc06abbea9873e98b414f34afd9043fd9c2a41 Signed-off-by: Mateusz Manowiecki Reviewed-on: http://openocd.zylin.com/3960 Tested-by: jenkins Reviewed-by: Eric Work Reviewed-by: Thomas Jarosch Reviewed-by: Jacob Alexander Reviewed-by: Tomas Vanek --- diff --git a/src/jtag/drivers/buspirate.c b/src/jtag/drivers/buspirate.c index aa0d8cc3..76be103e 100644 --- a/src/jtag/drivers/buspirate.c +++ b/src/jtag/drivers/buspirate.c @@ -22,6 +22,7 @@ #endif #include +#include #include #include @@ -48,9 +49,21 @@ static void buspirate_stableclocks(int num_cycles); #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_PERIPH 0x40 +#define CMD_RAW_SPEED 0x60 +#define CMD_RAW_MODE 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) @@ -81,6 +94,18 @@ 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; +static int queued_retval; +static char swd_features; + static const cc_t SHORT_TIMEOUT = 1; /* Must be at least 1. */ static const cc_t NORMAL_TIMEOUT = 10; @@ -93,6 +118,12 @@ static char *buspirate_port; static enum tap_state last_tap_state = TAP_RESET; +/* SWD interface */ +static int buspirate_swd_init(void); +static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk); +static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk); +static int buspirate_swd_switch_seq(enum swd_special_seq seq); +static int buspirate_swd_run_queue(void); /* TAP interface */ static void buspirate_tap_init(void); @@ -103,16 +134,24 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer, static void buspirate_tap_make_space(int scan, int bits); static void buspirate_reset(int trst, int srst); +static void buspirate_set_feature(int, char, char); +static void buspirate_set_mode(int, char); +static void buspirate_set_speed(int, char); /* 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_speed(int, char); +static void buspirate_swd_set_feature(int, char, char); +static void buspirate_swd_set_mode(int, char); + /* low level HW communication interface */ static int buspirate_serial_open(char *port); static int buspirate_serial_setspeed(int fd, char speed, cc_t timeout); @@ -295,18 +334,20 @@ 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) - buspirate_jtag_set_speed(buspirate_fd, SERIAL_FAST); + if (swd_mode || buspirate_baudrate != SERIAL_NORMAL) + buspirate_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, + if (!swd_mode) + buspirate_tap_init(); + + buspirate_set_mode(buspirate_fd, buspirate_pinmode); + buspirate_set_feature(buspirate_fd, FEATURE_VREG, (buspirate_vreg == 1) ? ACTION_ENABLE : ACTION_DISABLE); - buspirate_jtag_set_feature(buspirate_fd, FEATURE_PULLUP, + buspirate_set_feature(buspirate_fd, FEATURE_PULLUP, (buspirate_pullup == 1) ? ACTION_ENABLE : ACTION_DISABLE); buspirate_reset(0, 0); @@ -316,9 +357,9 @@ static int buspirate_init(void) static int buspirate_quit(void) { LOG_INFO("Shutting down buspirate."); - buspirate_jtag_set_mode(buspirate_fd, MODE_HIZ); + buspirate_set_mode(buspirate_fd, MODE_HIZ); + buspirate_set_speed(buspirate_fd, SERIAL_NORMAL); - buspirate_jtag_set_speed(buspirate_fd, SERIAL_NORMAL); buspirate_jtag_reset(buspirate_fd); buspirate_serial_close(buspirate_fd); @@ -336,6 +377,10 @@ COMMAND_HANDLER(buspirate_handle_adc_command) if (buspirate_fd == -1) return ERROR_OK; + /* unavailable in SWD mode */ + if (swd_mode) + return ERROR_OK; + /* send the command */ buspirate_jtag_get_adcs(buspirate_fd); @@ -382,11 +427,11 @@ COMMAND_HANDLER(buspirate_handle_led_command) if (atoi(CMD_ARGV[0]) == 1) { /* enable led */ - buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED, + buspirate_set_feature(buspirate_fd, FEATURE_LED, ACTION_ENABLE); } else if (atoi(CMD_ARGV[0]) == 0) { /* disable led */ - buspirate_jtag_set_feature(buspirate_fd, FEATURE_LED, + buspirate_set_feature(buspirate_fd, FEATURE_LED, ACTION_DISABLE); } else { LOG_ERROR("usage: buspirate_led <1|0>"); @@ -492,10 +537,22 @@ static const struct command_registration buspirate_command_handlers[] = { COMMAND_REGISTRATION_DONE }; +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 }; @@ -799,7 +856,7 @@ static void buspirate_tap_append_scan(int length, uint8_t *buffer, tap_pending_scans_num++; } -/*************** jtag wrapper functions *********************/ +/*************** wrapper functions *********************/ /* (1) assert or (0) deassert reset lines */ static void buspirate_reset(int trst, int srst) @@ -807,33 +864,148 @@ static void buspirate_reset(int trst, int srst) LOG_DEBUG("trst: %i, srst: %i", trst, srst); if (trst) - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_TRST, ACTION_DISABLE); + buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_DISABLE); else - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_TRST, ACTION_ENABLE); + buspirate_set_feature(buspirate_fd, FEATURE_TRST, ACTION_ENABLE); if (srst) - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_SRST, ACTION_DISABLE); + buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_DISABLE); + else + buspirate_set_feature(buspirate_fd, FEATURE_SRST, ACTION_ENABLE); +} + +static void buspirate_set_feature(int fd, char feat, char action) +{ + if (swd_mode) + buspirate_swd_set_feature(fd, feat, action); else - buspirate_jtag_set_feature(buspirate_fd, - FEATURE_SRST, ACTION_ENABLE); + buspirate_jtag_set_feature(fd, feat, action); +} + +static void buspirate_set_mode(int fd, char mode) +{ + if (swd_mode) + buspirate_swd_set_mode(fd, mode); + else + buspirate_jtag_set_mode(fd, mode); +} + +static void buspirate_set_speed(int fd, char speed) +{ + if (swd_mode) + buspirate_swd_set_speed(fd, speed); + else + buspirate_jtag_set_speed(fd, speed); +} + + +/*************** swd lowlevel functions ********************/ + +static void buspirate_swd_set_speed(int fd, char speed) +{ + int ret; + char tmp[1]; + + LOG_DEBUG("Buspirate speed setting in SWD mode defaults to 400 kHz"); + + /* 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); + } +} + +static void buspirate_swd_set_mode(int fd, char mode) +{ + int ret; + char tmp[1]; + + /* raw-wire mode configuration */ + if (mode == MODE_HIZ) + tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB; + else + tmp[0] = CMD_RAW_MODE | CMD_RAW_CONFIG_LSB | CMD_RAW_CONFIG_3V3; + + 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); + } +} + +static void buspirate_swd_set_feature(int fd, char feat, char action) +{ + int ret; + char tmp[1]; + + switch (feat) { + case FEATURE_TRST: + LOG_DEBUG("Buspirate TRST feature not available in SWD mode"); + return; + case FEATURE_LED: + LOG_ERROR("Buspirate LED feature not available in SWD mode"); + return; + case FEATURE_SRST: + swd_features = (action == ACTION_ENABLE) ? swd_features | 0x02 : swd_features & 0x0D; + break; + case FEATURE_PULLUP: + swd_features = (action == ACTION_ENABLE) ? swd_features | 0x04 : swd_features & 0x0B; + break; + case FEATURE_VREG: + swd_features = (action == ACTION_ENABLE) ? swd_features | 0x08 : swd_features & 0x07; + break; + default: + LOG_DEBUG("Buspirate unknown feature %d", feat); + return; + } + + tmp[0] = CMD_RAW_PERIPH | swd_features; + buspirate_serial_write(fd, tmp, 1); + ret = buspirate_serial_read(fd, tmp, 1); + if (ret != 1) { + LOG_DEBUG("Buspirate feature %d not supported in SWD mode", feat); + } else if (tmp[0] != 1) { + LOG_ERROR("Buspirate did not reply as expected to the configure command"); + exit(-1); + } } /*************** 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) { @@ -854,14 +1026,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! " @@ -1124,3 +1296,240 @@ static void buspirate_print_buffer(char *buf, int size) if (line[0] != 0) LOG_DEBUG("%s", line); } + +/************************* SWD related stuff **********/ + +static int buspirate_swd_init(void) +{ + LOG_INFO("Buspirate SWD mode enabled"); + swd_mode = true; + + return ERROR_OK; +} + +static int buspirate_swd_switch_seq(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 uint8_t buspirate_swd_write_header(uint8_t cmd) +{ + char tmp[8]; + int to_send; + + tmp[0] = 0x10; /* bus pirate: send 1 byte */ + tmp[1] = 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 = ((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 */ + + return tmp[2] << 2 | tmp[1] << 1 | tmp[0]; +} + +static void buspirate_swd_idle_clocks(uint32_t no_bits) +{ + uint32_t no_bytes; + char tmp[20]; + + no_bytes = (no_bits + 7) / 8; + memset(tmp + 1, 0x00, sizeof(tmp) - 1); + + /* unfortunately bus pirate misbehaves when clocks are sent in parts + * so we need to limit at 128 clock cycles + */ + if (no_bytes > 16) + no_bytes = 16; + + while (no_bytes) { + uint8_t to_send = no_bytes > 16 ? 16 : no_bytes; + tmp[0] = 0x10 + ((to_send - 1) & 0x0F); + + buspirate_serial_write(buspirate_fd, tmp, to_send + 1); + buspirate_serial_read(buspirate_fd, tmp, to_send + 1); + + no_bytes -= to_send; + } +} + +static void buspirate_swd_clear_sticky_errors(void) +{ + buspirate_swd_write_reg(swd_cmd(false, false, DP_ABORT), + STKCMPCLR | STKERRCLR | WDERRCLR | ORUNERRCLR, 0); +} + +static void buspirate_swd_read_reg(uint8_t cmd, uint32_t *value, uint32_t ap_delay_clk) +{ + char tmp[16]; + + LOG_DEBUG("buspirate_swd_read_reg"); + assert(cmd & SWD_CMD_RnW); + + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skip buspirate_swd_read_reg because queued_retval=%d", queued_retval); + return; + } + + cmd |= SWD_CMD_START | SWD_CMD_PARK; + uint8_t ack = buspirate_swd_write_header(cmd); + + /* 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 */ + uint32_t data = (uint8_t) tmp[0]; + data |= (uint8_t) tmp[1] << 8; + data |= (uint8_t) tmp[2] << 16; + data |= (uint8_t) tmp[3] << 24; + int parity = tmp[4] ? 0x01 : 0x00; + + 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", + cmd & SWD_CMD_APnDP ? "AP" : "DP", + cmd & SWD_CMD_RnW ? "read" : "write", + (cmd & SWD_CMD_A32) >> 1, + data); + + switch (ack) { + case SWD_ACK_OK: + if (parity != parity_u32(data)) { + LOG_DEBUG("Read data parity mismatch %x %x", parity, parity_u32(data)); + queued_retval = ERROR_FAIL; + return; + } + if (value) + *value = data; + if (cmd & SWD_CMD_APnDP) + buspirate_swd_idle_clocks(ap_delay_clk); + return; + case SWD_ACK_WAIT: + LOG_DEBUG("SWD_ACK_WAIT"); + buspirate_swd_clear_sticky_errors(); + return; + case SWD_ACK_FAULT: + LOG_DEBUG("SWD_ACK_FAULT"); + queued_retval = ack; + return; + default: + LOG_DEBUG("No valid acknowledge: ack=%d", ack); + queued_retval = ack; + return; + } +} + +static void buspirate_swd_write_reg(uint8_t cmd, uint32_t value, uint32_t ap_delay_clk) +{ + char tmp[16]; + + LOG_DEBUG("buspirate_swd_write_reg"); + assert(!(cmd & SWD_CMD_RnW)); + + if (queued_retval != ERROR_OK) { + LOG_DEBUG("Skip buspirate_swd_write_reg because queued_retval=%d", queued_retval); + return; + } + + cmd |= SWD_CMD_START | SWD_CMD_PARK; + uint8_t ack = buspirate_swd_write_header(cmd); + + /* do a write transaction */ + tmp[0] = 0x10 + ((4 + 1 - 1) & 0xF); /* bus pirate: send 4+1 bytes */ + buf_set_u32((uint8_t *) tmp + 1, 0, 32, value); + /* write sequence ends with parity bit and 7 idle ticks */ + tmp[5] = parity_u32(value) ? 0x01 : 0x00; + + buspirate_serial_write(buspirate_fd, tmp, 6); + buspirate_serial_read(buspirate_fd, tmp, 6); + + 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", + cmd & SWD_CMD_APnDP ? "AP" : "DP", + cmd & SWD_CMD_RnW ? "read" : "write", + (cmd & SWD_CMD_A32) >> 1, + value); + + switch (ack) { + case SWD_ACK_OK: + if (cmd & SWD_CMD_APnDP) + buspirate_swd_idle_clocks(ap_delay_clk); + return; + case SWD_ACK_WAIT: + LOG_DEBUG("SWD_ACK_WAIT"); + buspirate_swd_clear_sticky_errors(); + return; + case SWD_ACK_FAULT: + LOG_DEBUG("SWD_ACK_FAULT"); + queued_retval = ack; + return; + default: + LOG_DEBUG("No valid acknowledge: ack=%d", ack); + queued_retval = ack; + return; + } +} + +static int buspirate_swd_run_queue(void) +{ + LOG_DEBUG("buspirate_swd_run_queue"); + /* 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_clocks(8); + + int retval = queued_retval; + queued_retval = ERROR_OK; + LOG_DEBUG("SWD queue return value: %02x", retval); + return retval; +} + + diff --git a/tcl/interface/buspirate.cfg b/tcl/interface/buspirate.cfg index 2b68538b..c2f3a83c 100644 --- a/tcl/interface/buspirate.cfg +++ b/tcl/interface/buspirate.cfg @@ -15,7 +15,7 @@ buspirate_speed normal ;# or fast # voltage regulator Enabled = 1 Disabled = 0 #buspirate_vreg 0 -# pin mode normal or open-drain +# pin mode normal or open-drain (jtag only) #buspirate_mode normal # pullup state Enabled = 1 Disabled = 0