This is an automated email from Gerrit. "Michael Hough <dejitaru...@gmail.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6978
-- gerrit commit f5b61fac19ed9b64d564425cb76cbed5c7323cd4 Author: Michael Hough <dejitaru...@gmail.com> Date: Mon May 16 12:24:24 2022 -0400 topic: Add SWD support to FT232R driver Adds SWD protocol support to the existing FT232R JTAG driver. Implements bitbang.c/bitbang.h for backing logic; new code only needs to toggle GPIO accordingly. Minor clarifications in comments & code ('0' to 'NULL' when it's a pointer) Signed-off-by: Michael Hough <dejitaru...@gmail.com> Change-Id: Iabced6ee2b81d1f5407cde9244b7927fe7d25714 diff --git a/src/jtag/drivers/ft232r.c b/src/jtag/drivers/ft232r.c index c930d8c4c1..e296a7e4f7 100644 --- a/src/jtag/drivers/ft232r.c +++ b/src/jtag/drivers/ft232r.c @@ -16,6 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ +/* 2022-05: SWD support added by Michael Hough; + * strongly inspired by bcm2835gpio.c, implements bitbang.c driver */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -31,6 +34,7 @@ #include <jtag/commands.h> #include <helper/time_support.h> #include "libusb_helper.h" +#include "bitbang.h" /* system includes */ #include <string.h> @@ -50,7 +54,7 @@ #define IN_EP 0x02 #define OUT_EP 0x81 -/* Requests */ +/* Control request types: */ #define SIO_RESET 0 /* Reset the port */ #define SIO_MODEM_CTRL 1 /* Set the modem control register */ #define SIO_SET_FLOW_CTRL 2 /* Set flow control register */ @@ -63,10 +67,16 @@ #define SIO_GET_LATENCY_TIMER 10 #define SIO_SET_BITMODE 11 #define SIO_READ_PINS 12 + #define SIO_READ_EEPROM 0x90 #define SIO_WRITE_EEPROM 0x91 #define SIO_ERASE_EEPROM 0x92 +/* Reset requests: */ +#define SIO_RESET_SIO 0 /* Reset device */ +#define SIO_RESET_PURGE_RX 1 /* Reset USB RX (host->FT232R) buffer */ +#define SIO_RESET_PURGE_TX 2 /* Reset USB TX (FT232R->host) buffer */ + #define FT232R_BUF_SIZE_EXTRA 4096 static uint16_t ft232r_vid = 0x0403; /* FTDI */ @@ -86,23 +96,117 @@ static char *ft232r_bit_name_array[FT232R_BIT_COUNT] = { "RTS", /* 2: pin 3 TDO input */ "CTS", /* 3: pin 11 TMS output */ "DTR", /* 4: pin 2 /TRST output */ - "DSR", /* 5: pin 9 unused */ + "DSR", /* 5: pin 9 SWDIO */ "DCD", /* 6: pin 10 /SYSRST output */ - "RI" /* 7: pin 6 unused */ + "RI" /* 7: pin 6 SWCLK */ }; -static int tck_gpio; /* initialized to 0 by default */ +static int tck_gpio; /* = 0;*/ static int tdi_gpio = 1; static int tdo_gpio = 2; static int tms_gpio = 3; static int ntrst_gpio = 4; static int nsysrst_gpio = 6; +static int swdio_gpio = 5; +static int swclk_gpio = 7; +/* FIXME: It might make more sense to share clock pin and put SWDIO on TMS? + * These protocols can not be used at the same time, and it's already sharing reset pins. */ + static size_t ft232r_buf_size = FT232R_BUF_SIZE_EXTRA; -/** 0xFFFF disables restore by default, after exit serial port will not work. - * 0x15 sets TXD RTS DTR as outputs, after exit serial port will continue to work. - */ + +/* 0xFFFF disables restore by default, after exit serial port will not work. + * Any variant of 0x00nn should reset to default serial mode. */ static uint16_t ft232r_restore_bitmode = 0xFFFF; +static uint32_t swd_delay = 100; + +static int ft232r_swdio_read(void); +static void ft232r_swdio_drive(bool is_output); +static int ft232r_swd_write(int swclk, int swdio); + +/* Only implements SWD via bitbang driver - existing JTAG implementation seems faster. */ +static struct bitbang_interface ft232r_bitbang = { + .read = NULL, + .write = NULL, + .swdio_read = ft232r_swdio_read, + .swdio_drive = ft232r_swdio_drive, + .swd_write = ft232r_swd_write, + .blink = NULL /* Even if we had enough pins, can't blink without messing up I/O */ +}; + +/** + * Sets GPIO to synchronous bitbang mode (0x0400) with the given direction mask. + * 1 is output, 0 is input. + */ +static int ft232r_set_dirs(unsigned char dir_mask) +{ + return jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_SET_BITMODE, dir_mask | 0x0400, + 0, NULL, 0, 1000); +} + +/** + * Sets the designated SWCLK and SWDIO pins according to provided params. + * (swclk and swdio are ensured as boolean 0 or 1 in bitbang.c) + */ +static int ft232r_swd_write(int swclk, int swdio) +{ + char level_mask = (swclk << swclk_gpio) | (swdio << swdio_gpio); + + static int runs; + ++runs; + + int n; + if (jtag_libusb_bulk_write(adapter, IN_EP, &level_mask, 1, 1000, &n) != ERROR_OK) { + LOG_ERROR("usb bulk write failed"); + return ERROR_JTAG_DEVICE_ERROR; + } + + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = swd_delay; + nanosleep(&ts, &ts); + + if (runs > 127) { + /* This is needed to prevent Sync BB mode from seizing up. + Each write also reads to internal buffer (256 bytes); stops when that's full. + Telling it to nuke buffer is much, much faster than actually reading. */ + jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, + SIO_RESET, SIO_RESET_PURGE_TX, 0, NULL, 0, 1000); + runs = 0; + } + + return ERROR_OK; +} + +/** + * Sets whether the SWDIO pin will be driven by host (output) or target (input) + */ +static void ft232r_swdio_drive(bool is_output) +{ + unsigned char dir_mask = (1<<swclk_gpio) | (is_output<<swdio_gpio); + ft232r_set_dirs(dir_mask); +} + +/** + * Reads value on SWDIO pin. + * This uses immediate polling, bypassing device buffer. + * We are trusting that bitbang.c has put the pin into input mode by now. + */ +static int ft232r_swdio_read(void) +{ + unsigned char swdio_mask = (1 << swdio_gpio); + char data = 0; + jtag_libusb_control_transfer(adapter, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, + SIO_READ_PINS, 0, + 0, &data, 1, 1000); + + return (data & swdio_mask) != 0; +} + /** * Perform sync bitbang output/input transaction. * Before call, an array ft232r_output[] should be filled with data to send. @@ -210,7 +314,7 @@ static void ft232r_write(int tck, int tms, int tdi) * Control /TRST and /SYSRST pins. * Perform immediate bitbang transaction. */ -static void ft232r_reset(int trst, int srst) +static int ft232r_reset(int trst, int srst) { unsigned out_value = (1<<ntrst_gpio) | (1<<nsysrst_gpio); LOG_DEBUG("ft232r_reset(%d,%d)", trst, srst); @@ -230,11 +334,12 @@ static void ft232r_reset(int trst, int srst) if (ft232r_output_len >= ft232r_buf_size) { /* FIXME: should we just execute queue here? */ LOG_ERROR("ft232r_write: buffer overflow"); - return; + return ERROR_JTAG_DEVICE_ERROR; } ft232r_output[ft232r_output_len++] = out_value; ft232r_send_recv(); + return ERROR_OK; } static int ft232r_speed(int divisor) @@ -244,9 +349,12 @@ static int ft232r_speed(int divisor) 3000000 / divisor; LOG_DEBUG("ft232r_speed(%d) rate %d bits/sec", divisor, baud); + /* Manual delay, in nanoseconds: */ + swd_delay = 1000000 / baud; + if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_SET_BAUD_RATE, divisor, 0, 0, 0, 1000) != 0) { + SIO_SET_BAUD_RATE, divisor, 0, NULL, 0, 1000) != 0) { LOG_ERROR("cannot set baud rate"); return ERROR_JTAG_DEVICE_ERROR; } @@ -255,6 +363,8 @@ static int ft232r_speed(int divisor) static int ft232r_init(void) { + bitbang_interface = &ft232r_bitbang; + uint16_t avids[] = {ft232r_vid, 0}; uint16_t apids[] = {ft232r_pid, 0}; if (jtag_libusb_open(avids, apids, &adapter, NULL)) { @@ -263,6 +373,7 @@ static int ft232r_init(void) ft232r_vid, ft232r_pid, (!ft232r_serial_desc) ? "[any]" : ft232r_serial_desc); return ERROR_JTAG_INIT_FAILED; } + LOG_DEBUG("Device found"); if (ft232r_restore_bitmode == 0xFFFF) /* serial port will not be restored after jtag: */ libusb_detach_kernel_driver(adapter, 0); @@ -273,60 +384,61 @@ static int ft232r_init(void) LOG_ERROR("unable to claim interface"); return ERROR_JTAG_INIT_FAILED; } + LOG_DEBUG("Interface claimed"); /* Reset the device. */ if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_RESET, 0, 0, 0, 0, 1000) != 0) { + SIO_RESET, SIO_RESET_SIO, 0, NULL, 0, 1000) != 0) { LOG_ERROR("unable to reset device"); return ERROR_JTAG_INIT_FAILED; } + LOG_DEBUG("Device reset"); - /* Sync bit bang mode. */ - if (jtag_libusb_control_transfer(adapter, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_SET_BITMODE, (1<<tck_gpio) | (1<<tdi_gpio) | (1<<tms_gpio) | (1<<ntrst_gpio) | (1<<nsysrst_gpio) | 0x400, - 0, 0, 0, 1000) != 0) { + /* Sync bit bang mode. TDO is the only input to start. */ + unsigned char dir_mask = ~(1<<tdo_gpio); + if (ft232r_set_dirs(dir_mask) != 0) { LOG_ERROR("cannot set sync bitbang mode"); return ERROR_JTAG_INIT_FAILED; } + LOG_DEBUG("Bitbang mode enabled"); /* Exactly 500 nsec between updates. */ unsigned divisor = 1; unsigned char latency_timer = 1; /* Frequency divisor is 14-bit non-zero value. */ - if (jtag_libusb_control_transfer(adapter, - LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_SET_BAUD_RATE, divisor, - 0, 0, 0, 1000) != 0) { - LOG_ERROR("cannot set baud rate"); + if (ft232r_speed(divisor) != ERROR_OK) return ERROR_JTAG_INIT_FAILED; - } + + LOG_DEBUG("Baud rate set"); + if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, - SIO_SET_LATENCY_TIMER, latency_timer, 0, 0, 0, 1000) != 0) { + SIO_SET_LATENCY_TIMER, latency_timer, 0, NULL, 0, 1000) != 0) { LOG_ERROR("unable to set latency timer"); return ERROR_JTAG_INIT_FAILED; } + LOG_DEBUG("Latency set"); ft232r_output = malloc(ft232r_buf_size); if (!ft232r_output) { LOG_ERROR("Unable to allocate memory for the buffer"); return ERROR_JTAG_INIT_FAILED; } + LOG_DEBUG("Buffer allocated"); return ERROR_OK; } static int ft232r_quit(void) { - /* to restore serial port: set TXD RTS DTR as outputs, others as inputs, disable sync bit bang mode. */ + /* Disabling bit-bang mode will resume normal serial operations. */ if (ft232r_restore_bitmode != 0xFFFF) { if (jtag_libusb_control_transfer(adapter, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, SIO_SET_BITMODE, ft232r_restore_bitmode, - 0, 0, 0, 1000) != 0) { + 0, NULL, 0, 1000) != 0) { LOG_ERROR("cannot set bitmode to restore serial port"); } } @@ -537,6 +649,38 @@ COMMAND_HANDLER(ft232r_handle_srst_num_command) return ERROR_OK; } +COMMAND_HANDLER(ft232r_handle_swdio_num_command) +{ + if (CMD_ARGC == 1) + swdio_gpio = ft232r_bit_name_to_number(CMD_ARGV[0]); + else if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (swdio_gpio < 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + command_print(CMD, + "FT232R num: SWDIO = %d %s", swdio_gpio, ft232r_bit_number_to_name(swdio_gpio)); + + return ERROR_OK; +} + +COMMAND_HANDLER(ft232r_handle_swclk_num_command) +{ + if (CMD_ARGC == 1) + swclk_gpio = ft232r_bit_name_to_number(CMD_ARGV[0]); + else if (CMD_ARGC != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (swclk_gpio < 0) + return ERROR_COMMAND_SYNTAX_ERROR; + + command_print(CMD, + "FT232R num: SWCLK = %d %s", swclk_gpio, ft232r_bit_number_to_name(swclk_gpio)); + + return ERROR_OK; +} + COMMAND_HANDLER(ft232r_handle_restore_serial_command) { if (CMD_ARGC == 1) @@ -608,6 +752,20 @@ static const struct command_registration ft232r_subcommand_handlers[] = { .help = "gpio number for trst.", .usage = "<0-7|TXD|RXD|RTS|CTS|DTR|DSR|DCD|RI>", }, + { + .name = "swdio_num", + .handler = ft232r_handle_swdio_num_command, + .mode = COMMAND_CONFIG, + .help = "gpio number for swdio.", + .usage = "<0-7|TXD|RXD|RTS|CTS|DTR|DSR|DCD|RI>", + }, + { + .name = "swclk_num", + .handler = ft232r_handle_swclk_num_command, + .mode = COMMAND_CONFIG, + .help = "gpio number for swclk.", + .usage = "<0-7|TXD|RXD|RTS|CTS|DTR|DSR|DCD|RI>", + }, { .name = "restore_serial", .handler = ft232r_handle_restore_serial_command, @@ -660,8 +818,7 @@ static void syncbb_state_move(int skip) } /** - * Clock a bunch of TMS (or SWDIO) transitions, to change the JTAG - * (or SWD) state machine. + * Clock a bunch of TMS transitions, to change the JTAG state machine. */ static int syncbb_execute_tms(struct jtag_command *cmd) { @@ -906,6 +1063,8 @@ static int syncbb_execute_queue(void) return retval; } +static const char * const f232r_transports[] = { "jtag", "swd", NULL }; + static struct jtag_interface ft232r_interface = { .supported = DEBUG_CAP_TMS_SEQ, .execute_queue = syncbb_execute_queue, @@ -913,14 +1072,16 @@ static struct jtag_interface ft232r_interface = { struct adapter_driver ft232r_adapter_driver = { .name = "ft232r", - .transports = jtag_only, + .transports = f232r_transports, .commands = ft232r_command_handlers, .init = ft232r_init, .quit = ft232r_quit, + .reset = ft232r_reset, .speed = ft232r_speed, .khz = ft232r_khz, .speed_div = ft232r_speed_div, .jtag_ops = &ft232r_interface, + .swd_ops = &bitbang_swd, }; --