From 8bdbf1774ef397adc5ab75ab351a8665a3bb636f Mon Sep 17 00:00:00 2001 From: Joerg Wunsch Date: Sun, 16 Jul 2006 21:30:14 +0000 Subject: [PATCH] First stab at an implementation of the STK500 parallel programming feature (v2 firmware only), named "stk500pp". Still not yet complete: EEPROM writes not working, documentation missing, only ATmega16 parameters available in avrdude.conf.in, some parameters not yet implemented. * avrdude.conf.in: Add sample parameters for PP mode to ATmega16. * avrpart.h: Add the parallel programming control parameters. * avrpart.c: (Ditto.) * config_gram.y: Add stk500pp configuration grammar. * lexer.l: Add stk500pp token recognition. * stk500v2.h: Add declaration for stk500pp_initpgm(). * stk500v2.c: Add stk500pp implementation. git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@587 81a1dc3b-b13d-400b-aceb-764788c761c2 --- ChangeLog | 15 ++ avrdude.conf.in | 23 +- avrpart.c | 2 + avrpart.h | 21 ++ config_gram.y | 117 ++++++++++ lexer.l | 13 +- stk500v2.c | 577 +++++++++++++++++++++++++++++++++++++++++++++++- stk500v2.h | 2 + 8 files changed, 767 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 86f75f78..8a4c5391 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2006-07-16 Joerg Wunsch + + First stab at an implementation of the STK500 parallel programming + feature (v2 firmware only), named "stk500pp". Still not yet + complete: EEPROM writes not working, documentation missing, only + ATmega16 parameters available in avrdude.conf.in, some parameters + not yet implemented. + * avrdude.conf.in: Add sample parameters for PP mode to ATmega16. + * avrpart.h: Add the parallel programming control parameters. + * avrpart.c: (Ditto.) + * config_gram.y: Add stk500pp configuration grammar. + * lexer.l: Add stk500pp token recognition. + * stk500v2.h: Add declaration for stk500pp_initpgm(). + * stk500v2.c: Add stk500pp implementation. + 2006-07-11 Joerg Wunsch * avrdude.conf.in: Fix the signatures for the diff --git a/avrdude.conf.in b/avrdude.conf.in index e76944e9..c4542d31 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -15,7 +15,7 @@ # programmer # id = [, [, ] ...] ; # are quoted strings # desc = ; # quoted string -# type = par | stk500 | stk500v2 | avr910 | jtagmki | jtagmkii; # programmer type +# type = par | stk500 | stk500v2 | stk500pp | avr910 | jtagmki | jtagmkii; # programmer type # baudrate = ; # baudrate for avr910-programmer # vcc = [, ... ] ; # pin number(s) # reset = ; # pin number @@ -258,6 +258,12 @@ programmer type = stk500v2; ; +programmer + id = "stk500pp"; + desc = "Atmel STK500 V2 in parallel programming mode"; + type = stk500pp; +; + programmer id = "avr910"; desc = "Atmel Low Cost Serial Programmer"; @@ -2363,6 +2369,21 @@ part postdelay = 1; pollmethod = 0; + pp_controlstack = + 0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F, + 0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F, + 0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B, + 0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00; + ppenterstabdelay = 100; + progmodedelay = 100; + latchcycles = 6; + togglevtg = 0; + poweroffdelay = 0; + resetdelayms = 0; + resetdelayus = 0; + ppleavestabdelay = 15; + resetdelay = 15; + idr = 0x31; spmcr = 0x57; allowfullpagebitstream = yes; diff --git a/avrpart.c b/avrpart.c index cabea278..6de2d667 100644 --- a/avrpart.c +++ b/avrpart.c @@ -2,6 +2,7 @@ /* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000-2004 Brian S. Dean + * Copyright (C) 2006 Joerg Wunsch * * 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 @@ -371,6 +372,7 @@ AVRPART * avr_new_part(void) p->config_file[0] = 0; p->lineno = 0; memset(p->signature, 0xFF, 3); + p->ctl_stack_type = CTL_STACK_NONE; p->mem = lcreat(NULL, 0); diff --git a/avrpart.h b/avrpart.h index 84d01622..ab09e4d4 100644 --- a/avrpart.h +++ b/avrpart.h @@ -1,6 +1,7 @@ /* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2003-2004 Brian S. Dean + * Copyright (C) 2006 Joerg Wunsch * * 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 @@ -62,6 +63,12 @@ enum { /* these are assigned to reset_disposition of AVRPART */ RESET_IO /* reset pin might be configured as an I/O pin */ }; +enum ctl_stack_t { + CTL_STACK_NONE, /* no control stack defined */ + CTL_STACK_PP, /* parallel programming control stack */ + CTL_STACK_HVSP /* high voltage serial programming control stack */ +}; + /* * serial programming instruction bit specifications */ @@ -85,6 +92,7 @@ typedef struct opcode { #define AVR_DESCLEN 64 #define AVR_IDLEN 32 +#define CTL_STACK_SIZE 32 typedef struct avrpart { char desc[AVR_DESCLEN]; /* long part name */ char id[AVR_IDLEN]; /* short part name */ @@ -110,6 +118,19 @@ typedef struct avrpart { int postdelay; /* stk500 v2 xml file parameter */ int pollmethod; /* stk500 v2 xml file parameter */ + enum ctl_stack_t ctl_stack_type; /* what to use the ctl stack for */ + unsigned char controlstack[CTL_STACK_SIZE]; /* stk500v2 PP/HVSP ctl stack */ + + int ppenterstabdelay; /* stk500 v2 pp mode parameter */ + int progmodedelay; /* stk500 v2 pp mode parameter */ + int latchcycles; /* stk500 v2 pp mode parameter */ + int togglevtg; /* stk500 v2 pp mode parameter */ + int poweroffdelay; /* stk500 v2 pp mode parameter */ + int resetdelayms; /* stk500 v2 pp mode parameter */ + int resetdelayus; /* stk500 v2 pp mode parameter */ + int ppleavestabdelay; /* stk500 v2 pp mode parameter */ + int resetdelay; /* stk500 v2 pp mode parameter */ + unsigned char idr; /* JTAG ICE mkII XML file parameter */ unsigned char rampz; /* JTAG ICE mkII XML file parameter */ unsigned char spmcr; /* JTAG ICE mkII XML file parameter */ diff --git a/config_gram.y b/config_gram.y index 08896cf9..2464dc8d 100644 --- a/config_gram.y +++ b/config_gram.y @@ -1,6 +1,7 @@ /* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000-2004 Brian S. Dean + * Copyright (C) 2006 Joerg Wunsch * * 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 @@ -120,6 +121,7 @@ static int parse_cmdbits(OPCODE * op); %token K_SIGNATURE %token K_SIZE %token K_STK500 +%token K_STK500PP %token K_STK500V2 %token K_AVR910 %token K_BUTTERFLY @@ -132,6 +134,7 @@ static int parse_cmdbits(OPCODE * op); %token K_YES /* stk500 v2 xml file parameters */ +/* ISP */ %token K_TIMEOUT %token K_STABDELAY %token K_CMDEXEDELAY @@ -146,6 +149,18 @@ static int parse_cmdbits(OPCODE * op); %token K_DELAY %token K_BLOCKSIZE %token K_READSIZE +/* PP mode */ +%token K_PPENTERSTABDELAY +%token K_PROGMODEDELAY +%token K_LATCHCYCLES +%token K_TOGGLEVTG +%token K_POWEROFFDELAY +%token K_RESETDELAYMS +%token K_RESETDELAYUS +%token K_PPLEAVESTABDELAY +%token K_RESETDELAY + +%token K_PP_CONTROLSTACK /* JTAG ICE mkII specific parameters */ %token K_ALLOWFULLPAGEBITSTREAM /* @@ -350,6 +365,12 @@ prog_parm : } } | + K_TYPE TKN_EQUAL K_STK500PP { + { + stk500pp_initpgm(current_prog); + } + } | + K_TYPE TKN_EQUAL K_AVR910 { { avr910_initpgm(current_prog); @@ -544,6 +565,48 @@ part_parm : } } | + K_PP_CONTROLSTACK TKN_EQUAL num_list { + { + TOKEN * t; + unsigned nbytes; + int ok; + + if (current_part->ctl_stack_type != CTL_STACK_NONE) + { + fprintf(stderr, + "%s: error at line %d of %s: " + "control stack already defined\n", + progname, lineno, infile); + exit(1); + } + + current_part->ctl_stack_type = CTL_STACK_PP; + nbytes = 0; + ok = 1; + + while (lsize(number_list)) { + t = lrmv_n(number_list, 1); + if (nbytes < CTL_STACK_SIZE) + { + current_part->controlstack[nbytes] = t->value.number; + nbytes++; + } + else + { + ok = 0; + } + free_token(t); + } + if (!ok) + { + fprintf(stderr, + "%s: Warning: line %d of %s: " + "too many bytes in control stack\n", + progname, lineno, infile); + } + } + } | + K_CHIP_ERASE_DELAY TKN_EQUAL TKN_NUMBER { current_part->chip_erase_delay = $3->value.number; @@ -632,6 +695,60 @@ part_parm : free_token($3); } | + K_PPENTERSTABDELAY TKN_EQUAL TKN_NUMBER + { + current_part->ppenterstabdelay = $3->value.number; + free_token($3); + } | + + K_PROGMODEDELAY TKN_EQUAL TKN_NUMBER + { + current_part->progmodedelay = $3->value.number; + free_token($3); + } | + + K_LATCHCYCLES TKN_EQUAL TKN_NUMBER + { + current_part->latchcycles = $3->value.number; + free_token($3); + } | + + K_TOGGLEVTG TKN_EQUAL TKN_NUMBER + { + current_part->togglevtg = $3->value.number; + free_token($3); + } | + + K_POWEROFFDELAY TKN_EQUAL TKN_NUMBER + { + current_part->poweroffdelay = $3->value.number; + free_token($3); + } | + + K_RESETDELAYMS TKN_EQUAL TKN_NUMBER + { + current_part->resetdelayms = $3->value.number; + free_token($3); + } | + + K_RESETDELAYUS TKN_EQUAL TKN_NUMBER + { + current_part->resetdelayus = $3->value.number; + free_token($3); + } | + + K_PPLEAVESTABDELAY TKN_EQUAL TKN_NUMBER + { + current_part->ppleavestabdelay = $3->value.number; + free_token($3); + } | + + K_RESETDELAY TKN_EQUAL TKN_NUMBER + { + current_part->resetdelay = $3->value.number; + free_token($3); + } | + K_HAS_JTAG TKN_EQUAL yesno { if ($3->primary == K_YES) diff --git a/lexer.l b/lexer.l index 5d48144b..5bba2c5f 100644 --- a/lexer.l +++ b/lexer.l @@ -1,6 +1,7 @@ /* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000-2004 Brian S. Dean + * Copyright (C) 2006 Joerg Wunsch * * 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 @@ -170,6 +171,7 @@ signature { yylval=NULL; return K_SIGNATURE; } size { yylval=NULL; return K_SIZE; } spmcr { yylval=NULL; return K_SPMCR; } stk500 { yylval=NULL; return K_STK500; } +stk500pp { yylval=NULL; return K_STK500PP; } stk500v2 { yylval=NULL; return K_STK500V2; } stk500_devcode { yylval=NULL; return K_STK500_DEVCODE; } type { yylval=NULL; return K_TYPE; } @@ -190,7 +192,16 @@ mode { yylval=NULL; return K_MODE; } delay { yylval=NULL; return K_DELAY; } blocksize { yylval=NULL; return K_BLOCKSIZE; } readsize { yylval=NULL; return K_READSIZE; } - +pp_controlstack { yylval=NULL; return K_PP_CONTROLSTACK; } +ppenterstabdelay { yylval=NULL; return K_PPENTERSTABDELAY; } +progmodedelay { yylval=NULL; return K_PROGMODEDELAY; } +latchcycles { yylval=NULL; return K_LATCHCYCLES; } +togglevtg { yylval=NULL; return K_TOGGLEVTG; } +poweroffdelay { yylval=NULL; return K_POWEROFFDELAY; } +resetdelayms { yylval=NULL; return K_RESETDELAYMS; } +resetdelayus { yylval=NULL; return K_RESETDELAYUS; } +ppleavestabdelay { yylval=NULL; return K_PPLEAVESTABDELAY; } +resetdelay { yylval=NULL; return K_RESETDELAY; } dedicated { yylval=new_token(K_DEDICATED); return K_DEDICATED; } diff --git a/stk500v2.c b/stk500v2.c index a7ed9268..b4d489d8 100644 --- a/stk500v2.c +++ b/stk500v2.c @@ -3,6 +3,7 @@ * Copyright (C) 2005 Erik Walthinsen * Copyright (C) 2002-2004 Brian S. Dean * Copyright (C) 2006 David Moore + * Copyright (C) 2006 Joerg Wunsch * * 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 @@ -72,6 +73,21 @@ extern int verbose; extern char * progname; extern int do_cycles; +/* + * See stk500pp_read_byte() for an explanation of the flash and + * EEPROM page caches. + */ +static unsigned char *flash_pagecache; +static unsigned long flash_pageaddr; +static unsigned int flash_pagesize; + +static unsigned char *eeprom_pagecache; +static unsigned long eeprom_pageaddr; +static unsigned int eeprom_pagesize; + +/* page buffer for single-byte IO */ +static unsigned char *pagebuf; + static unsigned char command_sequence = 1; static int is_mk2; /* Is the device an AVRISP mkII? */ @@ -309,7 +325,8 @@ retry: return 0; } -static int stk500v2_command(PROGRAMMER * pgm, unsigned char * buf, size_t len, size_t maxlen) { +static int stk500v2_command +(PROGRAMMER * pgm, unsigned char * buf, size_t len, size_t maxlen) { int i; int tries = 0; int status; @@ -381,6 +398,16 @@ static int stk500v2_cmd(PROGRAMMER * pgm, unsigned char cmd[4], } +static int stk500pp_cmd(PROGRAMMER * pgm, unsigned char cmd[4], + unsigned char res[4]) +{ + + fprintf(stderr, "%s: stk500pp_command(): no direct SPI supported for PP mode\n", + progname); + return -1; +} + + /* * issue the 'chip erase' command to the AVR device */ @@ -410,6 +437,28 @@ static int stk500v2_chip_erase(PROGRAMMER * pgm, AVRPART * p) return result; } +/* + * issue the 'chip erase' command to the AVR device, parallel mode + */ +static int stk500pp_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + int result; + unsigned char buf[3]; + + pgm->pgm_led(pgm, ON); + + buf[0] = CMD_CHIP_ERASE_PP; + buf[1] = 0; /* pulseWidth */ + buf[2] = 5; /* pollTimeout */ + result = stk500v2_command(pgm, buf, 3, sizeof(buf)); + usleep(p->chip_erase_delay); + pgm->initialize(pgm, p); + + pgm->pgm_led(pgm, OFF); + + return result; +} + /* * issue the 'program enable' command to the AVR device */ @@ -436,6 +485,25 @@ static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p) return stk500v2_command(pgm, buf, 12, sizeof(buf)); } +/* + * issue the 'program enable' command to the AVR device, parallel mode + */ +static int stk500pp_program_enable(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char buf[16]; + + buf[0] = CMD_ENTER_PROGMODE_PP; + buf[1] = p->ppenterstabdelay; + buf[2] = p->progmodedelay; + buf[3] = p->latchcycles; + buf[4] = p->togglevtg; + buf[5] = p->poweroffdelay; + buf[6] = p->resetdelayms; + buf[7] = p->resetdelayus; + + return stk500v2_command(pgm, buf, 8, sizeof(buf)); +} + /* @@ -447,6 +515,79 @@ static int stk500v2_initialize(PROGRAMMER * pgm, AVRPART * p) } +/* + * initialize the AVR device and prepare it to accept commands, parallel mode + */ +static int stk500pp_initialize(PROGRAMMER * pgm, AVRPART * p) +{ + unsigned char buf[CTL_STACK_SIZE + 1]; + int result; + LNODEID ln; + AVRMEM * m; + + if (p->ctl_stack_type != CTL_STACK_PP) { + fprintf(stderr, + "%s: stk500pp_initialize(): " + "parallel programming control stack not defined for part \"%s\"\n", + progname, p->desc); + return -1; + } + + buf[0] = CMD_SET_CONTROL_STACK; + memcpy(buf + 1, p->controlstack, CTL_STACK_SIZE); + + result = stk500v2_command(pgm, buf, CTL_STACK_SIZE + 1, sizeof(buf)); + + if (result < 0 || buf[1] != STATUS_CMD_OK) { + fprintf(stderr, + "%s: stk500pp_initalize(): " + "failed to set control stack, got 0x%02x\n", + progname, buf[1]); + return -1; + } + + /* + * Examine the avrpart's memory definitions, and initialize the page + * caches. For devices/memory that are not page oriented, treat + * them as page size 1 for EEPROM, and 2 for flash. + */ + flash_pagesize = 2; + eeprom_pagesize = 1; + for (ln = lfirst(p->mem); ln; ln = lnext(ln)) { + m = ldata(ln); + if (strcmp(m->desc, "flash") == 0) { + flash_pagesize = m->page_size; + } else if (strcmp(m->desc, "eeprom") == 0) { + eeprom_pagesize = m->page_size; + } + } + free(flash_pagecache); + free(eeprom_pagecache); + free(pagebuf); + if ((pagebuf = malloc(flash_pagesize + 5)) == NULL) { + fprintf(stderr, "%s: stk500pp_initialize(): Out of memory\n", + progname); + return -1; + } + if ((flash_pagecache = malloc(flash_pagesize)) == NULL) { + fprintf(stderr, "%s: stk500pp_initialize(): Out of memory\n", + progname); + free(pagebuf); + return -1; + } + if ((eeprom_pagecache = malloc(eeprom_pagesize)) == NULL) { + fprintf(stderr, "%s: stk500pp_initialize(): Out of memory\n", + progname); + free(flash_pagecache); + free(pagebuf); + return -1; + } + flash_pageaddr = eeprom_pageaddr = (unsigned long)-1L; + + return pgm->program_enable(pgm, p); +} + + static void stk500v2_disable(PROGRAMMER * pgm) { unsigned char buf[16]; @@ -467,6 +608,35 @@ static void stk500v2_disable(PROGRAMMER * pgm) return; } +static void stk500pp_disable(PROGRAMMER * pgm) +{ + unsigned char buf[16]; + int result; + + free(pagebuf); + pagebuf = NULL; + free(flash_pagecache); + flash_pagecache = NULL; + free(eeprom_pagecache); + eeprom_pagecache = NULL; + + buf[0] = CMD_LEAVE_PROGMODE_PP; + buf[1] = 15; // p->ppleavestabdelay; + buf[2] = 15; // p->resetdelay; + + result = stk500v2_command(pgm, buf, 3, sizeof(buf)); + + if (result < 0 || buf[1] != STATUS_CMD_OK) { + fprintf(stderr, + "%s: stk500pp_disable(): " + "failed to leave programming mode, got 0x%02x\n", + progname,buf[1]); + exit(1); + } + + return; +} + static void stk500v2_enable(PROGRAMMER * pgm) { return; @@ -552,6 +722,229 @@ static int stk500v2_loadaddr(PROGRAMMER * pgm, unsigned int addr) } +static int stk500pp_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char * value) +{ + int result, cmdlen = 2; + unsigned long paddr = 0UL, *paddr_ptr = NULL; + unsigned int pagesize = 0, use_ext_addr = 0, addrshift = 0; + unsigned char *cache_ptr = NULL; + + if (verbose >= 2) + fprintf(stderr, "%s: stk500pp_read_byte(.., %s, 0x%lx, ...)\n", + progname, mem->desc, addr); + + if (stk500pp_program_enable(pgm, p) < 0) + return -1; + + if (strcmp(mem->desc, "flash") == 0) { + pagebuf[0] = CMD_READ_FLASH_PP; + cmdlen = 3; + pagesize = mem->page_size; + if (pagesize == 0) + pagesize = 2; + paddr = addr & ~(pagesize - 1); + paddr_ptr = &flash_pageaddr; + cache_ptr = flash_pagecache; + addrshift = 1; + /* + * If bit 31 is set, this indicates that the following read/write + * operation will be performed on a memory that is larger than + * 64KBytes. This is an indication to STK500 that a load extended + * address must be executed. + */ + if (mem->op[AVR_OP_LOAD_EXT_ADDR] != NULL) { + use_ext_addr = (1U << 31); + } + } else if (strcmp(mem->desc, "eeprom") == 0) { + pagebuf[0] = CMD_READ_EEPROM_PP; + cmdlen = 3; + pagesize = mem->page_size; + if (pagesize == 0) + pagesize = 1; + paddr = addr & ~(pagesize - 1); + paddr_ptr = &eeprom_pageaddr; + cache_ptr = eeprom_pagecache; + } else if (strcmp(mem->desc, "lfuse") == 0) { + pagebuf[0] = CMD_READ_FUSE_PP; + addr = 0; + } else if (strcmp(mem->desc, "hfuse") == 0) { + pagebuf[0] = CMD_READ_FUSE_PP; + addr = 1; + } else if (strcmp(mem->desc, "efuse") == 0) { + pagebuf[0] = CMD_READ_FUSE_PP; + addr = 2; + } else if (strcmp(mem->desc, "lock") == 0) { + pagebuf[0] = CMD_READ_LOCK_PP; + } else if (strcmp(mem->desc, "calibration") == 0) { + pagebuf[0] = CMD_READ_OSCCAL_PP; + } else if (strcmp(mem->desc, "signature") == 0) { + pagebuf[0] = CMD_READ_SIGNATURE_PP; + } + + /* + * In parallel mode, we have to use paged reads for flash and + * EEPROM, and cache the results in a page cache. + * + * Page cache validation is based on "{flash,eeprom}_pageaddr" + * (holding the base address of the most recent cache fill + * operation). This variable is set to (unsigned long)-1L when the + * cache needs to be invalidated. + */ + if (pagesize && paddr == *paddr_ptr) { + *value = cache_ptr[addr & (pagesize - 1)]; + return 0; + } + + if (cmdlen == 3) { + /* long command, fill in # of bytes */ + pagebuf[1] = (pagesize >> 8) & 0xff; + pagebuf[2] = pagesize & 0xff; + + /* flash and EEPROM reads require the load address command */ + stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)); + } else { + pagebuf[1] = addr; + } + + if (verbose >= 2) + fprintf(stderr, "%s: stk500pp_read_byte(): Sending read memory command: ", + progname); + + result = stk500v2_command(pgm, pagebuf, cmdlen, pagesize == 0? 3: pagesize + 3); + + if (result < 0 || pagebuf[1] != STATUS_CMD_OK) { + fprintf(stderr, + "%s: stk500pp_read_byte(): " + "timeout/error communicating with programmer (status %d)\n", + progname, result); + return -1; + } + + if (pagesize) { + *paddr_ptr = paddr; + memcpy(cache_ptr, pagebuf + 2, pagesize); + *value = cache_ptr[addr & (pagesize - 1)]; + } else { + *value = pagebuf[2]; + } + + return 0; +} + +static int stk500pp_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, + unsigned long addr, unsigned char data) +{ + int result, cmdlen; + unsigned long paddr = 0UL, *paddr_ptr = NULL; + unsigned int pagesize = 0, use_ext_addr = 0, addrshift = 0; + unsigned char *cache_ptr = NULL; + + if (verbose >= 2) + fprintf(stderr, "%s: stk500pp_write_byte(.., %s, 0x%lx, ...)\n", + progname, mem->desc, addr); + + if (stk500pp_program_enable(pgm, p) < 0) + return -1; + + if (strcmp(mem->desc, "flash") == 0) { + pagebuf[0] = CMD_PROGRAM_FLASH_PP; + pagesize = mem->page_size; + if (pagesize == 0) + pagesize = 2; + paddr = addr & ~(pagesize - 1); + paddr_ptr = &flash_pageaddr; + cache_ptr = flash_pagecache; + addrshift = 1; + /* + * If bit 31 is set, this indicates that the following read/write + * operation will be performed on a memory that is larger than + * 64KBytes. This is an indication to STK500 that a load extended + * address must be executed. + */ + if (mem->op[AVR_OP_LOAD_EXT_ADDR] != NULL) { + use_ext_addr = (1U << 31); + } + } else if (strcmp(mem->desc, "eeprom") == 0) { + pagebuf[0] = CMD_PROGRAM_EEPROM_PP; + pagesize = mem->page_size; + if (pagesize == 0) + pagesize = 1; + paddr = addr & ~(pagesize - 1); + paddr_ptr = &eeprom_pageaddr; + cache_ptr = eeprom_pagecache; + } else if (strcmp(mem->desc, "lfuse") == 0) { + pagebuf[0] = CMD_PROGRAM_FUSE_PP; + addr = 0; + } else if (strcmp(mem->desc, "hfuse") == 0) { + pagebuf[0] = CMD_PROGRAM_FUSE_PP; + addr = 1; + } else if (strcmp(mem->desc, "efuse") == 0) { + pagebuf[0] = CMD_PROGRAM_FUSE_PP; + addr = 2; + } else if (strcmp(mem->desc, "lock") == 0) { + pagebuf[0] = CMD_PROGRAM_LOCK_PP; + } else { + fprintf(stderr, + "%s: stk500pp_write_byte(): " + "unsupported memory type: %s\n", + progname, mem->desc); + return -1; + } + + cmdlen = 5 + pagesize; + + /* + * In parallel mode, we have to use paged writes for flash and + * EEPROM. As both, flash and EEPROM cells can only be programmed + * from `1' to `0' bits (even EEPROM does not support auto-erase in + * parallel mode), we just pre-fill the page cache with 0xff, so all + * those cells that are outside our current address will remain + * unaffected. + */ + if (pagesize) { + memset(cache_ptr, 0xff, pagesize); + cache_ptr[addr & (pagesize - 1)] = data; + + /* long command, fill in # of bytes */ + pagebuf[1] = (pagesize >> 8) & 0xff; + pagebuf[2] = pagesize & 0xff; + + pagebuf[3] = mem->mode | 0x80; + pagebuf[4] = mem->delay; + memcpy(pagebuf + 5, cache_ptr, pagesize); + + /* flash and EEPROM reads require the load address command */ + stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)); + } else { + pagebuf[1] = addr; + pagebuf[2] = data; + pagebuf[3] = 0; /* pulseWidth */ + pagebuf[4] = 5; /* pollTimeout */ + } + + if (verbose >= 2) + fprintf(stderr, "%s: stk500pp_write_byte(): Sending write memory command: ", + progname); + + result = stk500v2_command(pgm, pagebuf, cmdlen, cmdlen); + + if (result < 0 || pagebuf[1] != STATUS_CMD_OK) { + fprintf(stderr, + "%s: stk500pp_write_byte(): " + "timeout/error communicating with programmer (status %d)\n", + progname, result); + return -1; + } + + if (pagesize) { + /* Invalidate the page cache. */ + *paddr_ptr = (unsigned long)-1L; + } + + return 0; +} + static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, int page_size, int n_bytes) { @@ -684,6 +1077,82 @@ static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, return n_bytes; } +static int stk500pp_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + int page_size, int n_bytes) +{ + unsigned int addr, block_size, last_addr, hiaddr, addrshift, use_ext_addr; + unsigned char commandbuf[5]; + int result; + + DEBUG("STK500V2: stk500pp_paged_write(..,%s,%d,%d)\n",m->desc,page_size,n_bytes); + + if (page_size == 0) page_size = 256; + hiaddr = UINT_MAX; + addrshift = 0; + use_ext_addr = 0; + + // determine which command is to be used + if (strcmp(m->desc, "flash") == 0) { + addrshift = 1; + flash_pageaddr = (unsigned long)-1L; + commandbuf[0] = CMD_PROGRAM_FLASH_PP; + /* + * If bit 31 is set, this indicates that the following read/write + * operation will be performed on a memory that is larger than + * 64KBytes. This is an indication to STK500 that a load extended + * address must be executed. + */ + if (m->op[AVR_OP_LOAD_EXT_ADDR] != NULL) { + use_ext_addr = (1U << 31); + } + } else if (strcmp(m->desc, "eeprom") == 0) { + eeprom_pageaddr = (unsigned long)-1L; + commandbuf[0] = CMD_PROGRAM_EEPROM_PP; + } + commandbuf[3] = m->mode | 0x80; // yes, write the page to flash + commandbuf[4] = m->delay; + + last_addr = UINT_MAX; /* impossible address */ + + for (addr=0; addr < n_bytes; addr += page_size) { + report_progress(addr,n_bytes,NULL); + + if ((n_bytes-addr) < page_size) + block_size = n_bytes - addr; + else + block_size = page_size; + + DEBUG("block_size at addr %d is %d\n",addr,block_size); + + if (commandbuf[0] == CMD_PROGRAM_FLASH_PP){ + if (stk500v2_is_page_empty(addr, block_size, m->buf)) { + continue; + } + } + + memcpy(pagebuf, commandbuf, sizeof(commandbuf)); + + pagebuf[1] = block_size >> 8; + pagebuf[2] = block_size & 0xff; + + if ((last_addr == UINT_MAX) || (last_addr + block_size != addr)) { + stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)); + } + last_addr=addr; + + memcpy(pagebuf + 5, m->buf + addr, block_size); + + result = stk500v2_command(pgm, pagebuf, block_size + 5, page_size + 5); + if (pagebuf[1] != STATUS_CMD_OK) { + fprintf(stderr, "%s: stk500pp_paged_write: write command failed with %d\n", + progname, pagebuf[1]); + return -1; + } + } + + return n_bytes; +} + static int stk500v2_is_page_empty(unsigned int address, int page_size, const unsigned char *buf) { @@ -788,6 +1257,80 @@ static int stk500v2_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, } +static int stk500pp_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + int page_size, int n_bytes) +{ + unsigned int addr, block_size, hiaddr, addrshift, use_ext_addr; + unsigned char commandbuf[3]; + int result; + + DEBUG("STK500V2: stk500pp_paged_load(..,%s,%d,%d)\n",m->desc,page_size,n_bytes); + + page_size = m->readsize; + + hiaddr = UINT_MAX; + addrshift = 0; + use_ext_addr = 0; + + // determine which command is to be used + if (strcmp(m->desc, "flash") == 0) { + commandbuf[0] = CMD_READ_FLASH_PP; + addrshift = 1; + /* + * If bit 31 is set, this indicates that the following read/write + * operation will be performed on a memory that is larger than + * 64KBytes. This is an indication to STK500 that a load extended + * address must be executed. + */ + if (m->op[AVR_OP_LOAD_EXT_ADDR] != NULL) { + use_ext_addr = (1U << 31); + } + } + else if (strcmp(m->desc, "eeprom") == 0) { + commandbuf[0] = CMD_READ_EEPROM_PP; + } + + for (addr = 0; addr < n_bytes; addr += page_size) { + report_progress(addr, n_bytes, NULL); + + if ((n_bytes-addr) < page_size) + block_size = n_bytes - addr; + else + block_size = page_size; + DEBUG("block_size at addr %d is %d\n",addr,block_size); + + memcpy(pagebuf, commandbuf, sizeof(commandbuf)); + + pagebuf[1] = block_size >> 8; + pagebuf[2] = block_size & 0xff; + + // Ensure a new "load extended address" will be issued + // when crossing a 64 KB boundary in flash. + if (hiaddr != (addr & ~0xFFFF)) { + hiaddr = addr & ~0xFFFF; + stk500v2_loadaddr(pgm, use_ext_addr | (addr >> addrshift)); + } + + result = stk500v2_command(pgm, pagebuf, 3, block_size + 3); + if (pagebuf[1] != STATUS_CMD_OK) { + fprintf(stderr, "%s: stk500pp_paged_load: read command failed with %d\n", + progname, pagebuf[1]); + return -1; + } +#if 0 + for (i = 0; i < page_size; i++) { + fprintf(stderr, "%02X", buf[2 + i]); + if (i % 16 == 15) fprintf(stderr, "\n"); + } +#endif + + memcpy(&m->buf[addr], &pagebuf[2], block_size); + } + + return n_bytes; +} + + static int stk500v2_set_vtarget(PROGRAMMER * pgm, double v) { unsigned char uaref, utarg; @@ -1128,3 +1671,35 @@ void stk500v2_initpgm(PROGRAMMER * pgm) pgm->set_sck_period = stk500v2_set_sck_period; pgm->page_size = 256; } + +void stk500pp_initpgm(PROGRAMMER * pgm) +{ + strcpy(pgm->type, "STK500PP"); + + /* + * mandatory functions + */ + pgm->initialize = stk500pp_initialize; + pgm->display = stk500v2_display; + pgm->enable = stk500v2_enable; + pgm->disable = stk500pp_disable; + pgm->program_enable = stk500pp_program_enable; + pgm->chip_erase = stk500pp_chip_erase; + pgm->cmd = stk500pp_cmd; + pgm->open = stk500v2_open; + pgm->close = stk500v2_close; + + /* + * optional functions + */ + pgm->read_byte = stk500pp_read_byte; + pgm->write_byte = stk500pp_write_byte; + pgm->paged_write = stk500pp_paged_write; + pgm->paged_load = stk500pp_paged_load; + pgm->print_parms = stk500v2_print_parms; + pgm->set_vtarget = stk500v2_set_vtarget; + pgm->set_varef = stk500v2_set_varef; + pgm->set_fosc = stk500v2_set_fosc; + pgm->set_sck_period = stk500v2_set_sck_period; + pgm->page_size = 256; +} diff --git a/stk500v2.h b/stk500v2.h index 715136a0..4fc96286 100644 --- a/stk500v2.h +++ b/stk500v2.h @@ -1,6 +1,7 @@ /* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2002-2005 Brian S. Dean + * Copyright (C) 2006 Joerg Wunsch * * 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 @@ -23,6 +24,7 @@ #define stk500v2_h__ void stk500v2_initpgm (PROGRAMMER * pgm); +void stk500pp_initpgm (PROGRAMMER * pgm); #endif