aboutsummaryrefslogtreecommitdiff
path: root/bootloaders/optiboot/optiboot.c
diff options
context:
space:
mode:
authorDavid A. Mellis <d.mellis@arduino.cc>2010-09-22 09:04:33 -0400
committerDavid A. Mellis <d.mellis@arduino.cc>2010-09-22 09:04:33 -0400
commit8980259e0d8cfd11271bfa4ada54ad952dbc4085 (patch)
treeefcc2879e63a1640065e64949ab6da9f241d1225 /bootloaders/optiboot/optiboot.c
parentf9a3b5cfa9b591096b80a7541323c4319da2d32d (diff)
Adding optiboot and Arduino Uno boards menu entry.
Diffstat (limited to 'bootloaders/optiboot/optiboot.c')
-rw-r--r--bootloaders/optiboot/optiboot.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/bootloaders/optiboot/optiboot.c b/bootloaders/optiboot/optiboot.c
new file mode 100644
index 0000000..af92995
--- /dev/null
+++ b/bootloaders/optiboot/optiboot.c
@@ -0,0 +1,536 @@
+/**********************************************************/
+/* Optiboot bootloader for Arduino */
+/* */
+/* Heavily optimised bootloader that is faster and */
+/* smaller than the Arduino standard bootloader */
+/* */
+/* Enhancements: */
+/* Fits in 512 bytes, saving 1.5K of code space */
+/* Background page erasing speeds up programming */
+/* Higher baud rate speeds up programming */
+/* Written almost entirely in C */
+/* Customisable timeout with accurate timeconstant */
+/* */
+/* What you lose: */
+/* Implements a skeleton STK500 protocol which is */
+/* missing several features including EEPROM */
+/* programming and non-page-aligned writes */
+/* High baud rate breaks compatibility with standard */
+/* Arduino flash settings */
+/* */
+/* Currently supports: */
+/* ATmega168 based devices (Diecimila etc) */
+/* ATmega328P based devices (Duemilanove etc) */
+/* */
+/* Does not support: */
+/* ATmega1280 based devices (eg. Mega) */
+/* */
+/* Assumptions: */
+/* The code makes several assumptions that reduce the */
+/* code size. They are all true after a hardware reset, */
+/* but may not be true if the bootloader is called by */
+/* other means or on other hardware. */
+/* No interrupts can occur */
+/* UART and Timer 1 are set to their reset state */
+/* SP points to RAMEND */
+/* */
+/* Code builds on code, libraries and optimisations from: */
+/* stk500boot.c by Jason P. Kyle */
+/* Arduino bootloader http://arduino.cc */
+/* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
+/* avr-libc project http://nongnu.org/avr-libc */
+/* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
+/* AVR305 Atmel Application Note */
+/* */
+/* This program is free software; you can redistribute it */
+/* and/or modify it under the terms of the GNU General */
+/* Public License as published by the Free Software */
+/* Foundation; either version 2 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will */
+/* be useful, but WITHOUT ANY WARRANTY; without even the */
+/* implied warranty of MERCHANTABILITY or FITNESS FOR A */
+/* PARTICULAR PURPOSE. See the GNU General Public */
+/* License for more details. */
+/* */
+/* You should have received a copy of the GNU General */
+/* Public License along with this program; if not, write */
+/* to the Free Software Foundation, Inc., */
+/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/* */
+/* Licence can be viewed at */
+/* http://www.fsf.org/licenses/gpl.txt */
+/* */
+/**********************************************************/
+
+#include <inttypes.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/boot.h>
+
+//#define LED_DATA_FLASH
+
+#ifndef LED_START_FLASHES
+#define LED_START_FLASHES 0
+#endif
+
+/* Build-time variables */
+/* BAUD_RATE Programming baud rate */
+/* LED_NO_FLASHES Number of LED flashes on boot */
+/* FLASH_TIME_MS Duration of each LED flash */
+/* BOOT_TIMEOUT_MS Serial port wait time before exiting bootloader */
+
+/* set the UART baud rate */
+#ifndef BAUD_RATE
+#define BAUD_RATE 19200
+#endif
+
+#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
+/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */
+#define LED_DDR DDRB
+#define LED_PORT PORTB
+#define LED_PIN PINB
+#define LED PINB5
+
+/* Ports for soft UART */
+#ifdef SOFT_UART
+#define UART_PORT PORTD
+#define UART_PIN PIND
+#define UART_DDR DDRD
+#define UART_TX_BIT 1
+#define UART_RX_BIT 0
+#endif
+#endif
+
+#if defined(__AVR_ATtiny84__)
+/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */
+#define LED_DDR DDRA
+#define LED_PORT PORTA
+#define LED_PIN PINA
+#define LED PINA4
+
+/* Ports for soft UART - left port only for now*/
+#ifdef SOFT_UART
+#define UART_PORT PORTA
+#define UART_PIN PINA
+#define UART_DDR DDRA
+#define UART_TX_BIT 2
+#define UART_RX_BIT 3
+#endif
+#endif
+
+/* STK500 constants list, from AVRDUDE */
+#define STK_OK 0x10
+#define STK_FAILED 0x11 // Not used
+#define STK_UNKNOWN 0x12 // Not used
+#define STK_NODEVICE 0x13 // Not used
+#define STK_INSYNC 0x14 // ' '
+#define STK_NOSYNC 0x15 // Not used
+#define ADC_CHANNEL_ERROR 0x16 // Not used
+#define ADC_MEASURE_OK 0x17 // Not used
+#define PWM_CHANNEL_ERROR 0x18 // Not used
+#define PWM_ADJUST_OK 0x19 // Not used
+#define CRC_EOP 0x20 // 'SPACE'
+#define STK_GET_SYNC 0x30 // '0'
+#define STK_GET_SIGN_ON 0x31 // '1'
+#define STK_SET_PARAMETER 0x40 // '@'
+#define STK_GET_PARAMETER 0x41 // 'A'
+#define STK_SET_DEVICE 0x42 // 'B'
+#define STK_SET_DEVICE_EXT 0x45 // 'E'
+#define STK_ENTER_PROGMODE 0x50 // 'P'
+#define STK_LEAVE_PROGMODE 0x51 // 'Q'
+#define STK_CHIP_ERASE 0x52 // 'R'
+#define STK_CHECK_AUTOINC 0x53 // 'S'
+#define STK_LOAD_ADDRESS 0x55 // 'U'
+#define STK_UNIVERSAL 0x56 // 'V'
+#define STK_PROG_FLASH 0x60 // '`'
+#define STK_PROG_DATA 0x61 // 'a'
+#define STK_PROG_FUSE 0x62 // 'b'
+#define STK_PROG_LOCK 0x63 // 'c'
+#define STK_PROG_PAGE 0x64 // 'd'
+#define STK_PROG_FUSE_EXT 0x65 // 'e'
+#define STK_READ_FLASH 0x70 // 'p'
+#define STK_READ_DATA 0x71 // 'q'
+#define STK_READ_FUSE 0x72 // 'r'
+#define STK_READ_LOCK 0x73 // 's'
+#define STK_READ_PAGE 0x74 // 't'
+#define STK_READ_SIGN 0x75 // 'u'
+#define STK_READ_OSCCAL 0x76 // 'v'
+#define STK_READ_FUSE_EXT 0x77 // 'w'
+#define STK_READ_OSCCAL_EXT 0x78 // 'x'
+
+/* Watchdog settings */
+#define WATCHDOG_OFF (0)
+#define WATCHDOG_16MS (_BV(WDE))
+#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
+#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
+#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
+#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
+#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
+#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
+#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
+#define WATCHDOG_4S (_BV(WDE3) | _BV(WDE))
+#define WATCHDOG_8S (_BV(WDE3) | _BV(WDE0) | _BV(WDE))
+
+/* Function Prototypes */
+/* The main function is in init9, which removes the interrupt vector table */
+/* we don't need. It is also 'naked', which means the compiler does not */
+/* generate any entry or exit code itself. */
+int main(void) __attribute__ ((naked)) __attribute__ ((section (".init9")));
+void putch(char);
+uint8_t getch(void);
+static inline void getNch(uint8_t); /* "static inline" is a compiler hint to reduce code size */
+void verifySpace();
+static inline void flash_led(uint8_t);
+uint8_t getLen();
+static inline void watchdogReset();
+void watchdogConfig(uint8_t x);
+#ifdef SOFT_UART
+void uartDelay() __attribute__ ((naked));
+#endif
+void appStart() __attribute__ ((naked));
+
+/* C zero initialises all global variables. However, that requires */
+/* These definitions are NOT zero initialised, but that doesn't matter */
+/* This allows us to drop the zero init code, saving us memory */
+#define buff ((uint8_t*)(0x100))
+#define address (*(uint16_t*)(0x200))
+#define length (*(uint8_t*)(0x202))
+#ifdef VIRTUAL_BOOT_PARTITION
+#define rstVect (*(uint16_t*)(0x204))
+#define wdtVect (*(uint16_t*)(0x206))
+#endif
+/* main program starts here */
+int main(void) {
+ // After the zero init loop, this is the first code to run.
+ //
+ // This code makes the following assumptions:
+ // No interrupts will execute
+ // SP points to RAMEND
+ // r1 contains zero
+ //
+ // If not, uncomment the following instructions:
+ // cli();
+ // SP=RAMEND; // This is done by hardware reset
+ // asm volatile ("clr __zero_reg__");
+
+ uint8_t ch;
+
+#if LED_START_FLASHES > 0
+ // Set up Timer 1 for timeout counter
+ TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
+#endif
+#ifndef SOFT_UART
+ UCSR0A = _BV(U2X0); //Double speed mode USART0
+ UCSR0B = _BV(RXEN0) | _BV(TXEN0);
+ UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
+ UBRR0L = (uint8_t)( (F_CPU + BAUD_RATE * 4L) / (BAUD_RATE * 8L) - 1 );
+#endif
+
+ // Adaboot no-wait mod
+ ch = MCUSR;
+ MCUSR = 0;
+ if (!(ch & _BV(EXTRF))) appStart();
+
+ // Set up watchdog to trigger after 500ms
+ watchdogConfig(WATCHDOG_500MS);
+
+ /* Set LED pin as output */
+ LED_DDR |= _BV(LED);
+
+#ifdef SOFT_UART
+ /* Set TX pin as output */
+ UART_DDR |= _BV(UART_TX_BIT);
+#endif
+
+#if LED_START_FLASHES > 0
+ /* Flash onboard LED to signal entering of bootloader */
+ flash_led(LED_START_FLASHES * 2);
+#endif
+
+ /* Forever loop */
+ for (;;) {
+ /* get character from UART */
+ ch = getch();
+
+ if(ch == STK_GET_PARAMETER) {
+ // GET PARAMETER returns a generic 0x03 reply - enough to keep Avrdude happy
+ getNch(1);
+ putch(0x03);
+ }
+ else if(ch == STK_SET_DEVICE) {
+ // SET DEVICE is ignored
+ getNch(20);
+ }
+ else if(ch == STK_SET_DEVICE_EXT) {
+ // SET DEVICE EXT is ignored
+ getNch(5);
+ }
+ else if(ch == STK_LOAD_ADDRESS) {
+ // LOAD ADDRESS
+ address = getch();
+ address = (address & 0xff) | (getch() << 8);
+ address += address; // Convert from word address to byte address
+ verifySpace();
+ }
+ else if(ch == STK_UNIVERSAL) {
+ // UNIVERSAL command is ignored
+ getNch(4);
+ putch(0x00);
+ }
+ /* Write memory, length is big endian and is in bytes */
+ else if(ch == STK_PROG_PAGE) {
+ // PROGRAM PAGE - we support flash programming only, not EEPROM
+ uint8_t *bufPtr;
+ uint16_t addrPtr;
+
+ getLen();
+
+ // Immediately start page erase - this will 4.5ms
+ boot_page_erase((uint16_t)(void*)address);
+
+ // While that is going on, read in page contents
+ bufPtr = buff;
+ do *bufPtr++ = getch();
+ while (--length);
+
+ // Read command terminator, start reply
+ verifySpace();
+
+ // If only a partial page is to be programmed, the erase might not be complete.
+ // So check that here
+ boot_spm_busy_wait();
+
+#ifdef VIRTUAL_BOOT_PARTITION
+ if ((uint16_t)(void*)address == 0) {
+ // This is the reset vector page. We need to live-patch the code so the
+ // bootloader runs.
+ //
+ // Move RESET vector to WDT vector
+ uint16_t vect = buff[0] | (buff[1]<<8);
+ rstVect = vect;
+ wdtVect = buff[10] | (buff[11]<<8);
+ vect -= 4; // Instruction is a relative jump (rjmp), so recalculate.
+ buff[10] = vect & 0xff;
+ buff[11] = vect >> 8;
+
+ // Add jump to bootloader at RESET vector
+ buff[0] = 0x7f;
+ buff[1] = 0xce; // rjmp 0x1d00 instruction
+ }
+#endif
+
+ // Copy buffer into programming buffer
+ bufPtr = buff;
+ addrPtr = (uint16_t)(void*)address;
+ ch = SPM_PAGESIZE / 2;
+ do {
+ uint16_t a;
+ a = *bufPtr++;
+ a |= (*bufPtr++) << 8;
+ boot_page_fill((uint16_t)(void*)addrPtr,a);
+ addrPtr += 2;
+ } while (--ch);
+
+ // Write from programming buffer
+ boot_page_write((uint16_t)(void*)address);
+ boot_spm_busy_wait();
+
+#if defined(RWWSRE)
+ // Reenable read access to flash
+ boot_rww_enable();
+#endif
+
+ }
+ /* Read memory block mode, length is big endian. */
+ else if(ch == STK_READ_PAGE) {
+ // READ PAGE - we only read flash
+ getLen();
+ verifySpace();
+#ifdef VIRTUAL_BOOT_PARTITION
+ do {
+ // Undo vector patch in bottom page so verify passes
+ if (address == 0) ch=rstVect & 0xff;
+ else if (address == 1) ch=rstVect >> 8;
+ else if (address == 10) ch=wdtVect & 0xff;
+ else if (address == 11) ch=wdtVect >> 8;
+ else ch = pgm_read_byte_near(address);
+ address++;
+ putch(ch);
+ } while (--length);
+#else
+ do putch(pgm_read_byte_near(address++));
+ while (--length);
+#endif
+ }
+
+ /* Get device signature bytes */
+ else if(ch == STK_READ_SIGN) {
+ // READ SIGN - return what Avrdude wants to hear
+ verifySpace();
+ putch(SIGNATURE_0);
+ putch(SIGNATURE_1);
+ putch(SIGNATURE_2);
+ }
+ else if (ch == 'Q') {
+ // Adaboot no-wait mod
+ watchdogConfig(WATCHDOG_16MS);
+ verifySpace();
+ }
+ else {
+ // This covers the response to commands like STK_ENTER_PROGMODE
+ verifySpace();
+ }
+ putch(STK_OK);
+ }
+}
+
+void putch(char ch) {
+#ifndef SOFT_UART
+ while (!(UCSR0A & _BV(UDRE0)));
+ UDR0 = ch;
+#else
+ __asm__ __volatile__ (
+ " com %[ch]\n" // ones complement, carry set
+ " sec\n"
+ "1: brcc 2f\n"
+ " cbi %[uartPort],%[uartBit]\n"
+ " rjmp 3f\n"
+ "2: sbi %[uartPort],%[uartBit]\n"
+ " nop\n"
+ "3: rcall uartDelay\n"
+ " rcall uartDelay\n"
+ " lsr %[ch]\n"
+ " dec %[bitcnt]\n"
+ " brne 1b\n"
+ :
+ :
+ [bitcnt] "d" (10),
+ [ch] "r" (ch),
+ [uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
+ [uartBit] "I" (UART_TX_BIT)
+ :
+ "r25"
+ );
+#endif
+}
+
+uint8_t getch(void) {
+ uint8_t ch;
+
+ watchdogReset();
+
+#ifdef LED_DATA_FLASH
+ LED_PIN |= _BV(LED);
+#endif
+
+#ifdef SOFT_UART
+ __asm__ __volatile__ (
+ "1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge
+ " rjmp 1b\n"
+ " rcall uartDelay\n" // Get to middle of start bit
+ "2: rcall uartDelay\n" // Wait 1 bit period
+ " rcall uartDelay\n" // Wait 1 bit period
+ " clc\n"
+ " sbic %[uartPin],%[uartBit]\n"
+ " sec\n"
+ " dec %[bitCnt]\n"
+ " breq 3f\n"
+ " ror %[ch]\n"
+ " rjmp 2b\n"
+ "3:\n"
+ :
+ [ch] "=r" (ch)
+ :
+ [bitCnt] "d" (9),
+ [uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
+ [uartBit] "I" (UART_RX_BIT)
+ :
+ "r25"
+);
+#else
+ while(!(UCSR0A & _BV(RXC0)));
+ ch = UDR0;
+#endif
+
+#ifdef LED_DATA_FLASH
+ LED_PIN |= _BV(LED);
+#endif
+
+ return ch;
+}
+
+#ifdef SOFT_UART
+//#define UART_B_VALUE (((F_CPU/BAUD_RATE)-23)/6)
+#define UART_B_VALUE (((F_CPU/BAUD_RATE)-20)/6)
+#if UART_B_VALUE > 255
+#error Baud rate too slow for soft UART
+#endif
+
+void uartDelay() {
+ __asm__ __volatile__ (
+ "ldi r25,%[count]\n"
+ "1:dec r25\n"
+ "brne 1b\n"
+ "ret\n"
+ ::[count] "M" (UART_B_VALUE)
+ );
+}
+#endif
+
+void getNch(uint8_t count) {
+ do getch(); while (--count);
+ verifySpace();
+}
+
+void verifySpace() {
+ if (getch() != CRC_EOP) appStart();
+ putch(STK_INSYNC);
+}
+
+#if LED_START_FLASHES > 0
+void flash_led(uint8_t count) {
+ do {
+ TCNT1 = -(F_CPU/(1024*16));
+ TIFR1 = _BV(TOV1);
+ while(!(TIFR1 & _BV(TOV1)));
+ LED_PIN |= _BV(LED);
+ watchdogReset();
+ } while (--count);
+}
+#endif
+
+uint8_t getLen() {
+ getch();
+ length = getch();
+ return getch();
+}
+
+// Watchdog functions. These are only safe with interrupts turned off.
+void watchdogReset() {
+ __asm__ __volatile__ (
+ "wdr\n"
+ );
+}
+
+void watchdogConfig(uint8_t x) {
+ WDTCSR = _BV(WDCE) | _BV(WDE);
+ WDTCSR = x;
+}
+
+void appStart() {
+ watchdogConfig(WATCHDOG_OFF);
+ __asm__ __volatile__ (
+#ifdef VIRTUAL_BOOT_PARTITION
+ // Jump to WDT vector
+ "ldi r30,5\n"
+ "clr r31\n"
+#else
+ // Jump to RST vector
+ "clr r30\n"
+ "clr r31\n"
+#endif
+ "ijmp\n"
+ );
+}