avrdude/src/avrcache.c

660 lines
23 KiB
C
Raw Normal View History

Provide cached byte-wise read/write API (#1106) * Provide cached byte-wise read/write API int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, unsigned long addr, unsigned char *value); int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, unsigned long addr, unsigned char data); int avr_flush_cache(const PROGRAMMER *pgm, const AVRPART *p); int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p); int avr_reset_cache(const PROGRAMMER *pgm, const AVRPART *p); avr_read_byte_cached() and avr_write_byte_cached() use a cache if paged routines are available and if the device memory is EEPROM or flash, otherwise they fall back to pgm->read_byte() and pgm->write_byte(), respectively. Byte-wise cached read always gets its data from the cache, possibly after reading a page from the device memory. Byte-wise cached write with an address in memory range only ever modifies the cache. Any modifications are written to the device after calling avr_flush_cache() or when attempting to read or write from a location outside the address range of the device memory. avr_flush_cache() synchronises pending writes to EEPROM and flash with the device. With some programmer and part combinations, flash (and sometimes EEPROM, too) looks like a NOR memory, ie, one can only write 0 bits, not 1 bits. When this is detected, either page erase is deployed (eg, with parts that have PDI/UPDI interfaces), or if that is not available, both EEPROM and flash caches are fully read in, a pgm->chip_erase() command is issued and both EEPROM and flash are written back to the device. Hence, it can take minutes to ensure that a single previously cleared bit is set and, therefore, this routine should be called sparingly. avr_chip_erase_cached() erases the chip and discards pending writes() to flash or EEPROM. It presets the flash cache to all 0xff alleviating the need to read from the device flash. However, if the programmer serves bootloaders (pgm->prog_modes & PM_SPM) then the flash cache is reset instead, necessitating flash memory be fetched from the device on first read; the reason for this is that bootloaders emulate chip erase and they won't overwrite themselves (some bootloaders, eg, optiboot ignore chip erase commands altogether) making it truly unknowable what the flash contents on device is after a chip erase. For EEPROM avr_chip_erase_cached() concludes that it has been deleted if a previously cached EEPROM page that contained cleared bits now no longer has these clear bits on the device. Only with this evidence is the EEPROM cache preset to all 0xff otherwise the cache discards all pending writes to EEPROM and is left unchanged otherwise. Finally, avr_reset_cache() resets the cache without synchronising pending writes() to the device.
2022-10-05 21:16:15 +00:00
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022 Stefan Rueger <stefan.rueger@urclocks.c>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
/* $Id$ */
#include "ac_cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "avrdude.h"
#include "libavrdude.h"
#include "avrintel.h"
/*
* Provides an API for cached byte-wise access
*
* int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const
* AVRMEM *mem, unsigned long addr, unsigned char *value);
*
* int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const
* AVRMEM *mem, unsigned long addr, unsigned char data);
*
* int avr_flush_cache(const PROGRAMMER *pgm, const AVRPART *p);
*
* int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p);
*
* int avr_reset_cache(const PROGRAMMER *pgm, const AVRPART *p);
*
* avr_read_byte_cached() and avr_write_byte_cached() use a cache if paged
* routines are available and if the device memory is EEPROM or flash,
* otherwise they fall back to pgm->read_byte() and pgm->write_byte(),
* respectively. Byte-wise cached read always gets its data from the cache,
* possibly after reading a page from the device memory. Byte-wise cached
* write with an address in memory range only ever modifies the cache. Any
* modifications are written to the device after calling avr_flush_cache() or
* when attempting to read or write from a location outside the address range
* of the device memory.
*
* avr_flush_cache() synchronises pending writes to EEPROM and flash with the
* device. With some programmer and part combinations, flash (and sometimes
* EEPROM, too) looks like a NOR memory, ie, one can only write 0 bits, not 1
* bits. When this is detected, either page erase is deployed (eg, with parts
* that have PDI/UPDI interfaces), or if that is not available, both EEPROM
* and flash caches are fully read in, a pgm->chip_erase() command is issued
* and both EEPROM and flash are written back to the device. Hence, it can
* take minutes to ensure that a single previously cleared bit is set and,
* therefore, this routine should be called sparingly.
*
* avr_chip_erase_cached() erases the chip and discards pending writes() to
* flash or EEPROM. It presets the flash cache to all 0xff alleviating the
* need to read from the device flash. However, if the programmer serves
* bootloaders (pgm->prog_modes & PM_SPM) then the flash cache is reset
* instead, necessitating flash memory be fetched from the device on first
* read; the reason for this is that bootloaders emulate chip erase and they
* won't overwrite themselves (some bootloaders, eg, optiboot ignore chip
* erase commands) making it truly unknowable what the flash contents on
* device is after a chip erase.
*
* For EEPROM avr_chip_erase_cached() concludes that it has been deleted if a
* previously cached EEPROM page that contained cleared bits now no longer
* has these clear bits on the device. Only with this evidence is the EEPROM
* cache preset to all 0xff otherwise the cache discards all pending writes
* to EEPROM and is left unchanged otherwise.
*
* Finally, avr_reset_cache() resets the cache without synchronising pending
* writes() to the device.
*
* This file also holds the following utility functions
*
* // Does the programmer/memory combo have paged memory access?
* int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem);
*
* // Read the page containing addr from the device into buf
* int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf);
*
* // Write the data page to the device into the page containing addr
* int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *data);
*
* // Could memory region s1 be the result of a NOR-memory copy of s3 onto s2?
* int avr_is_and(const unsigned char *s1, const unsigned char *s2, const unsigned char *s3, size_t n);
*
*/
/*
* Paged access?
* - Programmer must have paged routines
* - Memory has positive page size, which is a power of two
* - Memory has positive size, which is a multiple of the page size
* - Memory is flash type or eeprom type
*/
int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem) {
return pgm->paged_load && pgm->paged_write &&
mem->page_size > 0 && (mem->page_size & (mem->page_size-1)) == 0 &&
mem->size > 0 && mem->size % mem->page_size == 0 &&
(avr_mem_is_flash_type(mem) || avr_mem_is_eeprom_type(mem));
}
/*
* Read the page containing addr from the device into buf
* - Caller to ensure buf has mem->page_size bytes
* - Part memory buffer mem is unaffected by this (though temporarily changed)
*/
int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf) {
if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
return LIBAVRDUDE_GENERAL_FAILURE;
int rc, pgsize = mem->page_size, off = addr & ~(pgsize-1);
unsigned char *pagecopy = cfg_malloc("avr_read_page_default()", pgsize);
memcpy(pagecopy, mem->buf + off, pgsize);
if((rc = pgm->paged_load(pgm, p, mem, pgsize, off, pgsize)) >= 0)
memcpy(buf, mem->buf + off, pgsize);
memcpy(mem->buf + off, pagecopy, pgsize);
free(pagecopy);
return rc;
}
/*
* Write the data page to the device into the page containing addr
* - Caller to provide all mem->page_size bytes incl padding if any
* - Part memory buffer mem is unaffected by this (though temporarily changed)
*/
int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *data) {
if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
return LIBAVRDUDE_GENERAL_FAILURE;
int rc, pgsize = mem->page_size, off = addr & ~(pgsize-1);
unsigned char *pagecopy = cfg_malloc("avr_write_page_default()", pgsize);
memcpy(pagecopy, mem->buf + off, pgsize);
memcpy(mem->buf + off, data, pgsize);
rc = pgm->paged_write(pgm, p, mem, pgsize, off, pgsize);
memcpy(mem->buf + off, pagecopy, pgsize);
free(pagecopy);
return rc;
}
// Could memory region s1 be the result of a NOR-memory copy of s3 onto s2?
int avr_is_and(const unsigned char *s1, const unsigned char *s2, const unsigned char *s3, size_t n) {
while(n--)
if(*s1++ != (*s2++ & *s3++))
return 0;
return 1;
}
static int initCache(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p) {
AVRMEM *basemem = avr_locate_mem(p, cp == pgm->cp_flash? "flash": "eeprom");
if(!basemem || !avr_has_paged_access(pgm, basemem))
return LIBAVRDUDE_GENERAL_FAILURE;
cp->size = basemem->size;
cp->page_size = basemem->page_size;
cp->offset = basemem->offset;
cp->cont = cfg_malloc("initCache()", cp->size);
cp->copy = cfg_malloc("initCache()", cp->size);
cp->iscached = cfg_malloc("initCache()", cp->size/cp->page_size);
return LIBAVRDUDE_SUCCESS;
}
static int cacheAddress(int addr, const AVR_Cache *cp, const AVRMEM *mem, int level) {
int cacheaddr = addr + (int) (mem->offset - cp->offset);
if(cacheaddr < 0 || cacheaddr >= cp->size) { // Should never happen (unless offsets wrong in avrdude.conf)
if(level != MSG_INFO)
avrdude_message(level, "%s: ", progname);
avrdude_message(level, "cacheAddress() %s cache address 0x%04x out of range [0, 0x%04x]\n", mem->desc, cacheaddr, cp->size);
return LIBAVRDUDE_GENERAL_FAILURE;
}
if(mem->page_size != cp->page_size) { // Should never happen (unless incompatible page sizes in avrdude.conf)
if(level != MSG_INFO)
avrdude_message(level, "%s: ", progname);
avrdude_message(level, "cacheAddress() %s page size %d incompatible with cache page size %d\n",
mem->desc, mem->page_size, cp->page_size);
return LIBAVRDUDE_GENERAL_FAILURE;
}
return cacheaddr;
}
static int loadCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, int cacheaddr, int level) {
int pgno = cacheaddr/cp->page_size;
if(!cp->iscached[pgno]) {
// Read cached section from device
int cachebase = cacheaddr & ~(cp->page_size-1);
if(avr_read_page_default(pgm, p, mem, addr & ~(cp->page_size-1), cp->cont + cachebase) < 0) {
report_progress(1, -1, NULL);
if(level != MSG_INFO || !quell_progress)
avrdude_message(level, "%s: ", progname);
avrdude_message(level, "loadCachePage() %s read failed at addr 0x%04x\n", mem->desc, addr);
return LIBAVRDUDE_GENERAL_FAILURE;
}
// Copy last read device page, so we can later check for changes
memcpy(cp->copy + cachebase, cp->cont + cachebase, cp->page_size);
cp->iscached[pgno] = 1;
}
return LIBAVRDUDE_SUCCESS;
}
static int writeCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int base, int level) {
// Write modified page cont to device
if(avr_write_page_default(pgm, p, mem, base, cp->cont + base) < 0) {
report_progress(1, -1, NULL);
if(level != MSG_INFO || !quell_progress)
avrdude_message(level, "%s: ", progname);
avrdude_message(level, "writeCachePage() %s write error at addr 0x%04x\n", mem->desc, base);
return LIBAVRDUDE_GENERAL_FAILURE;
}
// Read page back from device and update copy to what is on device
if(avr_read_page_default(pgm, p, mem, base, cp->copy + base) < 0) {
report_progress(1, -1, NULL);
if(level != MSG_INFO || !quell_progress)
avrdude_message(level, "%s: ", progname);
avrdude_message(level, "writeCachePage() %s read error at addr 0x%04x\n", mem->desc, base);
return LIBAVRDUDE_GENERAL_FAILURE;
}
return LIBAVRDUDE_SUCCESS;
}
// Does the memory region only haxe 0xff?
static int _is_all_0xff(const void *p, size_t n) {
const unsigned char *q = (const unsigned char *) p;
return n <= 0 || (*q == 0xff && memcmp(q, q+1, n-1) == 0);
}
// A coarse guess where any bootloader might start (prob underestimates the start)
static int guessBootStart(const PROGRAMMER *pgm, const AVRPART *p) {
int bootstart = 0;
const AVR_Cache *cp = pgm->cp_flash;
for(size_t i = 0; i < sizeof uP_table/sizeof*uP_table; i++)
if(p->mcuid == uP_table[i].mcuid) {
if(uP_table[i].nboots > 0 && uP_table[i].bootsize > 0 && uP_table[i].flashsize == cp->size)
bootstart = cp->size - uP_table[i].nboots * uP_table[i].bootsize;
break;
}
if(bootstart <= cp->size/2 || bootstart >= cp->size)
bootstart = cp->size > 32768? cp->size - 16384: cp->size*3/4;
return bootstart & ~(cp->page_size-1);
}
typedef struct {
AVRMEM *mem;
AVR_Cache *cp;
int isflash, zopaddr, pgerase;
} CacheDesc_t;
// Write both EEPROM and flash caches to device and free them
int avr_flush_cache(const PROGRAMMER *pgm, const AVRPART *p) {
CacheDesc_t mems[2] = {
{ avr_locate_mem(p, "flash"), pgm->cp_flash, 1, -1, 0 },
{ avr_locate_mem(p, "eeprom"), pgm->cp_eeprom, 0, -1, 0 },
};
int chpages = 0;
bool chiperase = 0;
// Count page changes and find a page that needs a clear bit set
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem || !cp->cont)
continue;
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
if(cp->iscached[pgno])
if(memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
chpages++;
if(mems[i].zopaddr == -1 && !avr_is_and(cp->cont + n, cp->copy + n, cp->cont + n, cp->page_size))
mems[i].zopaddr = n;
}
}
}
if(!chpages)
return LIBAVRDUDE_SUCCESS;
avrdude_message(MSG_INFO, "%s: synching cache to device... ", progname);
fflush(stderr);
// Check whether page erase needed and working and whether chip erase needed
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!cp->cont) // Ensure cache is initialised from now on
if(initCache(cp, pgm, p) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
if(chiperase || !mem || mems[i].zopaddr < 0)
continue;
int n=mems[i].zopaddr;
if(writeCachePage(cp, pgm, p, mem, n, MSG_INFO) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
// Same? OK, can set cleared bit to one, "normal" memory
if(!memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
chpages--;
continue;
}
// Probably NOR memory, check out page erase
if(pgm->page_erase && pgm->page_erase(pgm, p, mem, n) >= 0) {
if(writeCachePage(cp, pgm, p, mem, n, MSG_INFO) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
// Worked OK? Can use page erase on this memory
if(!memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
mems[i].pgerase = 1;
chpages--;
continue;
}
}
chiperase = 1;
}
if(!chpages)
return LIBAVRDUDE_SUCCESS;
if(chiperase) {
if(quell_progress) {
avrdude_message(MSG_INFO, "reading/chip erase/writing cycle needed ... ");
fflush(stderr);
}
int nrd = 0;
// Count read operations needed
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem)
continue;
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size)
if(!cp->iscached[pgno])
nrd++;
}
if(nrd) {
report_progress(0, 1, "Reading");
// Read full flash and EEPROM
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem)
continue;
for(int ird = 0, pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
if(!cp->iscached[pgno]) {
report_progress(ird++, nrd, NULL);
if(loadCachePage(cp, pgm, p, mem, n, n, MSG_INFO) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
}
}
}
report_progress(1, 0, NULL);
}
report_progress(0, 1, "Erasing");
if(avr_chip_erase(pgm, p) < 0) {
report_progress(1, -1, NULL);
if(!quell_progress)
avrdude_message(MSG_INFO, "%s: ", progname);
avrdude_message(MSG_INFO, "avr_flush_cache() chip erase failed\n");
return LIBAVRDUDE_GENERAL_FAILURE;
}
// Update cache copies after chip erase so that writing back is efficient
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem)
continue;
if(mems[i].isflash) { // flash
memset(cp->copy, 0xff, cp->size); // record device memory as erased
if(pgm->prog_modes & PM_SPM) { // Bootloaders will not overwrite themselves
// Read back generously estimated bootloader section to avoid verification errors
int bootstart = guessBootStart(pgm, p);
int nbo = (cp->size - bootstart)/cp->page_size;
for(int ibo = 0, n = bootstart; n < cp->size; n += cp->page_size) {
report_progress(1+ibo++, nbo+2, NULL);
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0) {
report_progress(1, -1, NULL);
if(!quell_progress)
avrdude_message(MSG_INFO, "%s: ", progname);
avrdude_message(MSG_INFO, "flash read failed at addr 0x%04x\n", n);
return LIBAVRDUDE_GENERAL_FAILURE;
}
}
}
} else { // EEPROM
// Don't know whether chip erase has zapped EEPROM
for(int n = 0; n < cp->size; n += cp->page_size) {
if(!_is_all_0xff(cp->copy + n, cp->page_size)) { // First page that had EEPROM data
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0) {
report_progress(1, -1, NULL);
if(!quell_progress)
avrdude_message(MSG_INFO, "%s: ", progname);
avrdude_message(MSG_INFO, "EEPROM read failed at addr 0x%04x\n", n);
return LIBAVRDUDE_GENERAL_FAILURE;
}
// EEPROM zapped by chip erase? Set all copy to 0xff
if(_is_all_0xff(cp->copy + n, cp->page_size))
memset(cp->copy, 0xff, cp->size);
break;
}
}
}
}
report_progress(1, 0, NULL);
}
int nwr = 0;
// Count number of writes
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem)
continue;
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size)
if(cp->iscached[pgno] && memcmp(cp->copy + n, cp->cont + n, cp->page_size))
nwr++;
}
if(nwr) {
report_progress(0, 1, "Writing");
// Write all modified pages to the device
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem || !cp->cont)
continue;
for(int iwr = 0, pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
if(cp->iscached[pgno] && memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
if(!chiperase && mems[i].pgerase)
pgm->page_erase(pgm, p, mem, n);
if(writeCachePage(cp, pgm, p, mem, n, MSG_INFO) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
if(memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
report_progress(1, -1, NULL);
if(!quell_progress)
avrdude_message(MSG_INFO, "%s: ", progname);
avrdude_message(MSG_INFO, "%s verification error at addr 0x%04x\n", mem->desc, n);
return LIBAVRDUDE_GENERAL_FAILURE;
}
report_progress(iwr++, nwr, NULL);
}
}
}
report_progress(1, 0, NULL);
}
avrdude_message(MSG_INFO, quell_progress? "done\n": "\n");
return LIBAVRDUDE_SUCCESS;
}
/*
* Read byte via a read/write cache
* - Used if paged routines available and if memory is EEPROM or flash
* - Otherwise fall back to pgm->read_byte()
* - Out of memory addr: synchronise cache and, if successful, pretend reading a zero
* - Cache is automagically created and initialised if needed
*/
int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char *value) {
// Use pgm->read_byte() if not EEPROM/flash or no paged access
if(!avr_has_paged_access(pgm, mem))
return pgm->read_byte(pgm, p, mem, addr, value);
// If address is out of range synchronise cache and, if successful, pretend reading a zero
if(addr >= (unsigned long) mem->size) {
if(avr_flush_cache(pgm, p) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
*value = 0;
return LIBAVRDUDE_SUCCESS;
}
AVR_Cache *cp = avr_mem_is_eeprom_type(mem)? pgm->cp_eeprom: pgm->cp_flash;
if(!cp->cont) // Init cache if needed
if(initCache(cp, pgm, p) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
int cacheaddr = cacheAddress((int) addr, cp, mem, MSG_NOTICE);
if(cacheaddr < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
// Ensure cache page is there
if(loadCachePage(cp, pgm, p, mem, addr, cacheaddr, MSG_NOTICE) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
*value = cp->cont[cacheaddr];
return LIBAVRDUDE_SUCCESS;
}
/*
* Write byte via a read/write cache
* - Used if paged routines available and if memory is EEPROM or flash
* - Otherwise fall back to pgm->write_byte()
* - Out of memory addr: synchronise cache with device and return whether successful
* - Cache is automagically created and initialised if needed
*/
int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char data) {
// Use pgm->read_byte() if not EEPROM/flash or no paged access
if(!avr_has_paged_access(pgm, mem))
return pgm->write_byte(pgm, p, mem, addr, data);
// If address is out of range synchronise caches with device and return whether successful
if(addr >= (unsigned long) mem->size)
return avr_flush_cache(pgm, p);
AVR_Cache *cp = avr_mem_is_eeprom_type(mem)? pgm->cp_eeprom: pgm->cp_flash;
if(!cp->cont) // Init cache if needed
if(initCache(cp, pgm, p) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
int cacheaddr = cacheAddress((int) addr, cp, mem, MSG_NOTICE);
if(cacheaddr < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
// Ensure cache page is there
if(loadCachePage(cp, pgm, p, mem, addr, cacheaddr, MSG_NOTICE) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
cp->cont[cacheaddr] = data;
return LIBAVRDUDE_SUCCESS;
}
// Erase the chip and set the cache accordingly
int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p) {
CacheDesc_t mems[2] = {
{ avr_locate_mem(p, "flash"), pgm->cp_flash, 1 },
{ avr_locate_mem(p, "eeprom"), pgm->cp_eeprom, 0 },
};
if(pgm->chip_erase(pgm, p) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem;
AVR_Cache *cp = mems[i].cp;
if(!mem || !avr_has_paged_access(pgm, mem))
continue;
if(!cp->cont) // Init cache if needed
if(initCache(cp, pgm, p) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
if(mems[i].isflash) { // flash
if(pgm->prog_modes & PM_SPM) { // reset cache to unknown
memset(cp->iscached, 0, cp->size/cp->page_size);
} else { // preset all pages as erased
memset(cp->copy, 0xff, cp->size);
memset(cp->cont, 0xff, cp->size);
memset(cp->iscached, 1, cp->size/cp->page_size);
}
} else { // EEPROM: test whether cached pages were zapped
bool erasedee = 0;
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
if(cp->iscached[pgno]) {
if(!_is_all_0xff(cp->copy + n, cp->page_size)) { // Page has EEPROM data?
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
erasedee = _is_all_0xff(cp->copy + n, cp->page_size);
break;
}
}
}
if(erasedee) { // EEPROM was erased, set cache correspondingly
memset(cp->copy, 0xff, cp->size);
memset(cp->cont, 0xff, cp->size);
memset(cp->iscached, 1, cp->size/cp->page_size);
} else { // discard previous writes, but leave cache
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size)
if(cp->iscached[pgno])
memcpy(cp->cont + n, cp->copy + n, cp->page_size);
}
}
}
return LIBAVRDUDE_SUCCESS;
}
// Free cache(s) discarding any pending writes
int avr_reset_cache(const PROGRAMMER *pgm, const AVRPART *p) {
AVR_Cache *mems[2] = { pgm->cp_flash, pgm->cp_eeprom, };
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVR_Cache *cp = mems[i];
if(cp->cont)
free(cp->cont);
if(cp->copy)
free(cp->copy);
if(cp->iscached)
free(cp->iscached);
memset(cp, 0, sizeof*cp);
}
return LIBAVRDUDE_SUCCESS;
}