diff options
Diffstat (limited to 'firmware/main.c')
-rw-r--r-- | firmware/main.c | 320 |
1 files changed, 108 insertions, 212 deletions
diff --git a/firmware/main.c b/firmware/main.c index e56de62..a6af148 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -4,26 +4,27 @@ * Creation Date: 2007-12-08 * Tabsize: 4 * Copyright: (c) 2012 Jenna Fox + * All changes past revision 1.06 authored by http://github.com/cpldcpu * Portions Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH (USBaspLoader) * Portions Copyright: (c) 2012 Louis Beaudoin (USBaspLoader-tiny85) * License: GNU GPL v2 (see License.txt) + * */ #define MICRONUCLEUS_VERSION_MAJOR 1 -#define MICRONUCLEUS_VERSION_MINOR 6 +#define MICRONUCLEUS_VERSION_MINOR 10 // how many milliseconds should host wait till it sends another erase or write? // needs to be above 4.5 (and a whole integer) as avr freezes for 4.5ms #define MICRONUCLEUS_WRITE_SLEEP 8 +// Use the old delay routines without NOP padding. This saves memory. +#define __DELAY_BACKWARD_COMPATIBLE__ #include <avr/io.h> -#include <avr/interrupt.h> #include <avr/pgmspace.h> #include <avr/wdt.h> #include <avr/boot.h> -//#include <avr/eeprom.h> #include <util/delay.h> -//#include <string.h> static void leaveBootloader() __attribute__((__noreturn__)); @@ -31,43 +32,6 @@ static void leaveBootloader() __attribute__((__noreturn__)); #include "usbdrv/usbdrv.c" /* ------------------------------------------------------------------------ */ - -#ifndef ulong -# define ulong unsigned long -#endif -#ifndef uint -# define uint unsigned int -#endif - -#ifndef BOOTLOADER_CAN_EXIT -# define BOOTLOADER_CAN_EXIT 0 -#endif - -/* allow compatibility with avrusbboot's bootloaderconfig.h: */ -#ifdef BOOTLOADER_INIT -# define bootLoaderInit() BOOTLOADER_INIT -# define bootLoaderExit() -#endif -#ifdef BOOTLOADER_CONDITION -# define bootLoaderCondition() BOOTLOADER_CONDITION -#endif - -/* device compatibility: */ -#ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */ -# define GICR MCUCR -#endif - -/* ------------------------------------------------------------------------ */ - -#define addr_t uint - -// typedef union longConverter{ -// addr_t l; -// uint w[sizeof(addr_t)/2]; -// uchar b[sizeof(addr_t)]; -// } longConverter_t; - -//////// Stuff Bluebie Added // postscript are the few bytes at the end of programmable memory which store tinyVectors // and used to in USBaspLoader-tiny85 store the checksum iirc #define POSTSCRIPT_SIZE 6 @@ -88,72 +52,63 @@ static void leaveBootloader() __attribute__((__noreturn__)); // events system schedules functions to run in the main loop static uchar events = 0; // bitmap of events to run #define EVENT_ERASE_APPLICATION 1 -#define EVENT_WRITE_PAGE 2 -#define EVENT_EXECUTE 4 +#define EVENT_WRITE_PAGE 2 +#define EVENT_EXECUTE 4 // controls state of events #define fireEvent(event) events |= (event) #define isEvent(event) (events & (event)) #define clearEvents() events = 0 -// length of bytes to write in to flash memory in upcomming usbFunctionWrite calls -//static unsigned char writeLength; - -// becomes 1 when some programming happened -// lets leaveBootloader know if needs to finish up the programming -static uchar didWriteSomething = 0; +// Definition of sei and cli without memory barrier keyword to prevent reloading of memory variables +#define sei() __asm__ __volatile__ ("sei") +#define cli() __asm__ __volatile__ ("cli") uint16_t idlePolls = 0; // how long have we been idle? - - static uint16_t vectorTemp[2]; // remember data to create tinyVector table before BOOTLOADER_ADDRESS -static addr_t currentAddress; // current progmem address, used for erasing and writing +static uint16_t currentAddress; // current progmem address, used for erasing and writing +#if OSCCAL_RESTORE + static uint8_t osccal_default; // due to compiler insanity, having this as global actually saves memory +#endif /* ------------------------------------------------------------------------ */ static inline void eraseApplication(void); static void writeFlashPage(void); static void writeWordToPageBuffer(uint16_t data); -static void fillFlashWithVectors(void); static uchar usbFunctionSetup(uchar data[8]); static uchar usbFunctionWrite(uchar *data, uchar length); -static inline void initForUsbConnectivity(void); -static inline void tiny85FlashInit(void); -static inline void tiny85FlashWrites(void); -//static inline void tiny85FinishWriting(void); static inline void leaveBootloader(void); // erase any existing application and write in jumps for usb interrupt and reset to bootloader // - Because flash can be erased once and programmed several times, we can write the bootloader // - vectors in now, and write in the application stuff around them later. -// - if vectors weren't written back in immidately, usb would fail. +// - if vectors weren't written back in immediately, usb would fail. static inline void eraseApplication(void) { // erase all pages until bootloader, in reverse order (so our vectors stay in place for as long as possible) // while the vectors don't matter for usb comms as interrupts are disabled during erase, it's important // to minimise the chance of leaving the device in a state where the bootloader wont run, if there's power failure // during upload - currentAddress = BOOTLOADER_ADDRESS; + + uint8_t i; + uint16_t ptr = BOOTLOADER_ADDRESS; cli(); - while (currentAddress) { - currentAddress -= SPM_PAGESIZE; - - boot_page_erase(currentAddress); - boot_spm_busy_wait(); + while (ptr) { + ptr -= SPM_PAGESIZE; + boot_page_erase(ptr); } - fillFlashWithVectors(); - sei(); + currentAddress = 0; + for (i=0; i<8; i++) writeWordToPageBuffer(0xFFFF); // Write first 8 words to fill in vectors. + writeFlashPage(); // enables interrupts } // simply write currently stored page in to already erased flash memory static void writeFlashPage(void) { - uint8_t previous_sreg = SREG; // backup current interrupt setting - didWriteSomething = 1; cli(); - boot_page_write(currentAddress - 2); - boot_spm_busy_wait(); // Wait until the memory is written. - SREG = previous_sreg; // restore interrupts to previous state + boot_page_write(currentAddress - 2); // will halt CPU, no waiting required + sei(); } // clear memory which stores data to be written by next writeFlashPage call @@ -174,10 +129,17 @@ static void writeWordToPageBuffer(uint16_t data) { uint8_t previous_sreg; // first two interrupt vectors get replaced with a jump to the bootloader's vector table - if (currentAddress == (RESET_VECTOR_OFFSET * 2) || currentAddress == (USBPLUS_VECTOR_OFFSET * 2)) { - data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; - } - + // remember vectors or the tinyvector table + if (currentAddress == RESET_VECTOR_OFFSET * 2) { + vectorTemp[0] = data; + data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; + } + + if (currentAddress == USBPLUS_VECTOR_OFFSET * 2) { + vectorTemp[1] = data; + data = 0xC000 + (BOOTLOADER_ADDRESS/2) - 1; + } + // at end of page just before bootloader, write in tinyVector table // see http://embedded-creations.com/projects/attiny85-usb-bootloader-overview/avr-jtag-programmer/ // for info on how the tiny vector table works @@ -185,58 +147,34 @@ static void writeWordToPageBuffer(uint16_t data) { data = vectorTemp[0] + ((FLASHEND + 1) - BOOTLOADER_ADDRESS)/2 + 2 + RESET_VECTOR_OFFSET; } else if (currentAddress == BOOTLOADER_ADDRESS - TINYVECTOR_USBPLUS_OFFSET) { data = vectorTemp[1] + ((FLASHEND + 1) - BOOTLOADER_ADDRESS)/2 + 1 + USBPLUS_VECTOR_OFFSET; +#if (!OSCCAL_RESTORE) && OSCCAL_16_5MHz } else if (currentAddress == BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET) { data = OSCCAL; +#endif } - + + previous_sreg=SREG; + cli(); // ensure interrupts are disabled // clear page buffer as a precaution before filling the buffer on the first page // in case the bootloader somehow ran after user program and there was something // in the page buffer already if (currentAddress == 0x0000) __boot_page_fill_clear(); - - previous_sreg = SREG; // backup previous interrupt settings - cli(); // ensure interrupts are disabled boot_page_fill(currentAddress, data); - SREG = previous_sreg; // restore previous interrupt setting - - // only need to erase if there is data already in the page that doesn't match what we're programming - // TODO: what about this: if (pgm_read_word(currentAddress) & data != data) { ??? should work right? - //if (pgm_read_word(currentAddress) != data && pgm_read_word(currentAddress) != 0xFFFF) { - //if ((pgm_read_word(currentAddress) & data) != data) { - // fireEvent(EVENT_PAGE_NEEDS_ERASE); - //} // increment progmem address by one word currentAddress += 2; -} - -// fills the rest of this page with vectors - interrupt vector or tinyvector tables where needed -static void fillFlashWithVectors(void) { - //int16_t i; - // - // fill all or remainder of page with 0xFFFF (as if unprogrammed) - //for (i = currentAddress % SPM_PAGESIZE; i < SPM_PAGESIZE; i += 2) { - // writeWordToPageBuffer(0xFFFF); // is where vector tables are sorted out - //} - - // TODO: Or more simply: - do { - writeWordToPageBuffer(0xFFFF); - } while (currentAddress % SPM_PAGESIZE); - - writeFlashPage(); + SREG=previous_sreg; } /* ------------------------------------------------------------------------ */ - static uchar usbFunctionSetup(uchar data[8]) { usbRequest_t *rq = (void *)data; - idlePolls = 0; // reset idle polls when we get usb traffic - + ((uint8_t*)&idlePolls)[1] = 0; // reset idle polls when we get usb traffic + static uchar replyBuffer[4] = { - (((uint)PROGMEM_SIZE) >> 8) & 0xff, - ((uint)PROGMEM_SIZE) & 0xff, + (((uint16_t)PROGMEM_SIZE) >> 8) & 0xff, + ((uint16_t)PROGMEM_SIZE) & 0xff, SPM_PAGESIZE, MICRONUCLEUS_WRITE_SLEEP }; @@ -246,9 +184,7 @@ static uchar usbFunctionSetup(uchar data[8]) { return 4; } else if (rq->bRequest == 1) { // write page - //writeLength = rq->wValue.word; - currentAddress = rq->wIndex.word; - + currentAddress = rq->wIndex.word; return USB_NO_MSG; // hands off work to usbFunctionWrite } else if (rq->bRequest == 2) { // erase application @@ -263,27 +199,11 @@ static uchar usbFunctionSetup(uchar data[8]) { return 0; } - // read in a page over usb, and write it in to the flash write buffer static uchar usbFunctionWrite(uchar *data, uchar length) { - //if (length > writeLength) length = writeLength; // test for missing final page bug - //writeLength -= length; - - do { - // remember vectors or the tinyvector table - if (currentAddress == RESET_VECTOR_OFFSET * 2) { - vectorTemp[0] = *(short *)data; - } - - if (currentAddress == USBPLUS_VECTOR_OFFSET * 2) { - vectorTemp[1] = *(short *)data; - } - + do { // make sure we don't write over the bootloader! - if (currentAddress >= BOOTLOADER_ADDRESS) { - //__boot_page_fill_clear(); - break; - } + if (currentAddress >= BOOTLOADER_ADDRESS) break; writeWordToPageBuffer(*(uint16_t *) data); data += 2; // advance data pointer @@ -291,8 +211,13 @@ static uchar usbFunctionWrite(uchar *data, uchar length) { } while(length); // if we have now reached another page boundary, we're done - //uchar isLast = (writeLength == 0); +#if SPM_PAGESIZE<256 + // Hack to reduce code size + uchar isLast = ((((uchar)currentAddress) % SPM_PAGESIZE) == 0); +#else uchar isLast = ((currentAddress % SPM_PAGESIZE) == 0); +#endif + // definitely need this if! seems usbFunctionWrite gets called again in future usbPoll's in the runloop! if (isLast) fireEvent(EVENT_WRITE_PAGE); // ask runloop to write our page @@ -300,7 +225,6 @@ static uchar usbFunctionWrite(uchar *data, uchar length) { } /* ------------------------------------------------------------------------ */ - void PushMagicWord (void) __attribute__ ((naked)) __attribute__ ((section (".init3"))); // put the word "B007" at the bottom of the stack (RAMEND - RAMEND-1) @@ -312,97 +236,53 @@ void PushMagicWord (void) { } /* ------------------------------------------------------------------------ */ - -static inline void initForUsbConnectivity(void) { - usbInit(); - /* enforce USB re-enumerate: */ - usbDeviceDisconnect(); /* do this while interrupts are disabled */ - _delay_ms(500); - usbDeviceConnect(); - sei(); -} - -static inline void tiny85FlashInit(void) { - // check for erased first page (no bootloader interrupt vectors), add vectors if missing - // this needs to happen for usb communication to work later - essential to first run after bootloader - // being installed - if(pgm_read_word(RESET_VECTOR_OFFSET * 2) != 0xC000 + (BOOTLOADER_ADDRESS/2) - 1 || - pgm_read_word(USBPLUS_VECTOR_OFFSET * 2) != 0xC000 + (BOOTLOADER_ADDRESS/2) - 1) { - - fillFlashWithVectors(); - } - - // TODO: necessary to reset currentAddress? - currentAddress = 0; -} - -static inline void tiny85FlashWrites(void) { - _delay_us(2000); // TODO: why is this here? - it just adds pointless two level deep loops seems like? - // write page to flash, interrupts will be disabled for > 4.5ms including erase - - // TODO: Do we need this? Wouldn't the programmer always send full sized pages? - if (currentAddress % SPM_PAGESIZE) { // when we aren't perfectly aligned to a flash page boundary - fillFlashWithVectors(); // fill up the rest of the page with 0xFFFF (unprogrammed) bits - } else { - writeFlashPage(); // otherwise just write it - } -} - -// finishes up writing to the flash, including adding the tinyVector tables at the end of memory -// TODO: can this be simplified? EG: currentAddress = PROGMEM_SIZE; fillFlashWithVectors(); -// static inline void tiny85FinishWriting(void) { -// // make sure remainder of flash is erased and write checksum and application reset vectors -// if (didWriteSomething) { -// while (currentAddress < BOOTLOADER_ADDRESS) { -// fillFlashWithVectors(); -// } -// } -// } - // reset system to a normal state and launch user program static inline void leaveBootloader(void) { _delay_ms(10); // removing delay causes USB errors - //DBG1(0x01, 0, 0); bootLoaderExit(); cli(); + usbDeviceDisconnect(); /* Disconnect micronucleus */ + USB_INTR_ENABLE = 0; USB_INTR_CFG = 0; /* also reset config bits */ // clear magic word from bottom of stack before jumping to the app - *(uint8_t*)(RAMEND) = 0x00; - *(uint8_t*)(RAMEND-1) = 0x00; + *(uint8_t*)(RAMEND) = 0x00; // A single write is sufficient to invalidate magic word +#if (!OSCCAL_RESTORE) && OSCCAL_16_5MHz // adjust clock to previous calibration value, so user program always starts with same calibration // as when it was uploaded originally - // TODO: Test this and find out, do we need the +1 offset? unsigned char stored_osc_calibration = pgm_read_byte(BOOTLOADER_ADDRESS - TINYVECTOR_OSCCAL_OFFSET); if (stored_osc_calibration != 0xFF && stored_osc_calibration != 0x00) { - //OSCCAL = stored_osc_calibration; // this should really be a gradual change, but maybe it's alright anyway? - // do the gradual change - failed to score extra free bytes anyway in 1.06 - while (OSCCAL > stored_osc_calibration) OSCCAL--; - while (OSCCAL < stored_osc_calibration) OSCCAL++; + OSCCAL=stored_osc_calibration; + asm volatile("nop"); } - +#endif // jump to application reset vector at end of flash asm volatile ("rjmp __vectors - 4"); } int main(void) { /* initialize */ - #ifdef RESTORE_OSCCAL - uint8_t osccal_default = OSCCAL; + #if OSCCAL_RESTORE + osccal_default = OSCCAL; #endif #if (!SET_CLOCK_PRESCALER) && LOW_POWER_MODE uint8_t prescaler_default = CLKPR; #endif - - wdt_disable(); /* main app may have enabled watchdog */ - tiny85FlashInit(); + bootLoaderInit(); - - + +# if AUTO_EXIT_NO_USB_MS + ((uint8_t*)&idlePolls)[1]=((AUTO_EXIT_MS-AUTO_EXIT_NO_USB_MS) * 10UL)>>8; // write only high byte to save 6 bytes +# endif + if (bootLoaderStartCondition()) { + + MCUSR=0; /* need this to properly disable watchdog */ + wdt_disable(); + #if LOW_POWER_MODE // turn off clock prescalling - chip must run at full speed for usb // if you might run chip at lower voltages, detect that in bootLoaderStartCondition @@ -410,26 +290,38 @@ int main(void) { CLKPR = 0; #endif - initForUsbConnectivity(); +# if LED_PRESENT + LED_INIT(); +# endif + + usbDeviceDisconnect(); /* do this while interrupts are disabled */ + _delay_ms(500); + usbDeviceConnect(); + usbInit(); // Initialize INT settings after reconnect + sei(); + do { - usbPoll(); + usbPoll(); _delay_us(100); - idlePolls++; // these next two freeze the chip for ~ 4.5ms, breaking usb protocol // and usually both of these will activate in the same loop, so host // needs to wait > 9ms before next usb request if (isEvent(EVENT_ERASE_APPLICATION)) eraseApplication(); - if (isEvent(EVENT_WRITE_PAGE)) tiny85FlashWrites(); - -# if BOOTLOADER_CAN_EXIT - if (isEvent(EVENT_EXECUTE)) { // when host requests device run uploaded program - break; - } -# endif - + if (isEvent(EVENT_WRITE_PAGE)) { + _delay_us(2000); // Wait for USB traffic to finish before halting CPU with write- + writeFlashPage(); + } + +# if BOOTLOADER_CAN_EXIT + if (isEvent(EVENT_EXECUTE)) break; // when host requests device run uploaded program +# endif clearEvents(); - + +# if LED_PRESENT + LED_MACRO( ((uint8_t*)&idlePolls)[1] ) +# endif + } while(bootLoaderCondition()); /* main event loop runs so long as bootLoaderCondition remains truthy */ } @@ -444,11 +336,15 @@ int main(void) { #endif #endif - // slowly bring down OSCCAL to it's original value before launching in to user program - #ifdef RESTORE_OSCCAL - while (OSCCAL > osccal_default) { OSCCAL -= 1; } - #endif +# if LED_PRESENT + LED_EXIT(); +# endif + +# if OSCCAL_RESTORE + OSCCAL=osccal_default; + asm volatile("nop"); // NOP to avoid CPU hickup during oscillator stabilization +# endif + leaveBootloader(); } - /* ------------------------------------------------------------------------ */ |