Skip to content

Commit

Permalink
Merge pull request #1836 from stefanrueger/erase-note
Browse files Browse the repository at this point in the history
Improve chip erase emulation for dryboot/dryrun
  • Loading branch information
stefanrueger committed Jul 7, 2024
2 parents 42c984c + fa618a8 commit c82a804
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 30 deletions.
21 changes: 11 additions & 10 deletions src/avrcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
* flash (and sometimes EEPROM, too) looks like a NOR memory, ie, a write can
* only clear bits, never set them. For NOR memories a page erase or, if not
* available, a chip erase needs to be issued before writing arbitrary data.
* Bootrow and usersig are generally unaffected by a chip erase, so will need
* a page erase. When a memory looks like a NOR memory, either page erase is
* Usersig is generally unaffected by a chip erase, so will always need a
* page erase. When a memory looks like a NOR memory, 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
Expand All @@ -90,7 +90,7 @@
* 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. avr_chip_erase_cached() does not
* affect the bootrow or usersig cache.
* affect the usersig cache.
*
* The avr_page_erase_cached() function erases a page and synchronises it
* with the cache.
Expand Down Expand Up @@ -689,10 +689,11 @@ int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM

// Erase the chip and set the cache accordingly
int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p) {
Cache_desc mems[3] = {
Cache_desc mems[] = {
{ avr_locate_flash(p), pgm->cp_flash, 1, 0, -1, 0 },
{ avr_locate_eeprom(p), pgm->cp_eeprom, 0, 1, -1, 0 },
// bootrow/usersig is unaffected by CE
{ avr_locate_bootrow(p), pgm->cp_bootrow, 0, 0, -1, 0 },
// usersig is unaffected by CE
};
int rc;

Expand All @@ -718,19 +719,19 @@ int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p) {
memset(cp->cont, 0xff, cp->size);
memset(cp->iscached, 1, cp->size/cp->page_size);
}
} else if(mems[i].iseeprom) { // Test whether cached EEPROM pages were zapped
bool erasedee = 0;
} else { // Test whether cached EEPROM/bootrow pages were zapped
bool erased = 0;
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
if(cp->iscached[pgno]) {
if(!is_memset(cp->copy + n, 0xff, cp->page_size)) { // Page has EEPROM data?
if(!is_memset(cp->copy + n, 0xff, cp->page_size)) { // Page has data?
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
erasedee = is_memset(cp->copy + n, 0xff, cp->page_size);
erased = is_memset(cp->copy + n, 0xff, cp->page_size);
break;
}
}
}
if(erasedee) { // EEPROM was erased, set cache correspondingly
if(erased) { // Memory 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);
Expand Down
14 changes: 8 additions & 6 deletions src/avrdude.1
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,11 @@ Temperature sensor calibration values
.It bootrow
Extra page of memory that is only accessible by the MCU in bootloader
code; UDPI can read and write this memory only when the device is
unlocked; bootrow is not erased during chip erase
unlocked
.It userrow
Extra page of EEPROM memory that can be used for firmware settings; this
memory is not erased during a chip erase
memory is not erased during a chip erase; UPDI cannot read this memory
when the device is locked
.It sib
Special system information block memory with information about AVR family, chip revision etc.
.It io
Expand Down Expand Up @@ -1263,8 +1264,9 @@ command line argument.
.Ar verify
flushes the cache before verifying memories.
.It Ar erase
Perform a chip erase and discard all pending writes to EEPROM and flash.
Note that EEPROM will be preserved if the EESAVE fuse bit is set.
Perform a chip erase and discard all pending writes to flash, EEPROM and bootrow.
Note that EEPROM will be preserved if the EESAVE fuse bit is active, ie, had
a corresponding value at the last reset prior to the operation.
.It Ar erase memory
Erase the entire specified memory.
.It Ar erase memory addr len
Expand All @@ -1274,8 +1276,8 @@ Synchronise with the device all pending writes to flash, EEPROM, bootrow and
usersig. With some programmer and part combinations, flash (and sometimes
EEPROM, too) looks like a NOR memory, i.e., a write can only clear bits,
never set them. For NOR memories a page erase or, if not available, a chip
erase needs to be issued before writing arbitrary data. Bootrow and usersig are
generally unaffected by a chip erase. When a memory looks like a NOR
erase needs to be issued before writing arbitrary data. Usersig is
unaffected by a chip erase. When a memory looks like a NOR
memory, either page erase is deployed (e.g., 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
Expand Down
11 changes: 6 additions & 5 deletions src/doc/avrdude.texi
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ Temperature sensor calibration values
@item bootrow
Extra page of memory that is only accessible by the MCU in bootloader
code; UDPI can read and write this memory only when the device is
unlocked; bootrow is not erased during chip erase
unlocked
@item userrow
Extra page of EEPROM memory that can be used for firmware settings; this
memory is not erased during a chip erase
Expand Down Expand Up @@ -2509,8 +2509,9 @@ comma separated list of memories just as in the @code{-U} command line
argument. @code{verify} flushes the cache before verifying memories.

@item erase
Perform a chip erase and discard all pending writes to EEPROM and flash.
Note that EEPROM will be preserved if the EESAVE fuse bit is set.
Perform a chip erase and discard all pending writes to flash, EEPROM and bootrow.
Note that EEPROM will be preserved if the EESAVE fuse bit is active, ie, had
a corresponding value at the last reset prior to the operation.

@item erase @var{memory}
Erase the entire specified memory.
Expand All @@ -2523,8 +2524,8 @@ Synchronise with the device all pending writes to flash, EEPROM, bootrow and
usersig. With some programmer and part combinations, flash (and sometimes
EEPROM, too) looks like a NOR memory, i.e., a write can only clear bits,
never set them. For NOR memories a page erase or, if not available, a chip
erase needs to be issued before writing arbitrary data. Bootrow and usersig are
generally unaffected by a chip erase. When a memory looks like a NOR
erase needs to be issued before writing arbitrary data. Usersig is
unaffected by a chip erase. When a memory looks like a NOR
memory, either page erase is deployed (e.g., 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
Expand Down
73 changes: 65 additions & 8 deletions src/dryrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ typedef struct {
int appstart, appsize; // Start and size of application section
int datastart, datasize; // Start and size of application data section (if any)
int bootstart, bootsize; // Start and size of boot section (if any)
int initialised; // 1 once the part memories are initialised
} Dryrun_data;

// Use private programmer data as if they were a global structure dry
Expand All @@ -87,19 +88,40 @@ static int dryrun_read_sig_bytes(const PROGRAMMER *pgm, const AVRPART *p, const
}


// Emulate chip erase (only erase flash, pretend EESAVE fuse is active - FIXME: check EESAVE fuse)
// Emulate chip erase
static int dryrun_chip_erase(const PROGRAMMER *pgm, const AVRPART *punused) {
AVRMEM *flm;
AVRMEM *mem;

pmsg_debug("%s()\n", __func__);
if(!dry.dp)
Return("no dryrun device?");
if(!(flm = avr_locate_flash(dry.dp)))
if(!(mem = avr_locate_flash(dry.dp)))
Return("cannot locate %s flash memory for chip erase", dry.dp->desc);
if(flm->size < 1)
Return("cannot erase %s flash memory owing to its size %d", dry.dp->desc, flm->size);
if(mem->size < 1)
Return("cannot erase %s flash memory owing to its size %d", dry.dp->desc, mem->size);

memset(flm->buf, 0xff, flm->size);
if(dry.bl) { // Bootloaders won't overwrite themselves
memset(mem->buf + (dry.bl == DRY_TOP? 0: dry.bootsize), 0xff, mem->size-dry.bootsize);
return 0; // Assume that's all a bootloader does
}

memset(mem->buf, 0xff, mem->size);

int eesave, bakverb = verbose;
verbose = -123;
if((mem = avr_locate_eeprom(dry.dp))) // Check whether EEPROM needs erasing
if(avr_get_config_value(pgm, dry.dp, "eesave", &eesave) == 0 && eesave == !(dry.dp->prog_modes & PM_UPDI))
if(mem->size > 0)
memset(mem->buf, 0xff, mem->size);
verbose = bakverb;

if((mem = avr_locate_bootrow(dry.dp))) // Also erase bootrow if it's there
if(mem->size > 0)
memset(mem->buf, 0xff, mem->size);

if((mem = avr_locate_lock(dry.dp)))
if(mem->initval != -1 && mem->size > 0 && mem->size <= (int) sizeof(mem->initval))
memcpy(mem->buf, &mem->initval, mem->size); // FIXME: relying on little endian here

return 0;
}
Expand All @@ -124,6 +146,30 @@ static int dryrun_cmd(const PROGRAMMER *pgm, const unsigned char *cmd, unsigned
}


static int dryrun_page_erase(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
unsigned int addr) {

pmsg_debug("%s(%s, 0x%04x)\n", __func__, m->desc, addr);
if(!dry.dp)
Return("no dryrun device?");

AVRMEM *dmem;
if(!(dmem = avr_locate_mem(dry.dp, m->desc)))
Return("cannot locate %s %s memory for paged write", dry.dp->desc, m->desc);

if(!avr_has_paged_access(pgm, dmem) || addr >= (unsigned) dmem->size)
Return("%s does not support paged access", dmem->desc);
addr &= ~(dmem->page_size-1);
if(addr + dmem->page_size > (unsigned) dmem->size)
Return("%s page erase of %s reaches outside %s?", dmem->desc,
str_ccinterval(addr, addr + dmem->page_size-1), str_ccinterval(0, dmem->size-1));

memset(dmem->buf+addr, 0xff, dmem->page_size);

return 0;
}


static int dryrun_program_enable(const PROGRAMMER *pgm, const AVRPART *p_unused) {
pmsg_debug("%s()\n", __func__);

Expand Down Expand Up @@ -603,6 +649,10 @@ static void dryrun_enable(PROGRAMMER *pgm, const AVRPART *p) {
if((m = avr_locate_flash(q)) && m->size >= 1024 && (pgm->prog_modes & PM_SPM))
dry.bl = (q->prog_modes & PM_UPDI)? DRY_BOTTOM: DRY_TOP;

// So that dryrun can emulate AVRDUDE page erase
if(!(pgm->prog_modes & PM_SPM) && (q->prog_modes & (PM_PDI | PM_UPDI)))
pgm->page_erase = dryrun_page_erase;

if(!dry.random && !dry.init) // OK, no further initialisation needed
return;

Expand Down Expand Up @@ -640,7 +690,12 @@ static void dryrun_enable(PROGRAMMER *pgm, const AVRPART *p) {
int ps = flm->page_size;
urbtsz = dry.bootsize? dry.bootsize: flm->size > 32768? 512: flm->size < 16384? 256: 384;
urbtsz = (urbtsz + ps-1)/ps*ps;
int ubaddr = dry.bootsize? dry.bootstart: flm->size - urbtsz;
if(!dry.bootsize && !dry.datasize) {
dry.bootsize += urbtsz;
dry.appsize -= urbtsz;
dry.bootstart = dry.appsize;
}
int ubaddr = dry.bootstart;
putflash(pgm, flm, ubaddr, urbtsz, urbtsz==384? U384: U512);
flm->buf[ubaddr] = 0xff; flm->buf[ubaddr+1] = 0xcf; // rjmp .-2
} else if(dry.bootsize) {
Expand All @@ -666,6 +721,8 @@ static void dryrun_enable(PROGRAMMER *pgm, const AVRPART *p) {
putother(pgm, q, m, "The five boxing wizards jump quickly. ");
if((m = avr_locate_bootrow(q)))
putother(pgm, q, m, "Lorem ipsum dolor sit amet. ");

dry.initialised = 1;
}


Expand Down Expand Up @@ -942,7 +999,7 @@ static int dryrun_readonly(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM

// @@@ check for bootloader write protection

if(mem_is_in_fuses(mem) || mem_is_lock(mem))
if(dry.initialised && (mem_is_in_fuses(mem) || mem_is_lock(mem)))
return 1;

return 0;
Expand Down
4 changes: 3 additions & 1 deletion src/term.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,7 +964,9 @@ static int cmd_erase(const PROGRAMMER *pgm, const AVRPART *p, int argc, const ch
return cmd_write(pgm, p, 6, args);
}

term_out("erasing chip ...\n");
term_out("%s chip erase; discarded pending writes to flash%s\n",
(pgm->prog_modes & PM_SPM)? "asking bootloader to perform": "performing",
avr_locate_bootrow(p)? ", EEPROM and bootrow": avr_locate_eeprom(p)? "and EEPROM": "");

// Erase chip and clear cache
int rc = pgm->chip_erase_cached(pgm, p);
Expand Down

0 comments on commit c82a804

Please sign in to comment.