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:
parent
c4cb242823
commit
d74b17b9b4
|
@ -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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
166
src/avr.c
166
src/avr.c
|
@ -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. */
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -1333,85 +1333,133 @@ 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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
17
src/pgm.c
17
src/pgm.c
|
@ -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);
|
||||||
|
|
162
src/term.c
162
src/term.c
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
if(hdr) {
|
||||||
|
avrdude_message(MSG_INFO, "\n");
|
||||||
|
last = done = 0;
|
||||||
|
if(header)
|
||||||
|
free(header);
|
||||||
|
header = cfg_strdup("update_progress_tty()", hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
hashes[50] = 0;
|
||||||
|
|
||||||
memset (hashes, ' ', 50);
|
|
||||||
for (i=0; i<percent; i+=2) {
|
|
||||||
hashes[i/2] = '#';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hdr) {
|
|
||||||
avrdude_message(MSG_INFO, "\n");
|
|
||||||
last = 0;
|
|
||||||
header = hdr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last == 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");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((percent == 100) && (done == 0)) {
|
|
||||||
avrdude_message(MSG_INFO, " | 100%% %0.2fs\n\n", etime);
|
|
||||||
last = 0;
|
|
||||||
done = 1;
|
done = 1;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
last = (percent>>1)*2; /* Make last a multiple of 2. */
|
last = percent;
|
||||||
|
|
||||||
setvbuf(stderr, (char*)NULL, _IOLBF, 0);
|
setvbuf(stderr, (char *) NULL, _IOLBF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
|
Loading…
Reference in New Issue