From 703ecd2ea13defd35793df26ad254440f347d91e Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Thu, 20 Oct 2022 23:54:24 +0100 Subject: [PATCH 1/8] Support optiboot, optiboot_dx and optiboot_x bootloaders for -c arduino --- src/stk500.c | 102 +++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/src/stk500.c b/src/stk500.c index fecd85255..eec4bc935 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -666,27 +666,44 @@ static void stk500_close(PROGRAMMER * pgm) } -static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, const unsigned int addr) { +// Address is byte address; a_div == 2: send word address; a_div == 1: send byte address +static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned int addr, int a_div) { unsigned char buf[16]; int tries; unsigned char ext_byte; - OPCODE * lext; + + addr /= a_div; tries = 0; retry: tries++; - /* To support flash > 64K words the correct Extended Address Byte is needed */ - lext = mem->op[AVR_OP_LOAD_EXT_ADDR]; - if (lext != NULL) { - ext_byte = (addr >> 16) & 0xff; - if (ext_byte != PDATA(pgm)->ext_addr_byte) { - /* Either this is the first addr load, or a different 64K word section */ - memset(buf, 0, 4); - avr_set_bits(lext, buf); - avr_set_addr(lext, buf, addr); - stk500_cmd(pgm, buf, buf); - PDATA(pgm)->ext_addr_byte = ext_byte; + // Support large flash by sending the correct extended address byte when needed + + if(pgm->prog_modes & PM_SPM) { // Bootloaders, eg, optiboot, optiboot_dx, optiboot_x + if(mem->size/a_div > 64*1024) { // Extended addressing needed + ext_byte = (addr >> 16) & 0xff; + if(ext_byte != PDATA(pgm)->ext_addr_byte) { // First addr load or a different 64k section + buf[0] = 0x4d; // Protocol bytes that bootloaders expect + buf[1] = 0x00; + buf[2] = ext_byte; + buf[3] = 0x00; + if(stk500_cmd(pgm, buf, buf) == 0) + PDATA(pgm)->ext_addr_byte = ext_byte; + } + } + } else { // Programmer *not* for bootloaders? Original stk500v1 protocol! + OPCODE *lext = mem->op[AVR_OP_LOAD_EXT_ADDR]; + + if(lext) { + ext_byte = (addr >> 16) & 0xff; + if(ext_byte != PDATA(pgm)->ext_addr_byte) { // First addr load or a different 64k section + memset(buf, 0, 4); // Part's load_ext_addr command is typically 4d 00 ext_addr 00 + avr_set_bits(lext, buf); + avr_set_addr(lext, buf, addr); + if(stk500_cmd(pgm, buf, buf) == 0) + PDATA(pgm)->ext_addr_byte = ext_byte; + } } } @@ -724,6 +741,29 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, const unsig } +static int set_memtype_a_div(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m, int *memtypep, int *a_divp) { + if(avr_mem_is_flash_type(m)) { + *memtypep = 'F'; + if(!(pgm->prog_modes & PM_SPM)) // Programmer *not* for bootloaders: original stk500v1 protocol + *a_divp = m->op[AVR_OP_LOADPAGE_LO] || m->op[AVR_OP_READ_LO]? 2: 1; + else if(p->prog_modes & (PM_UPDI | PM_PDI)) + *a_divp = 1; // For bootloaders whereas part is Xmega or "new" families (optiboot_x, optiboot_dx) + else + *a_divp = 2; // For bootloaders whereas part is a "classic" part (eg, optiboot) + return 0; + } + + if(avr_mem_is_eeprom_type(m)) { + *memtypep = 'E'; + // Word addr for bootloaders where part is a "classic" part (eg, optiboot, arduinoisp, ...), byte addr otherwise + *a_divp = (pgm->prog_modes & PM_SPM) && !(p->prog_modes & (PM_UPDI | PM_PDI))? 2: 1; + return 0; + } + + return -1; +} + + static int stk500_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m, unsigned int page_size, unsigned int addr, unsigned int n_bytes) @@ -736,25 +776,12 @@ static int stk500_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVR unsigned int n; unsigned int i; - if (strcmp(m->desc, "flash") == 0) { - memtype = 'F'; - a_div = 2; - } else if (strcmp(m->desc, "eeprom") == 0) { - memtype = 'E'; - /* - * The STK original 500 v1 protocol actually expects a_div = 1, but the - * v1.x FW of the STK500 kit has been superseded by v2 FW in the mid - * 2000s. Since optiboot, arduino as ISP and others assume a_div = 2, - * better use that. See https://github.com/avrdudes/avrdude/issues/967 - */ - a_div = 2; - } else { + if(set_memtype_a_div(pgm, p, m, &memtype, &a_div) < 0) return -2; - } n = addr + n_bytes; #if 0 - msg_info( + msg_debug( "n_bytes = %d\n" "n = %u\n" "a_div = %d\n" @@ -775,7 +802,7 @@ static int stk500_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVR tries = 0; retry: tries++; - stk500_loadaddr(pgm, m, addr/a_div); + stk500_loadaddr(pgm, m, addr, a_div); /* build command block and avoid multiple send commands as it leads to a crash of the silabs usb serial driver on mac os x */ @@ -830,21 +857,8 @@ static int stk500_paged_load(const PROGRAMMER *pgm, const AVRPART *p, const AVRM unsigned int n; int block_size; - if (strcmp(m->desc, "flash") == 0) { - memtype = 'F'; - a_div = 2; - } else if (strcmp(m->desc, "eeprom") == 0) { - memtype = 'E'; - /* - * The STK original 500 v1 protocol actually expects a_div = 1, but the - * v1.x FW of the STK500 kit has been superseded by v2 FW in the mid - * 2000s. Since optiboot, arduino as ISP and others assume a_div = 2, - * better use that. See https://github.com/avrdudes/avrdude/issues/967 - */ - a_div = 2; - } else { + if(set_memtype_a_div(pgm, p, m, &memtype, &a_div) < 0) return -2; - } n = addr + n_bytes; for (; addr < n; addr += block_size) { @@ -861,7 +875,7 @@ static int stk500_paged_load(const PROGRAMMER *pgm, const AVRPART *p, const AVRM tries = 0; retry: tries++; - stk500_loadaddr(pgm, m, addr/a_div); + stk500_loadaddr(pgm, m, addr, a_div); buf[0] = Cmnd_STK_READ_PAGE; buf[1] = (block_size >> 8) & 0xff; buf[2] = block_size & 0xff; From 95cbfc175332085563ee3b5cbfbbfb2410a11534 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Fri, 21 Oct 2022 11:51:41 +0100 Subject: [PATCH 2/8] Load ext addr for stk500v1 bootloaders after grazing 64k boundaries --- src/stk500.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/stk500.c b/src/stk500.c index eec4bc935..f03ffd26d 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -691,6 +691,17 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned in if(stk500_cmd(pgm, buf, buf) == 0) PDATA(pgm)->ext_addr_byte = ext_byte; } + /* + * Paper over a bug in some bootloaders: ensure next paged r/w will load ext addr again if + * page sits just below 64k a boundary. Some bootloaders mistakenly increment their copy of + * ext_addr_byte in that situation, eg, when they use elpm rx,Z+ to read a byte from flash + * and they keep ext_addr_byte in Z. So, if an upload with automated verify finishes just + * below 64k, AVRDUDE still holds ext_addr_byte at the current 64k segment whilst it's copy + * in the bootloader has been auto-incremented. Verifying the code from start exposes the + * discrepancy. Below intervention forces the next r/w to send the correct ext_addr_byte. + */ + if(addr & 0xffff0000 != (addr+mem->page_size/a_div) & 0xffff0000) + PDATA(pgm)->ext_addr_byte = 0xff; } } else { // Programmer *not* for bootloaders? Original stk500v1 protocol! OPCODE *lext = mem->op[AVR_OP_LOAD_EXT_ADDR]; From 2d6ec9e5b1e7584914f2558eb5237d7f0fcadde1 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Fri, 21 Oct 2022 12:10:41 +0100 Subject: [PATCH 3/8] Fix typo in stk500.c comment --- src/stk500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stk500.c b/src/stk500.c index f03ffd26d..90c3ca043 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -695,7 +695,7 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned in * Paper over a bug in some bootloaders: ensure next paged r/w will load ext addr again if * page sits just below 64k a boundary. Some bootloaders mistakenly increment their copy of * ext_addr_byte in that situation, eg, when they use elpm rx,Z+ to read a byte from flash - * and they keep ext_addr_byte in Z. So, if an upload with automated verify finishes just + * and they keep ext_addr_byte in RAMPZ. So, if an upload with automated verify finishes just * below 64k, AVRDUDE still holds ext_addr_byte at the current 64k segment whilst it's copy * in the bootloader has been auto-incremented. Verifying the code from start exposes the * discrepancy. Below intervention forces the next r/w to send the correct ext_addr_byte. From ff7628c16e4cc32b8d746db7e2010523367f76c3 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Fri, 21 Oct 2022 12:22:25 +0100 Subject: [PATCH 4/8] Change wording of justification for sending ext byte at 64k boundary --- src/stk500.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/stk500.c b/src/stk500.c index 90c3ca043..cd11adb05 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -694,10 +694,11 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned in /* * Paper over a bug in some bootloaders: ensure next paged r/w will load ext addr again if * page sits just below 64k a boundary. Some bootloaders mistakenly increment their copy of - * ext_addr_byte in that situation, eg, when they use elpm rx,Z+ to read a byte from flash - * and they keep ext_addr_byte in RAMPZ. So, if an upload with automated verify finishes just - * below 64k, AVRDUDE still holds ext_addr_byte at the current 64k segment whilst it's copy - * in the bootloader has been auto-incremented. Verifying the code from start exposes the + * ext_addr_byte in that situation, eg, when they use elpm rx,Z+ to read a byte from flash or + * `spm Z+` to write to flash whilst they keep ext_addr_byte in RAMPZ that gets incremented + * by `Z+` at 64k page boundaries. So, if an upload with automated verify finishes just below + * 64k, AVRDUDE still holds ext_addr_byte at the current 64k segment whilst it's copy in the + * bootloader has been auto-incremented. Verifying the code from start exposes the * discrepancy. Below intervention forces the next r/w to send the correct ext_addr_byte. */ if(addr & 0xffff0000 != (addr+mem->page_size/a_div) & 0xffff0000) From ec5a33f6413a49f4519c1f5e05e8b7bcf1fbc525 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Fri, 21 Oct 2022 12:25:08 +0100 Subject: [PATCH 5/8] Rectify precedence in condition in stk500.c --- src/stk500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stk500.c b/src/stk500.c index cd11adb05..2200adcfa 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -701,7 +701,7 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned in * bootloader has been auto-incremented. Verifying the code from start exposes the * discrepancy. Below intervention forces the next r/w to send the correct ext_addr_byte. */ - if(addr & 0xffff0000 != (addr+mem->page_size/a_div) & 0xffff0000) + if((addr & 0xffff0000) != (addr+mem->page_size/a_div) & 0xffff0000) PDATA(pgm)->ext_addr_byte = 0xff; } } else { // Programmer *not* for bootloaders? Original stk500v1 protocol! From 41a612a3de99c4eea55d7faab64eb2717e0d76f4 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Fri, 21 Oct 2022 12:27:44 +0100 Subject: [PATCH 6/8] Really fix precedence in condition in stk500.c --- src/stk500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stk500.c b/src/stk500.c index 2200adcfa..d30f5dbf4 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -701,7 +701,7 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned in * bootloader has been auto-incremented. Verifying the code from start exposes the * discrepancy. Below intervention forces the next r/w to send the correct ext_addr_byte. */ - if((addr & 0xffff0000) != (addr+mem->page_size/a_div) & 0xffff0000) + if((addr & 0xffff0000) != ((addr+mem->page_size/a_div) & 0xffff0000)) PDATA(pgm)->ext_addr_byte = 0xff; } } else { // Programmer *not* for bootloaders? Original stk500v1 protocol! From 89c44d0d39433a97109b929f2d38444619d45c05 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Fri, 21 Oct 2022 18:23:33 +0100 Subject: [PATCH 7/8] Reformulate justification for sending ext byte at 64k boundary --- src/stk500.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/stk500.c b/src/stk500.c index d30f5dbf4..cf99cf76d 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -692,14 +692,14 @@ static int stk500_loadaddr(const PROGRAMMER *pgm, const AVRMEM *mem, unsigned in PDATA(pgm)->ext_addr_byte = ext_byte; } /* - * Paper over a bug in some bootloaders: ensure next paged r/w will load ext addr again if - * page sits just below 64k a boundary. Some bootloaders mistakenly increment their copy of - * ext_addr_byte in that situation, eg, when they use elpm rx,Z+ to read a byte from flash or - * `spm Z+` to write to flash whilst they keep ext_addr_byte in RAMPZ that gets incremented - * by `Z+` at 64k page boundaries. So, if an upload with automated verify finishes just below - * 64k, AVRDUDE still holds ext_addr_byte at the current 64k segment whilst it's copy in the - * bootloader has been auto-incremented. Verifying the code from start exposes the - * discrepancy. Below intervention forces the next r/w to send the correct ext_addr_byte. + * Ensure next paged r/w will load ext addr again if page sits just below a 64k boundary + * + * Some bootloaders increment their copy of ext_addr_byte in that situation, eg, when they + * use elpm rx,Z+ to read a byte from flash or spm Z+ to write to flash whilst they keep + * ext_addr_byte in RAMPZ, which in turn gets incremented by Z+ at 64k page boundaries. So, + * if an upload with automated verify finishes just below 64k, AVRDUDE still holds + * ext_addr_byte at the current 64k segment whilst its copy in the bootloader has been + * auto-incremented. Verifying the code from start exposes the discrepancy. */ if((addr & 0xffff0000) != ((addr+mem->page_size/a_div) & 0xffff0000)) PDATA(pgm)->ext_addr_byte = 0xff; From 0b8a12a79fd3e0c081a412caecc0ec1a27c7cd57 Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Sat, 22 Oct 2022 23:55:59 +0100 Subject: [PATCH 8/8] Fix bootloader stk500v1 EEPROM r/w for classic parts with page size 1 --- src/stk500.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/stk500.c b/src/stk500.c index cf99cf76d..e0a659686 100644 --- a/src/stk500.c +++ b/src/stk500.c @@ -625,6 +625,12 @@ static void stk500_disable(const PROGRAMMER *pgm) { } static void stk500_enable(PROGRAMMER *pgm, const AVRPART *p) { + AVRMEM *mem; + if(pgm->prog_modes & PM_SPM) // For bootloaders (eg, arduino) + if(!(p->prog_modes & (PM_UPDI | PM_PDI | PM_aWire))) // Classic parts, eg, optiboot with word addresses + if((mem = avr_locate_mem(p, "eeprom"))) + if(mem->page_size == 1) // Increase pagesize if it is 1 + mem->page_size = 16; return; } @@ -758,10 +764,10 @@ static int set_memtype_a_div(const PROGRAMMER *pgm, const AVRPART *p, const AVRM *memtypep = 'F'; if(!(pgm->prog_modes & PM_SPM)) // Programmer *not* for bootloaders: original stk500v1 protocol *a_divp = m->op[AVR_OP_LOADPAGE_LO] || m->op[AVR_OP_READ_LO]? 2: 1; - else if(p->prog_modes & (PM_UPDI | PM_PDI)) - *a_divp = 1; // For bootloaders whereas part is Xmega or "new" families (optiboot_x, optiboot_dx) + else if(!(p->prog_modes & (PM_UPDI | PM_PDI | PM_aWire))) + *a_divp = 2; // Bootloader where part is a "classic" part (eg, optiboot) else - *a_divp = 2; // For bootloaders whereas part is a "classic" part (eg, optiboot) + *a_divp = 1; // Bootloader where part is Xmega or "new" families (optiboot_x, optiboot_dx) return 0; }