This may or may not be considered a bug. I have been struggling with Leonardo boards not being able to be reprogrammed after I program them with the following Test Sketch. I believe I now understand why. The Leonardo bootloader uses a 16-bit value stored at base address 0x800 as a variable to tell it why the watchdog timer reset the chip. If the value is set to 0x7777, then it assumes that the Arduino IDE has reset the chip for reprogramming; otherwise, it assumes that the sketch was uses the watchdog for its own reason. If the IDE had reset the chip, then the bootloader will go into a mode where it waits for 8 seconds for instructions to be reprogrammed otherwise the bootloader jumps to the sketch. The problem I was having is that the GCC linker was putting the variable
timer0_millis on the base address of 0x800. It's not the linker's fault because it has no way to know that this address is 'reserved.' This problem can be prevented if it knows not to use that 'reserved' address block. Can the linker script be modified so that the start or end address of SRAM is moved inwards one byte so the linker will never assign that address to any variable? If you run the Test Sketch below, then you will have to manually start the bootloader in ‘reprogram’ mode by pushing the reset button on the board. I consider this a bug because this is unexpected behavior. This is not the hardware stack overrunning some global state.
I'm using the Arduino IDE v1.5 on Windows XP SP3.
Test sketch
#include <util/delay.h> // using _delay_ms
byte buf[1736];
extern unsigned long timer0_millis;
void setup(void)
{
Serial.begin(9600);
pinMode(13, OUTPUT);
//TIMSK0 = 0;
}
void loop(void)
{
static uint16_t last_value = *(uint16_t *)0x0800;
// print the base address of the internal variable 'timer0_millis'
// that is used by the function 'millis()'
if (Serial) {
Serial.println((unsigned int)&timer0_millis, HEX);
_delay_ms(1000);
}
// pulse LED if bootKey is changing
if (*(uint16_t *)0x0800 != last_value) {
last_value = *(uint16_t *)0x0800;
digitalWrite(13, !digitalRead(13));
_delay_ms(100);
}
// prevent the optimizer from removing the array
buf[0] = buf[buf[0]];
}
From the file Caterina.c in the bootloader
uint16_t bootKey = 0x7777;
volatile uint16_t *const bootKeyPtr = (volatile uint16_t *)0x0800;
// ...
int main(void)
{
/* Save the value of the boot key memory before it is overwritten */
uint16_t bootKeyPtrVal = *bootKeyPtr;
*bootKeyPtr = 0;
/* Check the reason for the reset so we can act accordingly */
uint8_t mcusr_state = MCUSR; // store the initial state of the Status register
MCUSR = 0; // clear all reset flags
/* Watchdog may be configured with a 15 ms period so must disable it before going any further */
wdt_disable();
if (mcusr_state & (1<<EXTRF)) {
// External reset - we should continue to self-programming mode.
} else if (mcusr_state == (1<<PORF) && pgm_read_word(0) != 0xFFFF) {
// After a power-on reset skip the bootloader and jump straight to sketch
// if one exists.
StartSketch();
} else if ((mcusr_state == (1<<WDRF)) && (bootKeyPtrVal != bootKey) && (pgm_read_word(0) != 0xFFFF)) {
// If it looks like an "accidental" watchdog reset then start the sketch.
StartSketch();
}
From the file CDC.cpp
//...
*(uint16_t *)0x0800 = 0x7777;
wdt_enable(WDTO_120MS);
//...