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.
This commit is contained in:
Stefan Rueger 2022-10-05 22:16:15 +01:00 committed by GitHub
parent c4cb242823
commit d74b17b9b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1056 additions and 194 deletions

View File

@ -139,6 +139,7 @@ add_library(libavrdude
avr.c avr.c
avr910.c avr910.c
avr910.h avr910.h
avrcache.c
avrdude.h avrdude.h
avrftdi.c avrftdi.c
avrftdi.h avrftdi.h

View File

@ -92,6 +92,7 @@ libavrdude_a_SOURCES = \
avr.c \ avr.c \
avr910.c \ avr910.c \
avr910.h \ avr910.h \
avrcache.c \
avrdude.h \ avrdude.h \
avrftdi.c \ avrftdi.c \
avrftdi.h \ avrftdi.h \

168
src/avr.c
View File

@ -312,30 +312,39 @@ int avr_mem_hiaddr(const AVRMEM * mem)
/* /*
* Read the entirety of the specified memory type into the * Read the entirety of the specified memory type into the corresponding
* corresponding buffer of the avrpart pointed to by 'p'. * buffer of the avrpart pointed to by p. If v is non-NULL, verify against
* If v is non-NULL, verify against v's memory area, only * v's memory area, only those cells that are tagged TAG_ALLOCATED are
* those cells that are tagged TAG_ALLOCATED are verified. * verified.
* *
* Return the number of bytes read, or < 0 if an error occurs. * Return the number of bytes read, or < 0 if an error occurs.
*/ */
int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, const AVRPART *v) {
AVRPART * v) AVRMEM *mem = avr_locate_mem(p, memtype);
{
unsigned long i, lastaddr;
unsigned char cmd[4];
AVRMEM * mem, * vmem = NULL;
int rc;
mem = avr_locate_mem(p, memtype);
if (v != NULL)
vmem = avr_locate_mem(v, memtype);
if (mem == NULL) { if (mem == NULL) {
avrdude_message(MSG_INFO, "No \"%s\" memory for part %s\n", avrdude_message(MSG_INFO, "No %s memory for part %s\n", memtype, p->desc);
memtype, p->desc); return LIBAVRDUDE_GENERAL_FAILURE;
return -1;
} }
return avr_read_mem(pgm, p, mem, v);
}
/*
* Read the entirety of the specified memory into the corresponding buffer of
* the avrpart pointed to by p. If v is non-NULL, verify against v's memory
* area, only those cells that are tagged TAG_ALLOCATED are verified.
*
* Return the number of bytes read, or < 0 if an error occurs.
*/
int avr_read_mem(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, const AVRPART *v) {
unsigned long i, lastaddr;
unsigned char cmd[4];
AVRMEM *vmem = NULL;
int rc;
if (v != NULL)
vmem = avr_locate_mem(v, mem->desc);
/* /*
* start with all 0xff * start with all 0xff
*/ */
@ -364,7 +373,7 @@ int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
rc = pgm->cmd_tpi(pgm, cmd, 1, mem->buf + i, 1); rc = pgm->cmd_tpi(pgm, cmd, 1, mem->buf + i, 1);
lastaddr++; lastaddr++;
if (rc == -1) { if (rc == -1) {
avrdude_message(MSG_INFO, "avr_read(): error reading address 0x%04lx\n", i); avrdude_message(MSG_INFO, "avr_read_mem(): error reading address 0x%04lx\n", i);
return -1; return -1;
} }
} }
@ -422,7 +431,7 @@ int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
/* paged load failed, fall back to byte-at-a-time read below */ /* paged load failed, fall back to byte-at-a-time read below */
failure = 1; failure = 1;
} else { } else {
avrdude_message(MSG_DEBUG, "%s: avr_read(): skipping page %u: no interesting data\n", avrdude_message(MSG_DEBUG, "%s: avr_read_mem(): skipping page %u: no interesting data\n",
progname, pageaddr / mem->page_size); progname, pageaddr / mem->page_size);
} }
nread++; nread++;
@ -445,14 +454,13 @@ int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
{ {
rc = pgm->read_byte(pgm, p, mem, i, mem->buf + i); rc = pgm->read_byte(pgm, p, mem, i, mem->buf + i);
if (rc != LIBAVRDUDE_SUCCESS) { if (rc != LIBAVRDUDE_SUCCESS) {
avrdude_message(MSG_INFO, "avr_read(): error reading address 0x%04lx\n", i); avrdude_message(MSG_INFO, "avr_read_mem(): error reading address 0x%04lx\n", i);
if (rc == LIBAVRDUDE_GENERAL_FAILURE) { if (rc == LIBAVRDUDE_GENERAL_FAILURE) {
avrdude_message(MSG_INFO, " read operation not supported for memory \"%s\"\n", avrdude_message(MSG_INFO, " read operation not supported for memory %s\n",
memtype); mem->desc);
return LIBAVRDUDE_NOTSUPPORTED; return LIBAVRDUDE_NOTSUPPORTED;
} }
avrdude_message(MSG_INFO, " read operation failed for memory \"%s\"\n", avrdude_message(MSG_INFO, " read operation failed for memory %s\n", mem->desc);
memtype);
return LIBAVRDUDE_SOFTFAIL; return LIBAVRDUDE_SOFTFAIL;
} }
} }
@ -463,6 +471,7 @@ int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
} }
/* /*
* write a page data at the specified address * write a page data at the specified address
*/ */
@ -794,17 +803,33 @@ int avr_write_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
/* /*
* Write the whole memory region of the specified memory from the * Write the whole memory region of the specified memory from its buffer of
* corresponding buffer of the avrpart pointed to by 'p'. Write up to * the avrpart pointed to by p to the device. Write up to size bytes from
* 'size' bytes from the buffer. Data is only written if the new data * the buffer. Data is only written if the corresponding tags byte is set.
* value is different from the existing data value. Data beyond * Data beyond size bytes are not affected.
* 'size' bytes is not affected.
* *
* Return the number of bytes written, or -1 if an error occurs. * Return the number of bytes written, or LIBAVRDUDE_GENERAL_FAILURE on error.
*/ */
int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, int size, int auto_erase) {
int size, int auto_erase) AVRMEM *m = avr_locate_mem(p, memtype);
{ if (m == NULL) {
avrdude_message(MSG_INFO, "No \"%s\" memory for part %s\n",
memtype, p->desc);
return LIBAVRDUDE_GENERAL_FAILURE;
}
return avr_write_mem(pgm, p, m, size, auto_erase);
}
/*
* Write the whole memory region of the specified memory from its buffer of
* the avrpart pointed to by p to the device. Write up to size bytes from
* the buffer. Data is only written if the corresponding tags byte is set.
* Data beyond size bytes are not affected.
*
* Return the number of bytes written, or LIBAVRDUDE_GENERAL_FAILURE on error.
*/
int avr_write_mem(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m, int size, int auto_erase) {
int rc; int rc;
int newpage, page_tainted, flush_page, do_write; int newpage, page_tainted, flush_page, do_write;
int wsize; int wsize;
@ -812,14 +837,6 @@ int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
unsigned char data; unsigned char data;
int werror; int werror;
unsigned char cmd[4]; unsigned char cmd[4];
AVRMEM * m;
m = avr_locate_mem(p, memtype);
if (m == NULL) {
avrdude_message(MSG_INFO, "No \"%s\" memory for part %s\n",
memtype, p->desc);
return -1;
}
pgm->err_led(pgm, OFF); pgm->err_led(pgm, OFF);
@ -841,7 +858,7 @@ int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
if ((p->prog_modes & PM_TPI) && m->page_size > 1 && pgm->cmd_tpi) { if ((p->prog_modes & PM_TPI) && m->page_size > 1 && pgm->cmd_tpi) {
if (wsize == 1) { if (wsize == 1) {
/* fuse (configuration) memory: only single byte to write */ /* fuse (configuration) memory: only single byte to write */
return avr_write_byte(pgm, p, m, 0, m->buf[0]) == 0? 1: -1; return avr_write_byte(pgm, p, m, 0, m->buf[0]) == 0? 1: LIBAVRDUDE_GENERAL_FAILURE;
} }
while (avr_tpi_poll_nvmbsy(pgm)); while (avr_tpi_poll_nvmbsy(pgm));
@ -924,7 +941,7 @@ int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
/* paged write failed, fall back to byte-at-a-time write below */ /* paged write failed, fall back to byte-at-a-time write below */
failure = 1; failure = 1;
} else { } else {
avrdude_message(MSG_DEBUG, "%s: avr_write(): skipping page %u: no interesting data\n", avrdude_message(MSG_DEBUG, "%s: avr_write_mem(): skipping page %u: no interesting data\n",
progname, pageaddr / m->page_size); progname, pageaddr / m->page_size);
} }
nwritten++; nwritten++;
@ -1271,11 +1288,7 @@ int avr_mem_might_be_known(const char *str) {
int avr_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) { int avr_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) {
int rc; return pgm->chip_erase(pgm, p);
rc = pgm->chip_erase(pgm, p);
return rc;
} }
int avr_unlock(const PROGRAMMER *pgm, const AVRPART *p) { int avr_unlock(const PROGRAMMER *pgm, const AVRPART *p) {
@ -1288,52 +1301,51 @@ int avr_unlock(const PROGRAMMER *pgm, const AVRPART *p) {
} }
/* /*
* Report the progress of a read or write operation from/to the * Report the progress of a read or write operation from/to the device
* device.
* *
* The first call of report_progress() should look like this (for a write op): * The first call of report_progress() should look like this (for a write):
* *
* report_progress (0, 1, "Writing"); * report_progress(0, 1, "Writing");
* *
* Then hdr should be passed NULL on subsequent calls while the * Then hdr should be passed NULL on subsequent calls *
* operation is progressing. Once the operation is complete, a final * report_progress(k, n, NULL); // k/n signifies proportion of work done
* call should be made as such to ensure proper termination of the
* progress report:
* *
* report_progress (1, 1, NULL); * with 0 <= k < n, while the operation is progressing. Once the operation is
* complete, a final call should be made as such to ensure proper termination
* of the progress report; choose one of the following three forms:
* *
* It would be nice if we could reduce the usage to one and only one * report_progress(n, n, NULL); // finished OK, terminate with double \n
* call for each of start, during and end cases. As things stand now, * report_progress(1, 0, NULL); // finished OK, do not print terminating \n
* that is not possible and makes maintenance a bit more work. * report_progress(1, -1, NULL); // finished not OK, print double \n
*
* It is OK to call report_progress(1, -1, NULL) in a subroutine when
* encountering a fatal error to terminate the reporting here and there even
* though no report may have been started.
*/ */
void report_progress (int completed, int total, char *hdr)
{ void report_progress(int completed, int total, const char *hdr) {
static int last = 0; static int last;
static double start_time; static double start_time;
int percent = (total > 0) ? ((completed * 100) / total) : 100; int percent;
struct timeval tv; struct timeval tv;
double t; double t;
if (update_progress == NULL) if (update_progress == NULL)
return; return;
percent =
completed >= total || total <= 0? 100:
completed < 0? 0:
completed < INT_MAX/100? 100*completed/total: completed/(total/100);
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
t = tv.tv_sec + ((double)tv.tv_usec)/1000000; t = tv.tv_sec + ((double)tv.tv_usec)/1000000;
if (hdr) { if(hdr || !start_time)
last = 0;
start_time = t; start_time = t;
update_progress (percent, t - start_time, hdr);
}
if (percent > 100) if(hdr || percent > last) {
percent = 100;
if (percent > last) {
last = percent; last = percent;
update_progress (percent, t - start_time, hdr); update_progress(percent, t - start_time, hdr, total < 0? -1: !!total);
} }
if (percent == 100)
last = 0; /* Get ready for next time. */
} }

659
src/avrcache.c Normal file
View File

@ -0,0 +1,659 @@
/*
* 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;
}

View File

@ -310,7 +310,6 @@ programmer expects to be able to handle, together with the programming
interface(s) that can be used in that combination. In reality there can be interface(s) that can be used in that combination. In reality there can be
deviations from this list, particularly if programming is directly via a deviations from this list, particularly if programming is directly via a
bootloader. bootloader.
.Pp .Pp
Following parts need special attention: Following parts need special attention:
.Bl -tag -width "ATmega1234" .Bl -tag -width "ATmega1234"
@ -815,28 +814,87 @@ a command history using
so previously entered command lines can be recalled and edited. The so previously entered command lines can be recalled and edited. The
following commands are currently implemented: following commands are currently implemented:
.Bl -tag -offset indent -width indent .Bl -tag -offset indent -width indent
.It Ar dump memtype addr nbytes .It Ar dump memory addr len
Read Read
.Ar nbytes .Ar len
bytes from the specified memory area, and display them in the usual bytes from the specified memory area, and display them in the usual
hexadecimal and ASCII form. hexadecimal and ASCII form.
.It Ar dump memory addr ...
Read all bytes from the specified memory starting at address
.Ar addr,
and display them.
.It Ar dump memory addr
Read 256 bytes from the specified memory area, and display them.
.It Ar dump memory ...
Read all bytes from the specified memory, and display them.
.It Ar dump .It Ar dump
Continue dumping the memory contents for another Continue dumping the memory contents for another
.Ar nbytes .Ar 256
where the previous bytes where the previous
.Ar dump .Ar dump
command left off. command left off.
.It Ar write memtype addr byte1 ... byteN .It Ar write memory addr data[,] {data[,]}
Manually program the respective memory cells, starting at address Manually program the respective memory cells, starting at address
.Ar addr , .Ar addr ,
using the values using the data items provided.
.Ar byte1 The terminal implements reading from and writing to flash and EEPROM type
through memories normally through a cache and paged access functions. All other
.Ar byteN . memories are directly written to without use of a cache. Some
This feature is not implemented for bank-addressed memories such as older parts without paged access will also have flash and EEPROM directly
the flash memory of ATMega devices. accessed without cache.
.Pp
.Ar data
can be hexadecimal, octal or decimal integers, floating point numbers
or C-style strings and characters. For integers, an optional case-insensitive
suffix specifies the data size: HH 8 bit, H/S 16 bit, L 32 bit, LL 64 bit.
Suffix D indicates a 64-bit double, F a 32-bit float, whilst a floating point
number without suffix defaults to 32-bit float. Hexadecimal floating point
notation is supported. An ambiguous trailing suffix, eg, 0x1.8D, is read as
no-suffix float where D is part of the mantissa; use a zero exponent 0x1.8p0D
to clarify.
.Pp
An optional U suffix makes integers unsigned. Ordinary 0x hex integers are
always treated as unsigned. +0x or -0x hex numbers are treated as signed
unless they have a U suffix. Unsigned integers cannot be larger than 2^64-1.
If n is an unsigned integer then -n is also a valid unsigned integer as in C.
Signed integers must fall into the [-2^63, 2^63-1] range or a correspondingly
smaller range when a suffix specifies a smaller type. Out of range signed
numbers trigger a warning.
.Pp
Ordinary 0x hex integers with n hex digits (counting leading zeros) use the
smallest size of 1, 2, 4 and 8 bytes that can accommodate any n-digit hex
integer. If an integer suffix specifies a size explicitly the corresponding
number of least significant bytes are written. Otherwise, signed and unsigned
integers alike occupy the smallest of 1, 2, 4, or 8 bytes needed to
accommodate them in their respective representation.
.Pp
One trailing comma at the end of
.Ar data
items is ignored to facilitate copy & paste of lists.
.It Ar write memory addr len data[,] {data[,]} ...
The ellipsis ... form writes <len> bytes padded by repeating the last
.Ar data
item.
.It Ar flush
Synchronise with the device all pending cached writes to EEPROM or flash.
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 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 command should be used sparingly.
.It Ar abort
Normally, caches are only ever
actually written to the device when using the
.Ar flush
command, at the end of the terminal session after typing
.Ar quit ,
or after EOF on input is encountered. The abort command resets
the cache discarding all previous writes to the flash and EEPROM cache.
.It Ar erase .It Ar erase
Perform a chip erase. Perform a chip erase and discard all pending writes to EEPROM and flash.
.It Ar send b1 b2 b3 b4 .It Ar send b1 b2 b3 b4
Send raw instruction codes to the AVR device. If you need access to a Send raw instruction codes to the AVR device. If you need access to a
feature of an AVR part that is not directly supported by feature of an AVR part that is not directly supported by

View File

@ -1333,91 +1333,139 @@ The following commands are implemented:
@table @code @table @code
@item dump @var{memtype} [@var{start_addr} [@var{nbytes}]] @item dump @var{memtype} @var{addr} @var{nbytes}
Read @var{nbytes} from the specified memory area, and display them in Read @var{nbytes} from the specified memory area, and display them in
the usual hexadecimal and ASCII form. the usual hexadecimal and ASCII form.
@item dump @var{memtype} [@var{start_addr}] @dots{} @item dump @var{memtype} @var{addr} @dots{}
Start reading from @var{start_addr}, all the way to the last memory address. Start reading from @var{addr}, all the way to the last memory address.
@item dump @var{memtype} @var{addr}
Read 256 bytes from the specified memory area, and display them.
@item dump @var{memtype} @dots{}
Read all bytes from the specified memory, and display them.
@item dump @var{memtype} @item dump @var{memtype}
Continue dumping the memory contents for another @var{nbytes} where the Continue dumping the memory contents for another @var{nbytes} where the
previous dump command left off. previous dump command left off.
@item write @var{memtype} @var{start_addr} @var{data1} @var{data2} @dots{} @var{dataN} @item write @var{memtype} @var{addr} @var{data[,]} @{@var{data[,]}@}
Manually program the respective memory cells, starting at address @var{start_addr}, Manually program the respective memory cells, starting at address
using the values @var{data1} through @var{dataN}. This feature is not @var{addr}, using the data items provided. The terminal implements
implemented for bank-addressed memories such as the flash memory of reading from and writing to flash and EEPROM type memories normally
ATMega devices. through a cache and paged access functions. All other memories are
directly written to without use of a cache. Some older parts without paged
access will also have flash and EEPROM directly accessed without cache.
Items @var{dataN} can have the following formats: Items @var{data} can have the following formats:
@multitable @columnfractions .3 .4 .3 @multitable @columnfractions .3 .4 .3
@item @strong{Type} @item @strong{Type}
@tab @strong{Example} @tab @strong{Example}
@tab @strong{Size (bytes)} @tab @strong{Size (bytes)}
@item String
@tab @code{"Hello, world\n"}
@tab varying
@item Character @item Character
@tab @code{'A'} @tab @code{'A'}
@tab 1 @tab 1
@item Decimal integer @item Decimal integer
@tab 12345 @tab 12345
@tab 1, 2, 4, or 8 (see below) @tab 1, 2, 4, or 8
@item Octal integer @item Octal integer
@tab 012345 @tab 012345
@tab 1, 2, 4, or 8 (see below) @tab 1, 2, 4, or 8
@item Hexadecimal integer @item Hexadecimal integer
@tab 0x12345 @tab 0x12345
@tab 1, 2, 4, or 8 (see below) @tab 1, 2, 4, or 8
@item Float @item Float
@tab 3.1415926 @tab 3.1415926
@tab 4 @tab 4
@item Double
@tab 3.141592653589793D
@tab 8
@end multitable @end multitable
Integer constants can be 1, 2, 4, or 8 bytes long. @var{data}
By default, the smallest possible size will be used where can be hexadecimal, octal or decimal integers, floating point numbers
the specified number just fits into. or C-style strings and characters. For integers, an optional case-insensitive
A specific size can be denoted by appending one of these suffixes: suffix specifies the data size as in the table below:
@table @code @table @code
@item LL @item LL
@itemx ll
8 bytes / 64 bits 8 bytes / 64 bits
@item L @item L
@itemx l
4 bytes / 32 bits 4 bytes / 32 bits
@item H @item H or S
@itemx h
@itemx S
@itemx s
2 bytes / 16 bits 2 bytes / 16 bits
@item HH @item HH
@itemx hh
1 byte / 8 bits 1 byte / 8 bits
@end table @end table
Similarly, floating-point constants can have an @code{F} or @code{f} Suffix @code{D} indicates a 64-bit double, @code{F} a 32-bit float, whilst a
appended, but only 32-bit floating-point values are supported. floating point number without suffix defaults to 32-bit float. Hexadecimal
floating point notation is supported. An ambiguous trailing suffix, eg,
@code{0x1.8D}, is read as no-suffix float where @code{D} is part of the
mantissa; use a zero exponent @code{0x1.8p0D} to clarify.
@item write @var{memtype} @var{start_addr} @var{length} @var{data1} @var{data2} @var{dataN} @dots{} An optional @code{U} suffix makes integers unsigned. Ordinary @code{0x} hex
integers are always treated as unsigned. @code{+0x} or @code{-0x} hex
numbers are treated as signed unless they have a @code{U} suffix. Unsigned
integers cannot be larger than 2^64-1. If @var{n} is an unsigned integer then @var{-n}
is also a valid unsigned integer as in C. Signed integers must fall into
the [-2^63, 2^63-1] range or a correspondingly smaller range when a suffix
specifies a smaller type. Out of range signed numbers trigger a warning.
Similar to the above, but @var{length} byte of the memory are written. Ordinary @code{0x} hex integers with @var{n} hex digits (counting leading
For that purpose, after writing the initial items, @var{dataN} is zeros) use the smallest size of 1, 2, 4 and 8 bytes that can accommodate
replicated as many times as needed. any n-digit hex integer. If an integer suffix specifies a size explicitly
the corresponding number of least significant bytes are written.
Otherwise, signed and unsigned integers alike occupy the smallest of 1, 2,
4, or 8 bytes needed to accommodate them in their respective
representation.
One trailing comma at the end of data items is ignored to facilitate copy
and paste of lists.
@item write @var{memtype} @var{addr} @var{length} @var{data[,]} @{@var{data[,]}@} @dots{}
The ellipses form @dots{} of write is similar to above, but @var{length}
byte of the memory are written. For that purpose, after writing the
initial items, the last @var{data} item is replicated as many times as
needed.
@item flush
Synchronise with the device all pending cached writes to EEPROM or flash.
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 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 command should be used sparingly.
@item abort
Normally, caches are only ever actually written to the device when using
@code{flush}, at the end of the terminal session after typing @code{quit},
or after EOF on input is encountered. The @code{abort} command resets the
cache discarding all previous writes to the flash and EEPROM cache.
@item erase @item erase
Perform a chip erase. Perform a chip erase and discard all pending writes to EEPROM and flash.
@item send @var{b1} @var{b2} @var{b3} @var{b4} @item send @var{b1} @var{b2} @var{b3} @var{b4}
Send raw instruction codes to the AVR device. If you need access to a Send raw instruction codes to the AVR device. If you need access to a
feature of an AVR part that is not directly supported by AVRDUDE, this feature of an AVR part that is not directly supported by AVRDUDE, this
command allows you to use it, even though AVRDUDE does not implement the command allows you to use it, even though AVRDUDE does not implement the
command. When using direct SPI mode, up to 3 bytes command. When using direct SPI mode, up to 3 bytes
can be omitted. can be omitted.
@item sig @item sig

View File

@ -35,7 +35,7 @@ typedef uint32_t pinmask_t;
#define LIBAVRDUDE_SUCCESS 0 #define LIBAVRDUDE_SUCCESS 0
#define LIBAVRDUDE_GENERAL_FAILURE (-1) #define LIBAVRDUDE_GENERAL_FAILURE (-1)
#define LIBAVRDUDE_NOTSUPPORTED (-2) // operation not supported #define LIBAVRDUDE_NOTSUPPORTED (-2) // operation not supported
#define LIBAVRDUDE_SOFTFAIL (-3) // returned by avr_signature() if caller #define LIBAVRDUDE_SOFTFAIL (-3) // returned, eg, by avr_signature() if caller
// might proceed with chip erase // might proceed with chip erase
/* formerly lists.h */ /* formerly lists.h */
@ -665,6 +665,14 @@ extern struct serial_device usbhid_serdev;
#define serial_drain (serdev->drain) #define serial_drain (serdev->drain)
#define serial_set_dtr_rts (serdev->set_dtr_rts) #define serial_set_dtr_rts (serdev->set_dtr_rts)
// See avrcache.c
typedef struct { // Memory cache for a subset of cached pages
int size, page_size; // Size of cache (flash or eeprom size) and page size
unsigned int offset; // Offset of flash/eeprom memory
unsigned char *cont, *copy; // current memory contens and device copy of it
unsigned char *iscached; // iscached[i] set when page i has been loaded
} AVR_Cache;
/* formerly pgm.h */ /* formerly pgm.h */
#define ON 1 #define ON 1
@ -788,6 +796,16 @@ typedef struct programmer_t {
void (*setup) (struct programmer_t *pgm); void (*setup) (struct programmer_t *pgm);
void (*teardown) (struct programmer_t *pgm); void (*teardown) (struct programmer_t *pgm);
// Cached r/w API for terminal reads/writes
int (*write_byte_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
unsigned long addr, unsigned char value);
int (*read_byte_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
unsigned long addr, unsigned char *value);
int (*chip_erase_cached)(const struct programmer_t *pgm, const AVRPART *p);
int (*flush_cache) (const struct programmer_t *pgm, const AVRPART *p);
int (*reset_cache) (const struct programmer_t *pgm, const AVRPART *p);
AVR_Cache *cp_flash, *cp_eeprom;
const char *config_file; // Config file where defined const char *config_file; // Config file where defined
int lineno; // Config file line number int lineno; // Config file line number
void *cookie; // For private use by the programmer void *cookie; // For private use by the programmer
@ -827,7 +845,7 @@ void sort_programmers(LISTID programmers);
/* formerly avr.h */ /* formerly avr.h */
typedef void (*FP_UpdateProgress)(int percent, double etime, char *hdr); typedef void (*FP_UpdateProgress)(int percent, double etime, const char *hdr, int finish);
extern struct avrpart parts[]; extern struct avrpart parts[];
extern const char *avr_mem_order[100]; extern const char *avr_mem_order[100];
@ -844,7 +862,9 @@ int avr_tpi_program_enable(const PROGRAMMER *pgm, const AVRPART *p, unsigned cha
int avr_read_byte_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int avr_read_byte_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char * value); unsigned long addr, unsigned char * value);
int avr_read(const PROGRAMMER * pgm, const AVRPART *p, const char *memtype, AVRPART *v); int avr_read_mem(const PROGRAMMER * pgm, const AVRPART *p, const AVRMEM *mem, const AVRPART *v);
int avr_read(const PROGRAMMER * pgm, const AVRPART *p, const char *memtype, const AVRPART *v);
int avr_write_page(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int avr_write_page(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr); unsigned long addr);
@ -855,8 +875,9 @@ int avr_write_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
int avr_write_byte_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int avr_write_byte_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char data); unsigned long addr, unsigned char data);
int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, int size, int avr_write_mem(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int size, int auto_erase);
int auto_erase);
int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, int size, int auto_erase);
int avr_signature(const PROGRAMMER *pgm, const AVRPART *p); int avr_signature(const PROGRAMMER *pgm, const AVRPART *p);
@ -883,7 +904,22 @@ int avr_chip_erase(const PROGRAMMER *pgm, const AVRPART *p);
int avr_unlock(const PROGRAMMER *pgm, const AVRPART *p); int avr_unlock(const PROGRAMMER *pgm, const AVRPART *p);
void report_progress (int completed, int total, char *hdr); void report_progress(int completed, int total, const char *hdr);
int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *m);
int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf);
int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *data);
int avr_is_and(const unsigned char *s1, const unsigned char *s2, const unsigned char *s3, size_t n);
// byte-wise cached 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_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p);
int avr_flush_cache(const PROGRAMMER *pgm, const AVRPART *p);
int avr_reset_cache(const PROGRAMMER *pgm, const AVRPART *p);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -78,6 +78,10 @@ PROGRAMMER *pgm_new(void) {
pgm->usbproduct = nulp; pgm->usbproduct = nulp;
pgm->config_file = nulp; pgm->config_file = nulp;
// Allocate cache structures for flash and EEPROM, *do not* free in pgm_free()
pgm->cp_flash = cfg_malloc("pgm_new()", sizeof(AVR_Cache));
pgm->cp_eeprom = cfg_malloc("pgm_new()", sizeof(AVR_Cache));
// Default values // Default values
pgm->initpgm = NULL; pgm->initpgm = NULL;
pgm->lineno = 0; pgm->lineno = 0;
@ -115,6 +119,11 @@ PROGRAMMER *pgm_new(void) {
pgm->err_led = pgm_default_led; pgm->err_led = pgm_default_led;
pgm->pgm_led = pgm_default_led; pgm->pgm_led = pgm_default_led;
pgm->vfy_led = pgm_default_led; pgm->vfy_led = pgm_default_led;
pgm->read_byte_cached = avr_read_byte_cached;
pgm->write_byte_cached = avr_write_byte_cached;
pgm->chip_erase_cached = avr_chip_erase_cached;
pgm->flush_cache = avr_flush_cache;
pgm->reset_cache = avr_reset_cache;
/* /*
* optional functions - these are checked to make sure they are * optional functions - these are checked to make sure they are
@ -166,6 +175,7 @@ void pgm_free(PROGRAMMER *p) {
} }
// Never free const char *, eg, p->desc, which are set by cache_string() // Never free const char *, eg, p->desc, which are set by cache_string()
// p->cookie is freed by pgm_teardown // p->cookie is freed by pgm_teardown
// Never free cp_eeprom or cp_flash cache structures
free(p); free(p);
} }
} }
@ -177,7 +187,14 @@ PROGRAMMER *pgm_dup(const PROGRAMMER *src) {
ldestroy_cb(pgm->id, free); ldestroy_cb(pgm->id, free);
ldestroy_cb(pgm->usbpid, free); ldestroy_cb(pgm->usbpid, free);
ldestroy_cb(pgm->hvupdi_support, free); ldestroy_cb(pgm->hvupdi_support, free);
// There must be only one cache, even though the part is duplicated
if(pgm->cp_flash)
free(pgm->cp_flash);
if(pgm->cp_eeprom)
free(pgm->cp_eeprom);
memcpy(pgm, src, sizeof(*pgm)); memcpy(pgm, src, sizeof(*pgm));
pgm->id = lcreat(NULL, 0); pgm->id = lcreat(NULL, 0);
pgm->usbpid = lcreat(NULL, 0); pgm->usbpid = lcreat(NULL, 0);
pgm->hvupdi_support = lcreat(NULL, 0); pgm->hvupdi_support = lcreat(NULL, 0);

View File

@ -51,6 +51,12 @@ static int cmd_dump (PROGRAMMER * pgm, struct avrpart * p,
static int cmd_write (PROGRAMMER * pgm, struct avrpart * p, static int cmd_write (PROGRAMMER * pgm, struct avrpart * p,
int argc, char *argv[]); int argc, char *argv[]);
static int cmd_flush (PROGRAMMER * pgm, struct avrpart * p,
int argc, char *argv[]);
static int cmd_abort (PROGRAMMER * pgm, struct avrpart * p,
int argc, char *argv[]);
static int cmd_erase (PROGRAMMER * pgm, struct avrpart * p, static int cmd_erase (PROGRAMMER * pgm, struct avrpart * p,
int argc, char *argv[]); int argc, char *argv[]);
@ -100,6 +106,8 @@ struct command cmd[] = {
{ "dump", cmd_dump, "%s <memory> [<addr> <len> | <addr> ... | <addr> | ...]" }, { "dump", cmd_dump, "%s <memory> [<addr> <len> | <addr> ... | <addr> | ...]" },
{ "read", cmd_dump, "alias for dump" }, { "read", cmd_dump, "alias for dump" },
{ "write", cmd_write, "%s <memory> <addr> [<data>[,] {<data>[,]} | <len> <data>[,] {<data>[,]} ...]" }, { "write", cmd_write, "%s <memory> <addr> [<data>[,] {<data>[,]} | <len> <data>[,] {<data>[,]} ...]" },
{ "flush", cmd_flush, "synchronise flash & EEPROM writes with the device" },
{ "abort", cmd_abort, "abort flash & EEPROM writes (reset the r/w cache)" },
{ "erase", cmd_erase, "perform a chip erase" }, { "erase", cmd_erase, "perform a chip erase" },
{ "sig", cmd_sig, "display device signature bytes" }, { "sig", cmd_sig, "display device signature bytes" },
{ "part", cmd_part, "display the current part information" }, { "part", cmd_part, "display the current part information" },
@ -115,7 +123,7 @@ struct command cmd[] = {
{ "quell", cmd_quell, "set quell level for progress bars" }, { "quell", cmd_quell, "set quell level for progress bars" },
{ "help", cmd_help, "help" }, { "help", cmd_help, "help" },
{ "?", cmd_help, "help" }, { "?", cmd_help, "help" },
{ "quit", cmd_quit, "quit" } { "quit", cmd_quit, "quit after writing out cache for flash & EEPROM" }
}; };
#define NCMDS ((int)(sizeof(cmd)/sizeof(struct command))) #define NCMDS ((int)(sizeof(cmd)/sizeof(struct command)))
@ -321,8 +329,9 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
report_progress(0, 1, "Reading"); report_progress(0, 1, "Reading");
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
int rc = pgm->read_byte(pgm, p, mem, addr + i, &buf[i]); int rc = pgm->read_byte_cached(pgm, p, mem, addr + i, &buf[i]);
if (rc != 0) { if (rc != 0) {
report_progress(1, -1, NULL);
terminal_message(MSG_INFO, "%s (dump): error reading %s address 0x%05lx of part %s\n", terminal_message(MSG_INFO, "%s (dump): error reading %s address 0x%05lx of part %s\n",
progname, mem->desc, (long) addr + i, p->desc); progname, mem->desc, (long) addr + i, p->desc);
if (rc == -1) if (rc == -1)
@ -403,7 +412,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
"or C-style strings and characters. For integers, an optional case-insensitive\n" "or C-style strings and characters. For integers, an optional case-insensitive\n"
"suffix specifies the data size: HH 8 bit, H/S 16 bit, L 32 bit, LL 64 bit.\n" "suffix specifies the data size: HH 8 bit, H/S 16 bit, L 32 bit, LL 64 bit.\n"
"Suffix D indicates a 64-bit double, F a 32-bit float, whilst a floating point\n" "Suffix D indicates a 64-bit double, F a 32-bit float, whilst a floating point\n"
"number without suffix defaults to 32-bit float. Hexadecimal floating point\n" "number without suffix defaults to 32-bit float. Hexadecimal floating point\n"
"notation is supported. An ambiguous trailing suffix, eg, 0x1.8D, is read as\n" "notation is supported. An ambiguous trailing suffix, eg, 0x1.8D, is read as\n"
"no-suffix float where D is part of the mantissa; use a zero exponent 0x1.8p0D\n" "no-suffix float where D is part of the mantissa; use a zero exponent 0x1.8p0D\n"
"to clarify.\n" "to clarify.\n"
@ -689,9 +698,9 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
pgm->err_led(pgm, OFF); pgm->err_led(pgm, OFF);
bool werror = false; bool werror = false;
report_progress(0, 1, "Writing"); report_progress(0, 1, avr_has_paged_access(pgm, mem)? "Caching": "Writing");
for (i = 0; i < (len + data.bytes_grown); i++) { for (i = 0; i < len + data.bytes_grown; i++) {
int rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]); int rc = pgm->write_byte_cached(pgm, p, mem, addr+i, buf[i]);
if (rc) { if (rc) {
terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n", terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx, rc=%d\n",
progname, buf[i], (long) addr+i, (int) rc); progname, buf[i], (long) addr+i, (int) rc);
@ -702,18 +711,17 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
} }
uint8_t b; uint8_t b;
rc = pgm->read_byte(pgm, p, mem, addr+i, &b); rc = pgm->read_byte_cached(pgm, p, mem, addr+i, &b);
if (b != buf[i]) { if (b != buf[i]) {
terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n", terminal_message(MSG_INFO, "%s (write): error writing 0x%02x at 0x%05lx cell=0x%02x\n",
progname, buf[i], (long) addr+i, b); progname, buf[i], (long) addr+i, b);
werror = true; werror = true;
} }
if (werror) { if (werror)
pgm->err_led(pgm, ON); pgm->err_led(pgm, ON);
}
report_progress(i, (len + data.bytes_grown), NULL); report_progress(i, len + data.bytes_grown, NULL);
} }
report_progress(1, 1, NULL); report_progress(1, 1, NULL);
@ -723,6 +731,18 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
} }
static int cmd_flush(PROGRAMMER *pgm, struct avrpart *p, int ac, char *av[]) {
pgm->flush_cache(pgm, p);
return 0;
}
static int cmd_abort(PROGRAMMER *pgm, struct avrpart *p, int ac, char *av[]) {
pgm->reset_cache(pgm, p);
return 0;
}
static int cmd_send(PROGRAMMER * pgm, struct avrpart * p, static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
int argc, char * argv[]) int argc, char * argv[])
{ {
@ -789,7 +809,9 @@ static int cmd_erase(PROGRAMMER * pgm, struct avrpart * p,
int argc, char * argv[]) int argc, char * argv[])
{ {
terminal_message(MSG_INFO, "%s: erasing chip\n", progname); terminal_message(MSG_INFO, "%s: erasing chip\n", progname);
pgm->chip_erase(pgm, p); // Erase chip and clear cache
pgm->chip_erase_cached(pgm, p);
return 0; return 0;
} }
@ -1020,8 +1042,10 @@ static int cmd_help(PROGRAMMER * pgm, struct avrpart * p,
fprintf(stdout, cmd[i].desc, cmd[i].name); fprintf(stdout, cmd[i].desc, cmd[i].name);
fprintf(stdout, "\n"); fprintf(stdout, "\n");
} }
fprintf(stdout, fprintf(stdout, "\n"
"\nUse the 'part' command to display valid memory types for use with the\n" "Note that flash and EEPROM type memories are normally read and written\n"
"using a cache and paged r/w access; the cache is synchronised on quit.\n"
"Use the 'part' command to display valid memory types for use with the\n"
"'dump' and 'write' commands.\n\n"); "'dump' and 'write' commands.\n\n");
return 0; return 0;
@ -1320,6 +1344,8 @@ int terminal_mode(PROGRAMMER * pgm, struct avrpart * p)
free(cmdbuf); free(cmdbuf);
} }
pgm->flush_cache(pgm, p);
return rc; return rc;
} }
@ -1340,70 +1366,74 @@ int terminal_message(const int msglvl, const char *format, ...) {
} }
static void update_progress_tty (int percent, double etime, char *hdr) static void update_progress_tty(int percent, double etime, const char *hdr, int finish) {
{
static char hashes[51];
static char *header; static char *header;
static int last = 0; static int last, done;
int i; int i;
setvbuf(stderr, (char*)NULL, _IONBF, 0); setvbuf(stderr, (char *) NULL, _IONBF, 0);
hashes[50] = 0; if(hdr) {
memset (hashes, ' ', 50);
for (i=0; i<percent; i+=2) {
hashes[i/2] = '#';
}
if (hdr) {
avrdude_message(MSG_INFO, "\n"); avrdude_message(MSG_INFO, "\n");
last = 0; last = done = 0;
header = hdr; if(header)
free(header);
header = cfg_strdup("update_progress_tty()", hdr);
} }
if (last == 0) { percent = percent > 100? 100: percent < 0? 0: percent;
if(!done) {
if(!header)
header = cfg_strdup("update_progress_tty()", "report");
int showperc = finish >= 0? percent: last;
char hashes[51];
memset(hashes, finish >= 0? ' ': '-', 50);
for(i=0; i<showperc; i+=2)
hashes[i/2] = '#';
hashes[50] = 0;
avrdude_message(MSG_INFO, "\r%s | %s | %d%% %0.2fs", avrdude_message(MSG_INFO, "\r%s | %s | %d%% %0.2fs",
header, hashes, percent, etime); header, hashes, showperc, etime);
} if(percent == 100) {
if(finish)
if (percent == 100) { avrdude_message(MSG_INFO, "\n\n");
if (!last) avrdude_message(MSG_INFO, "\n\n"); done = 1;
last = 1;
}
setvbuf(stderr, (char*)NULL, _IOLBF, 0);
}
static void update_progress_no_tty (int percent, double etime, char *hdr)
{
static int done = 0;
static int last = 0;
int cnt = (percent>>1)*2;
setvbuf(stderr, (char*)NULL, _IONBF, 0);
if (hdr) {
avrdude_message(MSG_INFO, "\n%s | ", hdr);
last = 0;
done = 0;
}
else {
while ((cnt > last) && (done == 0)) {
avrdude_message(MSG_INFO, "#");
cnt -= 2;
} }
} }
last = percent;
if ((percent == 100) && (done == 0)) { setvbuf(stderr, (char *) NULL, _IOLBF, 0);
avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime); }
last = 0;
done = 1; static void update_progress_no_tty(int percent, double etime, const char *hdr, int finish) {
static int last, done;
setvbuf(stderr, (char *) NULL, _IONBF, 0);
percent = percent > 100? 100: percent < 0? 0: percent;
if(hdr) {
avrdude_message(MSG_INFO, "\n%s | ", hdr);
last = done = 0;
} }
else
last = (percent>>1)*2; /* Make last a multiple of 2. */
setvbuf(stderr, (char*)NULL, _IOLBF, 0); if(!done) {
for(int cnt = percent/2; cnt > last/2; cnt--)
avrdude_message(MSG_INFO, finish >= 0? "#": "-");
if(percent == 100) {
avrdude_message(MSG_INFO, " | %d%% %0.2fs", etime, finish >= 0? 100: last);
if(finish)
avrdude_message(MSG_INFO, "\n\n");
done = 1;
}
}
last = percent;
setvbuf(stderr, (char *) NULL, _IOLBF, 0);
} }
void terminal_setup_update_progress() { void terminal_setup_update_progress() {