Index: avrdude.1 =================================================================== --- avrdude.1 (revision 816) +++ avrdude.1 (working copy) @@ -142,6 +142,10 @@ They both feature simple firwmare-only USB implementations, running on an ATmega8 (or ATmega88), or ATtiny2313, respectively. .Pp +The Digilent Inc. USB JTAG/ISP adapter (usbdigilent) is supported, provided +.Nm avrdude +has been compiled with libusb support. +.Pp Input files can be provided, and output files can be written in different file formats, such as raw binary files containing the data to download to the chip, Intel hex format, or Motorola S-record Index: doc/avrdude.texi =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/x-texinfo Index: config_gram.y =================================================================== --- config_gram.y (revision 816) +++ config_gram.y (working copy) @@ -43,6 +43,7 @@ #include "avr910.h" #include "butterfly.h" #include "usbasp.h" +#include "usbdigilent.h" #include "usbtiny.h" #include "avr.h" #include "jtagmkI.h" @@ -144,6 +145,7 @@ %token K_STK600PP %token K_AVR910 %token K_USBASP +%token K_USBDIGILENT %token K_USBTINY %token K_BUTTERFLY %token K_TYPE @@ -455,6 +457,12 @@ } } | + K_TYPE TKN_EQUAL K_USBDIGILENT { + { + usbdigilent_initpgm(current_prog); + } + } | + K_TYPE TKN_EQUAL K_USBTINY { { usbtiny_initpgm(current_prog); Index: usbdigilent.c =================================================================== --- usbdigilent.c (revision 0) +++ usbdigilent.c (revision 0) @@ -0,0 +1,577 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2007 Dick Streefland, adapted for 5.4 by Limor Fried + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Driver for Digilent's USB JTAG/SPI programming cable + * Author: Dirk Robinson - address@hidden + * Based on usbtiny.c + */ + +#include "ac_cfg.h" + +#include +#include +#include +#include +#include +#include + +#include "avrdude.h" +#include "avr.h" +#include "pgm.h" +#include "usbdigilent.h" + +#if defined(HAVE_LIBUSB) // we use LIBUSB to talk to the board +#include + +/* + * Private data for this programmer. + */ +struct pdata +{ + usb_dev_handle *usb_handle; + int retries; + unsigned char buf[USBDIG_BULK_LEN]; // 64 byte buffer for USB transfers +}; + +#define PDATA(pgm) ((struct pdata *)(pgm->cookie)) + +// ---------------------------------------------------------------------- + +static void usbdigilent_setup(PROGRAMMER * pgm) +{ + if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) { + fprintf(stderr, + "%s: usbdigilent_setup(): Out of memory allocating private data\n", + progname); + exit(1); + } + memset(pgm->cookie, 0, sizeof(struct pdata)); +} + +static void usbdigilent_teardown(PROGRAMMER * pgm) +{ + free(pgm->cookie); +} + +// Wrapper for simple usb_bulk messages to send data to programmer +// Assumes data part of buffer already set in PDATA(pgm)->buf +static int usb_out (PROGRAMMER * pgm, unsigned char cmd, unsigned int len) +{ + int nbytes; + + // Assemble the USB packet: + // 1 byte - command + // 2 bytes - length (big endian) + // 1 to 61 bytes - data + if (3+len > USBDIG_BULK_LEN) { + fprintf(stderr, "\n%s: error: usbdigilent_send: packet too big\n", + progname); + return -1; + } + PDATA(pgm)->buf[0] = cmd; + PDATA(pgm)->buf[1] = len >> 8; + PDATA(pgm)->buf[2] = len & 0xff; + + nbytes = usb_bulk_write( PDATA(pgm)->usb_handle, USBDIG_BULK_OUT, + (char *)PDATA(pgm)->buf, USBDIG_BULK_LEN, USBDIG_TIMEOUT); + if (nbytes != USBDIG_BULK_LEN) { + fprintf(stderr, + "\n%s: error: usbdigilent_send: %s (expected %d, got %d)\n", + progname, usb_strerror(), USBDIG_BULK_LEN, nbytes); + return -1; + } + + return nbytes; +} + +// Wrapper for simple usb_bulk messages to receive data from programmer +// Result goes to PDATA(pgm)->buf (overwriting output packet) +static int usb_in (PROGRAMMER * pgm, unsigned char cmd, unsigned int len) +{ + int nbytes; + int i; + + // First we have to send the output data + if ( usb_out(pgm, cmd, len) < 0 ) { + return -1; + } + + for (i = 0; i < 10; i++) { + nbytes = usb_bulk_read( PDATA(pgm)->usb_handle, USBDIG_BULK_IN, + (char *)PDATA(pgm)->buf, USBDIG_BULK_LEN, USBDIG_TIMEOUT) ; + if (nbytes == USBDIG_BULK_LEN) { + return len; // Return the number of meaningful bytes + } + PDATA(pgm)->retries++; + } + fprintf(stderr, + "\n%s: error: usbdigilent_receive: %s (expected %d, got %d)\n", + progname, usb_strerror(), USBDIG_BULK_LEN, nbytes); + return -1; + +} + +// Report the number of retries, and reset the counter. +static void check_retries (PROGRAMMER * pgm, const char* operation) +{ + if (PDATA(pgm)->retries > 0 && quell_progress < 2) { + printf("%s: %d retries during %s\n", progname, + PDATA(pgm)->retries, operation); + } + PDATA(pgm)->retries = 0; +} + +// Sometimes we just need to know the SPI command for the part to perform +// a function. Here we wrap this request for an operation so that we +// can just specify the part and operation and it'll do the right stuff +// to get the information from AvrDude and send to the USBdigilent +static int usbdigilent_avr_op (PROGRAMMER * pgm, + OPCODE *op, int addr, unsigned char res[4]) +{ + unsigned char cmd[4]; + + if (op == NULL) { + fprintf( stderr, "Operation not defined for this chip/memory!\n" ); + return -1; + } + memset(cmd, 0, sizeof(cmd)); + avr_set_bits(op, cmd); + if ( addr != 0 ) { + avr_set_addr(op, cmd, addr); + } + + return pgm->cmd(pgm, cmd, res); +} + +// ---------------------------------------------------------------------- + +/* Find a device with the correct VID/PID match for USBdigilent */ + +static int usbdigilent_open(PROGRAMMER* pgm, char* name) +{ + struct usb_bus *bus; + struct usb_device *dev = 0; + + usb_init(); // initialize the libusb system + usb_find_busses(); // have libusb scan all the usb busses available + usb_find_devices(); // have libusb scan all the usb devices available + + PDATA(pgm)->usb_handle = NULL; + + // now we iterate through all the busses and devices + for ( bus = usb_busses; bus; bus = bus->next ) { + for ( dev = bus->devices; dev; dev = dev->next ) { + if (dev->descriptor.idVendor == USBDIG_VENDOR + && dev->descriptor.idProduct == USBDIG_PRODUCT ) { // found match? + + PDATA(pgm)->usb_handle = usb_open(dev); // attempt to connect to device + usb_reset(PDATA(pgm)->usb_handle); + // wrong permissions or something? + if (!PDATA(pgm)->usb_handle) { + fprintf(stderr, "%s: Warning: cannot open USB device: %s\n", + progname, usb_strerror()); + continue; + } + } + } + } + + if (!PDATA(pgm)->usb_handle) { + fprintf( stderr, + "%s: Error: Could not find USBdigilent device (0x%x/0x%x)\n", + progname, USBDIG_VENDOR, USBDIG_PRODUCT ); + return -1; + } + + return 0; // If we got here, we must have found a good USB device +} + +/* Clean up the handle for the usbdigilent */ +static void usbdigilent_close ( PROGRAMMER* pgm ) +{ + if (! PDATA(pgm)->usb_handle) { + return; // not a valid handle, bail! + } + usb_close(PDATA(pgm)->usb_handle); // ask libusb to clean up + PDATA(pgm)->usb_handle = NULL; +} + +/* Set clock, data and reset lines (not necesarily in that order) */ +static int usbdigilent_start (PROGRAMMER *pgm) { + unsigned char *data=PDATA(pgm)->buf+3; + + data[0] = 0; // Clear MOSI + if (usb_out(pgm, USBDIG_SET_MOSI, 1) < 0) { + return -1; + } + usleep(50000); + data[0] = 1; // Drive the programming lines + if (usb_out(pgm, USBDIG_PROG_EN, 1) < 0) { + return -1; + } + usleep(50000); + data[0] = 0; // Reset driven low + if (usb_out(pgm, USBDIG_SET_RESET, 1) < 0) { + return -1; + } + usleep(50000); + return 0; +} + +static int usbdigilent_stop (PROGRAMMER *pgm) { + unsigned char *data=PDATA(pgm)->buf+3; + + data[0] = 1; // Reset driven high + if (usb_out(pgm, USBDIG_SET_RESET, 1) < 0) { + return -1; + } + usleep(50000); + data[0] = 0; // Release programming lines + if (usb_out(pgm, USBDIG_PROG_EN, 1) < 0) { + return -1; + } + usleep(50000); + return 0; +} + +static int usbdigilent_initialize (PROGRAMMER *pgm, AVRPART *p ) +{ + unsigned char res[4]; // store the response from usbdigilentisp + + if ( usbdigilent_start(pgm) < 0 ) { + return -1; + } + + // Attempt to use the underlying avrdude methods to connect + if ( usbdigilent_avr_op(pgm, p->op[AVR_OP_PGM_ENABLE], 0, res) < 0) { + // no response, RESET and try again + if (usbdigilent_stop(pgm) < 0 || + usbdigilent_start(pgm) < 0) { + return -1; + } + usleep(50000); + if ( usbdigilent_avr_op( pgm, p->op[AVR_OP_PGM_ENABLE], 0, res) < 0) { + // give up + return -1; + } + } + return 0; +} + +/* Tell the USBdigilent to release the output pins, etc */ +static void usbdigilent_powerdown(PROGRAMMER * pgm) +{ + if (!PDATA(pgm)->usb_handle) { + return; // wasn't connected in the first place + } + usbdigilent_stop(pgm); // Send USB commands to stop device +} + +/* Send a 4-byte SPI command to the USBdigilentISP for execution + This procedure is used by higher-level Avrdude procedures */ +static int usbdigilent_cmd(PROGRAMMER * pgm, unsigned char cmd[4], + unsigned char res[4]) +{ + unsigned char *data=PDATA(pgm)->buf+3; + int nbytes; + + // Copy cmd to res: the usb_in command will then overwrite it + memcpy(data, cmd, 4 ); + nbytes = usb_in( pgm, USBDIG_SPI, 4); + if (nbytes < 0) { + return -1; + } + memcpy(res, data, 4 ); + + check_retries(pgm, "SPI command"); + if (verbose > 1) { + // print out the data we sent and received + printf( "CMD: [%02x %02x %02x %02x] [%02x %02x %02x %02x]\n", + cmd[0], cmd[1], cmd[2], cmd[3], + res[0], res[1], res[2], res[3] ); + } + if ((nbytes == 4) && // should have read 4 bytes + res[2] == cmd[1]) { // AVR's do a delayed-echo thing + return nbytes; + } else { + return -1; + } +} + +/* Send the chip-erase command */ +static int usbdigilent_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char res[4]; + + if (p->op[AVR_OP_CHIP_ERASE] == NULL) { + fprintf(stderr, "Chip erase instruction not defined for part \"%s\"\n", + p->desc); + return -1; + } + + // get the command for erasing this chip and transmit to avrdude + if ( usbdigilent_avr_op( pgm, p->op[AVR_OP_CHIP_ERASE], 0, res ) < 0) { + return -1; + } + usleep( p->chip_erase_delay ); + + // prepare for further instruction + pgm->initialize(pgm, p); + + return 0; +} + +// These are required functions but don't actually do anything +static void usbdigilent_enable ( PROGRAMMER* pgm ) { } + +static void usbdigilent_disable ( PROGRAMMER* pgm ) { } + +// Helper function to limit read/write increments to page boundaries +// Returns 1 if the increment ends on a page boundary +static int limit_to_page(int *p_chunk, int start, int page_size) +{ + int page_num = start/page_size; + if ( *p_chunk >= (page_num+1)*page_size - start ) { + *p_chunk = (page_num+1)*page_size - start; + return 1; + } else { + return 0; + } +} + +/* To speed up programming and reading, we do 'chunked' transfers. + * This function handles both writes and reads. + * It just assembles up to 15 SPI commands into one USB packet. +*/ +static int usbdigilent_paged_transfer (PROGRAMMER * pgm, AVRPART * p, + AVRMEM* m, int page_size, int n_bytes, int write) +{ + int i, j; + int chunk; + int n_pad_bytes=0; + int ext_address=0; + int full_atmel_page; // End of the AVR page + int full_digilent_page; // End of digilent programmer buffer page + unsigned char *data=PDATA(pgm)->buf+3; + unsigned char res[4]; + OPCODE * op[2]; // Commands for reading/writing low and high bytes + int len_word; + + if ( page_size == 0 ) { // Use the Digilent page size if not set. + page_size = USBDIG_PAGE_SIZE; + } + + // Set up the atmel transfer commands: + if (write) { // Set up for writing: + // Find the number of bytes to pad so we end on a full page + if ( n_bytes % page_size != 0 ) { + n_pad_bytes = page_size - (n_bytes%page_size); + } + if (m->op[AVR_OP_LOADPAGE_HI]) { + op[0] = m->op[AVR_OP_LOADPAGE_LO]; + op[1] = m->op[AVR_OP_LOADPAGE_HI]; + len_word = 2; + } else { + op[0] = m->op[AVR_OP_LOADPAGE_LO]; + op[1] = m->op[AVR_OP_LOADPAGE_LO]; + len_word = 1; + } + } else { // Set up for reading: + if (m->op[AVR_OP_READ]) { + op[0] = m->op[AVR_OP_READ]; + op[1] = m->op[AVR_OP_READ]; + len_word = 1; + } else { + op[0] = m->op[AVR_OP_READ_LO]; + op[1] = m->op[AVR_OP_READ_HI]; + len_word = 2; + } + } + + // Transfer the data: + for (i = 0; i < n_bytes+n_pad_bytes; i += chunk) { + // Extended addressing: + if ( ((i/len_word)>>16) != ext_address ) { + ext_address = (i/len_word)>>16; + if ( usbdigilent_avr_op( pgm, m->op[AVR_OP_LOAD_EXT_ADDR], + i/len_word, res ) < 0) { + return -1; + } + } + + chunk = USBDIG_CHUNK_SIZE; // start with the maximum chunk size possible + full_atmel_page = 0; + full_digilent_page = 0; + + // If we want to xmit less than a chunk, that's OK + if (chunk > n_bytes+n_pad_bytes-i) { + chunk = n_bytes+n_pad_bytes - i; + } + // Stop on Atmel page boundaries: + if ( limit_to_page(&chunk, i, page_size) ) { + full_atmel_page = 1; + } + // Stop on Digilent page boundaries: + if ( limit_to_page(&chunk, i, USBDIG_PAGE_SIZE) ) { + full_digilent_page = 1; + } + + // Set up the USB packet: + memset(data, 0, USBDIG_BULK_LEN-3); + for (j = 0; j < chunk; j++) { + avr_set_bits(op[(i+j)%2], data+4*j); + avr_set_addr(op[(i+j)%2], data+4*j, (i+j)/len_word); + // If we're writing, add on the output data + if ( write ) { + if( i+j < n_bytes ) { + data[4*j+3] = m->buf[i+j]; // Normal data + } else { + data[4*j+3] = 0xff; // Pad bytes + } + } + } + + // Do the USB transfer: + if ( write ) { + // Send the packet + if ( chunkpaged ) { + if ( usbdigilent_avr_op( pgm, m->op[AVR_OP_WRITEPAGE], + i/len_word, res ) < 0) { + return -1; + } + usleep(m->min_write_delay); /* microseconds */ + } + } else { // read + // Send the packet and get the result: + if ( chunkbuf[i+j] = data[4*j+3]; + } + } + + // Tell avrdude how we're doing to provide user feedback + report_progress(i + chunk, n_bytes+n_pad_bytes, NULL ); + } + + // Extended addressing - set back to zero if necessary: + if ( 0 != ext_address ) { + if ( usbdigilent_avr_op( pgm, m->op[AVR_OP_LOAD_EXT_ADDR], 0, res ) < 0) { + return -1; + } + } + + return n_bytes; +} + +/* To speed up programming and reading, we do a 'chunked' read. */ +static int usbdigilent_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + int page_size, int n_bytes) +{ + return usbdigilent_paged_transfer(pgm, p, m, page_size, n_bytes, 0); +} + +/* To speed up programming and reading, we do a 'chunked' write. */ +static int usbdigilent_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + int page_size, int n_bytes) +{ + if ( ! m->paged ) { // Only paged memories are allowed for write + return -1; // (Since we can't add delay between single writes) + } + return usbdigilent_paged_transfer(pgm, p, m, page_size, n_bytes, 1); +} + +extern void usbdigilent_initpgm ( PROGRAMMER* pgm ) +{ + strcpy(pgm->type, "USBdigilent"); + + /* Mandatory Functions */ + pgm->initialize = usbdigilent_initialize; + pgm->enable = usbdigilent_enable; + pgm->disable = usbdigilent_disable; + pgm->program_enable = NULL; + pgm->chip_erase = usbdigilent_chip_erase; + pgm->cmd = usbdigilent_cmd; + pgm->open = usbdigilent_open; + pgm->close = usbdigilent_close; + pgm->read_byte = avr_read_byte_default; + pgm->write_byte = avr_write_byte_default; + + /* Optional Functions */ + pgm->powerup = NULL; + pgm->powerdown = usbdigilent_powerdown; + pgm->paged_load = usbdigilent_paged_load; + pgm->paged_write = usbdigilent_paged_write; + pgm->set_sck_period = NULL; + pgm->setup = usbdigilent_setup; + pgm->teardown = usbdigilent_teardown; +} + +#else /* !HAVE_LIBUSB */ + +// Give a proper error if we were not compiled with libusb + +static int usbdigilent_nousb_open(struct programmer_t *pgm, char * name) +{ + fprintf(stderr, + "%s: error: no usb support. Please compile again with libusb installed.\n", + progname); + + return -1; +} + +void usbdigilent_initpgm(PROGRAMMER * pgm) +{ + strcpy(pgm->type, "usbdigilent"); + + pgm->open = usbdigilent_nousb_open; +} + +#endif /* HAVE_LIBUSB */ +// vim:sw=2:ts=8 Index: usbdigilent.h =================================================================== --- usbdigilent.h (revision 0) +++ usbdigilent.h (revision 0) @@ -0,0 +1,71 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2007 Limor Fried + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef usbdigilent_h +#define usbdigilent_h + +#include "avrpart.h" + +#define USBDIG_VENDOR 0x1443 +#define USBDIG_PRODUCT 0x0001 + +#define USBDIG_BULK_OUT 4 +#define USBDIG_BULK_IN 2 +#define USBDIG_BULK_LEN 64 + +/* Digilent commands are sent with USB bulk transfers and always 64 bytes long. + * Commands that have a response also return a 64 byte packet. + * Byte 1: command (output) / response code (response) + * Byte 2: length (upper byte) + * Byte 3: length (lower byte) + * Bytes 4-64: data + * + * Most commands send raw SPI data. Thus up to 15 commands can fit in + * data part of one USB transfer. + */ + +#define USBDIG_PROG_EN 0x08 // Enable driving of programming lines +#define USBDIG_SET_RESET 0x09 // Set/clear the reset line +#define USBDIG_SET_MOSI 0x0a // Set/clear the Atmel's input + +#define USBDIG_WRITE_PAGE 0x0b // Write 15 bytes, no response +#define USBDIG_WRITE_WAIT 0x8b // Write up to 15 bytes, wait for response +#define USBDIG_READ_PAGE 0x4b // Read 15 bytes +#define USBDIG_SPI 0xcb // Write/Read up to 15 bytes (with response) + +// How much data, max, do we want to send in one USB packet? +#define USBDIG_CHUNK_SIZE 15 // 15 SPI commands = 60 bytes +#define USBDIG_PAGE_SIZE 64 // Digilent programmer can hold + // 64 SPI instructions + +// The default USB Timeout +#define USBDIG_TIMEOUT 500 // msec + +#ifdef __cplusplus +extern "C" { +#endif + +void usbdigilent_initpgm (PROGRAMMER * pgm); + +#ifdef __cplusplus +} +#endif + +#endif /* usbdigilent_h */ Index: avrdude.conf.in =================================================================== --- avrdude.conf.in (revision 816) +++ avrdude.conf.in (working copy) @@ -411,6 +411,12 @@ ; programmer + id = "usbdigilent"; + desc = "Digilent USB Programmer, http://www.digilentinc.com"; + type = usbdigilent; +; + +programmer id = "usbtiny"; desc = "USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/"; type = usbtiny; Index: avr.c =================================================================== --- avr.c (revision 816) +++ avr.c (working copy) @@ -149,7 +149,7 @@ int verbose) { unsigned char rbyte; - unsigned long i; + long i; unsigned char * buf; AVRMEM * mem; int rc; @@ -540,7 +540,7 @@ { int rc; int wsize; - unsigned long i; + long i; unsigned char data; int werror; AVRMEM * m; Index: lexer.l =================================================================== --- lexer.l (revision 816) +++ lexer.l (working copy) @@ -121,6 +121,7 @@ avr910 { yylval=NULL; return K_AVR910; } avr910_devcode { yylval=NULL; return K_AVR910_DEVCODE; } usbasp { yylval=NULL; return K_USBASP; } +usbdigilent { yylval=NULL; return K_USBDIGILENT; } usbtiny { yylval=NULL; return K_USBTINY; } bank_size { yylval=NULL; return K_PAGE_SIZE; } banked { yylval=NULL; return K_PAGED; } Index: Makefile.am =================================================================== --- Makefile.am (revision 816) +++ Makefile.am (working copy) @@ -138,6 +138,8 @@ usbasp.c \ usbasp.h \ usbdevs.h \ + usbdigilent.h \ + usbdigilent.c \ usb_libusb.c \ usbtiny.h \ usbtiny.c \