From da9c2fe1c8ff548d1f86b4a6c4c2b2798c1ef3a7 Mon Sep 17 00:00:00 2001 From: Rene Liebscher Date: Wed, 19 Jun 2013 17:40:32 +0000 Subject: [PATCH] use bitbanging on ftdi mpsse when wrong pins are used * avrftdi.c, avrftdi_private.h: added additional pin check and bitbanging fallback * pindefs.[ch]: added a flag to enable/disable output * ft245r.c: changes because of added flag above git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk@1184 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/ChangeLog | 8 ++ avrdude/avrftdi.c | 265 +++++++++++++++++++++++++++++++------- avrdude/avrftdi_private.h | 4 +- avrdude/ft245r.c | 2 +- avrdude/pindefs.c | 49 ++++--- avrdude/pindefs.h | 3 +- 6 files changed, 258 insertions(+), 73 deletions(-) diff --git a/avrdude/ChangeLog b/avrdude/ChangeLog index aafa6f26..cf17d512 100644 --- a/avrdude/ChangeLog +++ b/avrdude/ChangeLog @@ -1,3 +1,11 @@ +2013-06-19 Rene Liebscher + + use bitbanging on ftdi mpsse when wrong pins are used + * avrftdi.c, avrftdi_private.h: added additional pin check + and bitbanging fallback + * pindefs.[ch]: added a flag to enable/disable output + * ft245r.c: changes because of added flag above + 2013-05-17 Joerg Wunsch Submitted by "Malte" and John McCorquodale: diff --git a/avrdude/avrftdi.c b/avrdude/avrftdi.c index 8bc519e8..c29783c6 100644 --- a/avrdude/avrftdi.c +++ b/avrdude/avrftdi.c @@ -295,12 +295,124 @@ static void avrftdi_powerdown(PROGRAMMER * pgm) set_pin(pgm, PPI_AVR_VCC, OFF); } +static inline int set_data(PROGRAMMER * pgm, unsigned char *buf, unsigned char data, bool read_data) { + int j; + int buf_pos = 0; + unsigned char bit = 0x80; + avrftdi_t* pdata = to_pdata(pgm); + + for (j=0; j<8; j++) { + pdata->pin_value = SET_BITS_0(pdata->pin_value,pgm,PIN_AVR_MOSI,data & bit); + pdata->pin_value = SET_BITS_0(pdata->pin_value,pgm,PIN_AVR_SCK,0); + buf[buf_pos++] = SET_BITS_LOW; + buf[buf_pos++] = (pdata->pin_value) & 0xff; + buf[buf_pos++] = (pdata->pin_direction) & 0xff; + buf[buf_pos++] = SET_BITS_HIGH; + buf[buf_pos++] = ((pdata->pin_value) >> 8) & 0xff; + buf[buf_pos++] = ((pdata->pin_direction) >> 8) & 0xff; + + pdata->pin_value = SET_BITS_0(pdata->pin_value,pgm,PIN_AVR_SCK,1); + buf[buf_pos++] = SET_BITS_LOW; + buf[buf_pos++] = (pdata->pin_value) & 0xff; + buf[buf_pos++] = (pdata->pin_direction) & 0xff; + buf[buf_pos++] = SET_BITS_HIGH; + buf[buf_pos++] = ((pdata->pin_value) >> 8) & 0xff; + buf[buf_pos++] = ((pdata->pin_direction) >> 8) & 0xff; + + if (read_data) { + buf[buf_pos++] = GET_BITS_LOW; + buf[buf_pos++] = GET_BITS_HIGH; + } + + bit >>= 1; + } + return buf_pos; +} + +static inline unsigned char extract_data(PROGRAMMER * pgm, unsigned char *buf, int offset) { + int j; + unsigned char bit = 0x80; + unsigned char r = 0; + + buf += offset * 16; // 2 bytes per bit, 8 bits + for (j=0; j<8; j++) { + uint16_t in = buf[0] | (buf[1] << 8); + if (GET_BITS_0(in,pgm,PIN_AVR_MISO)) { + r |= bit; + } + buf += 2; // 2 bytes per input + bit >>= 1; + } + return r; +} + + +static int avrftdi_transmit_bb(PROGRAMMER * pgm, unsigned char mode, unsigned char *buf, + unsigned char *data, int buf_size) +{ + size_t blocksize; + size_t remaining = buf_size; + size_t written = 0; + avrftdi_t* pdata = to_pdata(pgm); + + // more than this does not work with FT2232D + blocksize = 16;//pdata->rx_buffer_size/2; // we are reading 2 bytes per data byte + + while(remaining) + { + + size_t transfer_size = (remaining > blocksize) ? blocksize : remaining; + + // (8*2) outputs per data byte, 6 transmit bytes per output (SET_BITS_LOW/HIGH), + // (8*1) inputs per data byte, 2 transmit bytes per input (GET_BITS_LOW/HIGH), + // 1x SEND_IMMEDIATE + unsigned char send_buffer[(8*2*6)*transfer_size+(8*1*2)*transfer_size+7]; + int len = 0; + int i; + + for(i = 0 ; i< transfer_size; i++) { + len += set_data(pgm, send_buffer + len, buf[written+i], (mode & MPSSE_DO_READ) != 0); + } + + pdata->pin_value = SET_BITS_0(pdata->pin_value,pgm,PIN_AVR_SCK,0); + send_buffer[len++] = SET_BITS_LOW; + send_buffer[len++] = (pdata->pin_value) & 0xff; + send_buffer[len++] = (pdata->pin_direction) & 0xff; + send_buffer[len++] = SET_BITS_HIGH; + send_buffer[len++] = ((pdata->pin_value) >> 8) & 0xff; + send_buffer[len++] = ((pdata->pin_direction) >> 8) & 0xff; + + send_buffer[len++] = SEND_IMMEDIATE; + + E(ftdi_write_data(pdata->ftdic, send_buffer, len) != len, pdata->ftdic); + if (mode & MPSSE_DO_READ) { + unsigned char recv_buffer[2*16*transfer_size]; + int n; + int k = 0; + do { + n = ftdi_read_data(pdata->ftdic, &recv_buffer[k], 2*16*transfer_size - k); + E(n < 0, pdata->ftdic); + k += n; + } while (k < transfer_size); + + for(i = 0 ; i< transfer_size; i++) { + data[written + i] = extract_data(pgm, recv_buffer, i); + } + } + + written += transfer_size; + remaining -= transfer_size; + } + + return written; +} + /* Send 'buf_size' bytes from 'cmd' to device and return data from device in * buffer 'data'. * Write is only performed when mode contains MPSSE_DO_WRITE. * Read is only performed when mode contains MPSSE_DO_WRITE and MPSSE_DO_READ. */ -static int avrftdi_transmit(avrftdi_t* pdata, unsigned char mode, unsigned char *buf, +static int avrftdi_transmit_mpsse(avrftdi_t* pdata, unsigned char mode, unsigned char *buf, unsigned char *data, int buf_size) { size_t blocksize; @@ -358,6 +470,15 @@ static int avrftdi_transmit(avrftdi_t* pdata, unsigned char mode, unsigned char return written; } +static inline int avrftdi_transmit(PROGRAMMER * pgm, unsigned char mode, unsigned char *buf, + unsigned char *data, int buf_size) +{ + avrftdi_t* pdata = to_pdata(pgm); + if (pdata->use_bitbanging) + return avrftdi_transmit_bb(pgm, mode, buf, data, buf_size); + else + return avrftdi_transmit_mpsse(pdata, mode, buf, data, buf_size); +} /* this function tries to sync up with the FTDI. See FTDI application note AN_129. * AN_135 uses 0xab as bad command and enables/disables loopback around synchronisation. * This may fail if data is left in the buffer (i.e. avrdude aborted with ctrl-c) @@ -477,14 +598,41 @@ static int write_flush(avrftdi_t* pdata) } -static int avrftdi_pin_setup(PROGRAMMER * pgm) +static int avrftdi_check_pins_bb(PROGRAMMER * pgm, bool output) { - /************* - * pin setup * - *************/ - int pin; + /* pin checklist. */ + struct pin_checklist_t pin_checklist[N_PINS]; + + avrftdi_t* pdata = to_pdata(pgm); + + /* value for 8/12/16 bit wide interface */ + int valid_mask = ((1 << pdata->pin_limit) - 1); + + log_debug("Using valid mask bibanging: 0x%08x\n", valid_mask); + static struct pindef_t valid_pins; + valid_pins.mask[0] = valid_mask; + valid_pins.inverse[0] = valid_mask ; + + /* build pin checklist */ + for(pin = 0; pin < N_PINS; ++pin) { + pin_checklist[pin].pinname = pin; + pin_checklist[pin].mandatory = 0; + pin_checklist[pin].valid_pins = &valid_pins; + } + + /* assumes all checklists above have same number of entries */ + return pins_check(pgm, pin_checklist, N_PINS, output); +} + +static int avrftdi_check_pins_mpsse(PROGRAMMER * pgm, bool output) +{ + int pin; + + /* pin checklist. */ + struct pin_checklist_t pin_checklist[N_PINS]; + avrftdi_t* pdata = to_pdata(pgm); /* SCK/MOSI/MISO are fixed and not invertable?*/ @@ -498,24 +646,67 @@ static int avrftdi_pin_setup(PROGRAMMER * pgm) /* mask out SCK/MISO/MOSI */ valid_mask &= ~((1 << FTDI_SCK) | (1 << FTDI_MOSI) | (1 << FTDI_MISO)); - log_debug("Using valid mask: 0x%08x\n", valid_mask); + log_debug("Using valid mask mpsse: 0x%08x\n", valid_mask); static struct pindef_t valid_pins_others; valid_pins_others.mask[0] = valid_mask; valid_pins_others.inverse[0] = valid_mask ; /* build pin checklist */ for(pin = 0; pin < N_PINS; ++pin) { - pdata->pin_checklist[pin].pinname = pin; - pdata->pin_checklist[pin].mandatory = 0; - pdata->pin_checklist[pin].valid_pins = &valid_pins_others; + pin_checklist[pin].pinname = pin; + pin_checklist[pin].mandatory = 0; + pin_checklist[pin].valid_pins = &valid_pins_others; } - pdata->pin_checklist[PIN_AVR_SCK].mandatory = 1; - pdata->pin_checklist[PIN_AVR_SCK].valid_pins = &valid_pins_SCK; - pdata->pin_checklist[PIN_AVR_MOSI].mandatory = 1; - pdata->pin_checklist[PIN_AVR_MOSI].valid_pins = &valid_pins_MOSI; - pdata->pin_checklist[PIN_AVR_MISO].mandatory = 1; - pdata->pin_checklist[PIN_AVR_MISO].valid_pins = &valid_pins_MISO; - pdata->pin_checklist[PIN_AVR_RESET].mandatory = 1; + + /* now set mpsse specific pins */ + pin_checklist[PIN_AVR_SCK].mandatory = 1; + pin_checklist[PIN_AVR_SCK].valid_pins = &valid_pins_SCK; + pin_checklist[PIN_AVR_MOSI].mandatory = 1; + pin_checklist[PIN_AVR_MOSI].valid_pins = &valid_pins_MOSI; + pin_checklist[PIN_AVR_MISO].mandatory = 1; + pin_checklist[PIN_AVR_MISO].valid_pins = &valid_pins_MISO; + pin_checklist[PIN_AVR_RESET].mandatory = 1; + + /* assumes all checklists above have same number of entries */ + return pins_check(pgm, pin_checklist, N_PINS, output); +} + +static int avrftdi_pin_setup(PROGRAMMER * pgm) +{ + int pin; + + /************* + * pin setup * + *************/ + + avrftdi_t* pdata = to_pdata(pgm); + + bool pin_check_mpsse = (0 == avrftdi_check_pins_mpsse(pgm, verbose>3)); + + bool pin_check_bitbanging = (0 == avrftdi_check_pins_bb(pgm, verbose>3)); + + if (!pin_check_mpsse && !pin_check_bitbanging) { + log_err("No valid pin configuration found.\n"); + avrftdi_check_pins_bb(pgm, true); + log_err("Pin configuration for FTDI MPSSE must be:\n"); + log_err("%s: 0, %s: 1, %s: 2 (is: %s, %s, %s)\n", avr_pin_name(PIN_AVR_SCK), + avr_pin_name(PIN_AVR_MOSI), avr_pin_name(PIN_AVR_MISO), + pins_to_str(&pgm->pin[PIN_AVR_SCK]), + pins_to_str(&pgm->pin[PIN_AVR_MOSI]), + pins_to_str(&pgm->pin[PIN_AVR_MISO])); + log_err("If other pin configuration is used, fallback to slower bitbanging mode is used.\n"); + + return -1; + } + + pdata->use_bitbanging = !pin_check_mpsse || 1; + if (pdata->use_bitbanging) log_info("Because of pin configuration fallback to bitbanging mode.\n"); + + /* + * TODO: No need to fail for a wrongly configured led or something. + * Maybe we should only fail for SCK; MISO, MOSI, RST (and probably + * VCC and BUFF). + */ /* everything is an output, except MISO */ for(pin = 0; pin < N_PINS; ++pin) { @@ -528,25 +719,6 @@ static int avrftdi_pin_setup(PROGRAMMER * pgm) pdata->led_mask |= pgm->pin[pin].mask[0]; } - /* assumes all checklists above have same number of entries */ - if (pins_check(pgm, pdata->pin_checklist,N_PINS)) { - log_err("Pin configuration for FTDI MPSSE must be:\n"); - log_err("%s: 0, %s: 1, %s: 2 (is: %s, %s, %s)\n", avr_pin_name(PIN_AVR_SCK), - avr_pin_name(PIN_AVR_MOSI), avr_pin_name(PIN_AVR_MISO), - pins_to_str(&pgm->pin[PIN_AVR_SCK]), - pins_to_str(&pgm->pin[PIN_AVR_MOSI]), - pins_to_str(&pgm->pin[PIN_AVR_MISO])); - log_err("Please correct your cabling and/or configuration.\n"); - log_err("If your hardware is fixed, consider using a bitbang programmer.\n"); - - return -1; - } - - /* - * TODO: No need to fail for a wrongly configured led or something. - * Maybe we should only fail for SCK; MISO, MOSI, RST (and probably - * VCC and BUFF). - */ log_info("Pin direction mask: %04x\n", pdata->pin_direction); log_info("Pin value mask: %04x\n", pdata->pin_value); @@ -754,7 +926,7 @@ static int avrftdi_cmd(PROGRAMMER * pgm, unsigned char cmd[4], unsigned char res { /* Do not use 'sizeof(cmd)'. => message from cppcheck: Using sizeof for array given as function argument returns the size of pointer. */ - return avrftdi_transmit(to_pdata(pgm), MPSSE_DO_READ | MPSSE_DO_WRITE, cmd, res, 4); + return avrftdi_transmit(pgm, MPSSE_DO_READ | MPSSE_DO_WRITE, cmd, res, 4); } @@ -838,8 +1010,7 @@ avrftdi_lext(avrftdi_t* pdata, AVRPART *p, AVRMEM *m, unsigned int address) static int avrftdi_eeprom_write(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m, unsigned int page_size, unsigned int addr, unsigned int len) { - unsigned char cmd[] = - { MPSSE_DO_WRITE | MPSSE_WRITE_NEG, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }; + unsigned char cmd[] = { 0x00, 0x00, 0x00, 0x00 }; unsigned char *data = &m->buf[addr]; unsigned int add; @@ -847,14 +1018,12 @@ static int avrftdi_eeprom_write(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m, for (add = addr; add < addr + len; add++) { - avr_set_addr(m->op[AVR_OP_WRITE], &cmd[3], add); - avr_set_input(m->op[AVR_OP_WRITE], &cmd[3], *data++); - - //avrftdi_transmit(to_pdata(pgm), MPSSE_DO_WRITE, cmd, cmd, 4); - E(ftdi_write_data(to_pdata(pgm)->ftdic, cmd, sizeof(cmd)) != sizeof(cmd), - to_pdata(pgm)->ftdic); + avr_set_addr(m->op[AVR_OP_WRITE], cmd, add); + avr_set_input(m->op[AVR_OP_WRITE], cmd, *data++); + avrftdi_transmit(pgm, MPSSE_DO_WRITE, cmd, cmd, 4); usleep((m->max_write_delay)); + } return len; } @@ -873,7 +1042,7 @@ static int avrftdi_eeprom_read(PROGRAMMER *pgm, AVRPART *p, AVRMEM *m, avr_set_bits(m->op[AVR_OP_READ], cmd); avr_set_addr(m->op[AVR_OP_READ], cmd, add); - avrftdi_transmit(to_pdata(pgm), MPSSE_DO_READ | MPSSE_DO_WRITE, cmd, cmd, 4); + avrftdi_transmit(pgm, MPSSE_DO_READ | MPSSE_DO_WRITE, cmd, cmd, 4); avr_get_output(m->op[AVR_OP_READ], cmd, bufptr++); } @@ -963,7 +1132,7 @@ static int avrftdi_flash_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, buf_dump(buf, buf_size, "command buffer", 0, 16*2); log_info("Transmitting buffer of size: %d\n", buf_size); - avrftdi_transmit(to_pdata(pgm), MPSSE_DO_WRITE, buf, buf, buf_size); + avrftdi_transmit(pgm, MPSSE_DO_WRITE, buf, buf, buf_size); bufptr = buf; /* find a poll byte. we cannot poll a value of 0xff, so look @@ -1053,7 +1222,7 @@ static int avrftdi_flash_read(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, buf_dump(o_buf, sizeof(o_buf), "o_buf", 0, 32); } - avrftdi_transmit(to_pdata(pgm), MPSSE_DO_READ | MPSSE_DO_WRITE, o_buf, i_buf, len * 4); + avrftdi_transmit(pgm, MPSSE_DO_READ | MPSSE_DO_WRITE, o_buf, i_buf, len * 4); if(verbose > TRACE) { buf_dump(i_buf, sizeof(i_buf), "i_buf", 0, 32); diff --git a/avrdude/avrftdi_private.h b/avrdude/avrftdi_private.h index 495c6795..d9250bb4 100644 --- a/avrdude/avrftdi_private.h +++ b/avrdude/avrftdi_private.h @@ -78,8 +78,8 @@ typedef struct avrftdi_s { int pin_limit; /* internal RX buffer of the device. needed for INOUT transfers */ int rx_buffer_size; - /* pin checklist. */ - struct pin_checklist_t pin_checklist[N_PINS]; + /* use bitbanging instead of mpsse spi */ + bool use_bitbanging; } avrftdi_t; void avrftdi_log(int level, const char * func, int line, const char * fmt, ...); diff --git a/avrdude/ft245r.c b/avrdude/ft245r.c index 5c208540..38052ac0 100644 --- a/avrdude/ft245r.c +++ b/avrdude/ft245r.c @@ -528,7 +528,7 @@ static int ft245r_open(PROGRAMMER * pgm, char * port) { int rv; int devnum = -1; - rv = pins_check(pgm,pin_checklist,sizeof(pin_checklist)/sizeof(pin_checklist[0])); + rv = pins_check(pgm,pin_checklist,sizeof(pin_checklist)/sizeof(pin_checklist[0]), true); if(rv) { pgm->display(pgm, progbuf); return rv; diff --git a/avrdude/pindefs.c b/avrdude/pindefs.c index b4268a7d..8842649c 100644 --- a/avrdude/pindefs.c +++ b/avrdude/pindefs.c @@ -201,7 +201,7 @@ const char * pinmask_to_str(const pinmask_t * const pinmask) { * @param[in] size the number of entries in checklist * @returns 0 if all pin definitions are valid, -1 otherwise */ -int pins_check(const struct programmer_t * const pgm, const struct pin_checklist_t * const checklist, const int size) { +int pins_check(const struct programmer_t * const pgm, const struct pin_checklist_t * const checklist, const int size, bool output) { static const struct pindef_t no_valid_pins = {{0}, {0}}; // default value if check list does not contain anything else int rv = 0; // return value int pinname; // loop counter through pinnames @@ -253,46 +253,53 @@ int pins_check(const struct programmer_t * const pgm, const struct pin_checklist already_used_all[segment] |= pgm->pin[pinname].mask[segment]; } if(invalid) { - fprintf(stderr, - "%s: %s: Following pins are not valid pins for this function: %s\n", - progname, avr_pin_name(pinname), pinmask_to_str(invalid_used)); - if(verbose >= 2) { + if(output) { fprintf(stderr, - "%s: %s: Valid pins for this function are: %s\n", - progname, avr_pin_name(pinname), pinmask_to_str(valid_pins->mask)); + "%s: %s: Following pins are not valid pins for this function: %s\n", + progname, avr_pin_name(pinname), pinmask_to_str(invalid_used)); + if(verbose >= 2) { + fprintf(stderr, + "%s: %s: Valid pins for this function are: %s\n", + progname, avr_pin_name(pinname), pinmask_to_str(valid_pins->mask)); + } } is_ok = false; } if(inverse) { - fprintf(stderr, + if(output) { + fprintf(stderr, "%s: %s: Following pins are not usable as inverse pins for this function: %s\n", progname, avr_pin_name(pinname), pinmask_to_str(inverse_used)); - if(verbose >= 2) { - fprintf(stderr, - "%s: %s: Valid inverse pins for this function are: %s\n", - progname, avr_pin_name(pinname), pinmask_to_str(valid_pins->inverse)); + if(verbose >= 2) { + fprintf(stderr, + "%s: %s: Valid inverse pins for this function are: %s\n", + progname, avr_pin_name(pinname), pinmask_to_str(valid_pins->inverse)); + } } is_ok = false; } if(used) { - fprintf(stderr, - "%s: %s: Following pins are set for other functions too: %s\n", - progname, avr_pin_name(pinname), pinmask_to_str(already_used)); - is_ok = false; + if(output) { + fprintf(stderr, + "%s: %s: Following pins are set for other functions too: %s\n", + progname, avr_pin_name(pinname), pinmask_to_str(already_used)); + is_ok = false; + } } if(!mandatory_used && is_mandatory && !invalid) { - fprintf(stderr, - "%s: %s: Mandatory pin is not defined.\n", - progname, avr_pin_name(pinname)); + if(output) { + fprintf(stderr, + "%s: %s: Mandatory pin is not defined.\n", + progname, avr_pin_name(pinname)); + } is_ok = false; } if(!is_ok) { rv = -1; - } else if(verbose >= 3) { + } else if(output && verbose >= 3) { fprintf(stderr, "%s: %s: Pin is ok.\n", progname, avr_pin_name(pinname)); - } } return rv; diff --git a/avrdude/pindefs.h b/avrdude/pindefs.h index c36f99a4..c0607084 100644 --- a/avrdude/pindefs.h +++ b/avrdude/pindefs.h @@ -165,9 +165,10 @@ void pgm_fill_old_pins(struct programmer_t * const pgm); * @param[in] pgm the programmer to check * @param[in] checklist the constraint for the pins * @param[in] size the number of entries in checklist + * @param[in] output false suppresses error messages to the user * @returns 0 if all pin definitions are valid, -1 otherwise */ -int pins_check(const struct programmer_t * const pgm, const struct pin_checklist_t * const checklist, const int size); +int pins_check(const struct programmer_t * const pgm, const struct pin_checklist_t * const checklist, const int size, const bool output); /** * Returns the name of the pin as string.