diff --git a/cpu/atmega_common/cpu.c b/cpu/atmega_common/cpu.c new file mode 100644 index 0000000000..f02469beb4 --- /dev/null +++ b/cpu/atmega_common/cpu.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * 2017 RWTH Aachen, Josua Arndt + * 2018 Matthew Blue + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega_common + * @{ + * + * @file + * @brief Implementation of the CPU initialization + * + * @author Hinnerk van Bruinehsen + * @author Steffen Robertz + * @author Josua Arndt + * @author Matthew Blue + * @author Francisco Acosta + + * @} + */ + +#include +#include +#include + +#include "cpu.h" +#include "board.h" +#include "periph/init.h" +#include "panic.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* +* Since atmega MCUs do not feature a software reset, the watchdog timer +* is being used. It will be set to the shortest time and then force a +* reset. Therefore the MCUSR register needs to be resetted as fast as +* possible. +* Which means in the bootloader or in the following init0 if no bootloader is used. +* Bootloader resets watchdog and pass MCUSR in r2 (e.g. Optiboot) in order to pass +* information about the reset cause to the application. +* When no Bootloader is used the watchdog will be disabled in the init0 section. +* When a software reset was triggered, r3 will contain 0xAA. +* In order to prevent changes to the values from the .init section, MCUSR and r3 +* are saved in the .init0 section +*/ +uint8_t mcusr_mirror __attribute__((section(".noinit"))); +uint8_t soft_rst __attribute__((section(".noinit"))); +void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init0"))); + +void get_mcusr(void) +{ + /* save soft reset flag set in reset routine */ + __asm__ __volatile__("mov %0, r3\n" : "=r" (soft_rst) :); +#ifdef BOOTLOADER_CLEARS_WATCHDOG_AND_PASSES_MCUSR + /* save the reset flags passed from the bootloader */ + __asm__ __volatile__("mov %0, r2\n" : "=r" (mcusr_mirror) :); +#else + /* save the reset flags */ +#ifdef MCUCSR + mcusr_mirror = MCUCSR; + MCUSR = 0; +#else + mcusr_mirror = MCUSR; + MCUSR = 0; +#endif + wdt_disable(); +#endif +} + +void _reset_cause(void) +{ + if (mcusr_mirror & (1 << PORF)) { + DEBUG("Power-on reset.\n"); + } + if (mcusr_mirror & (1 << EXTRF)) { + DEBUG("External reset!\n"); + } + if (mcusr_mirror & (1 << BORF)) { + DEBUG("Brownout reset!\n"); + } + if (mcusr_mirror & (1 << WDRF)) { + if (soft_rst & 0xAA) { + DEBUG("Software reset!\n"); + } else { + DEBUG("Watchdog reset!\n"); + } + } +#if !defined (CPU_ATMEGA328P) + if (mcusr_mirror & (1 << JTRF)) { + DEBUG("JTAG reset!\n"); + } +#endif +} + +void cpu_init(void) +{ + _reset_cause(); + + wdt_reset(); /* should not be nececessary as done in bootloader */ + wdt_disable(); /* but when used without bootloader this is needed */ + + /* Initialize peripherals for which modules are included in the makefile.*/ + /* spi_init */ + /* rtc_init */ + /* hwrng_init */ + periph_init(); +} + +/* This is a vector which is aliased to __vector_default, + * the vector executed when an ISR fires with no accompanying + * ISR handler. This may be used along with the ISR() macro to + * create a catch-all for undefined but used ISRs for debugging + * purposes. + * SCIRQS – Symbol Counter Interrupt Status Register + * BATMON – Battery Monitor Control and Status Register + * IRQ_STATUS /1 – Transceiver Interrupt Status Register + * EIFR – External Interrupt Flag Register + * PCIFR – Pin Change Interrupt Flag Register + */ +ISR(BADISR_vect) +{ + _reset_cause(); + +#if defined (CPU_ATMEGA256RFR2) + printf("IRQ_STATUS %#02x\nIRQ_STATUS1 %#02x\n", + (unsigned int)IRQ_STATUS, (unsigned int)IRQ_STATUS1); + + printf("SCIRQS %#02x\nBATMON %#02x\n", (unsigned int)SCIRQS, (unsigned int)BATMON); + + printf("EIFR %#02x\nPCIFR %#02x\n", (unsigned int)EIFR, (unsigned int)PCIFR); +#endif +#ifdef LED_PANIC + /* Use LED light to signal ERROR. */ + LED_PANIC; +#endif + + core_panic(PANIC_GENERAL_ERROR, PSTR("FATAL ERROR: BADISR_vect called, unprocessed Interrupt.\n" + "STOP Execution.\n")); +} + +#if defined (CPU_ATMEGA256RFR2) +ISR(BAT_LOW_vect, ISR_BLOCK) +{ + __enter_isr(); + DEBUG("BAT_LOW\n"); + __exit_isr(); +} +#endif diff --git a/cpu/atmega_common/periph/pm.c b/cpu/atmega_common/periph/pm.c index 609d2ce4bd..341f95d45a 100644 --- a/cpu/atmega_common/periph/pm.c +++ b/cpu/atmega_common/periph/pm.c @@ -30,14 +30,12 @@ void pm_reboot(void) { -#if defined(CPU_ATMEGA256RFR2) /* clear MCU Status Register Interrupt flags */ MCUSR = 0x00; /* Softreset recognition feature, "r3" will be read out in .init0 * to be able to distinguish WDT reset and WDT software reset */ __asm__ __volatile__("mov r3, %0\n" :: "r" (0xAA)); -#endif /* CPU_ATMEGA256RFR2 */ /* * Since the AVR doesn't support a real software reset, we set the Watchdog diff --git a/cpu/atmega_common/startup.c b/cpu/atmega_common/startup.c new file mode 100644 index 0000000000..1792fac5d8 --- /dev/null +++ b/cpu/atmega_common/startup.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin, Hinnerk van Bruinehsen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega_common + * @{ + * + * @file + * @brief Startup code and interrupt vector definition + * + * @author Hinnerk van Bruinehsen + * @author Josua Arndt + * @author Steffen Robertz + * @} + */ + +#include +#include +#include + +/* For Catchall-Loop */ +#include "board.h" + +/** + * @brief functions for initializing the board, std-lib and kernel + */ +extern void board_init(void); +extern void kernel_init(void); +extern void __libc_init_array(void); + +/** + * @brief This pair of functions hook circumvent the call to main + * + * avr-libc normally uses the .init9 section for a call to main. This call + * seems to be not replaceable without hacking inside the library. We + * circumvent the call to main by using section .init7 to call the function + * reset_handler which therefore is the real entry point and section .init8 + * which should never be reached but just in case jumps to exit. + * This way there should be no way to call main directly. + */ +void init7_ovr(void) __attribute__((section(".init7"))); +void init8_ovr(void) __attribute__((section(".init8"))); + +__attribute__((used, naked)) void init7_ovr(void) +{ + __asm__ ("call reset_handler"); +} + +__attribute__((used, naked)) void init8_ovr(void) +{ + __asm__ ("jmp exit"); +} + +/** + * @brief This function is the entry point after a system reset + * + * After a system reset, the following steps are necessary and carried out: + * 1. initialize the board (sync clock, setup std-IO) + * 2. initialize and start RIOTs kernel + */ +__attribute__((used)) void reset_handler(void) +{ + /* initialize the board and startup the kernel */ + board_init(); + /* startup the kernel */ + kernel_init(); +}