|
@@ -1,3 +1,5 @@
|
|
|
+#define FUNC_READ 1
|
|
|
+#define FUNC_WRITE 1
|
|
|
|
|
|
|
|
|
|
|
@@ -94,16 +96,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
@@ -151,6 +150,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
@@ -195,38 +212,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
-#define OPTIBOOT_MAJVER 5
|
|
|
+#define OPTIBOOT_MAJVER 6
|
|
|
#define OPTIBOOT_MINVER 0
|
|
|
|
|
|
+
|
|
|
+ * OPTIBOOT_CUSTOMVER should be defined (by the makefile) for custom edits
|
|
|
+ * of optiboot. That way you don't wind up with very different code that
|
|
|
+ * matches the version number of a "released" optiboot.
|
|
|
+ */
|
|
|
+
|
|
|
+#if !defined(OPTIBOOT_CUSTOMVER)
|
|
|
+#define OPTIBOOT_CUSTOMVER 0
|
|
|
+#endif
|
|
|
+
|
|
|
+#if 0
|
|
|
#define MAKESTR(a) #a
|
|
|
#define MAKEVER(a, b) MAKESTR(a*256+b)
|
|
|
|
|
|
asm(" .section .version\n"
|
|
|
"optiboot_version: .word " MAKEVER(OPTIBOOT_MAJVER, OPTIBOOT_MINVER) "\n"
|
|
|
" .section .text\n");
|
|
|
+#else
|
|
|
+unsigned int __attribute__((section(".version")))
|
|
|
+optiboot_version = 256*OPTIBOOT_MAJVER + OPTIBOOT_MINVER + OPTIBOOT_CUSTOMVER;
|
|
|
+#endif
|
|
|
+
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
#include <avr/io.h>
|
|
|
#include <avr/pgmspace.h>
|
|
|
+#include <avr/eeprom.h>
|
|
|
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+ * Note that we use our own version of "boot.h"
|
|
|
+ * <avr/boot.h> uses sts instructions, but this version uses out instructions
|
|
|
+ * This saves cycles and program memory. Sorry for the name overlap.
|
|
|
+ */
|
|
|
#include "boot.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
+ * pin_defs.h
|
|
|
+ * This contains most of the rather ugly defines that implement our
|
|
|
+ * ability to use UART=n and LED=D3, and some avr family bit name differences.
|
|
|
+ */
|
|
|
#include "pin_defs.h"
|
|
|
+
|
|
|
+
|
|
|
+ * stk500.h contains the constant definitions for the stk500v1 comm protocol
|
|
|
+ */
|
|
|
#include "stk500.h"
|
|
|
|
|
|
#ifndef LED_START_FLASHES
|
|
|
#define LED_START_FLASHES 0
|
|
|
#endif
|
|
|
|
|
|
-#ifdef LUDICROUS_SPEED
|
|
|
-#define BAUD_RATE 230400L
|
|
|
-#endif
|
|
|
-
|
|
|
|
|
|
#ifndef BAUD_RATE
|
|
|
#if F_CPU >= 8000000L
|
|
@@ -297,63 +339,48 @@ asm(" .section .version\n"
|
|
|
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
|
|
|
#endif
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+ * The main() function is in init9, which removes the interrupt vector table
|
|
|
+ * we don't need. It is also 'OS_main', which means the compiler does not
|
|
|
+ * generate any entry or exit code itself (but unlike 'naked', it doesn't
|
|
|
+ * supress some compile-time options we want.)
|
|
|
+ */
|
|
|
+
|
|
|
int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9")));
|
|
|
-void putch(char);
|
|
|
-uint8_t getch(void);
|
|
|
-static inline void getNch(uint8_t);
|
|
|
-void verifySpace();
|
|
|
+
|
|
|
+void __attribute__((noinline)) putch(char);
|
|
|
+uint8_t __attribute__((noinline)) getch(void);
|
|
|
+void __attribute__((noinline)) verifySpace();
|
|
|
+void __attribute__((noinline)) watchdogConfig(uint8_t x);
|
|
|
+
|
|
|
+static inline void getNch(uint8_t);
|
|
|
static inline void flash_led(uint8_t);
|
|
|
-uint8_t getLen();
|
|
|
static inline void watchdogReset();
|
|
|
-void watchdogConfig(uint8_t x);
|
|
|
+static inline void writebuffer(int8_t memtype, uint8_t *mybuff,
|
|
|
+ uint16_t address, uint16_t len);
|
|
|
+static inline void read_mem(uint8_t memtype,
|
|
|
+ uint16_t address, uint16_t len);
|
|
|
+
|
|
|
#ifdef SOFT_UART
|
|
|
void uartDelay() __attribute__ ((naked));
|
|
|
#endif
|
|
|
void appStart(uint8_t rstFlags) __attribute__ ((naked));
|
|
|
|
|
|
|
|
|
- * NRWW memory
|
|
|
- * Addresses below NRWW (Non-Read-While-Write) can be programmed while
|
|
|
- * continuing to run code from flash, slightly speeding up programming
|
|
|
- * time. Beware that Atmel data sheets specify this as a WORD address,
|
|
|
- * while optiboot will be comparing against a 16-bit byte address. This
|
|
|
- * means that on a part with 128kB of memory, the upper part of the lower
|
|
|
- * 64k will get NRWW processing as well, even though it doesn't need it.
|
|
|
- * That's OK. In fact, you can disable the overlapping processing for
|
|
|
- * a part entirely by setting NRWWSTART to zero. This reduces code
|
|
|
- * space a bit, at the expense of being slightly slower, overall.
|
|
|
- *
|
|
|
* RAMSTART should be self-explanatory. It's bigger on parts with a
|
|
|
- * lot of peripheral registers.
|
|
|
+ * lot of peripheral registers. Let 0x100 be the default
|
|
|
+ * Note that RAMSTART need not be exactly at the start of RAM.
|
|
|
*/
|
|
|
-#if defined(__AVR_ATmega168__)
|
|
|
-#define RAMSTART (0x100)
|
|
|
-#define NRWWSTART (0x3800)
|
|
|
-#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32__)
|
|
|
-#define RAMSTART (0x100)
|
|
|
-#define NRWWSTART (0x7000)
|
|
|
-#elif defined (__AVR_ATmega644P__)
|
|
|
-#define RAMSTART (0x100)
|
|
|
-#define NRWWSTART (0xE000)
|
|
|
+#if !defined(RAMSTART)
|
|
|
+#define RAMSTART 0x100
|
|
|
+#if defined (__AVR_ATmega644P__)
|
|
|
|
|
|
#undef SIGNATURE_2
|
|
|
#define SIGNATURE_2 0x0A
|
|
|
-#elif defined (__AVR_ATmega1284P__)
|
|
|
-#define RAMSTART (0x100)
|
|
|
-#define NRWWSTART (0xE000)
|
|
|
-#elif defined(__AVR_ATtiny84__)
|
|
|
-#define RAMSTART (0x100)
|
|
|
-#define NRWWSTART (0x0000)
|
|
|
#elif defined(__AVR_ATmega1280__)
|
|
|
+#undef RAMSTART
|
|
|
#define RAMSTART (0x200)
|
|
|
-#define NRWWSTART (0xE000)
|
|
|
-#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
|
|
|
-#define RAMSTART (0x100)
|
|
|
-#define NRWWSTART (0x1800)
|
|
|
+#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
@@ -365,45 +392,6 @@ void appStart(uint8_t rstFlags) __attribute__ ((naked));
|
|
|
#define wdtVect (*(uint16_t*)(RAMSTART+SPM_PAGESIZE*2+6))
|
|
|
#endif
|
|
|
|
|
|
-
|
|
|
- * Handle devices with up to 4 uarts (eg m1280.) Rather inelegantly.
|
|
|
- * Note that mega8/m32 still needs special handling, because ubrr is handled
|
|
|
- * differently.
|
|
|
- */
|
|
|
-#if UART == 0
|
|
|
-# define UART_SRA UCSR0A
|
|
|
-# define UART_SRB UCSR0B
|
|
|
-# define UART_SRC UCSR0C
|
|
|
-# define UART_SRL UBRR0L
|
|
|
-# define UART_UDR UDR0
|
|
|
-#elif UART == 1
|
|
|
-#if !defined(UDR1)
|
|
|
-#error UART == 1, but no UART1 on device
|
|
|
-#endif
|
|
|
-# define UART_SRA UCSR1A
|
|
|
-# define UART_SRB UCSR1B
|
|
|
-# define UART_SRC UCSR1C
|
|
|
-# define UART_SRL UBRR1L
|
|
|
-# define UART_UDR UDR1
|
|
|
-#elif UART == 2
|
|
|
-#if !defined(UDR2)
|
|
|
-#error UART == 2, but no UART2 on device
|
|
|
-#endif
|
|
|
-# define UART_SRA UCSR2A
|
|
|
-# define UART_SRB UCSR2B
|
|
|
-# define UART_SRC UCSR2C
|
|
|
-# define UART_SRL UBRR2L
|
|
|
-# define UART_UDR UDR2
|
|
|
-#elif UART == 3
|
|
|
-#if !defined(UDR1)
|
|
|
-#error UART == 3, but no UART3 on device
|
|
|
-#endif
|
|
|
-# define UART_SRA UCSR3A
|
|
|
-# define UART_SRB UCSR3B
|
|
|
-# define UART_SRC UCSR3C
|
|
|
-# define UART_SRL UBRR3L
|
|
|
-# define UART_UDR UDR3
|
|
|
-#endif
|
|
|
|
|
|
|
|
|
int main(void) {
|
|
@@ -416,7 +404,7 @@ int main(void) {
|
|
|
* necessary, and uses 4 bytes of flash.)
|
|
|
*/
|
|
|
register uint16_t address = 0;
|
|
|
- register uint8_t length;
|
|
|
+ register uint16_t length;
|
|
|
|
|
|
|
|
|
|
|
@@ -441,6 +429,7 @@ int main(void) {
|
|
|
|
|
|
TCCR1B = _BV(CS12) | _BV(CS10);
|
|
|
#endif
|
|
|
+
|
|
|
#ifndef SOFT_UART
|
|
|
#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega32__)
|
|
|
UCSRA = _BV(U2X);
|
|
@@ -473,7 +462,7 @@ int main(void) {
|
|
|
flash_led(LED_START_FLASHES * 2);
|
|
|
#endif
|
|
|
|
|
|
-
|
|
|
+
|
|
|
for (;;) {
|
|
|
|
|
|
ch = getch();
|
|
@@ -525,34 +514,23 @@ int main(void) {
|
|
|
|
|
|
else if(ch == STK_PROG_PAGE) {
|
|
|
|
|
|
+ uint8_t desttype;
|
|
|
uint8_t *bufPtr;
|
|
|
- uint16_t addrPtr;
|
|
|
-
|
|
|
- getch();
|
|
|
- length = getch();
|
|
|
- getch();
|
|
|
+ uint16_t savelength;
|
|
|
|
|
|
-
|
|
|
- if (address < NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
|
|
+ length = getch()<<8;
|
|
|
+ length |= getch();
|
|
|
+ savelength = length;
|
|
|
+ desttype = getch();
|
|
|
|
|
|
-
|
|
|
+
|
|
|
bufPtr = buff;
|
|
|
do *bufPtr++ = getch();
|
|
|
while (--length);
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- if (address >= NRWWSTART) __boot_page_erase_short((uint16_t)(void*)address);
|
|
|
-
|
|
|
|
|
|
verifySpace();
|
|
|
|
|
|
-
|
|
|
-
|
|
|
- boot_spm_busy_wait();
|
|
|
-
|
|
|
#ifdef VIRTUAL_BOOT_PARTITION
|
|
|
if ((uint16_t)(void*)address == 0) {
|
|
|
|
|
@@ -572,58 +550,20 @@ int main(void) {
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-
|
|
|
- bufPtr = buff;
|
|
|
- addrPtr = (uint16_t)(void*)address;
|
|
|
- ch = SPM_PAGESIZE / 2;
|
|
|
- do {
|
|
|
- uint16_t a;
|
|
|
- a = *bufPtr++;
|
|
|
- a |= (*bufPtr++) << 8;
|
|
|
- __boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
|
|
- addrPtr += 2;
|
|
|
- } while (--ch);
|
|
|
-
|
|
|
-
|
|
|
- __boot_page_write_short((uint16_t)(void*)address);
|
|
|
- boot_spm_busy_wait();
|
|
|
+ writebuffer(desttype, buff, address, savelength);
|
|
|
|
|
|
-#if defined(RWWSRE)
|
|
|
-
|
|
|
- boot_rww_enable();
|
|
|
-#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(ch == STK_READ_PAGE) {
|
|
|
-
|
|
|
- getch();
|
|
|
- length = getch();
|
|
|
- getch();
|
|
|
+ uint8_t desttype;
|
|
|
+ length = getch()<<8;
|
|
|
+ length |= getch();
|
|
|
+ desttype = getch();
|
|
|
|
|
|
verifySpace();
|
|
|
- do {
|
|
|
-#ifdef VIRTUAL_BOOT_PARTITION
|
|
|
-
|
|
|
- if (address == 0) ch=rstVect & 0xff;
|
|
|
- else if (address == 1) ch=rstVect >> 8;
|
|
|
- else if (address == 8) ch=wdtVect & 0xff;
|
|
|
- else if (address == 9) ch=wdtVect >> 8;
|
|
|
- else ch = pgm_read_byte_near(address);
|
|
|
- address++;
|
|
|
-#elif defined(RAMPZ)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- __asm__ ("elpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address));
|
|
|
-#else
|
|
|
-
|
|
|
- __asm__ ("lpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address));
|
|
|
-#endif
|
|
|
- putch(ch);
|
|
|
- } while (--length);
|
|
|
+
|
|
|
+ read_mem(desttype, address, length);
|
|
|
}
|
|
|
|
|
|
|
|
@@ -822,3 +762,110 @@ void appStart(uint8_t rstFlags) {
|
|
|
"ijmp\n"
|
|
|
);
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
+ * void writebuffer(memtype, buffer, address, length)
|
|
|
+ */
|
|
|
+static inline void writebuffer(int8_t memtype, uint8_t *mybuff,
|
|
|
+ uint16_t address, uint16_t len)
|
|
|
+{
|
|
|
+ switch (memtype) {
|
|
|
+ case 'E':
|
|
|
+#if defined(SUPPORT_EEPROM) || defined(BIGBOOT)
|
|
|
+ while(len--) {
|
|
|
+ eeprom_write_byte((uint8_t *)(address++), *mybuff++);
|
|
|
+ }
|
|
|
+#else
|
|
|
+
|
|
|
+ * On systems where EEPROM write is not supported, just busy-loop
|
|
|
+ * until the WDT expires, which will eventually cause an error on
|
|
|
+ * host system (which is what it should do.)
|
|
|
+ */
|
|
|
+ while (1)
|
|
|
+ ;
|
|
|
+#endif
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+
|
|
|
+ * Default to writing to Flash program memory. By making this
|
|
|
+ * the default rather than checking for the correct code, we save
|
|
|
+ * space on chips that don't support any other memory types.
|
|
|
+ */
|
|
|
+ {
|
|
|
+
|
|
|
+ uint8_t *bufPtr = mybuff;
|
|
|
+ uint16_t addrPtr = (uint16_t)(void*)address;
|
|
|
+
|
|
|
+
|
|
|
+ * Start the page erase and wait for it to finish. There
|
|
|
+ * used to be code to do this while receiving the data over
|
|
|
+ * the serial link, but the performance improvement was slight,
|
|
|
+ * and we needed the space back.
|
|
|
+ */
|
|
|
+ __boot_page_erase_short((uint16_t)(void*)address);
|
|
|
+ boot_spm_busy_wait();
|
|
|
+
|
|
|
+
|
|
|
+ * Copy data from the buffer into the flash write buffer.
|
|
|
+ */
|
|
|
+ do {
|
|
|
+ uint16_t a;
|
|
|
+ a = *bufPtr++;
|
|
|
+ a |= (*bufPtr++) << 8;
|
|
|
+ __boot_page_fill_short((uint16_t)(void*)addrPtr,a);
|
|
|
+ addrPtr += 2;
|
|
|
+ } while (len -= 2);
|
|
|
+
|
|
|
+
|
|
|
+ * Actually Write the buffer to flash (and wait for it to finish.)
|
|
|
+ */
|
|
|
+ __boot_page_write_short((uint16_t)(void*)address);
|
|
|
+ boot_spm_busy_wait();
|
|
|
+#if defined(RWWSRE)
|
|
|
+
|
|
|
+ boot_rww_enable();
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline void read_mem(uint8_t memtype, uint16_t address, uint16_t length)
|
|
|
+{
|
|
|
+ uint8_t ch;
|
|
|
+
|
|
|
+ switch (memtype) {
|
|
|
+
|
|
|
+#if defined(SUPPORT_EEPROM) || defined(BIGBOOT)
|
|
|
+ case 'E':
|
|
|
+ do {
|
|
|
+ putch(eeprom_read_byte((uint8_t *)(address++)));
|
|
|
+ } while (--length);
|
|
|
+ break;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ do {
|
|
|
+#ifdef VIRTUAL_BOOT_PARTITION
|
|
|
+
|
|
|
+ if (address == 0) ch=rstVect & 0xff;
|
|
|
+ else if (address == 1) ch=rstVect >> 8;
|
|
|
+ else if (address == 8) ch=wdtVect & 0xff;
|
|
|
+ else if (address == 9) ch=wdtVect >> 8;
|
|
|
+ else ch = pgm_read_byte_near(address);
|
|
|
+ address++;
|
|
|
+#elif defined(RAMPZ)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ __asm__ ("elpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address));
|
|
|
+#else
|
|
|
+
|
|
|
+ __asm__ ("lpm %0,Z+\n" : "=r" (ch), "=z" (address): "1" (address));
|
|
|
+#endif
|
|
|
+ putch(ch);
|
|
|
+ } while (--length);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|