summaryrefslogtreecommitdiffstats
path: root/firmware/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/main.c')
-rw-r--r--firmware/main.c320
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();
}
-
/* ------------------------------------------------------------------------ */