Added bootloader
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 12 Jan 2017 00:14:26 +0000 (16:14 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 12 Jan 2017 00:14:26 +0000 (16:14 -0800)
12 files changed:
Makefile
src/xboot/eeprom_driver.c [new file with mode: 0644]
src/xboot/eeprom_driver.h [new file with mode: 0644]
src/xboot/protocol.h [new file with mode: 0644]
src/xboot/sp_driver.S [new file with mode: 0644]
src/xboot/sp_driver.h [new file with mode: 0644]
src/xboot/uart.c [new file with mode: 0644]
src/xboot/uart.h [new file with mode: 0644]
src/xboot/watchdog.c [new file with mode: 0644]
src/xboot/watchdog.h [new file with mode: 0644]
src/xboot/xboot.c [new file with mode: 0644]
src/xboot/xboot.h [new file with mode: 0644]

index e03a9178d1912811370d7c7521346147d83d966c..9cfe043fff67b10d77cf0715d9a43520a7e233df 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 # Makefile for the project Bulidbotics firmware
-PROJECT = buildbotics
-MCU     = atxmega192a3u
-CLOCK   = 32000000
+PROJECT         = buildbotics
+MCU             = atxmega192a3u
+CLOCK           = 32000000
 
 TARGET  = $(PROJECT).elf
 
@@ -14,13 +14,13 @@ COMMON = -mmcu=$(MCU)
 CFLAGS += $(COMMON)
 CFLAGS += -Wall -Werror # -Wno-error=unused-function
 CFLAGS += -Wno-error=strict-aliasing # for _invsqrt
-CFLAGS += -gdwarf-2 -std=gnu99 -DF_CPU=$(CLOCK)UL -O3 -funroll-loops
+CFLAGS += -std=gnu99 -DF_CPU=$(CLOCK)UL -O3 #-funroll-loops
 CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char
 CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d
 CFLAGS += -Isrc
 
 # Linker flags
-LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm -Wl,-Map=$(PROJECT).map
+LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm
 LIBS += -lm
 
 # EEPROM flags
@@ -29,14 +29,14 @@ EEFLAGS += --set-section-flags=.eeprom="alloc,load"
 EEFLAGS += --change-section-lma .eeprom=0 --no-change-warnings
 
 # Programming flags
-#PROGRAMMER = avrispmkII
-PROGRAMMER = jtag3pdi
-PDEV = usb 
+PROGRAMMER = avrispmkII
+#PROGRAMMER = jtag3pdi
+PDEV = usb
 AVRDUDE_OPTS = -c $(PROGRAMMER) -p $(MCU) -P $(PDEV)
 
 FUSE0=0xff
 FUSE1=0x00
-FUSE2=0xfe
+FUSE2=0xbe
 FUSE4=0xff
 FUSE5=0xeb
 
@@ -45,27 +45,51 @@ SRC = $(wildcard src/*.c)
 SRC += $(wildcard src/plan/*.c)
 OBJ = $(patsubst src/%.c,build/%.o,$(SRC))
 
+# Boot SRC
+BOOT_SRC = $(wildcard src/xboot/*.S)
+BOOT_SRC += $(wildcard src/xboot/*.c)
+BOOT_OBJ = $(patsubst src/%.c,build/%.o,$(BOOT_SRC))
+BOOT_OBJ := $(patsubst src/%.S,build/%.o,$(BOOT_OBJ))
+
+BOOT_LDFLAGS = $(LDFLAGS) -Wl,--section-start=.text=0x030000
+
 # Build
-all: $(TARGET) $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss size
+all: $(TARGET) $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss xboot.hex \
+  boot-size size
 
 # Compile
 build/%.o: src/%.c
        @mkdir -p $(shell dirname $@)
        $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $<
 
+build/%.o: src/%.S
+       @mkdir -p $(shell dirname $@)
+       $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $<
+
 # Link
 $(TARGET): $(OBJ)
-       $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $(TARGET)
+       $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
+
+xboot.elf: $(BOOT_OBJ)
+       $(CC) $(BOOT_LDFLAGS) $(BOOT_OBJ) -o $@
 
-%.hex: $(TARGET)
+%.hex: %.elf
        avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature $< $@
 
-%.eep: $(TARGET)
+%.eep: %.elf
        avr-objcopy $(EEFLAGS) -O ihex $< $@
 
-%.lss: $(TARGET)
+%.lss: %.elf
        avr-objdump -h -S $< > $@
 
+boot-size: xboot.elf
+       @echo '********************************************************************'
+       @avr-size -A --mcu=$(MCU) xboot.elf
+       @echo '********************************************************************'
+       @avr-size -B --mcu=$(MCU) xboot.elf
+       @echo '********************************************************************'
+       @avr-size -C --mcu=$(MCU) xboot.elf
+
 size: $(TARGET)
        @echo '********************************************************************'
        @avr-size -A --mcu=$(MCU) $(TARGET)
@@ -75,6 +99,11 @@ size: $(TARGET)
        @avr-size -C --mcu=$(MCU) $(TARGET)
 
 # Program
+init:
+       $(MAKE) fuses
+       $(MAKE) program-boot
+       $(MAKE) program
+
 reset:
        avrdude $(AVRDUDE_OPTS)
 
@@ -87,6 +116,9 @@ program: $(PROJECT).hex
 verify: $(PROJECT).hex
        avrdude $(AVRDUDE_OPTS) -U flash:v:$(PROJECT).hex:i
 
+program-boot: xboot.hex
+       avrdude $(AVRDUDE_OPTS) -U flash:w:xboot.hex:i
+
 fuses:
        avrdude $(AVRDUDE_OPTS) -U fuse0:w:$(FUSE0):m -U fuse1:w:$(FUSE1):m \
          -U fuse2:w:$(FUSE2):m -U fuse4:w:$(FUSE4):m -U fuse5:w:$(FUSE5):m
@@ -110,7 +142,7 @@ tidy:
 
 clean: tidy
        rm -rf $(PROJECT).elf $(PROJECT).hex $(PROJECT).eep $(PROJECT).lss \
-         $(PROJECT).map build
+         $(PROJECT).map build xboot.hex xboot.elf
 
 .PHONY: tidy clean size all reset erase program fuses read_fuses prodsig
 .PHONY: signature usersig
diff --git a/src/xboot/eeprom_driver.c b/src/xboot/eeprom_driver.c
new file mode 100644 (file)
index 0000000..38b6eea
--- /dev/null
@@ -0,0 +1,58 @@
+/************************************************************************/
+/* XMEGA EEPROM Driver                                                  */
+/*                                                                      */
+/* eeprom.c                                                             */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2011 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#include "eeprom_driver.h"
+
+
+static inline void NVM_EXEC(void) {
+  void *z = (void *)&NVM_CTRLA;
+
+  __asm__ volatile("out %[ccp], %[ioreg]"  "\n\t"
+                   "st z, %[cmdex]"
+                   :
+                   : [ccp] "I" (_SFR_IO_ADDR(CCP)),
+                   [ioreg] "d" (CCP_IOREG_gc),
+                   [cmdex] "r" (NVM_CMDEX_bm),
+                     [z] "z" (z)
+                   );
+}
+
+
+void wait_for_nvm() {
+  while (NVM.STATUS & NVM_NVMBUSY_bm) continue;
+}
+
+
+void EEPROM_erase_all() {
+  wait_for_nvm();
+  NVM.CMD = NVM_CMD_ERASE_EEPROM_gc;
+  NVM_EXEC();
+}
diff --git a/src/xboot/eeprom_driver.h b/src/xboot/eeprom_driver.h
new file mode 100644 (file)
index 0000000..c139a43
--- /dev/null
@@ -0,0 +1,56 @@
+/************************************************************************/
+/* XMEGA EEPROM Driver                                                  */
+/*                                                                      */
+/* eeprom.h                                                             */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2011 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#pragma once
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/eeprom.h>
+
+#include "xboot.h"
+
+#ifndef EEPROM_PAGE_SIZE
+#define EEPROM_PAGE_SIZE E2PAGESIZE
+#endif
+
+#define EEPROM_read_byte(addr) \
+  eeprom_read_byte((const uint8_t *)((uint16_t)(addr)))
+
+#define EEPROM_write_byte(addr, value) \
+  eeprom_write_byte((uint8_t *)((uint16_t)(addr)), (value))
+
+#define EEPROM_read_block(addr, dest, len) \
+  eeprom_read_block((dest), (void *)((uint16_t)(addr)), (len))
+
+#define EEPROM_write_block(addr, src, len) \
+  eeprom_write_block((src), (void *)((uint16_t)(addr)), (len))
+
+void EEPROM_erase_all();
diff --git a/src/xboot/protocol.h b/src/xboot/protocol.h
new file mode 100644 (file)
index 0000000..4336094
--- /dev/null
@@ -0,0 +1,102 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* XBoot Protocol Definition                                            */
+/*                                                                      */
+/* protocol.h                                                           */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#pragma once
+
+#include "xboot.h"
+
+// General Commands
+#define CMD_SYNC                '\x1b'
+
+// Informational Commands
+#define CMD_CHECK_AUTOINCREMENT 'a'
+#define CMD_CHECK_BLOCK_SUPPORT 'b'
+#define CMD_PROGRAMMER_TYPE     'p'
+#define CMD_DEVICE_CODE         't'
+#define CMD_PROGRAM_ID          'S'
+#define CMD_VERSION             'V'
+#define CMD_READ_SIGNATURE      's'
+
+// Addressing
+#define CMD_SET_ADDRESS         'A'
+#define CMD_SET_EXT_ADDRESS     'H'
+
+// Erase
+#define CMD_CHIP_ERASE          'e'
+
+// Block Access
+#define CMD_BLOCK_LOAD          'B'
+#define CMD_BLOCK_READ          'g'
+
+// Byte Access
+#define CMD_READ_BYTE           'R'
+#define CMD_WRITE_LOW_BYTE      'c'
+#define CMD_WRITE_HIGH_BYTE     'C'
+#define CMD_WRITE_PAGE          'm'
+#define CMD_WRITE_EEPROM_BYTE   'D'
+#define CMD_READ_EEPROM_BYTE    'd'
+
+// Lock and Fuse Bits
+#define CMD_WRITE_LOCK_BITS     'l'
+#define CMD_READ_LOCK_BITS      'r'
+#define CMD_READ_LOW_FUSE_BITS  'F'
+#define CMD_READ_HIGH_FUSE_BITS 'N'
+#define CMD_READ_EXT_FUSE_BITS  'Q'
+
+// Bootloader Commands
+#define CMD_ENTER_PROG_MODE     'P'
+#define CMD_LEAVE_PROG_MODE     'L'
+#define CMD_EXIT_BOOTLOADER     'E'
+#define CMD_SET_LED             'x'
+#define CMD_CLEAR_LED           'y'
+#define CMD_SET_TYPE            'T'
+
+#define CMD_CRC                 'h'
+
+// Memory types for block access
+#define MEM_EEPROM              'E'
+#define MEM_FLASH               'F'
+#define MEM_USERSIG             'U'
+#define MEM_PRODSIG             'P'
+
+// Sections for CRC checks
+#define SECTION_FLASH           'F'
+#define SECTION_APPLICATION     'A'
+#define SECTION_BOOT            'B'
+#define SECTION_APP             'a'
+#define SECTION_APP_TEMP        't'
+
+// Command Responses
+#define REPLY_ACK               '\r'
+#define REPLY_YES               'Y'
+#define REPLY_ERROR             '?'
diff --git a/src/xboot/sp_driver.S b/src/xboot/sp_driver.S
new file mode 100644 (file)
index 0000000..e6fff99
--- /dev/null
@@ -0,0 +1,794 @@
+;******************************************************************************
+;*
+;* XMEGA Self-programming driver assembly source file.
+;*
+;*      This file contains the low-level implementations for the
+;*      XMEGA Self-programming driver. It is written for the GCC Assembler.
+;*
+;*      If any SPM instructions are used, the linker file must define
+;*      a segment named bootloader which must be located in the device Boot section.
+;*      This can be done by passing "-Wl,--section-start=.BOOT=0x020000" to the
+;*      linker with the correct address for the boot section.
+;*
+;*      None of these routines clean up the NVM Command Register after use.
+;*      It is therefore important to write NVM_CMD_NO_OPERATION_gc (0x00) to this
+;*      register when you are finished using any of the functions in this driver.
+;*
+;*      For all routines, it is important that any interrupt handlers do not
+;*      perform any NVM operations. The user must implement a scheme for mutually
+;*      exclusive access to the NVM. However, the 4-cycle timeout will work fine,
+;*      since writing to the Configuration Change Protection register (CCP)
+;*      automatically disables interrupts for 4 instruction cycles.
+;*
+;*      Note on IAR calling convention:
+;*         Scratch registers:   R18-R27, R30-R31
+;*         Preserved registers: R2-R17, R28-R29
+;*         Parameter registers: R8-R25 (2-,4-, or 8- byte alignment)
+;*         Return registers:    R18-R25 (up to 64-bit)
+;*
+;* Application note:
+;*      AVR1316: XMEGA Self-programming
+;*
+;* Documentation
+;*      For comprehensive code documentation, supported compilers, compiler
+;*      settings and supported devices see readme.html
+;*
+;*      Atmel Corporation: http:;www.atmel.com \n
+;*      Support email: avr@atmel.com
+;*
+;* $Revision: 1153 $
+;* $Date: 2007-12-18 09:48:23 +0100 (ti, 18 des 2007) $
+;*
+;* Copyright (c) 2007, Atmel Corporation All rights reserved.
+;*
+;* Redistribution and use in source and binary forms, with or without
+;* modification, are permitted provided that the following conditions are met:
+;*
+;* 1. Redistributions of source code must retain the above copyright notice,
+;* this list of conditions and the following disclaimer.
+;*
+;* 2. Redistributions in binary form must reproduce the above copyright notice,
+;* this list of conditions and the following disclaimer in the documentation
+;* and/or other materials provided with the distribution.
+;*
+;* 3. The name of ATMEL may not be used to endorse or promote products derived
+;* from this software without specific prior written permission.
+;*
+;* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+;* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+;* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
+;* SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
+;* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+;* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+;* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+;* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+;* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+;* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+;******************************************************************************
+
+#include <avr/io.h>
+;#include "Flash_Defines.h"
+
+/* Define the size of the flash page if not defined in the header files. */
+#ifndef APP_SECTION_PAGE_SIZE
+       #error  APP_SECTION_PAGE_SIZE must be defined if not defined in header files.
+       //#define APP_SECTION_PAGE_SIZE 512
+#endif /*APP_SECTION_PAGE_SIZE*/
+
+/* Defines not yet included in header file. */
+#define NVM_CMD_NO_OPERATION_gc (0x00<<0)      // Noop/Ordinary LPM
+#define NVM_CMD_READ_USER_SIG_ROW_gc (0x01<<0) // Read user signature row
+#define NVM_CMD_READ_CALIB_ROW_gc (0x02<<0)    // Read calibration row
+#define NVM_CMD_READ_EEPROM_gc (0x06<<0)       // Read EEPROM
+#define NVM_CMD_READ_FUSES_gc (0x07<<0)        // Read fuse byte
+#define NVM_CMD_WRITE_LOCK_BITS_gc (0x08<<0)   // Write lock bits
+#define NVM_CMD_ERASE_USER_SIG_ROW_gc (0x18<<0)        // Erase user signature row
+#define NVM_CMD_WRITE_USER_SIG_ROW_gc (0x1A<<0)        // Write user signature row
+#define NVM_CMD_ERASE_APP_gc (0x20<<0) // Erase Application Section
+#define NVM_CMD_ERASE_APP_PAGE_gc (0x22<<0)    // Erase Application Section page
+#define NVM_CMD_LOAD_FLASH_BUFFER_gc (0x23<<0) // Load Flash page buffer
+#define NVM_CMD_WRITE_APP_PAGE_gc (0x24<<0)    // Write Application Section page
+#define NVM_CMD_ERASE_WRITE_APP_PAGE_gc (0x25<<0)      // Erase-and-write Application Section page
+#define NVM_CMD_ERASE_FLASH_BUFFER_gc (0x26<<0)        // Erase/flush Flash page buffer
+#define NVM_CMD_ERASE_BOOT_PAGE_gc (0x2A<<0)   // Erase Boot Section page
+#define NVM_CMD_WRITE_BOOT_PAGE_gc (0x2C<<0)   // Write Boot Section page
+#define NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc (0x2D<<0)     // Erase-and-write Boot Section page
+#define NVM_CMD_ERASE_EEPROM_gc (0x30<<0)      // Erase EEPROM
+#define NVM_CMD_ERASE_EEPROM_PAGE_gc (0x32<<0) // Erase EEPROM page
+#define NVM_CMD_LOAD_EEPROM_BUFFER_gc (0x33<<0)        // Load EEPROM page buffer
+#define NVM_CMD_WRITE_EEPROM_PAGE_gc (0x34<<0) // Write EEPROM page
+#define NVM_CMD_ERASE_WRITE_EEPROM_PAGE_gc (0x35<<0)   // Erase-and-write EEPROM page
+#define NVM_CMD_ERASE_EEPROM_BUFFER_gc (0x36<<0)       // Erase/flush EEPROM page buffer
+#define NVM_CMD_APP_CRC_gc (0x38<<0)   // Generate Application section CRC
+#define NVM_CMD_BOOT_CRC_gc (0x39<<0)  // Generate Boot Section CRC
+#define NVM_CMD_FLASH_RANGE_CRC_gc (0x3A<<0)   // Generate Flash Range CRC
+#define CCP_SPM_gc (0x9D<<0)   // SPM Instruction Protection
+#define CCP_IOREG_gc (0xD8<<0) // IO Register Protection
+
+
+
+; ---
+; This routine reads a byte from flash given by the address in
+; R25:R24:R23:R22.
+;
+; Input:
+;     R25:R24:R23:R22.
+;
+; Returns:
+;     R24 - Read byte.
+; ---
+
+.section .text
+.global SP_ReadByte
+
+SP_ReadByte:
+       in      r19, RAMPZ      ; Save RAMPZ.
+       out     RAMPZ, r24      ; Load RAMPZ with the MSB of the address.
+       movw    ZL, r22         ; Move the low bytes to the Z pointer
+       elpm    r24, Z          ; Extended load byte from address pointed to by Z.
+       out     RAMPZ, r19      ; Restore RAMPZ register.
+       ret
+
+
+
+; ---
+; This routine reads a word from flash given by the address in
+; R25:R24:R23:R22.
+;
+; Input:
+;     R25:R24:R23:R22.
+;
+; Returns:
+;     R25:R24 - Read word.
+; ---
+
+.section .text
+.global SP_ReadWord
+
+SP_ReadWord:
+       in      r19, RAMPZ      ; Save RAMPZ.
+       out     RAMPZ, r24      ; Load RAMPZ with the MSB of the address.
+       movw    ZL, r22         ; Move the low bytes to the Z pointer
+       elpm    r24, Z+         ; Extended load byte from address pointed to by Z.
+       elpm    r25, Z          ; Extended load byte from address pointed to by Z.
+       out     RAMPZ, r19      ; Restore RAMPZ register.
+       ret
+
+
+
+; ---
+; This routine reads the calibration byte given by the index in R24.
+;
+; Input:
+;     R24 - Byte index.
+;
+; Returns:
+;     R24 - Calibration byte.
+; ---
+
+.section .text
+.global SP_ReadCalibrationByte 
+
+SP_ReadCalibrationByte:
+       ldi     r20, NVM_CMD_READ_CALIB_ROW_gc    ; Prepare NVM command in R20.
+       rjmp    SP_CommonLPM                      ; Jump to common LPM code.
+
+
+
+; ---
+; This routine reads the user signature byte given by the index in R25:R24.
+;
+; Input:
+;     R25:R24 - Byte index.
+;
+; Returns:
+;     R24 - Signature byte.
+; ---
+
+.section .text 
+.global SP_ReadUserSignatureByte
+
+SP_ReadUserSignatureByte:
+       ldi     r20, NVM_CMD_READ_USER_SIG_ROW_gc  ; Prepare NVM command in R20.
+       rjmp    SP_CommonLPM                       ; Jump to common LPM code.
+
+
+
+; ---
+; This routine reads the fuse byte given by the index in R24.
+;
+; Input:
+;     R24 - Byte index.
+;
+; Returns:
+;     R24 - Fuse byte.
+; ---
+
+.section .text 
+.global SP_ReadFuseByte
+
+SP_ReadFuseByte:
+       sts     NVM_ADDR0, r24              ; Load fuse byte index into NVM Address Register 0.
+       clr     r24                         ; Prepare a zero.
+       sts     NVM_ADDR1, r24              ; Load zero into NVM Address Register 1.
+       sts     NVM_ADDR2, r24              ; Load zero into NVM Address Register 2.
+       ldi     r20, NVM_CMD_READ_FUSES_gc  ; Prepare NVM command in R20.
+       rcall   SP_CommonCMD                ; Jump to common NVM Action code.
+       movw    r24, r22                    ; Move low byte to 1 byte return address.
+       ret
+
+
+
+; ---
+; This routine sets the lock bits from R24. Note that unlocking is only
+; possible by doing a full chip erase, not available from software.
+;
+; Input:
+;     R24 - Lock bits.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text 
+.global SP_WriteLockBits
+
+SP_WriteLockBits:
+       sts     NVM_DATA0, r24                  ; Load lock bits into NVM Data Register 0.
+       ldi     r20, NVM_CMD_WRITE_LOCK_BITS_gc ; Prepare NVM command in R20.
+       rjmp    SP_CommonCMD                    ; Jump to common NVM Action code.
+
+
+
+; ---
+; This routine reads the lock bits.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     R24 - Lock bits.
+; ---
+
+.section .text         
+.global SP_ReadLockBits
+
+SP_ReadLockBits:
+       lds     r24, NVM_LOCKBITS       ; Read IO-mapped lock bits.
+       ret
+
+
+
+; ---
+; This routine erases the user signature row.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_EraseUserSignatureRow
+
+SP_EraseUserSignatureRow:
+       in      r19, RAMPZ                         ; Save RAMPZ, which is restored in SP_CommonSPM.
+       ldi     r20, NVM_CMD_ERASE_USER_SIG_ROW_gc ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                       ; Jump to common SPM code.
+
+
+
+; ---
+; This routine writes the flash buffer to the user signature row.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_WriteUserSignatureRow
+
+SP_WriteUserSignatureRow:
+       in      r19, RAMPZ                          ; Save RAMPZ, which is restored in SP_CommonSPM.
+       ldi     r20, NVM_CMD_WRITE_USER_SIG_ROW_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                        ; Jump to common SPM code.
+
+
+
+; ---
+; This routine erases the entire application section.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_EraseApplicationSection
+
+SP_EraseApplicationSection:
+       in      r19, RAMPZ                 ; Save RAMPZ, which is restored in SP_CommonSPM.
+       clr     r24                        ; Prepare a zero.
+       clr     r25
+       out     RAMPZ, r24                 ; Point into Application area.
+       ldi     r20, NVM_CMD_ERASE_APP_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM               ; Jump to common SPM code.
+
+
+
+; ---
+; This routine erases the page at address R25:R24:R23:R22 in the application
+; section. The address can point anywhere inside the page.
+;
+; Input:
+;     R25:R24:R23:R22 - Byte address into Flash page.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text 
+.global SP_EraseApplicationPage
+
+SP_EraseApplicationPage:
+       in      r19, RAMPZ                      ; Save RAMPZ, which is restored in SP_CommonSPM.
+       out     RAMPZ, r24                      ; Load RAMPZ with the MSB of the address.
+       movw    r24, r22                        ; Move low bytes for ZH:ZL to R25:R24
+       ldi     r20, NVM_CMD_ERASE_APP_PAGE_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                    ; Jump to common SPM code.
+
+
+
+; ---
+; This routine writes the word from R23:R22 into the Flash page buffer at
+; address R25:R24.
+;
+; Input:
+;     R25:R24 - Byte address into Flash page.
+;     R23:R22 - Word to write.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_LoadFlashWord
+
+SP_LoadFlashWord:
+       in      r19, RAMPZ                         ; Save RAMPZ, which is restored in SP_CommonSPM.
+       movw    r0, r22                            ; Prepare flash word in R1:R0.
+       ldi     r20, NVM_CMD_LOAD_FLASH_BUFFER_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                       ; Jump to common SPM code.
+
+
+
+; ---
+; This routine writes an entire page from the SRAM buffer at
+; address R25:R24 into the Flash page buffer.
+;
+; Note that you must define "-Wl,--section-start=.BOOT=0x020000" for the
+; linker to place this function in the boot section with the correct address.
+;
+; Input:
+;     R25:R24 - 16-bit pointer to SRAM buffer.
+;
+; Returns:
+;     Nothing.
+; ---
+               
+.section .text
+.global SP_LoadFlashPage
+
+SP_LoadFlashPage:
+       clr     ZL              ; Clear low byte of Z, to indicate start of page.
+       clr     ZH              ; Clear high byte of Z, to indicate start of page.
+
+       out     RAMPX, r1       ; Clear RAMPX pointer.
+       movw    XL, r24         ; Load X with data buffer address.
+
+       ldi     r20, NVM_CMD_LOAD_FLASH_BUFFER_gc  ; Prepare NVM command code in R20.
+       sts     NVM_CMD, r20                       ; Load it into NVM command register.
+
+#if APP_SECTION_PAGE_SIZE > 512
+       ldi     r22, ((APP_SECTION_PAGE_SIZE/2) >> 8)
+#endif
+
+       ldi     r21, ((APP_SECTION_PAGE_SIZE/2)&0xFF)    ; Load R21 with page word count.
+       ldi     r18, CCP_SPM_gc                    ; Prepare Protect SPM signature in R16.
+
+SP_LoadFlashPage_1:
+       ld      r0, X+         ; Load low byte from buffer into R0.
+       ld      r1, X+         ; Load high byte from buffer into R1.
+       sts     CCP, r18       ; Enable SPM operation (this disables interrupts for 4 cycles).
+       spm                    ; Self-program.
+       adiw    ZL, 2          ; Move Z to next Flash word.
+
+#if APP_SECTION_PAGE_SIZE > 512
+       subi    r21, 1         ; Decrement word count.
+       sbci    r22, 0
+#else
+       dec     r21            ; Decrement word count.
+#endif
+
+       brne    SP_LoadFlashPage_1   ; Repeat until word cont is zero.
+
+       clr     r1                   ; Clear R1 for GCC _zero_reg_ to function properly.
+       ret
+
+
+
+; ---
+; This routine reads an entire Flash page from address R23:R22:R21:R20 into the
+; SRAM buffer at address R25:R24.
+;
+;
+; Input:
+;     R23:R22:R21:R20 - Flash byte address.
+;     R25:R24 - 16-bit pointer to SRAM buffer.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text         
+.global SP_ReadFlashPage
+
+SP_ReadFlashPage:
+
+       in      r19, RAMPZ                   ; Save RAMPZ during assembly.
+       out     RAMPZ, r22                   ; Load RAMPZ with MSB of address
+       movw    ZL, r20                      ; Load Z with Flash address.
+
+       out     RAMPX, r1                    ; Load RAMPX with data pointer
+       movw    XL, r24                      ; Load X with data buffer address.
+
+       ldi     r20, NVM_CMD_NO_OPERATION_gc ; Prepare NVM command code in R20.
+       sts     NVM_CMD, r20                 ; Set NVM command to No Operation so that LPM reads Flash.
+
+#if APP_SECTION_PAGE_SIZE > 512
+       ldi     r22, ((APP_SECTION_PAGE_SIZE/2) >> 8) ; Load R22 with byte cont high if flash page is large.
+#endif 
+
+       ldi     r21, ((APP_SECTION_PAGE_SIZE)&0xFF)   ; Load R21 with byte count.
+
+SP_ReadFlashPage_1:
+       elpm    r24, Z+                         ; Load Flash bytes into R18:r19
+       elpm    r25, Z+
+       st      X+, r24                         ; Write bytes to buffer.
+       st      X+, r25
+
+#if APP_SECTION_PAGE_SIZE > 512
+       subi    r21, 1                          ; Decrement word count.
+       sbci    r22, 0
+#else
+       dec     r21                             ; Decrement word count.
+#endif 
+
+       brne    SP_ReadFlashPage_1              ; Repeat until byte count is zero.
+
+       out     RAMPZ, r19
+       ret
+
+
+
+; ---
+; This routine writes the page buffer to the Flash page at address R25:R24:R23:R22
+; in the application section. The address can point anywhere inside the page.
+;
+; Input:
+;     R25:R24:R23:R22 - Byte address into Flash page.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text         
+.global SP_WriteApplicationPage
+
+SP_WriteApplicationPage:
+       in      r19, RAMPZ                       ; Save RAMPZ, which is restored in SP_CommonSPM.
+       out     RAMPZ, r24                       ; Load RAMPZ with the MSB of the address.
+       movw    r24, r22                         ; Move low bytes of address to ZH:ZL from R23:R22
+       ldi     r20, NVM_CMD_WRITE_APP_PAGE_gc   ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                     ; Jump to common SPM code.
+
+
+
+; ---
+; This routine erases first and then writes the page buffer to the
+; Flash page at address R25:R24:R23:R22 in the application section. The address
+; can point anywhere inside the page.
+;
+; Input:
+;     R25:R24:R23:R22 - Byte address into Flash page.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_EraseWriteApplicationPage
+
+SP_EraseWriteApplicationPage:
+       in      r19, RAMPZ                            ; Save RAMPZ, which is restored in SP_CommonSPM.
+       out     RAMPZ, r24                            ; Load RAMPZ with the MSB of the address.
+       movw    r24, r22                              ; Move low bytes of address to ZH:ZL from R23:R22
+       ldi     r20, NVM_CMD_ERASE_WRITE_APP_PAGE_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                          ; Jump to common SPM code.
+
+
+
+; ---
+; This routine flushes the Flash page buffer.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text         
+.global SP_EraseFlashBuffer
+
+SP_EraseFlashBuffer:
+       in      r19, RAMPZ                          ; Save RAMPZ, which is restored in SP_CommonSPM.
+       ldi     r20, NVM_CMD_ERASE_FLASH_BUFFER_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                        ; Jump to common SPM code.
+
+
+
+; ---
+; This routine erases the page at address R25:R24:R23:R22 in the Boot section. The
+; address can point anywhere inside the page.
+;
+; Input:
+;     R25:R24:R23:R22 - Byte address into Flash page.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text         
+.global SP_EraseBootPage
+
+SP_EraseBootPage:
+       in      r19, RAMPZ                         ; Save RAMPZ, which is restored in SP_CommonSPM.
+       out     RAMPZ, r24                         ; Load RAMPZ with the MSB of the address.
+       movw    r24, r22                           ; Move low bytes of address to ZH:ZL from R23:R22
+       ldi     r20, NVM_CMD_ERASE_BOOT_PAGE_gc    ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                       ; Jump to common SPM code.
+
+
+
+; ---
+; This routine writes the page buffer to the Flash page at address R25:R24:R23:R22
+; in the BOOT section. The address can point anywhere inside the page.
+;
+; Input:
+;     R25:R24:R23:R22 - Byte address into Flash page.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text         
+.global SP_WriteBootPage
+
+SP_WriteBootPage:
+       in      r19, RAMPZ                       ; Save RAMPZ, which is restored in SP_CommonSPM.
+       out     RAMPZ, r24                       ; Load RAMPZ with the MSB of the address.
+       movw    r24, r22                         ; Move low bytes of address to ZH:ZL from R23:R22
+       ldi     r20, NVM_CMD_WRITE_BOOT_PAGE_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                     ; Jump to common SPM code.
+
+
+
+; ---
+; This routine erases first and then writes the page buffer to the
+; Flash page at address R25:R24:R23:R22 in the Boot section. The address
+; can point anywhere inside the page.
+;
+; Input:
+;     R25:R24:R23:R22 - Byte address into Flash page.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text         
+.global SP_EraseWriteBootPage
+
+SP_EraseWriteBootPage:
+       in      r19, RAMPZ                             ; Save RAMPZ, which is restored in SP_CommonSPM.
+       out     RAMPZ, r24                             ; Load RAMPZ with the MSB of the address.
+       movw    r24, r22                               ; Move low bytes of address to ZH:ZL from R23:R22
+       ldi     r20, NVM_CMD_ERASE_WRITE_BOOT_PAGE_gc  ; Prepare NVM command in R20.
+       jmp     SP_CommonSPM                           ; Jump to common SPM code.
+
+
+
+; ---
+; This routine calculates a CRC for the application section.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bit used).
+; ---
+
+.section .text 
+.global SP_ApplicationCRC
+
+SP_ApplicationCRC:
+       ldi     r20, NVM_CMD_APP_CRC_gc    ; Prepare NVM command in R20.
+       rjmp    SP_CommonCMD               ; Jump to common NVM Action code.
+
+
+
+; ---
+; This routine calculates a CRC for the Boot section.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bit used).
+; ---
+
+.section .text
+.global SP_BootCRC
+
+SP_BootCRC:
+       ldi     r20, NVM_CMD_BOOT_CRC_gc   ; Prepare NVM command in R20.
+       rjmp    SP_CommonCMD               ; Jump to common NVM Action code.
+
+
+
+; ---
+; This routine locks all further access to SPM operations until next reset.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_LockSPM
+
+SP_LockSPM:
+       ldi     r18, CCP_IOREG_gc     ; Prepare Protect IO-register signature in R18.
+       sts     CCP, r18              ; Enable IO-register operation (this disables interrupts for 4 cycles).
+       ldi     r18, NVM_SPMLOCK_bm   ; Prepare bitmask for locking SPM into R18.
+       sts     NVM_CTRLB, r18        ; Load bitmask into NVM Control Register B, which locks SPM.
+       ret
+       
+
+
+; ---
+; This routine wait for the SPM to finish and clears the command register.
+;
+; Note that this routine is blocking, and will halt any execution until the SPM
+; is finished.
+;
+; Input:
+;     Nothing.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+.global SP_WaitForSPM          
+
+SP_WaitForSPM:
+       lds     r18, NVM_STATUS     ; Load the NVM Status register.
+       sbrc    r18, NVM_NVMBUSY_bp ; Check if bit is cleared.
+       rjmp    SP_WaitForSPM       ; Repeat check if bit is not cleared.
+       clr     r18
+       sts     NVM_CMD, r18        ; Clear up command register to NO_OPERATION.
+       ret
+
+
+
+; ---
+; This routine is called by several other routines, and contains common code
+; for executing an NVM command, including the return statement itself.
+;
+; If the operation (NVM command) requires the NVM Address registers to be
+; prepared, this must be done before jumping to this routine.
+;
+; Note that R25:R24:R23:R22 is used for returning results, even if the
+; C-domain calling function only expects a single byte or even void.
+;
+; Input:
+;     R20 - NVM Command code.
+;
+; Returns:
+;     R25:R24:R23:R22 - 32-bit result from NVM operation.
+; ---
+
+.section .text         
+
+SP_CommonCMD:
+       sts     NVM_CMD, r20        ; Load command into NVM Command register.
+       ldi     r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
+       ldi     r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
+       sts     CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
+       sts     NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
+       lds     r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
+       lds     r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
+       lds     r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
+       clr     r25                 ; Clear R25 in order to return a clean 32-bit value.
+       ret
+
+
+
+; ---
+; This routine is called by several other routines, and contains common code
+; for executing an LPM command, including the return statement itself.
+;
+; Note that R24 is used for returning results, even if the
+; C-domain calling function expects a void.
+;
+; Input:
+;     R25:R24 - Low bytes of Z pointer.
+;     R20     - NVM Command code.
+;
+; Returns:
+;     R24     - Result from LPM operation.
+; ---
+
+.section .text         
+
+SP_CommonLPM:
+       movw    ZL, r24             ; Load index into Z.
+       sts     NVM_CMD, r20        ; Load prepared command into NVM Command register.
+       lpm     r24,Z
+       ret
+
+
+
+; ---
+; This routine is called by several other routines, and contains common code
+; for executing an SPM command, including the return statement itself.
+;
+; If the operation (SPM command) requires the R1:R0 registers to be
+; prepared, this must be done before jumping to this routine.
+;
+; Note that you must define "-Wl,--section-start=.BOOT=0x020000" for the
+; linker to place this function in the boot section with the correct address.
+;
+; Input:
+;     R1:R0    - Optional input to SPM command.
+;     R25:R24  - Low bytes of Z pointer.
+;     R20      - NVM Command code.
+;
+; Returns:
+;     Nothing.
+; ---
+
+.section .text
+
+SP_CommonSPM:
+       movw    ZL, r24          ; Load R25:R24 into Z.
+       sts     NVM_CMD, r20     ; Load prepared command into NVM Command register.
+       ldi     r18, CCP_SPM_gc  ; Prepare Protect SPM signature in R18
+       sts     CCP, r18         ; Enable SPM operation (this disables interrupts for 4 cycles).
+       spm                      ; Self-program.
+       clr     r1               ; Clear R1 for GCC _zero_reg_ to function properly.
+       out     RAMPZ, r19       ; Restore RAMPZ register.
+       ret
+       
+       
+; END OF FILE
+
diff --git a/src/xboot/sp_driver.h b/src/xboot/sp_driver.h
new file mode 100644 (file)
index 0000000..0bcbce9
--- /dev/null
@@ -0,0 +1,298 @@
+/* This file has been prepared for Doxygen automatic documentation generation.*/
+/*! \file *********************************************************************
+ *
+ * \brief  XMEGA Self-programming driver header file.
+ *
+ *      This file contains the function prototypes for the
+ *      XMEGA Self-programming driver.
+ *      If any SPM instructions are used, the linker file must define
+ *      a segment named BOOT which must be located in the device boot section.
+ *
+ *
+ *      None of these functions clean up the NVM Command Register after use.
+ *      It is therefore important to write NVMCMD_NO_OPERATION (0x00) to this
+ *      register when you are finished using any of the functions in this
+ *      driver.
+ *
+ *      For all functions, it is important that no interrupt handlers perform
+ *      any NVM operations. The user must implement a scheme for mutually
+ *      exclusive access to the NVM. However, the 4-cycle timeout will work
+ *      fine, since writing to the Configuration Change Protection register
+ *      (CCP) automatically disables interrupts for 4 instruction cycles.
+ *
+ * \par Application note:
+ *      AVR1316: XMEGA Self-programming
+ *
+ * \par Documentation
+ *      For comprehensive code documentation, supported compilers, compiler
+ *      settings and supported devices see readme.html
+ *
+ * \author
+ *      Atmel Corporation: http://www.atmel.com \n
+ *      Support email: avr@atmel.com
+ *
+ * $Revision: 1691 $
+ * $Date: 2008-07-29 13:25:40 +0200 (ti, 29 jul 2008) $  \n
+ *
+ * Copyright (c) 2008, Atmel Corporation All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. The name of ATMEL may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
+ * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#ifndef SP_DRIVER_H
+#define SP_DRIVER_H
+
+//#include "avr_compiler.h"
+//#include "Flash_Defines.h"
+
+
+
+/* Define the size of the flash page if not defined in the header files. */
+#ifndef APP_SECTION_PAGE_SIZE
+#error  APP_SECTION_PAGE_SIZE must be defined if not defined in header files.
+//#define APP_SECTION_PAGE_SIZE 512
+#endif /*FLASH_PAGE_SIZE*/
+
+// Define the Start of the application table if not defined in the header files.
+#ifndef APPTABLE_SECTION_START
+#error  APPTABLE_SECTION_START must be defined if not defined in header files.
+//#define APPTABLE_SECTION_START 0x01E000 //APPTABLE address for ATxmega128A1
+#endif /*APPTABLE_SECTION_START*/
+
+/*! \brief Read a byte from flash.
+ *
+ *  This function reads one byte from the flash.
+ *
+ *  \note Both IAR and GCC have functions to do this, but
+ *        we include the fucntions for easier use.
+ *
+ *  \param address Address to the location of the byte to read.
+ *
+ *  \retval Byte read from flash.
+ */
+uint8_t SP_ReadByte(uint32_t address);
+
+/*! \brief Read a word from flash.
+ *
+ *  This function reads one word from the flash.
+ *
+ *  \note Both IAR and GCC have functions to do this automatically, but
+ *        we include the fucntions for easier use.
+ *
+ *  \param address Address to the location of the word to read.
+ *
+ *  \retval word read from flash.
+ */
+uint16_t SP_ReadWord(uint32_t address);
+
+/*! \brief Read calibration byte at given index.
+ *
+ *  This function reads one calibration byte from the Calibration signature row.
+ *
+ *  \param index  Index of the byte in the calibration signature row.
+ *
+ *  \retval Calibration byte
+ */
+uint8_t SP_ReadCalibrationByte(uint8_t index);
+
+/*! \brief Read fuse byte from given index.
+ *
+ *  This function reads the fuse byte at the given index.
+ *
+ *  \param index  Index of the fuse byte.
+ *
+ *  \retval Fuse byte
+ */
+uint8_t SP_ReadFuseByte(uint8_t index);
+
+/*! \brief Write lock bits.
+ *
+ *  This function changes the lock bits.
+ *
+ *  \note It is only possible to change the lock bits to a higher security l
+ *  evel.
+ *
+ *  \param data  The new value of the lock bits.
+ */
+void SP_WriteLockBits(uint8_t data);
+
+/*! \brief Read lock bits.
+ *
+ *  This function reads the lock bits.
+ *
+ *  \retval Lock bits
+ */
+uint8_t SP_ReadLockBits(void);
+
+/*! \brief Read user signature at given index.
+ *
+ *  This function reads one byte from the user signature row.
+ *
+ *  \param index  Index of the byte in the user signature row.
+ *
+ *  \retval User signature byte
+ */
+uint8_t SP_ReadUserSignatureByte(uint16_t index);
+
+/*! \brief Erase user signature row.
+ *
+ *  This function erase the entire user signature row.
+ */
+void SP_EraseUserSignatureRow(void);
+
+/*! \brief Write user signature row.
+ *
+ *  This function write the flash buffer in the user signature row.
+ */
+void SP_WriteUserSignatureRow(void);
+
+/*! \brief Erase entire application section.
+ *
+ *  This function erases the entire application and application table section
+ *
+ *  \note If the lock bits is set to not allow spm in the application or
+ *        application table section the erase is not done.
+ */
+void SP_EraseApplicationSection(void);
+
+/*! \brief Erase page at byte address in application or application table
+ * section.
+ *
+ *  This function erase one page given by the byte address.
+ *
+ *  \param address Byte address for flash page.
+ */
+void SP_EraseApplicationPage(uint32_t address);
+
+/*! \brief Erase and write page buffer to application or application table
+ *  section at byte address.
+ *
+ *  This function does a combined erase and write to a flash page in the
+ *  application or application table section.
+ *
+ *  \param address Byte address for flash page.
+ */
+void SP_EraseWriteApplicationPage(uint32_t address);
+
+/*! \brief Write page buffer to application or application table section at
+ *  byte address.
+ *
+ *  This function writes the Flash page buffer to a page in the application or
+ *  application table section given by the byte address.
+ *
+ *  \note The page that is written to must be erased before it is written to.
+ *
+ *  \param address Byte address for flash page.
+ */
+void SP_WriteApplicationPage(uint32_t address);
+
+/*! \brief Load one word into Flash page buffer.
+ *
+ *  This function Loads one word into the Flash page buffer.
+ *
+ *  \param  address   Position in inside the flash page buffer.
+ *  \param  data      Value to be put into the buffer.
+ */
+void SP_LoadFlashWord(uint16_t address, uint16_t data);
+
+/*! \brief Load entire page from SRAM buffer into Flash page buffer.
+ *
+ *  This function load an entire page from SRAM.
+ *
+ *     \param data   Pointer to the data to put in buffer.
+ *
+ *     \note The __near keyword limits the pointer to two bytes which means that
+ *        only data up to 64K (internal SRAM) can be used.
+ */
+void SP_LoadFlashPage(const uint8_t * data);
+
+/*! \brief Read entire Flash page into SRAM buffer.
+ *
+ *  This function reads an entire flash page and puts it to SRAM.
+ *
+ *     \param data      Pointer to where to store the data.
+ *     \param address   Address to page to read from flash.
+ */
+void SP_ReadFlashPage(const uint8_t * data, uint32_t address);
+
+/*! \brief Flush Flash page buffer.
+ *
+ *  This function flush the Flash page buffer.
+ */
+void SP_EraseFlashBuffer(void);
+
+/*! \brief Erase page at byte address in boot section.
+ *
+ *  This function erase one page given by the byte address.
+ *
+ *  \param address Byte address for flash page.
+ */
+void SP_EraseBootPage(uint32_t address);
+
+/*! \brief Erase and write page buffer to boot section at byte address.
+ *
+ *  This function does a combined erase and write to a flash page in the boot
+ *  section.
+ *
+ *  \param address Byte address for flash page.
+ */
+void SP_EraseWriteBootPage(uint32_t address);
+
+/*! \brief Write page buffer to boot section at byte address.
+ *
+ *  This function writes the Flash page buffer to a page in the boot section
+ *  given by the byte address.
+ *
+ *  \note The page that is written to must be erased before it is written to.
+ *
+ *  \param address Byte address for flash page.
+ */
+void SP_WriteBootPage(uint32_t address);
+
+/*! \brief Generate CRC from application section.
+ *
+ *  \retval 24-bit CRC value
+ */
+uint32_t SP_ApplicationCRC(void);
+
+/*! \brief Generate CRC from boot section.
+ *
+ *  \retval 24-bit CRC value
+ */
+uint32_t SP_BootCRC(void);
+
+/*! \brief Lock SPM instruction.
+ *
+ *   This function locks the SPM instruction, and will disable the use of
+ *   SPM until the next reset occurs.
+ */
+void SP_LockSPM(void);
+
+/*! \brief Wait for SPM to finish.
+ *
+ *   This routine waits for the SPM to finish and clears the command register.
+ */
+void SP_WaitForSPM(void);
+
+#endif /* SP_DRIVER_H */
diff --git a/src/xboot/uart.c b/src/xboot/uart.c
new file mode 100644 (file)
index 0000000..c4cf091
--- /dev/null
@@ -0,0 +1,72 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* UART Module                                                          */
+/*                                                                      */
+/* uart.c                                                               */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#include "uart.h"
+#include "xboot.h"
+
+
+bool uart_has_char() {return UART_DEVICE.STATUS & USART_RXCIF_bm;}
+uint8_t uart_recv_char() {return UART_DEVICE.DATA;}
+
+
+void uart_send_char_blocking(uint8_t c) {
+  UART_DEVICE.DATA = c;
+  while (!(UART_DEVICE.STATUS & USART_TXCIF_bm)) continue;
+  UART_DEVICE.STATUS |= USART_TXCIF_bm;
+}
+
+
+void uart_init() {
+  UART_PORT.DIRSET = 1 << UART_TX_PIN;
+  UART_DEVICE.BAUDCTRLA = UART_BSEL_VALUE & USART_BSEL_gm;
+  UART_DEVICE.BAUDCTRLB =
+    ((UART_BSCALE_VALUE << USART_BSCALE_gp) & USART_BSCALE_gm) |
+    ((UART_BSEL_VALUE >> 8) & ~USART_BSCALE_gm);
+
+#if UART_CLK2X
+  UART_DEVICE.CTRLB = USART_RXEN_bm | USART_CLK2X_bm | USART_TXEN_bm;
+#else
+  UART_DEVICE.CTRLB = USART_RXEN_bm | USART_TXEN_bm;
+#endif // UART_CLK2X
+
+  PORTC.OUTCLR = 1 << 4; // CTS Lo (enable)
+  PORTC.DIRSET = 1 << 4; // CTS Output
+}
+
+
+void uart_deinit() {
+  UART_DEVICE.CTRLB = 0;
+  UART_DEVICE.BAUDCTRLA = 0;
+  UART_DEVICE.BAUDCTRLB = 0;
+  UART_PORT.DIRCLR = 1 << UART_TX_PIN;
+}
diff --git a/src/xboot/uart.h b/src/xboot/uart.h
new file mode 100644 (file)
index 0000000..0392f7e
--- /dev/null
@@ -0,0 +1,43 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* UART Module                                                          */
+/*                                                                      */
+/* uart.h                                                               */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool uart_has_char();
+uint8_t uart_recv_char();
+void uart_send_char_blocking(uint8_t c);
+void uart_init(void);
+void uart_deinit(void);
diff --git a/src/xboot/watchdog.c b/src/xboot/watchdog.c
new file mode 100644 (file)
index 0000000..a7e1064
--- /dev/null
@@ -0,0 +1,51 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* Watchdog Module                                                      */
+/*                                                                      */
+/* watchdog.c                                                           */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#include "watchdog.h"
+
+
+void WDT_EnableAndSetTimeout() {
+  uint8_t temp = WDT_ENABLE_bm | WDT_CEN_bm | WATCHDOG_TIMEOUT;
+  CCP = CCP_IOREG_gc;
+  WDT.CTRL = temp;
+
+  // Wait for WD to synchronize with new settings.
+  while (WDT_IsSyncBusy()) continue;
+}
+
+
+void WDT_Disable() {
+  uint8_t temp = (WDT.CTRL & ~WDT_ENABLE_bm) | WDT_CEN_bm;
+  CCP = CCP_IOREG_gc;
+  WDT.CTRL = temp;
+}
diff --git a/src/xboot/watchdog.h b/src/xboot/watchdog.h
new file mode 100644 (file)
index 0000000..f9fcc74
--- /dev/null
@@ -0,0 +1,42 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* Watchdog Module                                                      */
+/*                                                                      */
+/* watchdog.c                                                           */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#pragma once
+
+#include "xboot.h"
+
+#define WDT_IsSyncBusy() (WDT.STATUS & WDT_SYNCBUSY_bm)
+#define WDT_Reset() asm("wdr")
+
+void WDT_EnableAndSetTimeout();
+void WDT_Disable();
diff --git a/src/xboot/xboot.c b/src/xboot/xboot.c
new file mode 100644 (file)
index 0000000..7fd5ac0
--- /dev/null
@@ -0,0 +1,401 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* xboot.c                                                              */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#include "xboot.h"
+
+#include <util/delay.h>
+
+#include <stdbool.h>
+
+
+uint8_t buffer[SPM_PAGESIZE];
+
+
+void NVM_Wait() {
+  while (NVM_STATUS & NVM_NVMBUSY_bp)
+    // reset watchdog while waiting for erase completion
+    WDT_Reset();
+}
+
+
+int main() {
+  // Initialization section
+  // Entry point and communication methods are initialized here
+  // --------------------------------------------------
+#ifdef USE_32MHZ_RC
+#if (F_CPU != 32000000L)
+#error F_CPU must match oscillator setting!
+#endif // F_CPU
+  OSC.CTRL |= OSC_RC32MEN_bm; // turn on 32 MHz oscillator
+  while (!(OSC.STATUS & OSC_RC32MRDY_bm)) continue; // wait for it to start
+  CCP = CCP_IOREG_gc;
+  CLK.CTRL = CLK_SCLKSEL_RC32M_gc;
+#ifdef USE_DFLL
+  OSC.CTRL |= OSC_RC32KEN_bm;
+  while(!(OSC.STATUS & OSC_RC32KRDY_bm));
+  DFLLRC32M.CTRL = DFLL_ENABLE_bm;
+#endif // USE_DFLL
+#else // USE_32MHZ_RC
+#if (F_CPU != 2000000L)
+#error F_CPU must match oscillator setting!
+#endif // F_CPU
+#ifdef USE_DFLL
+  OSC.CTRL |= OSC_RC32KEN_bm;
+  while(!(OSC.STATUS & OSC_RC32KRDY_bm));
+  DFLLRC2M.CTRL = DFLL_ENABLE_bm;
+#endif // USE_DFLL
+#endif // USE_32MHZ_RC
+
+  // Initialize UART
+  uart_init();
+
+  // End initialization section
+
+  uint32_t address = 0;
+  uint16_t i = 0;
+
+  bool in_bootloader = false;
+  uint16_t j = ENTER_WAIT;
+  while (!in_bootloader && 0 < j--) {
+    // Check for trigger
+    if (uart_has_char()) in_bootloader = uart_recv_char() == CMD_SYNC;
+
+    _delay_ms(1);
+  }
+
+  WDT_EnableAndSetTimeout();
+
+  // Main bootloader
+  while (in_bootloader) {
+    uint8_t val = get_char();
+    WDT_Reset();
+
+    // Main bootloader parser
+    switch (val) {
+    case CMD_CHECK_AUTOINCREMENT: send_char(REPLY_YES); break;
+
+    case CMD_SET_ADDRESS:
+      address = get_word();
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_SET_EXT_ADDRESS:
+      address = ((uint32_t)get_char() << 16) | get_word();
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_CHIP_ERASE:
+      // Erase the application section
+      SP_EraseApplicationSection();
+
+      // Wait for completion
+      NVM_Wait();
+
+      // Erase EEPROM
+      EEPROM_erase_all();
+
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_CHECK_BLOCK_SUPPORT:
+      send_char(REPLY_YES);
+      // Send block size (page size)
+      send_char((SPM_PAGESIZE >> 8) & 0xFF);
+      send_char(SPM_PAGESIZE & 0xFF);
+      break;
+
+    case CMD_BLOCK_LOAD:
+      i = get_word(); // Block size
+      val = get_char(); // Memory type
+      send_char(BlockLoad(i, val, &address)); // Load it
+      break;
+
+    case CMD_BLOCK_READ:
+      i = get_word(); // Block size
+      val = get_char(); // Memory type
+      BlockRead(i, val, &address); // Read it
+      break;
+
+    case CMD_READ_BYTE: {
+      unsigned w = SP_ReadWord((address << 1));
+
+      send_char(w >> 8);
+      send_char(w);
+
+      address++;
+      break;
+    }
+
+    case CMD_WRITE_LOW_BYTE:
+      i = get_char(); // get low byte
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_WRITE_HIGH_BYTE:
+      i |= (get_char() << 8); // get high byte; combine
+      SP_LoadFlashWord((address << 1), i);
+      address++;
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_WRITE_PAGE:
+      if (address >= (APP_SECTION_SIZE >> 1))
+        send_char(REPLY_ERROR); // don't allow bootloader overwrite
+
+      else {
+        SP_WriteApplicationPage(address << 1);
+        send_char(REPLY_ACK);
+      }
+      break;
+
+    case CMD_WRITE_EEPROM_BYTE:
+      EEPROM_write_byte(address, get_char());
+      address++;
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_READ_EEPROM_BYTE:
+      send_char(EEPROM_read_byte(address));
+      address++;
+      break;
+
+    case CMD_READ_LOW_FUSE_BITS: send_char(SP_ReadFuseByte(0)); break;
+    case CMD_READ_HIGH_FUSE_BITS: send_char(SP_ReadFuseByte(1)); break;
+    case CMD_READ_EXT_FUSE_BITS: send_char(SP_ReadFuseByte(2)); break;
+
+    case CMD_ENTER_PROG_MODE: case CMD_LEAVE_PROG_MODE:
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_EXIT_BOOTLOADER:
+      in_bootloader = false;
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_PROGRAMMER_TYPE: send_char('S'); break; // serial
+
+    case CMD_DEVICE_CODE:
+      send_char(123); // send only this device
+      send_char(0); // terminator
+      break;
+
+    case CMD_SET_LED: case CMD_CLEAR_LED: case CMD_SET_TYPE:
+      get_char(); // discard parameter
+      send_char(REPLY_ACK);
+      break;
+
+    case CMD_PROGRAM_ID:
+      send_char('X');
+      send_char('B');
+      send_char('o');
+      send_char('o');
+      send_char('t');
+      send_char('+');
+      send_char('+');
+      break;
+
+    case CMD_VERSION:
+      send_char('0' + XBOOT_VERSION_MAJOR);
+      send_char('0' + XBOOT_VERSION_MINOR);
+      break;
+
+    case CMD_READ_SIGNATURE:
+      send_char(SIGNATURE_2);
+      send_char(SIGNATURE_1);
+      send_char(SIGNATURE_0);
+      break;
+
+    case CMD_CRC: {
+      uint32_t start = 0;
+      uint32_t length = 0;
+      uint16_t crc;
+
+      val = get_char();
+
+      switch (val) {
+      case SECTION_FLASH: length = PROGMEM_SIZE; break;
+      case SECTION_APPLICATION: length = APP_SECTION_SIZE; break;
+      case SECTION_BOOT:
+        start = BOOT_SECTION_START;
+        length = BOOT_SECTION_SIZE;
+        break;
+
+      default:
+        send_char(REPLY_ERROR);
+        continue;
+      }
+
+      crc = crc16_block(start, length);
+
+      send_char((crc >> 8) & 0xff);
+      send_char(crc & 0xff);
+      break;
+    }
+
+    case CMD_SYNC: break; // ESC (0x1b) to sync
+
+    default: // otherwise, error
+      send_char(REPLY_ERROR);
+      break;
+    }
+
+    // Wait for any lingering SPM instructions to finish
+    NVM_Wait();
+  }
+
+  // Bootloader exit
+  // Code here runs after the bootloader has exited,
+  // but before the application code has started
+
+  // Shut down UART
+  uart_deinit();
+
+  WDT_Disable();
+
+  // Jump into main code
+  asm("jmp 0");
+}
+
+
+uint8_t get_char() {
+  while (!uart_has_char()) continue;
+  return uart_recv_char();
+}
+
+
+void send_char(uint8_t c) {uart_send_char_blocking(c);}
+uint16_t get_word() {return (get_char() << 8) | get_char();}
+
+
+uint8_t BlockLoad(unsigned size, uint8_t mem, uint32_t *address) {
+  WDT_Reset();
+
+  // fill up buffer
+  for (int i = 0; i < SPM_PAGESIZE; i++) {
+    char c = 0xff;
+    if (i < size) c = get_char();
+    buffer[i] = c;
+  }
+
+  switch (mem) {
+  case MEM_EEPROM:
+    EEPROM_write_block(*address, buffer, size);
+    *address += size;
+    break;
+
+  case MEM_FLASH:
+    // NOTE: Flash programming, address is given in words.
+    SP_LoadFlashPage(buffer);
+    SP_EraseWriteApplicationPage(*address << 1);
+    *address += size >> 1;
+    NVM_Wait();
+    break;
+
+  case MEM_USERSIG:
+    SP_LoadFlashPage(buffer);
+    SP_EraseUserSignatureRow();
+    NVM_Wait();
+    SP_WriteUserSignatureRow();
+    NVM_Wait();
+    break;
+
+  default: return REPLY_ERROR;
+  }
+
+  return REPLY_ACK;
+}
+
+
+
+void BlockRead(unsigned size, uint8_t mem, uint32_t *address) {
+  int offset = 0;
+  int size2 = size;
+
+  switch (mem) {
+  case MEM_EEPROM:
+    EEPROM_read_block(*address, buffer, size);
+    (*address) += size;
+    break;
+
+  case MEM_FLASH: case MEM_USERSIG: case MEM_PRODSIG: {
+    *address <<= 1; // Convert address to bytes temporarily
+
+    do {
+      switch (mem) {
+      case MEM_FLASH: buffer[offset++] = SP_ReadByte(*address); break;
+
+      case MEM_USERSIG:
+        buffer[offset++] = SP_ReadUserSignatureByte(*address);
+        break;
+
+      case MEM_PRODSIG:
+        buffer[offset++] = SP_ReadCalibrationByte(*address);
+        break;
+      }
+
+      NVM_Wait();
+
+      (*address)++;    // Select next word in memory.
+      size--;          // Subtract two bytes from number of bytes to read
+    } while (size);    // Repeat until all block has been read
+
+    *address >>= 1;    // Convert address back to Flash words again.
+    break;
+  }
+
+  default: send_char(REPLY_ERROR); break;
+  }
+
+  // send bytes
+  for (int i = 0; i < size2; i++)
+    send_char(buffer[i]);
+}
+
+
+uint16_t crc16_block(uint32_t start, uint32_t length) {
+  uint16_t crc = 0;
+
+  int bc = SPM_PAGESIZE;
+
+  for (; length > 0; length--) {
+    if (bc == SPM_PAGESIZE) {
+      SP_ReadFlashPage(buffer, start);
+      start += SPM_PAGESIZE;
+      bc = 0;
+    }
+
+    crc = _crc16_update(crc, buffer[bc]);
+
+    bc++;
+  }
+
+  return crc;
+}
diff --git a/src/xboot/xboot.h b/src/xboot/xboot.h
new file mode 100644 (file)
index 0000000..7b327d7
--- /dev/null
@@ -0,0 +1,168 @@
+/************************************************************************/
+/* XBoot Extensible AVR Bootloader                                      */
+/*                                                                      */
+/* xboot.h                                                              */
+/*                                                                      */
+/* Alex Forencich <alex@alexforencich.com>                              */
+/*                                                                      */
+/* Copyright (c) 2010 Alex Forencich                                    */
+/*                                                                      */
+/* Permission is hereby granted, free of charge, to any person          */
+/* obtaining a copy of this software and associated documentation       */
+/* files(the "Software"), to deal in the Software without restriction,  */
+/* including without limitation the rights to use, copy, modify, merge, */
+/* publish, distribute, sublicense, and/or sell copies of the Software, */
+/* and to permit persons to whom the Software is furnished to do so,    */
+/* subject to the following conditions:                                 */
+/*                                                                      */
+/* The above copyright notice and this permission notice shall be       */
+/* included in all copies or substantial portions of the Software.      */
+/*                                                                      */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                */
+/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS  */
+/* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN   */
+/* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN    */
+/* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
+/* SOFTWARE.                                                            */
+/*                                                                      */
+/************************************************************************/
+
+#pragma once
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/crc16.h>
+
+// token pasting
+#define CAT2_int(x, y) x ## y
+#define CAT2(x, y) CAT2_int(x, y)
+#define CAT3_int(x, y, z) x ## y ## z
+#define CAT3(x, y, z) CAT3_int(x, y, z)
+
+// Version
+#define XBOOT_VERSION_MAJOR 1
+#define XBOOT_VERSION_MINOR 7
+
+// Configuration
+#define ENTER_WAIT 1000 // In ms
+#define UART_BAUD_RATE 115200
+#define UART_NUMBER 0
+#define UART_PORT_NAME C
+#define USE_AVR1008_EEPROM
+#define USE_DFLL
+#define WATCHDOG_TIMEOUT WDT_PER_1KCLK_gc
+
+// clock config
+// use 32MHz osc if makefile calls for it
+#if (F_CPU == 32000000L)
+// defaults to 2MHz RC oscillator
+// define USE_32MHZ_RC to override
+#define USE_32MHZ_RC
+#endif // F_CPU
+
+// UART RS485 Enable Output
+#define UART_EN_PORT            CAT2(PORT, UART_EN_PORT_NAME)
+
+#if (UART_NUMBER == 0)
+#define UART_RX_PIN             2
+#define UART_TX_PIN             3
+#else
+#define UART_RX_PIN             6
+#define UART_TX_PIN             7
+#endif
+#define UART_PORT               CAT2(PORT, UART_PORT_NAME)
+#define UART_DEVICE_PORT        CAT2(UART_PORT_NAME, UART_NUMBER)
+#define UART_DEVICE             CAT2(USART, UART_DEVICE_PORT)
+#define UART_DEVICE_RXC_ISR     CAT3(USART, UART_DEVICE_PORT, _RXC_vect)
+#define UART_DEVICE_DRE_ISR     CAT3(USART, UART_DEVICE_PORT, _DRE_vect)
+#define UART_DEVICE_TXC_ISR     CAT3(USART, UART_DEVICE_PORT, _TXC_vect)
+#define UART_RX_PIN_CTRL        CAT3(UART_PORT.PIN, UART_RX_PIN, CTRL)
+#define UART_TX_PIN_CTRL        CAT3(UART_PORT.PIN, UART_TX_PIN, CTRL)
+
+// BAUD Rate Values
+// Known good at 2MHz
+#if (F_CPU == 2000000L) && (UART_BAUD_RATE == 19200)
+#define UART_BSEL_VALUE         12
+#define UART_BSCALE_VALUE       0
+#define UART_CLK2X              1
+#elif (F_CPU == 2000000L) && (UART_BAUD_RATE == 38400)
+#define UART_BSEL_VALUE         22
+#define UART_BSCALE_VALUE       -2
+#define UART_CLK2X              1
+#elif (F_CPU == 2000000L) && (UART_BAUD_RATE == 57600)
+#define UART_BSEL_VALUE         26
+#define UART_BSCALE_VALUE       -3
+#define UART_CLK2X              1
+#elif (F_CPU == 2000000L) && (UART_BAUD_RATE == 115200)
+#define UART_BSEL_VALUE         19
+#define UART_BSCALE_VALUE       -4
+#define UART_CLK2X              1
+
+// Known good at 32MHz
+#elif (F_CPU == 32000000L) && (UART_BAUD_RATE == 19200)
+#define UART_BSEL_VALUE         103
+#define UART_BSCALE_VALUE       0
+#define UART_CLK2X              0
+#elif (F_CPU == 32000000L) && (UART_BAUD_RATE == 38400)
+#define UART_BSEL_VALUE         51
+#define UART_BSCALE_VALUE       0
+#define UART_CLK2X              0
+#elif (F_CPU == 32000000L) && (UART_BAUD_RATE == 57600)
+#define UART_BSEL_VALUE         34
+#define UART_BSCALE_VALUE       0
+#define UART_CLK2X              0
+#elif (F_CPU == 32000000L) && (UART_BAUD_RATE == 115200)
+#define UART_BSEL_VALUE         1047
+#define UART_BSCALE_VALUE       -6
+#define UART_CLK2X              0
+
+#else // None of the above, so calculate something
+#warning Not using predefined BAUD rate, possible BAUD rate error!
+#if (F_CPU == 2000000L)
+#define UART_BSEL_VALUE         ((F_CPU) / ((uint32_t)UART_BAUD_RATE * 8) - 1)
+#define UART_BSCALE_VALUE       0
+#define UART_CLK2X              1
+
+#else
+#define UART_BSEL_VALUE         ((F_CPU) / ((uint32_t)UART_BAUD_RATE * 16) - 1)
+#define UART_BSCALE_VALUE       0
+#define UART_CLK2X              0
+#endif
+#endif
+
+#ifndef EEPROM_PAGE_SIZE
+#define EEPROM_PAGE_SIZE E2PAGESIZE
+#endif
+
+#ifndef EEPROM_BYTE_ADDRESS_MASK
+#if EEPROM_PAGE_SIZE == 32
+#define EEPROM_BYTE_ADDRESS_MASK 0x1f
+#elif EEPROM_PAGE_SIZE == 16
+#define EEPROM_BYTE_ADDRESS_MASK = 0x0f
+#elif EEPROM_PAGE_SIZE == 8
+#define EEPROM_BYTE_ADDRESS_MASK = 0x07
+#elif EEPROM_PAGE_SIZE == 4
+#define EEPROM_BYTE_ADDRESS_MASK = 0x03
+#else
+#error Unknown EEPROM page size!  Please add new byte address value!
+#endif
+#endif
+
+// Includes
+#include "protocol.h"
+#include "sp_driver.h"
+#include "eeprom_driver.h"
+#include "uart.h"
+#include "watchdog.h"
+
+// Functions
+uint8_t get_char(void);
+void send_char(uint8_t c);
+uint16_t get_word(void);
+
+uint8_t BlockLoad(unsigned size, uint8_t mem, uint32_t *address);
+void BlockRead(unsigned size, uint8_t mem, uint32_t *address);
+
+uint16_t crc16_block(uint32_t start, uint32_t length);