Skip to content

Commit

Permalink
Provide cached byte-wise read/write API (#1106)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
stefanrueger authored Oct 5, 2022
1 parent c4cb242 commit d74b17b
Show file tree
Hide file tree
Showing 9 changed files with 1,052 additions and 190 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ add_library(libavrdude
avr.c
avr910.c
avr910.h
avrcache.c
avrdude.h
avrftdi.c
avrftdi.h
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ libavrdude_a_SOURCES = \
avr.c \
avr910.c \
avr910.h \
avrcache.c \
avrdude.h \
avrftdi.c \
avrftdi.h \
Expand Down
168 changes: 90 additions & 78 deletions src/avr.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,30 +312,39 @@ int avr_mem_hiaddr(const AVRMEM * mem)


/*
* Read the entirety of the specified memory type 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.
* Read the entirety of the specified memory type 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.
* Return the number of bytes read, or < 0 if an error occurs.
*/
int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
AVRPART * v)
{
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);
int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, const AVRPART *v) {
AVRMEM *mem = avr_locate_mem(p, memtype);
if (mem == NULL) {
avrdude_message(MSG_INFO, "No \"%s\" memory for part %s\n",
memtype, p->desc);
return -1;
avrdude_message(MSG_INFO, "No %s memory for part %s\n", memtype, p->desc);
return LIBAVRDUDE_GENERAL_FAILURE;
}

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
*/
Expand Down Expand Up @@ -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);
lastaddr++;
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;
}
}
Expand Down Expand Up @@ -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 */
failure = 1;
} 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);
}
nread++;
Expand All @@ -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);
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) {
avrdude_message(MSG_INFO, " read operation not supported for memory \"%s\"\n",
memtype);
avrdude_message(MSG_INFO, " read operation not supported for memory %s\n",
mem->desc);
return LIBAVRDUDE_NOTSUPPORTED;
}
avrdude_message(MSG_INFO, " read operation failed for memory \"%s\"\n",
memtype);
avrdude_message(MSG_INFO, " read operation failed for memory %s\n", mem->desc);
return LIBAVRDUDE_SOFTFAIL;
}
}
Expand All @@ -463,6 +471,7 @@ int avr_read(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype,
}



/*
* write a page data at the specified address
*/
Expand Down Expand Up @@ -794,32 +803,40 @@ int avr_write_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,


/*
* Write the whole memory region of the specified memory from the
* corresponding buffer of the avrpart pointed to by 'p'. Write up to
* 'size' bytes from the buffer. Data is only written if the new data
* value is different from the existing data value. Data beyond
* 'size' bytes is not affected.
* 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 -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 size, int auto_erase)
{
int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, 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 newpage, page_tainted, flush_page, do_write;
int wsize;
unsigned int i, lastaddr;
unsigned char data;
int werror;
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);

Expand All @@ -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 (wsize == 1) {
/* 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));
Expand Down Expand Up @@ -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 */
failure = 1;
} 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);
}
nwritten++;
Expand Down Expand Up @@ -1271,11 +1288,7 @@ int avr_mem_might_be_known(const char *str) {


int avr_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) {
int rc;

rc = pgm->chip_erase(pgm, p);

return rc;
return pgm->chip_erase(pgm, p);
}

int avr_unlock(const PROGRAMMER *pgm, const AVRPART *p) {
Expand All @@ -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
* device.
* Report the progress of a read or write operation from/to the device
*
* The first call of report_progress() should look like this (for a write):
*
* The first call of report_progress() should look like this (for a write op):
* report_progress(0, 1, "Writing");
*
* report_progress (0, 1, "Writing");
* Then hdr should be passed NULL on subsequent calls *
* report_progress(k, n, NULL); // k/n signifies proportion of work done
*
* Then hdr should be passed NULL on subsequent calls 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:
* 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:
*
* report_progress (1, 1, NULL);
* report_progress(n, n, NULL); // finished OK, terminate with double \n
* report_progress(1, 0, NULL); // finished OK, do not print terminating \n
* report_progress(1, -1, NULL); // finished not OK, print double \n
*
* It would be nice if we could reduce the usage to one and only one
* call for each of start, during and end cases. As things stand now,
* that is not possible and makes maintenance a bit more work.
* 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)
{
static int last = 0;

void report_progress(int completed, int total, const char *hdr) {
static int last;
static double start_time;
int percent = (total > 0) ? ((completed * 100) / total) : 100;
int percent;
struct timeval tv;
double t;

if (update_progress == NULL)
return;

percent =
completed >= total || total <= 0? 100:
completed < 0? 0:
completed < INT_MAX/100? 100*completed/total: completed/(total/100);

gettimeofday(&tv, NULL);
t = tv.tv_sec + ((double)tv.tv_usec)/1000000;

if (hdr) {
last = 0;
if(hdr || !start_time)
start_time = t;
update_progress (percent, t - start_time, hdr);
}

if (percent > 100)
percent = 100;

if (percent > last) {
if(hdr || percent > last) {
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. */
}
Loading

0 comments on commit d74b17b

Please sign in to comment.