From 5d0bfcaf4c8b51e46423cf7cce8a4bea8129c89c Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Sat, 26 Jul 2008 22:53:40 +0000 Subject: [PATCH] First implementation of ATxmega support. By now, only the PDI mode of the STK600 is supported. Single-byte EEPROM (and flash) updates do not work yet. * avr.c: "boot" memory is a candidate memory region for paged operations, besides "flash" and "eeprom". * avrdude.conf.in: add ATxmega128A1 and ATxmega128A1revD * avrpart.h: add the AVRPART_HAS_PDI flag (used to distinguish ATxmega parts from classic AVRs), the nvm_base part field, and the offset field for a memory region. * config_gram.y: add "has_pdi", "nvm_base", and "offset" * lexer.l: (Ditto.) * main.c: disable auto_erase for ATxmega parts * stk500v2.c: implement the XPROG functionality, and divert to this for ATxmega parts * avrdude.1: Document the changes. git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@777 81a1dc3b-b13d-400b-aceb-764788c761c2 --- ChangeLog | 19 ++ NEWS | 3 + avr.c | 6 +- avrdude.1 | 14 +- avrdude.conf.in | 148 +++++++++++++ avrpart.h | 3 + config_gram.y | 25 +++ doc/avrdude.texi | 17 +- lexer.l | 3 + main.c | 9 +- stk500v2.c | 543 ++++++++++++++++++++++++++++++++++++++++++++++- 11 files changed, 769 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index e7b818d0..77176cbe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2008-07-27 Joerg Wunsch + + First implementation of ATxmega support. By now, only the + PDI mode of the STK600 is supported. Single-byte EEPROM + (and flash) updates do not work yet. + * avr.c: "boot" memory is a candidate memory region for paged + operations, besides "flash" and "eeprom". + * avrdude.conf.in: add ATxmega128A1 and ATxmega128A1revD + * avrpart.h: add the AVRPART_HAS_PDI flag (used to distinguish + ATxmega parts from classic AVRs), the nvm_base part field, and + the offset field for a memory region. + * config_gram.y: add "has_pdi", "nvm_base", and "offset" + * lexer.l: (Ditto.) + * main.c: disable auto_erase for ATxmega parts + * stk500v2.c: implement the XPROG functionality, and divert to + this for ATxmega parts + * avrdude.1: Document the changes. + * doc/avrdude.texi: (Ditto.) + 2008-07-25 Joerg Wunsch Fix a bunch of warnings. diff --git a/NEWS b/NEWS index 0560a283..b7e0956e 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,9 @@ Current: * Add support to bootstrap with GNU autoconf 2.61, and automake 1.10, respectively. + * Add support for ATxmega128A1 (including the revision D engineering + samples) for STK600 tools using PDI + Version 5.5: * Add support for the USBtinyISP programmer (patch #6233) diff --git a/avr.c b/avr.c index 45ed124c..56946da7 100644 --- a/avr.c +++ b/avr.c @@ -171,7 +171,8 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, */ memset(buf, 0xff, size); - if ((strcmp(mem->desc, "flash")==0) || (strcmp(mem->desc, "eeprom")==0)) { + if ((strcmp(mem->desc, "flash")==0) || (strcmp(mem->desc, "eeprom")==0) || + (strcmp(mem->desc, "boot")==0)) { if (pgm->paged_load != NULL && mem->page_size != 0) { /* * the programmer supports a paged mode read, perhaps more @@ -568,7 +569,8 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size, progbuf, wsize); } - if ((strcmp(m->desc, "flash")==0) || (strcmp(m->desc, "eeprom")==0)) { + if ((strcmp(m->desc, "flash")==0) || (strcmp(m->desc, "eeprom")==0) || + (strcmp(m->desc, "boot")==0)) { if (pgm->paged_write != NULL && m->page_size != 0) { /* * the programmer supports a paged mode write, perhaps more diff --git a/avrdude.1 b/avrdude.1 index c9e5d829..ea0c60ac 100644 --- a/avrdude.1 +++ b/avrdude.1 @@ -19,7 +19,7 @@ .\" .\" $Id$ .\" -.Dd DATE March 24, 2008 +.Dd DATE July 27, 2008 .Os .Dt AVRDUDE 1 .Sh NAME @@ -109,6 +109,7 @@ both parallel and serial .Pp Atmel's STK600 programmer is supported in ISP and high-voltage programming modes, and connects through the USB. +For ATxmega devices, the STK600 is supported in PDI mode. .Pp The simple serial programmer described in Atmel's application note AVR910, and the bootloader described in Atmel's application note @@ -232,6 +233,8 @@ t25 ATtiny25 t26 ATtiny26 t45 ATtiny45 t85 ATtiny85 +x128a1 ATxmega128A1 +x128a1d ATxmega128A1revD .TE .Bl -tag -width "(**) " .It "(*)" @@ -286,11 +289,18 @@ option with flash memory is specified, will perform a chip erase before starting any of the programming operations, since it generally is a mistake to program the flash without performing an erase first. This option disables that. +Auto erase is not used for ATxmega devices as these devices can +use page erase before writing each page so no explicit chip erase +is required. +Note however that any page not affected by the current operation +will retain its previous contents. .It Fl e Causes a chip erase to be executed. This will reset the contents of the flash ROM and EEPROM to the value .Ql 0xff , -and is basically a prerequisite command before the flash ROM can be +and clear all lock bits. +Except for ATxmega devices which can use page erase, +it is basically a prerequisite command before the flash ROM can be reprogrammed again. The only exception would be if the new contents would exclusively cause bits to be programmed from the value .Ql 1 diff --git a/avrdude.conf.in b/avrdude.conf.in index 3f78008e..8b879ebb 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -12512,3 +12512,151 @@ part "0 0 0 0 0 0 0 0 o o o o o o o o"; ; ; + +#------------------------------------------------------------ +# ATxmega128A1 rev. D (engineering samples) +#------------------------------------------------------------ + +part + id = "x128a1d"; + desc = "ATXMEGA128A1REVD"; + signature = 0x1E 0x97 0x41; + has_jtag = yes; + has_pdi = yes; + nvm_base = 0x01C0; + + memory "eeprom" + page_size = 32; + size = 2048; + ; + + memory "flash" + size = 0x20000; + page_size = 512; + ; + + memory "boot" + size = 0x2000; + page_size = 512; + ; + +# signature is actually in IO address space + memory "signature" + size = 3; + offset = 0x90; + ; + + memory "fuse0" + size = 1; + offset = 0x20; + ; + + memory "fuse1" + size = 1; + offset = 0x21; + ; + + memory "fuse2" + size = 1; + offset = 0x22; + ; + + memory "fuse4" + size = 1; + offset = 0x24; + ; + + memory "fuse5" + size = 1; + offset = 0x25; + ; + + memory "lockbits" + size = 1; + offset = 0x27; + ; + + memory "calibration" + size = 512; + offset = 0x200; + ; + + memory "usersig" + size = 512; + ; + + ; + +#------------------------------------------------------------ +# ATxmega128A1 +#------------------------------------------------------------ + +part + id = "x128a1"; + desc = "ATXMEGA128A1"; + signature = 0x1E 0x97 0x4C; + has_jtag = yes; + has_pdi = yes; + nvm_base = 0x01C0; + + memory "eeprom" + page_size = 32; + size = 2048; + ; + + memory "flash" + size = 0x20000; + page_size = 512; + ; + + memory "boot" + size = 0x2000; + page_size = 512; + ; + +# signature is actually in IO address space + memory "signature" + size = 3; + offset = 0x90; + ; + + memory "fuse0" + size = 1; + offset = 0x20; + ; + + memory "fuse1" + size = 1; + offset = 0x21; + ; + + memory "fuse2" + size = 1; + offset = 0x22; + ; + + memory "fuse4" + size = 1; + offset = 0x24; + ; + + memory "fuse5" + size = 1; + offset = 0x25; + ; + + memory "lockbits" + size = 1; + offset = 0x27; + ; + + memory "calibration" + size = 512; + ; + + memory "usersig" + size = 512; + offset = 0x200; + ; + + ; diff --git a/avrpart.h b/avrpart.h index 359e3765..fb58f306 100644 --- a/avrpart.h +++ b/avrpart.h @@ -88,6 +88,7 @@ typedef struct opcode { #define AVRPART_ALLOWFULLPAGEBITSTREAM 0x0010 /* JTAG ICE mkII param. */ #define AVRPART_ENABLEPAGEPROGRAMMING 0x0020 /* JTAG ICE mkII param. */ #define AVRPART_HAS_DW 0x0040 /* part has a debugWire i/f */ +#define AVRPART_HAS_PDI 0x0080 /* part has PDI i/f rather than ISP (ATxmega) */ #define AVR_DESCLEN 64 #define AVR_IDLEN 32 @@ -147,6 +148,7 @@ typedef struct avrpart { unsigned char rampz; /* JTAG ICE mkII XML file parameter */ unsigned char spmcr; /* JTAG ICE mkII XML file parameter */ unsigned short eecr; /* JTAC ICE mkII XML file parameter */ + unsigned int nvm_base; /* Base address of NVM controller in ATxmega devices */ OPCODE * op[AVR_OP_MAX]; /* opcodes */ @@ -162,6 +164,7 @@ typedef struct avrmem { int size; /* total memory size in bytes */ int page_size; /* size of memory page (if page addressed) */ int num_pages; /* number of pages (if page addressed) */ + unsigned int offset; /* offset in IO memory (ATxmega) */ int min_write_delay; /* microseconds */ int max_write_delay; /* microseconds */ int pwroff_after_write; /* after this memory type is written to, diff --git a/config_gram.y b/config_gram.y index c4e8c6e1..7bbe0242 100644 --- a/config_gram.y +++ b/config_gram.y @@ -111,6 +111,8 @@ static int parse_cmdbits(OPCODE * op); %token K_MISO %token K_MOSI %token K_NUM_PAGES +%token K_NVM_BASE +%token K_OFFSET %token K_PAGEL %token K_PAR %token K_PARALLEL @@ -201,6 +203,7 @@ static int parse_cmdbits(OPCODE * op); %token K_ENABLEPAGEPROGRAMMING /* ? yes for mega256*, mega406 */ %token K_HAS_JTAG /* MCU has JTAG i/f. */ %token K_HAS_DW /* MCU has debugWire i/f. */ +%token K_HAS_PDI /* MCU has PDI i/f rather than ISP (ATxmega). */ %token K_IDR /* address of OCD register in IO space */ %token K_RAMPZ /* address of RAMPZ reg. in IO space */ %token K_SPMCR /* address of SPMC[S]R in memory space */ @@ -1028,6 +1031,16 @@ part_parm : free_token($3); } | + K_HAS_PDI TKN_EQUAL yesno + { + if ($3->primary == K_YES) + current_part->flags |= AVRPART_HAS_PDI; + else if ($3->primary == K_NO) + current_part->flags &= ~AVRPART_HAS_PDI; + + free_token($3); + } | + K_ALLOWFULLPAGEBITSTREAM TKN_EQUAL yesno { if ($3->primary == K_YES) @@ -1072,6 +1085,12 @@ part_parm : free_token($3); } | + K_NVM_BASE TKN_EQUAL TKN_NUMBER + { + current_part->nvm_base = $3->value.number; + free_token($3); + } | + K_SERIAL TKN_EQUAL yesno { if ($3->primary == K_YES) @@ -1189,6 +1208,12 @@ mem_spec : free_token($3); } | + K_OFFSET TKN_EQUAL TKN_NUMBER + { + current_mem->offset = $3->value.number; + free_token($3); + } | + K_MIN_WRITE_DELAY TKN_EQUAL TKN_NUMBER { current_mem->min_write_delay = $3->value.number; diff --git a/doc/avrdude.texi b/doc/avrdude.texi index 22b8004e..d6e708bb 100644 --- a/doc/avrdude.texi +++ b/doc/avrdude.texi @@ -327,6 +327,8 @@ Currently, the following MCU types are understood: @item @code{t26} @tab ATtiny26 @item @code{t45} @tab ATtiny45 @item @code{t85} @tab ATtiny85 +@item @code{x128a1} @tab ATxmega128A1 +@item @code{x128a1d} @tab ATxmega128A1revD @end multitable (*) The AT90S2323 and ATtiny22 use the same algorithm. @@ -470,7 +472,7 @@ Atmel STK500, running a version 1.x firmware @item @code{stk500v2} @tab Atmel STK500, running a version 2.x firmware @item @code{stk600} @tab -Atmel STK600 in ISP mode +Atmel STK600 in ISP mode, or in PDI mode for ATxmega devices @item @code{stk600hvsp} @tab Atmel STK600 in high-voltage serial programming mode @item @code{stk600pp} @tab @@ -501,13 +503,18 @@ the method of searching for the configuration file for Windows. Disable auto erase for flash. When the -U option with flash memory is specified, avrdude will perform a chip erase before starting any of the programming operations, since it generally is a mistake to program the flash -without performing an erase first. This option disables that. However, to -remain backward compatible, the -i, and -m options automatically disable the -auto erase feature. +without performing an erase first. This option disables that. +Auto erase is not used for ATxmega devices as these devices can +use page erase before writing each page so no explicit chip erase +is required. +Note however that any page not affected by the current operation +will retain its previous contents. @item -e Causes a chip erase to be executed. This will reset the contents of the -flash ROM and EEPROM to the value `0xff', and is basically a +flash ROM and EEPROM to the value `0xff', and clear all lock bits. +Except for ATxmega devices which can use page erase, +it is basically a prerequisite command before the flash ROM can be reprogrammed again. The only exception would be if the new contents would exclusively cause bits to be programmed from the value `1' to `0'. Note that in order diff --git a/lexer.l b/lexer.l index e5f7bc75..7bca3860 100644 --- a/lexer.l +++ b/lexer.l @@ -145,6 +145,7 @@ errled { yylval=NULL; return K_ERRLED; } flash { yylval=NULL; return K_FLASH; } has_jtag { yylval=NULL; return K_HAS_JTAG; } has_debugwire { yylval=NULL; return K_HAS_DW; } +has_pdi { yylval=NULL; return K_HAS_PDI; } id { yylval=NULL; return K_ID; } idr { yylval=NULL; return K_IDR; } jtagmki { yylval=NULL; return K_JTAG_MKI; } @@ -158,6 +159,8 @@ miso { yylval=NULL; return K_MISO; } mosi { yylval=NULL; return K_MOSI; } num_banks { yylval=NULL; return K_NUM_PAGES; } num_pages { yylval=NULL; return K_NUM_PAGES; } +nvm_base { yylval=NULL; return K_NVM_BASE; } +offset { yylval=NULL; return K_OFFSET; } page_size { yylval=NULL; return K_PAGE_SIZE; } paged { yylval=NULL; return K_PAGED; } pagel { yylval=NULL; return K_PAGEL; } diff --git a/main.c b/main.c index d2e7685a..6d2c7fb5 100644 --- a/main.c +++ b/main.c @@ -953,7 +953,14 @@ int main(int argc, char * argv []) } - + if ((p->flags & AVRPART_HAS_PDI) != 0) { + /* + * This is an ATxmega which can page erase, so no auto erase is + * needed. + */ + auto_erase = 0; + } + if ((erase == 0) && (auto_erase == 1)) { AVRMEM * m; diff --git a/stk500v2.c b/stk500v2.c index 76c6bad1..f4d1fa3d 100644 --- a/stk500v2.c +++ b/stk500v2.c @@ -220,6 +220,9 @@ static int stk500v2_set_sck_period_mk2(PROGRAMMER * pgm, double v); static int stk600_set_sck_period(PROGRAMMER * pgm, double v); +static void stk600_setup_xprog(PROGRAMMER * pgm); +static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p); + static void stk500v2_setup(PROGRAMMER * pgm) { if ((pgm->cookie = malloc(sizeof(struct pdata))) == 0) { @@ -629,14 +632,47 @@ retry: fprintf(stderr, "%s: stk500v2_command(): short reply\n", progname); return -1; } - if (buf[1] == STATUS_CMD_OK) - return status; - if (buf[1] == STATUS_CMD_FAILED) - fprintf(stderr, "%s: stk500v2_command(): command failed\n", progname); - else - fprintf(stderr, "%s: stk500v2_command(): unknown status 0x%02x\n", - progname, buf[1]); - return -1; + if (buf[0] == CMD_XPROG_SETMODE || buf[0] == CMD_XPROG) { + /* + * Decode XPROG wrapper errors. + */ + const char *msg; + int i; + + /* + * For CMD_XPROG_SETMODE, the status is returned in buf[1]. + * For CMD_XPROG, buf[1] contains the XPRG_CMD_* command, and + * buf[2] contains the status. + */ + i = buf[0] == CMD_XPROG_SETMODE? 1: 2; + + if (buf[i] != XPRG_ERR_OK) { + switch (buf[i]) { + case XPRG_ERR_FAILED: msg = "Failed"; break; + case XPRG_ERR_COLLISION: msg = "Collision"; break; + case XPRG_ERR_TIMEOUT: msg = "Timeout"; break; + default: msg = "Unknown"; break; + } + fprintf(stderr, "%s: stk500v2_command(): error in %s: %s\n", + progname, + (buf[0] == CMD_XPROG_SETMODE? "CMD_XPROG_SETMODE": "CMD_XPROG"), + msg); + return -1; + } + return 0; + } else { + /* + * Decode STK500v2 errors. + */ + if (buf[1] == STATUS_CMD_OK) + return status; + if (buf[1] == STATUS_CMD_FAILED) + fprintf(stderr, "%s: stk500v2_command(): command failed\n", progname); + else + fprintf(stderr, "%s: stk500v2_command(): unknown status 0x%02x\n", + progname, buf[1]); + return -1; + } } // otherwise try to sync up again @@ -799,7 +835,7 @@ static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p) if (p->op[AVR_OP_PGM_ENABLE] == NULL) { fprintf(stderr, "%s: stk500v2_program_enable(): program enable instruction not defined for part \"%s\"\n", - progname, p->desc); + progname, p->desc); return -1; } @@ -924,6 +960,15 @@ static int stk500hvsp_program_enable(PROGRAMMER * pgm, AVRPART * p) static int stk500v2_initialize(PROGRAMMER * pgm, AVRPART * p) { + if (pgmtype == PGMTYPE_STK600 && + (p->flags & AVRPART_HAS_PDI) != 0) { + /* + * This is an ATxmega device, must use XPROG protocol for the + * remaining actions. + */ + stk600_setup_xprog(pgm); + } + return pgm->program_enable(pgm, p); } @@ -1240,7 +1285,7 @@ static int stk500hv_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, enum hvmode mode) { int result, cmdlen = 2; - char buf[266]; + unsigned char buf[266]; unsigned long paddr = 0UL, *paddr_ptr = NULL; unsigned int pagesize = 0, use_ext_addr = 0, addrshift = 0; unsigned char *cache_ptr = NULL; @@ -1371,7 +1416,7 @@ static int stk500hv_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, enum hvmode mode) { int result, cmdlen, timeout = 0, pulsewidth = 0; - char buf[266]; + unsigned char buf[266]; unsigned long paddr = 0UL, *paddr_ptr = NULL; unsigned int pagesize = 0, use_ext_addr = 0, addrshift = 0; unsigned char *cache_ptr = NULL; @@ -2822,6 +2867,482 @@ static int stk500v2_dragon_hv_open(PROGRAMMER * pgm, char * port) return 0; } +/* + * XPROG wrapper + */ +static int stk600_xprog_command(PROGRAMMER * pgm, unsigned char *b, + unsigned int cmdsize, unsigned int responsesize) +{ + unsigned char *newb; + unsigned int s; + int rv; + + if (cmdsize < responsesize) + s = responsesize; + else + s = cmdsize; + + if ((newb = malloc(s + 1)) == 0) { + fprintf(stderr, "%s: stk600_xprog_cmd(): out of memory\n", + progname); + return -1; + } + + newb[0] = CMD_XPROG; + memcpy(newb + 1, b, cmdsize); + rv = stk500v2_command(pgm, newb, cmdsize + 1, responsesize + 1); + if (rv == 0) { + memcpy(b, newb + 1, responsesize); + } + + free(newb); + + return rv; +} + + +/* + * issue the 'program enable' command to the AVR device, XPROG version + */ +static int stk600_xprog_program_enable(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char buf[16]; + unsigned int eepagesize = 42; + unsigned int nvm_base; + AVRMEM *mem; + + if (p->nvm_base == 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): no nvm_base parameter for XPROG device\n", + progname); + return -1; + } + if ((mem = avr_locate_mem(p, "eeprom")) != NULL) { + if (mem->page_size == 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): no EEPROM page_size parameter for XPROG device\n", + progname); + return -1; + } + eepagesize = mem->page_size; + } + + buf[0] = CMD_XPROG_SETMODE; + buf[1] = 0; /* PDI mode */ + if (stk500v2_command(pgm, buf, 2, sizeof(buf)) < 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): CMD_XPROG_SETMODE failed\n", + progname); + return -1; + } + + buf[0] = XPRG_CMD_ENTER_PROGMODE; + if (stk600_xprog_command(pgm, buf, 1, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): XPRG_CMD_ENTER_PROGMODE failed\n", + progname); + return -1; + } + + buf[0] = XPRG_CMD_SET_PARAM; + buf[1] = XPRG_PARAM_NVMBASE; + nvm_base = p->nvm_base; + /* + * The 0x01000000 appears to be an indication to the programmer + * that the respective address is located in IO (i.e., SRAM) + * memory address space rather than flash. This is not documented + * anywhere in AVR079 but matches what AVR Studio does. + */ + nvm_base |= 0x01000000; + buf[2] = nvm_base >> 24; + buf[3] = nvm_base >> 16; + buf[4] = nvm_base >> 8; + buf[5] = nvm_base; + if (stk600_xprog_command(pgm, buf, 6, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): XPRG_CMD_SET_PARAM(XPRG_PARAM_NVMBASE) failed\n", + progname); + return -1; + } + + if (mem != NULL) { + buf[0] = XPRG_CMD_SET_PARAM; + buf[1] = XPRG_PARAM_EEPPAGESIZE; + buf[2] = eepagesize >> 8; + buf[3] = eepagesize; + if (stk600_xprog_command(pgm, buf, 4, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): XPRG_CMD_SET_PARAM(XPRG_PARAM_EEPPAGESIZE) failed\n", + progname); + return -1; + } + } + + return 0; +} + +static void stk600_xprog_disable(PROGRAMMER * pgm) +{ + unsigned char buf[2]; + + buf[0] = XPRG_CMD_LEAVE_PROGMODE; + if (stk600_xprog_command(pgm, buf, 1, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_program_enable(): XPRG_CMD_LEAVE_PROGMODE failed\n", + progname); + } +} + +static int stk600_xprog_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char data) +{ + unsigned char b[10]; + + /* + * Fancy offsets everywhere. + * This is probably what AVR079 means when writing about the + * "TIF address space". + */ + if (strcmp(mem->desc, "flash") == 0) { + b[1] = XPRG_MEM_TYPE_APPL; + addr += 0x00800000; + } else if (strcmp(mem->desc, "boot") == 0) { + b[1] = XPRG_MEM_TYPE_BOOT; + addr += 0x00800000; + } else if (strcmp(mem->desc, "eeprom") == 0) { + b[1] = XPRG_MEM_TYPE_EEPROM; + addr += 0x008c0000; + } else if (strcmp(mem->desc, "lockbits") == 0) { + b[1] = XPRG_MEM_TYPE_LOCKBITS; + addr += 0x008f0000; + } else if (strcmp(mem->desc, "usersig") == 0) { + b[1] = XPRG_MEM_TYPE_USERSIG; + addr += 0x008e0000; + } else { + fprintf(stderr, + "%s: stk600_xprog_write_byte(): unknown memory \"%s\"\n", + progname, mem->desc); + return -1; + } + addr += mem->offset; + + b[0] = XPRG_CMD_WRITE_MEM; + b[2] = 0; /* pagemode: non-paged write */ + b[3] = addr >> 24; + b[4] = addr >> 16; + b[5] = addr >> 8; + b[6] = addr; + b[7] = 0; + b[8] = 1; + b[9] = data; + if (stk600_xprog_command(pgm, b, 10, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_write_byte(): XPRG_CMD_WRITE_MEM failed\n", + progname); + return -1; + } + return 0; +} + + +static int stk600_xprog_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char * value) +{ + unsigned char b[8]; + + /* + * Fancy offsets everywhere. + * This is probably what AVR079 means when writing about the + * "TIF address space". + */ + if (strcmp(mem->desc, "flash") == 0) { + b[1] = XPRG_MEM_TYPE_APPL; + addr += 0x00800000; + } else if (strcmp(mem->desc, "boot") == 0) { + b[1] = XPRG_MEM_TYPE_BOOT; + addr += 0x00800000; + } else if (strcmp(mem->desc, "eeprom") == 0) { + b[1] = XPRG_MEM_TYPE_EEPROM; + addr += 0x008c0000; + } else if (strcmp(mem->desc, "signature") == 0) { + b[1] = XPRG_MEM_TYPE_APPL; + addr += 0x01000000; + } else if (strncmp(mem->desc, "fuse", strlen("fuse")) == 0) { + b[1] = XPRG_MEM_TYPE_FUSE; + addr += 0x008f0000; + } else if (strcmp(mem->desc, "lockbits") == 0) { + b[1] = XPRG_MEM_TYPE_LOCKBITS; + addr += 0x008f0000; + } else if (strcmp(mem->desc, "calibration") == 0) { + b[1] = XPRG_MEM_TYPE_FACTORY_CALIBRATION; + addr += 0x008e0000; + } else if (strcmp(mem->desc, "usersig") == 0) { + b[1] = XPRG_MEM_TYPE_USERSIG; + addr += 0x008e0000; + } else { + fprintf(stderr, + "%s: stk600_xprog_read_byte(): unknown memory \"%s\"\n", + progname, mem->desc); + return -1; + } + addr += mem->offset; + + b[0] = XPRG_CMD_READ_MEM; + b[2] = addr >> 24; + b[3] = addr >> 16; + b[4] = addr >> 8; + b[5] = addr; + b[6] = 0; + b[7] = 1; + if (stk600_xprog_command(pgm, b, 8, 3) < 0) { + fprintf(stderr, + "%s: stk600_xprog_read_byte(): XPRG_CMD_READ_MEM failed\n", + progname); + return -1; + } + *value = b[2]; + return 0; +} + + +static int stk600_xprog_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + int page_size, int n_bytes) +{ + unsigned char *b; + unsigned int addr; + unsigned int offset; + unsigned char memtype; + int n_bytes_orig = n_bytes; + + /* + * The XPROG read command supports at most 256 bytes in one + * transfer. + */ + if (page_size > 256) + page_size = 256; /* not really a page size anymore */ + + /* + * Fancy offsets everywhere. + * This is probably what AVR079 means when writing about the + * "TIF address space". + */ + if (strcmp(mem->desc, "flash") == 0) { + memtype = XPRG_MEM_TYPE_APPL; + addr = 0x00800000; + } else if (strcmp(mem->desc, "boot") == 0) { + memtype = XPRG_MEM_TYPE_BOOT; + addr = 0x00800000; + } else if (strcmp(mem->desc, "eeprom") == 0) { + memtype = XPRG_MEM_TYPE_EEPROM; + addr = 0x008c0000; + } else { + fprintf(stderr, + "%s: stk600_xprog_paged_load(): unknown paged memory \"%s\"\n", + progname, mem->desc); + return -1; + } + + if ((b = malloc(page_size + 2)) == NULL) { + fprintf(stderr, + "%s: stk600_xprog_paged_load(): out of memory\n", + progname); + return -1; + } + + offset = 0; + while (n_bytes != 0) { + report_progress(offset, n_bytes_orig, NULL); + b[0] = XPRG_CMD_READ_MEM; + b[1] = memtype; + b[2] = addr >> 24; + b[3] = addr >> 16; + b[4] = addr >> 8; + b[5] = addr; + b[6] = page_size >> 8; + b[7] = page_size; + if (stk600_xprog_command(pgm, b, 8, page_size + 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_paged_load(): XPRG_CMD_READ_MEM failed\n", + progname); + return -1; + } + memcpy(mem->buf + offset, b + 2, page_size); + if (n_bytes < page_size) { + n_bytes = page_size; + } + offset += page_size; + addr += page_size; + n_bytes -= page_size; + } + free(b); + + return n_bytes_orig; +} + +static int stk600_xprog_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + int page_size, int n_bytes) +{ + unsigned char *b; + unsigned int addr; + unsigned int offset; + unsigned char memtype; + int n_bytes_orig = n_bytes; + + /* + * The XPROG read command supports at most 256 bytes in one + * transfer. + */ + if (page_size > 512) { + fprintf(stderr, + "%s: stk600_xprog_paged_write(): cannot handle page size > 512\n", + progname); + return -1; + } + + /* + * Fancy offsets everywhere. + * This is probably what AVR079 means when writing about the + * "TIF address space". + */ + if (strcmp(mem->desc, "flash") == 0) { + memtype = XPRG_MEM_TYPE_APPL; + addr = 0x00800000; + } else if (strcmp(mem->desc, "boot") == 0) { + memtype = XPRG_MEM_TYPE_BOOT; + addr = 0x00800000; + } else if (strcmp(mem->desc, "eeprom") == 0) { + memtype = XPRG_MEM_TYPE_EEPROM; + addr = 0x008c0000; + } else { + fprintf(stderr, + "%s: stk600_xprog_paged_write(): unknown paged memory \"%s\"\n", + progname, mem->desc); + return -1; + } + + if ((b = malloc(page_size + 9)) == NULL) { + fprintf(stderr, + "%s: stk600_xprog_paged_write(): out of memory\n", + progname); + return -1; + } + + offset = 0; + while (n_bytes != 0) { + report_progress(offset, n_bytes_orig, NULL); + + if (page_size > 256) { + /* + * AVR079 is not quite clear. While it suggests that + * downloading up to 512 bytes (256 words) were OK, it + * obviously isn't -- 512-byte pages on the ATxmega128A1 + * are getting corrupted when written as a single piece. + * It writes random junk somewhere beyond byte 256. + * Splitting it into 256 byte chunks, and only setting the + * erase page / write page bits in the final chunk helps. + */ + if (page_size % 256 != 0) { + fprintf(stderr, + "%s: stk600_xprog_paged_write(): page size not multiple of 256\n", + progname); + return -1; + } + unsigned int chunk; + for (chunk = 0; chunk < page_size; chunk += 256) { + memset(b + 9, 0xff, 256); + b[0] = XPRG_CMD_WRITE_MEM; + b[1] = memtype; + if (chunk + 256 == page_size) { + b[2] = 3; /* last chunk: erase page | write page */ + } else { + b[2] = 0; /* initial/intermediate chunk: just download */ + } + b[3] = addr >> 24; + b[4] = addr >> 16; + b[5] = addr >> 8; + b[6] = addr; + b[7] = 1; + b[8] = 0; + memcpy(b + 9, mem->buf + offset, n_bytes); + if (stk600_xprog_command(pgm, b, 256 + 9, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_paged_write(): XPRG_CMD_WRITE_MEM failed\n", + progname); + return -1; + } + if (n_bytes < 256) + n_bytes = 256; + + offset += 256; + addr += 256; + n_bytes -= 256; + } + } else { + if (n_bytes < page_size) + /* + * This can easily happen if the input file was not a + * multiple of the page size. + */ + memset(b + 9 + n_bytes, 0xff, page_size - n_bytes); + b[0] = XPRG_CMD_WRITE_MEM; + b[1] = memtype; + b[2] = 3; /* erase page | write page */ + b[3] = addr >> 24; + b[4] = addr >> 16; + b[5] = addr >> 8; + b[6] = addr; + b[7] = page_size >> 8; + b[8] = page_size; + memcpy(b + 9, mem->buf + offset, n_bytes); + if (stk600_xprog_command(pgm, b, page_size + 9, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_paged_write(): XPRG_CMD_WRITE_MEM failed\n", + progname); + return -1; + } + if (n_bytes < page_size) + n_bytes = page_size; + + offset += page_size; + addr += page_size; + n_bytes -= page_size; + } + } + free(b); + + return n_bytes_orig; +} + +static int stk600_xprog_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char b[6]; + + b[0] = XPRG_CMD_ERASE; + b[1] = XPRG_ERASE_CHIP; + b[2] = b[3] = b[4] = b[5] = 0; + if (stk600_xprog_command(pgm, b, 6, 2) < 0) { + fprintf(stderr, + "%s: stk600_xprog_chip_erase(): XPRG_CMD_ERASE(XPRG_ERASE_CHIP) failed\n", + progname); + return -1; + } + return 0; +} + +/* + * Modify pgm's methods for XPROG operation. + */ +static void stk600_setup_xprog(PROGRAMMER * pgm) +{ + pgm->program_enable = stk600_xprog_program_enable; + pgm->disable = stk600_xprog_disable; + pgm->read_byte = stk600_xprog_read_byte; + pgm->write_byte = stk600_xprog_write_byte; + pgm->paged_load = stk600_xprog_paged_load; + pgm->paged_write = stk600_xprog_paged_write; + pgm->chip_erase = stk600_xprog_chip_erase; +} + void stk500v2_initpgm(PROGRAMMER * pgm) {