/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See the file COPYING * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2010 Frank van Maarseveen */ #ifndef _BOOT_H_ #define _BOOT_H_ /* * Example bootloader makefile changes: * * PROGRAM_BASE = 0x0800 * CFLAGS += -DPROGRAM_BASE=$(PROGRAM_BASE) * LDFLAGS += -Wl,--defsym,PROGRAM_BASE=$(PROGRAM_BASE) * * Corresponding program makefile changes: * * PROGRAM_BASE = 0x0800 * LDFLAGS += -Wl,--section-start=.text=$(PROGRAM_BASE) * * The bootloader should define which interrupts are for the program * only and which ones are shared, i.e. used by the bootloader too. Example: * * ISR_PROGRAM(0x01, INT0_vect); // External Interrupt Request 0 * ISR_SHARED(0x02, PCINT0_vect); // Pin Change Interrupt Request 0 * ... */ #ifdef PROGRAM_BASE /* * we're a bootloader and need to redirect interrupts. When bit 0 of GPIOR0 * is set we jump to the table at PROGRAM_BASE. The original ISR macro is * redefined and the bootloader should define handlers for all interrupts with * either ISR_SHARED or ISR_PROGRAM. */ #include #ifndef ISR # error ISR not defined. #endif /* Verify if the definition is what we expect: gcc will warn otherwise */ # define ISR(vector, ...) \ void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \ void vector (void) #undef ISR #define ISR(a, ...) void a ## _bootloader(void) __attribute__((signal)) __VA_ARGS__; \ void a ## _bootloader(void) #define ISR_BOOT(a, ...) void a(void) __attribute__((signal)) __VA_ARGS__; \ void a(void) #define ISR_SHARED(num, vect) \ ISR_BOOT(vect, ISR_NAKED) \ { \ asm volatile(" sbic %0, 0" \ "\n rjmp PROGRAM_BASE + 2 * " #num \ "\n rjmp " #vect "_bootloader" \ :: "I"(_SFR_IO_ADDR(GPIOR0)) : "memory"); \ } /* * If the bootloader gets a spurious interrupt intended for the program then * we probably need a (watchdog) reset. For now we jump to the reset vector. */ #define ISR_PROGRAM(num, vect) \ ISR_BOOT(vect, ISR_NAKED) \ { \ asm volatile(" sbic %0, 0" \ "\n rjmp PROGRAM_BASE + 2 * " #num \ "\n rjmp __vectors" \ :: "I"(_SFR_IO_ADDR(GPIOR0)) : "memory"); \ } /* * Jump to the program. The caller should reset hardware as far as necessary * and probably globally disable interrupts (e.g. by clearing SREG). */ __attribute__((always_inline,noreturn)) static inline void exec_program(void) { // SP = RAMEND; asm volatile(" sbi %0, 0" /* switch ISR table */ "\n rjmp PROGRAM_BASE" :: "I"(_SFR_IO_ADDR(GPIOR0)) : "memory"); __builtin_unreachable(); } #else /* * Jump to the bootloader. The caller should reset hardware as far as necessary * and probably globally disable interrupts (e.g. by clearing SREG). */ __attribute__((always_inline,noreturn)) static inline void exec_bootloader(void) { // SP = RAMEND; asm volatile(" cbi %0, 0" /* reset ISR table */ "\n ijmp" :: "I"(_SFR_IO_ADDR(GPIOR0)), "z"(0 >> 1) : "memory"); __builtin_unreachable(); } #endif #endif