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();  } -  /* ------------------------------------------------------------------------ */ | 
