diff --git a/Makefile b/Makefile index a5a62bbb..551bcdb7 100644 --- a/Makefile +++ b/Makefile @@ -45,13 +45,13 @@ ifeq ($(OS),Windows_NT) MSPDEBUG_CC = $(CC) BINARY = mspdebug.exe ifneq ($(UNAME_O),Cygwin) - OS_LIBS = -lws2_32 -lregex + OS_LIBS = -lws2_32 -lregex -lftdi1 OS_CFLAGS = -D__Windows__ -DNO_SHELLCMD RM = del endif ifneq (, $findstring(MINGW, $(UNAME_S))) - PORTS_CFLAGS := $(shell pkg-config --cflags libusb) - PORTS_LDFLAGS := $(shell pkg-config --libs libusb) + PORTS_CFLAGS := $(shell pkg-config --cflags libusb libftdi1) + PORTS_LDFLAGS := $(shell pkg-config --libs libusb libftdi1) RM = rm -rf endif else @@ -72,14 +72,14 @@ else ifeq ($(UNAME_S),Darwin) # Mac OS X/MacPorts stuff ifeq ($(shell fink -V > /dev/null 2>&1 && echo ok),ok) - PORTS_CFLAGS := $(shell pkg-config --cflags hidapi libusb) - PORTS_LDFLAGS := $(shell pkg-config --libs hidapi libusb) -ltermcap -pthread + PORTS_CFLAGS := $(shell pkg-config --cflags hidapi libusb libftdi1) + PORTS_LDFLAGS := $(shell pkg-config --libs hidapi libusb libftdi1) -ltermcap -pthread else ifeq ($(shell brew --version > /dev/null 2>&1 && echo ok),ok) - PORTS_CFLAGS := $(shell pkg-config --cflags hidapi libusb) - PORTS_LDFLAGS := $(shell pkg-config --libs hidapi libusb) -framework IOKit -framework CoreFoundation + PORTS_CFLAGS := $(shell pkg-config --cflags hidapi libusb libftdi1) + PORTS_LDFLAGS := $(shell pkg-config --libs hidapi libusb libftdi1) -framework IOKit -framework CoreFoundation else ifeq ($(shell port version > /dev/null 2>&1 && echo ok),ok) - PORTS_CFLAGS := $(shell pkg-config --cflags hidapi libusb) - PORTS_LDFLAGS := $(shell pkg-config --libs hidapi libusb) -framework IOKit -framework CoreFoundation + PORTS_CFLAGS := $(shell pkg-config --cflags hidapi libusb libftdi1) + PORTS_LDFLAGS := $(shell pkg-config --libs hidapi libusb libftdi1) -framework IOKit -framework CoreFoundation else PORTS_CFLAGS := -I/opt/local/include PORTS_LDFLAGS := -L/opt/local/lib -lhidapi -framework IOKit -framework CoreFoundation @@ -88,8 +88,8 @@ else RF25000_OBJ += transport/rf2500hidapi.o LDFLAGS = else ifneq ($(filter $(UNAME_S),OpenBSD NetBSD DragonFly),) - PORTS_CFLAGS := $(shell pkg-config --cflags libusb) - PORTS_LDFLAGS := $(shell pkg-config --libs libusb) -ltermcap -pthread + PORTS_CFLAGS := $(shell pkg-config --cflags libusb libftdi1) + PORTS_LDFLAGS := $(shell pkg-config --libs libusb libftdi1) -ltermcap -pthread else PORTS_CFLAGS := PORTS_LDFLAGS := @@ -170,7 +170,7 @@ OBJ=\ util/gpio.o \ transport/cp210x.o \ transport/cdc_acm.o \ - transport/ftdi.o \ + transport/ftdi_serial.o \ transport/mehfet_xport.o \ transport/ti3410.o \ transport/comport.o \ @@ -193,6 +193,7 @@ OBJ=\ drivers/fet_olimex_db.o \ drivers/jtdev.o \ drivers/jtdev_bus_pirate.o \ + drivers/jtdev_ftdi_bitbang.o \ drivers/jtdev_gpio.o \ drivers/jtaglib.o \ drivers/mehfet_proto.o \ diff --git a/README b/README index 8973790f..04d892a7 100644 --- a/README +++ b/README @@ -33,14 +33,19 @@ Compiling from source Ensure that you have the necessary packages to compile programs that use libusb (on Debian or Ubuntu systems, you might need to do apt-get -install libusb-dev | on Arch systems, you might need to do sudo -pacman -S libusb-compact). After that, unpack and compile the source code -with: +install libusb-dev libftdi-dev | on Arch systems, you might need to do sudo +pacman -S libusb-compact). As libusb-0.1 is used, you may also need to +install a compatibility layer for libusb-1.0 (for example, +libusb-compat-git on MSYS). After that, unpack and compile the source +code with: tar xvfz mspdebug-version.tar.gz cd mspdebug-version make +On Windows systems with MSYS, you may also need to install a regex +implementation such as libgnurx. + On Debian Ubuntu systems sudo apt-get install libreadline-dev may be required. On Arch systems sudo pacman -S readline may be required. If you don't want GNU readline support, you can invoke make with: diff --git a/drivers/fet.c b/drivers/fet.c index 0309b116..1b621b3a 100644 --- a/drivers/fet.c +++ b/drivers/fet.c @@ -27,7 +27,7 @@ #include "fet_core.h" #include "output.h" #include "comport.h" -#include "ftdi.h" +#include "ftdi_serial.h" #include "rf2500.h" #include "ti3410.h" #include "cp210x.h" diff --git a/drivers/jtdev.h b/drivers/jtdev.h index 0755d920..4afc3a59 100644 --- a/drivers/jtdev.h +++ b/drivers/jtdev.h @@ -21,10 +21,16 @@ #define JTDEV_H_ #include +#include struct jtdev_func; struct jtdev { - int port; + union { + // anything that uses a file descriptor + int port; + // anything that uses an opaque pointer + void *handle; + }; uint8_t data_register; uint8_t control_register; int failed; @@ -39,6 +45,7 @@ struct jtdev_func{ * field in the jtdev structure. */ int (*jtdev_open)(struct jtdev *p, const char *device); + int (*jtdev_open_ex)(struct jtdev *p, const char *device, const uint16_t *vid, const uint16_t *pid); void (*jtdev_close)(struct jtdev *p); /* Connect/release JTAG */ @@ -70,10 +77,12 @@ struct jtdev_func{ uint16_t (*jtdev_dr_shift_16)(struct jtdev *p, uint16_t dr); void (*jtdev_tms_sequence)(struct jtdev *p, int bits, unsigned int value); void (*jtdev_init_dap)(struct jtdev *p); + int (*jtdev_set_fast_baud)(struct jtdev *p, bool fast); }; extern const struct jtdev_func jtdev_func_pif; extern const struct jtdev_func jtdev_func_gpio; extern const struct jtdev_func jtdev_func_bp; +extern const struct jtdev_func jtdev_func_ftdi_bitbang; #endif diff --git a/drivers/jtdev_ftdi_bitbang.c b/drivers/jtdev_ftdi_bitbang.c new file mode 100644 index 00000000..fd91f9d3 --- /dev/null +++ b/drivers/jtdev_ftdi_bitbang.c @@ -0,0 +1,424 @@ +/* MSPDebug - debugging tool for MSP430 MCUs + * Copyright (C) 2009-2012 Daniel Beer + * Copyright (C) 2012 Peter Bägel + * + * ppdev/ppi abstraction inspired by uisp src/DARPA.C + * originally written by Sergey Larin; + * corrected by Denis Chertykov, Uros Platise and Marek Michalkiewicz. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "jtaglib.h" +#include "output.h" +#include "util.h" + +/*===== private symbols ======================================================*/ + +/*--- FTDI port pins ---*/ +#define FTDI_TXD ((unsigned char)0x01) // TCK/SK +#define FTDI_RXD ((unsigned char)0x02) // TDI/DO +#define FTDI_RTS ((unsigned char)0x04) // TDO/DI +#define FTDI_CTS ((unsigned char)0x08) // TMS/CS +#define FTDI_DTR ((unsigned char)0x10) // GPIOL0 +#define FTDI_DSR ((unsigned char)0x20) // GPIOL1 +#define FTDI_DCD ((unsigned char)0x40) // GPIOL2 +#define FTDI_RI ((unsigned char)0x80) // GPIOL3 + +/*--- JTAG signal mapping ---*/ +// while technically re-assignable, for now they're just hardcoded +#define TDO FTDI_RTS +#define TDI FTDI_RXD +#define TMS FTDI_CTS +#define TCK FTDI_TXD +#define RESET FTDI_DCD // OpenOCD uses this as the default SYSRST pin + +#define OUT_BITS (TDI | TMS | TCK | RESET) + +#define DEFAULT_VID 0x0403 +static const uint16_t default_pids[] = { + 0x6001, // FT232RL + 0x6010, // FT2232HL + 0x6011, // FT4232HL + 0x6014, // FT232HL +}; + +// ft232r has 128 byte Tx buffer, but openocd suggests that anything larger than +// 64 bytes causes hangups +static uint8_t fast_buf[64]; +static size_t fast_buf_i; + +/*===== public functions =====================================================*/ + +static void ftdi_print_err(const char *msg, int code, struct jtdev *p) +{ + printc_err("jtdev: %s: %d (%s)\n", msg, code, ftdi_get_error_string(p->handle)); +} + +static void do_bitbang_write(struct jtdev *p) +{ + int f; + + if((f = ftdi_write_data(p->handle, &p->data_register, 1)) < 0) { + ftdi_print_err("failed writing to FTDI port", f, p); + p->failed = 1; + return; + } +} + +static void do_bitbang_bitset(struct jtdev *p, uint8_t mask, int out) +{ + if (out) + p->data_register |= mask; + else + p->data_register &= ~mask; + + do_bitbang_write(p); +} + +static int do_bitbang_read(struct jtdev *p, uint8_t bit) +{ + int f; + uint8_t buf; + + if((f = ftdi_read_pins(p->handle, &buf)) < 0) { + ftdi_print_err("failed reading from FTDI port", f, p); + p->failed = 1; + return 0; + } + + return (buf & bit) ? 1 : 0; +} + +static int jtbitbang_open_ex(struct jtdev *p, const char *device, const uint16_t *vid, const uint16_t *pid) +{ + int f = -1; + size_t i; + uint16_t _vid, _pid; + + if ((p->handle = ftdi_new()) == NULL) { + printc_err("jtdev: ftdi_new failed\n"); + return -1; + } + + if(vid == NULL && pid == NULL) { + // iterate through all the default VID/PID pairs for auto-detect + for(i = 0; i < ARRAY_LEN(default_pids) && f < 0; i++) { + f = ftdi_usb_open(p->handle, DEFAULT_VID, default_pids[i]); + } + } else { + // pick sane defaults if either provided + _vid = vid ? *vid : DEFAULT_VID; + _pid = pid ? *pid : 0x6010; + f = ftdi_usb_open(p->handle, _vid, _pid); + } + + if(f < 0) { + ftdi_print_err("unable to open ftdi device", f, p); + p->f->jtdev_close(p); + return -1; + } + + if((f = ftdi_set_bitmode(p->handle, OUT_BITS, BITMODE_BITBANG)) < 0) { + ftdi_print_err("unable to enable ftdi bitbang mode", f, p); + p->f->jtdev_close(p); + return -1; + } + + p->f->jtdev_set_fast_baud(p, false); + + p->data_register = 0; + p->control_register = 0; + p->failed = 0; + + do_bitbang_write(p); + + return 0; +} + +static void jtbitbang_close(struct jtdev *p) +{ + if(p->handle != NULL) { + ftdi_set_bitmode(p->handle, OUT_BITS, BITMODE_RESET); + ftdi_usb_close(p->handle); + ftdi_free(p->handle); + + p->handle = NULL; + } +} + +static int jtbitbang_set_fast_baud(struct jtdev *p, bool fast) +{ + int f; + // baud is multiplied by 4 for some reason in bitbang mode? + //int baud = fast ? 350000/4 : 9600/4; + int baud = fast ? 350000 : 9600; + + if((f = ftdi_set_baudrate(p->handle, baud)) < 0) { + ftdi_print_err("unable to set ftdi baud", f, p); + return -1; + } + printc_dbg("jtdev: set ftdi baud to %d\n", baud); + + return 0; +} + +static void jtbitbang_power_on(struct jtdev *p) +{ + // Some FTDI ports have PWREN pins, but not implemented here +} + +static void jtbitbang_power_off(struct jtdev *p) +{ + // Some FTDI ports have PWREN pins, but not implemented here +} + +static void jtbitbang_connect(struct jtdev *p) +{ + // unsure what this function does, I presume my setup w/ FTDI is + // "always enabled" +} + +static void jtbitbang_release(struct jtdev *p) +{ + // unsure what this function does, I presume my setup w/ FTDI is + // "always enabled" +} + +static void jtbitbang_tck(struct jtdev *p, int out) +{ + do_bitbang_bitset(p, TCK, out); +} + +static void jtbitbang_tms(struct jtdev *p, int out) +{ + do_bitbang_bitset(p, TMS, out); +} + +static void jtbitbang_tdi(struct jtdev *p, int out) +{ + do_bitbang_bitset(p, TDI, out); +} + +static void jtbitbang_rst(struct jtdev *p, int out) +{ + do_bitbang_bitset(p, RESET, out); +} + +static void jtbitbang_tst(struct jtdev *p, int out) +{ + // Test not supported in bitbang +} + +static int jtbitbang_tdo_get(struct jtdev *p) +{ + return do_bitbang_read(p, TDO); +} + +static void jtbitbang_tclk(struct jtdev *p, int out) +{ + jtbitbang_tdi(p, out); +} + +static int jtbitbang_tclk_get(struct jtdev *p) +{ + return do_bitbang_read(p, TDI); +} + +static void jtbitbang_led_green(struct jtdev *p, int out) +{ + // LED responds to data, not directly controllable? +} + +static void jtbitbang_led_red(struct jtdev *p, int out) +{ + // LED responds to data, not directly controllable? +} + +static void fast_flush(struct jtdev *p) +{ + if(fast_buf_i == 0) { + return; + } + + int f = ftdi_write_data(p->handle, fast_buf, fast_buf_i); + fast_buf_i = 0; + if(f < 0) { + ftdi_print_err("failed writing to FTDI port", f, p); + p->failed = 1; + return; + } +} + +static void fast_push(struct jtdev *p, uint8_t data_reg) +{ + if(fast_buf_i >= sizeof(fast_buf)) { + fast_flush(p); + } + + fast_buf[fast_buf_i++] = data_reg; + p->data_register = data_reg; +} + +static void fast_clock(struct jtdev *p) +{ + fast_push(p, p->data_register & ~TCK); + fast_push(p, p->data_register | TCK); +} + +static void fast_clock_and_dat(struct jtdev *p, uint8_t data_reg) +{ + // data always latched on rising edge, loaded on falling edge + fast_push(p, data_reg & ~TCK); + /// p->data_register was updated just before + fast_push(p, p->data_register | TCK); +} + +static void jtbitbang_tclk_strobe(struct jtdev *p, unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + fast_push(p, p->data_register | TDI); + fast_push(p, p->data_register & ~TDI); + } + + fast_flush(p); +} + +static void fast_tclk_prep (struct jtdev *p) +{ + /* JTAG state = Exit-DR */ + fast_clock(p); + + /* JTAG state = Update-DR */ + fast_clock_and_dat(p, p->data_register & ~TMS); + + /* JTAG state = Run-Test/Idle */ +} + +static unsigned int fast_shift( struct jtdev *p, + unsigned char num_bits, + unsigned int data_out ) +{ + unsigned int data_in; + unsigned int mask; + unsigned int tclk_save; + + tclk_save = p->data_register & TDI; + + data_in = 0; + for (mask = 0x0001U << (num_bits - 1); mask != 0; mask >>= 1) { + uint8_t out; + if ((data_out & mask) != 0) + out = p->data_register | TDI; + else + out = p->data_register & ~TDI; + + if (mask == 1) + out |= TMS; + + fast_clock_and_dat(p, out); + + // need to flush so jtdev_tdo_get has the right value. Tried to optimise + // using ftdi_read but speed actually decreased and logic was complex + fast_flush(p); + + if (p->f->jtdev_tdo_get(p) == 1) + data_in |= mask; + } + + p->data_register &= ~TDI; + fast_push(p, p->data_register | tclk_save); + + /* Set JTAG state back to Run-Test/Idle */ + fast_tclk_prep(p); + + // Potentially returning to non-fast function, flush bytes + fast_flush(p); + + return data_in; +} + +static uint8_t fast_ir_shift(struct jtdev *p, uint8_t ir) +{ + /* JTAG state = Run-Test/Idle */ + fast_clock_and_dat(p, p->data_register | TMS); + + /* JTAG state = Select DR-Scan */ + fast_clock(p); + + /* JTAG state = Select IR-Scan */ + fast_clock_and_dat(p, p->data_register & ~TMS); + + /* JTAG state = Capture-IR */ + fast_clock(p); + + /* JTAG state = Shift-IR, Shift in TDI (8-bit) */ + return fast_shift(p, 8, ir); + + /* JTAG state = Run-Test/Idle */ +} + +static uint16_t fast_dr_shift_16(struct jtdev *p, uint16_t data) +{ + /* JTAG state = Run-Test/Idle */ + fast_clock_and_dat(p, p->data_register | TMS); + + /* JTAG state = Select DR-Scan */ + fast_clock_and_dat(p, p->data_register & ~TMS); + + /* JTAG state = Capture-DR */ + fast_clock(p); + + /* JTAG state = Shift-DR, Shift in TDI (16-bit) */ + return fast_shift(p, 16, data); + + /* JTAG state = Run-Test/Idle */ +} + + +const struct jtdev_func jtdev_func_ftdi_bitbang = { + // note: using open_ex as we need PID/VID populated + .jtdev_open_ex = jtbitbang_open_ex, + .jtdev_close = jtbitbang_close, + .jtdev_power_on = jtbitbang_power_on, + .jtdev_power_off = jtbitbang_power_off, + .jtdev_connect = jtbitbang_connect, + .jtdev_release = jtbitbang_release, + .jtdev_tck = jtbitbang_tck, + .jtdev_tms = jtbitbang_tms, + .jtdev_tdi = jtbitbang_tdi, + .jtdev_rst = jtbitbang_rst, + .jtdev_tst = jtbitbang_tst, + .jtdev_tdo_get = jtbitbang_tdo_get, + .jtdev_tclk = jtbitbang_tclk, + .jtdev_tclk_get = jtbitbang_tclk_get, + .jtdev_tclk_strobe = jtbitbang_tclk_strobe, + .jtdev_led_green = jtbitbang_led_green, + .jtdev_led_red = jtbitbang_led_red, + + .jtdev_set_fast_baud = jtbitbang_set_fast_baud, + // optimised sending for hot functions used for flash programming + reading + .jtdev_ir_shift = fast_ir_shift, + .jtdev_dr_shift_16 = fast_dr_shift_16, + // these aren't called too often and can be default + .jtdev_dr_shift_8 = jtag_default_dr_shift_8, + .jtdev_tms_sequence = jtag_default_tms_sequence, + .jtdev_init_dap = jtag_default_init_dap, +}; + diff --git a/drivers/pif.c b/drivers/pif.c index 70bca8bf..bc066722 100644 --- a/drivers/pif.c +++ b/drivers/pif.c @@ -139,6 +139,13 @@ static int init_device(struct jtdev *p) return -1; } + // JTAG fuse check has been performed, so we can now switch to a + // higher-speed physical transport suitable for ~350 kHz TCLK strobes used + // in (and required for) flash programming. This function is optional. + if (p->f->jtdev_set_fast_baud && p->f->jtdev_set_fast_baud(p, true) < 0) { + return -1; + } + return 0; } @@ -414,6 +421,56 @@ static device_t bp_open(const struct device_args *args) return &dev->base; } +/*----------------------------------------------------------------------------*/ + + +static device_t ftdi_bitbang_open(const struct device_args *args) +{ + struct pif_device *dev; + const uint16_t *vid; + const uint16_t *pid; + + if (args->flags & DEVICE_FLAG_TTY) { + printc_err("ftdi bitbang: this driver does not support TTY USB access\n"); + return NULL; + } + + if (!(args->flags & DEVICE_FLAG_JTAG)) { + printc_err("ftdi bitbang: this driver does not support Spy-Bi-Wire\n"); + return NULL; + } + + dev = malloc(sizeof(*dev)); + if (!dev) { + printc_err("ftdi bitbang: malloc: %s\n", last_error()); + return NULL; + } + + memset(dev, 0, sizeof(*dev)); + dev->base.type = &device_pif; + dev->base.max_breakpoints = 2; //supported by all devices + dev->base.need_probe = 1; + dev->jtag.f = &jtdev_func_ftdi_bitbang; + + vid = (args->flags & DEVICE_FLAG_HAS_VID_PID) ? &args->vid : NULL; + pid = (args->flags & DEVICE_FLAG_HAS_VID_PID) ? &args->pid : NULL; + + // note: _ex variant + if (dev->jtag.f->jtdev_open_ex(&dev->jtag, NULL, vid, pid) < 0) { + free(dev); + return NULL; + } + + if (init_device(&dev->jtag) < 0) { + printc_err("ftdi bitbang: initialization failed\n"); + dev->jtag.f->jtdev_close(&dev->jtag); + free(dev); + return NULL; + } + + return &dev->base; +} + /*----------------------------------------------------------------------------*/ static void pif_destroy(device_t dev_base) { @@ -471,3 +528,18 @@ const struct device_class device_bp = { .erase = pif_erase, .getconfigfuses = pif_getconfigfuses }; + +const struct device_class device_ftdi_bitbang = { + .name = "ftdi-bitbang", + .help = "FTDI in Bitbang mode (FT232R, FT2232H, FT4232H) JTAG, RTS-TDO, RXD-TDI, CTS-TMS, DCD-RESET, TXD-TCK", + .open = ftdi_bitbang_open, + .destroy = pif_destroy, + .readmem = pif_readmem, + .writemem = pif_writemem, + .getregs = pif_getregs, + .setregs = pif_setregs, + .ctl = pif_ctl, + .poll = pif_poll, + .erase = pif_erase, + .getconfigfuses = pif_getconfigfuses +}; diff --git a/drivers/pif.h b/drivers/pif.h index e10b75a0..34064491 100644 --- a/drivers/pif.h +++ b/drivers/pif.h @@ -30,8 +30,9 @@ /* pif implementation */ extern const struct device_class device_pif; -/* share wiht gpio implementation */ +/* share with gpio implementation */ extern const struct device_class device_gpio; extern const struct device_class device_bp; +extern const struct device_class device_ftdi_bitbang; #endif diff --git a/transport/ftdi.c b/transport/ftdi_serial.c similarity index 99% rename from transport/ftdi.c rename to transport/ftdi_serial.c index 52c634a8..c963ae85 100644 --- a/transport/ftdi.c +++ b/transport/ftdi_serial.c @@ -20,7 +20,7 @@ #include #include -#include "ftdi.h" +#include "ftdi_serial.h" #include "util.h" #include "usbutil.h" #include "output.h" diff --git a/transport/ftdi.h b/transport/ftdi_serial.h similarity index 100% rename from transport/ftdi.h rename to transport/ftdi_serial.h diff --git a/ui/main.c b/ui/main.c index 073c8481..426c06ff 100644 --- a/ui/main.c +++ b/ui/main.c @@ -90,6 +90,7 @@ static const struct device_class *const driver_table[] = { &device_goodfet, &device_pif, &device_gpio, + &device_ftdi_bitbang, &device_loadbsl, &device_ezfet, &device_rom_bsl,