diff --git a/cpu/esp8266/Makefile.features b/cpu/esp8266/Makefile.features index adba4e110f..34cb6e9efc 100644 --- a/cpu/esp8266/Makefile.features +++ b/cpu/esp8266/Makefile.features @@ -3,3 +3,4 @@ include $(RIOTCPU)/esp_common/Makefile.features FEATURES_PROVIDED += arch_esp8266 +FEATURES_PROVIDED += periph_rtt diff --git a/cpu/esp8266/include/periph_cpu.h b/cpu/esp8266/include/periph_cpu.h index 2361cb6510..492ec765f2 100644 --- a/cpu/esp8266/include/periph_cpu.h +++ b/cpu/esp8266/include/periph_cpu.h @@ -27,6 +27,12 @@ extern "C" { #endif +/** + * @brief Memory marked with this attribute is retained during deep sleep + */ +#define BACKUP_RAM __attribute__((section(".rtc.bss"))) +#define BACKUP_RAM_DATA __attribute__((section(".rtc.data"))) + /** * @brief Length of the CPU_ID in octets */ @@ -217,6 +223,14 @@ typedef struct { #define RNG_DATA_REG_ADDR (0x3ff20e44) /** @} */ +/** + * @name RTT and RTC configuration + * @{ + */ +#define RTT_FREQUENCY (312500UL) +#define RTT_MAX_VALUE (0xFFFFFFFFUL) +/** @} */ + /** * @name SPI configuration * diff --git a/cpu/esp8266/include/sdk_conf.h b/cpu/esp8266/include/sdk_conf.h index 3d40a99347..32fe5a0af4 100644 --- a/cpu/esp8266/include/sdk_conf.h +++ b/cpu/esp8266/include/sdk_conf.h @@ -61,6 +61,7 @@ extern "C" { #define CONFIG_TASK_WDT_PANIC #define CONFIG_TASK_WDT_TIMEOUT_S (15) +#define CONFIG_RESET_REASON (1) #define CONFIG_WIFI_PPT_TASKSTACK_SIZE (3584) #define CONFIG_MAIN_TASK_STACK_SIZE (2048) diff --git a/cpu/esp8266/ld/esp8266.peripherals.ld b/cpu/esp8266/ld/esp8266.peripherals.ld index 99d1697a29..3583306794 100644 --- a/cpu/esp8266/ld/esp8266.peripherals.ld +++ b/cpu/esp8266/ld/esp8266.peripherals.ld @@ -4,6 +4,7 @@ PROVIDE ( uart0 = 0x60000000 ); PROVIDE ( uart1 = 0x60000f00 ); PROVIDE ( frc1 = 0x60000600 ); +PROVIDE ( frc2 = 0x60000620 ); PROVIDE ( rtc_sys_info = 0x60001100 ); diff --git a/cpu/esp8266/ld/esp8266.riot-os.ld b/cpu/esp8266/ld/esp8266.riot-os.ld index 4f00adcf4c..f9becdf071 100644 --- a/cpu/esp8266/ld/esp8266.riot-os.ld +++ b/cpu/esp8266/ld/esp8266.riot-os.ld @@ -84,6 +84,13 @@ SECTIONS _rtc_data_end = ABSOLUTE(.); } > rtc_seg + .rtc.bss : + { + _rtc_bss_start = ABSOLUTE(.); + *(.rtc.bss) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_seg + .data : ALIGN(4) { _data_start = ABSOLUTE(.); diff --git a/cpu/esp8266/periph/pm.c b/cpu/esp8266/periph/pm.c index 94c71457e4..4cae9b354d 100644 --- a/cpu/esp8266/periph/pm.c +++ b/cpu/esp8266/periph/pm.c @@ -26,7 +26,7 @@ void pm_set_lowest(void) { - DEBUG ("%s enter to sleep @%u\n", __func__, system_get_time()); + DEBUG("%s enter to sleep @%u\n", __func__, system_get_time()); /* reset system watchdog timer */ system_wdt_feed(); @@ -36,7 +36,7 @@ void pm_set_lowest(void) __asm__ volatile ("waiti 0"); #endif - DEBUG ("%s exit from sleep @%u\n", __func__, system_get_time()); + DEBUG("%s exit from sleep @%u\n", __func__, system_get_time()); /* reset system watchdog timer */ system_wdt_feed(); @@ -44,13 +44,19 @@ void pm_set_lowest(void) void pm_off(void) { - DEBUG ("%s\n", __func__); + DEBUG("%s\n", __func__); system_deep_sleep(0); } void pm_reboot(void) { - DEBUG ("%s\n", __func__); + DEBUG("%s\n", __func__); + +#ifdef MODULE_PERIPH_RTT + /* save counters */ + extern void rtt_save_counter(void); + rtt_save_counter(); +#endif /* shut down WIFI and call system_restart_local after timer */ system_restart (); diff --git a/cpu/esp8266/periph/rtt.c b/cpu/esp8266/periph/rtt.c new file mode 100644 index 0000000000..482a9c4687 --- /dev/null +++ b/cpu/esp8266/periph/rtt.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2020 Gunar Schorcht + * + * 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_esp8266 + * @ingroup drivers_periph_rtt + * @{ + * + * @file + * @brief Low-level RTT driver implementation for ESP8266 + * + * @author Gunar Schorcht + * + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "cpu.h" +#include "log.h" +#include "periph/rtt.h" + +#include "esp/common_macros.h" +#include "esp/dport_regs.h" +#include "esp/rtc_regs.h" +#include "sdk/sdk.h" + +#define FRC2_CLK_DIV_256 2 /* divider for the 80 MHz AHB clock */ + +#define RTC_BSS_ATTR __attribute__((section(".rtc.bss"))) + +/** + * FRC2 is a 32-bit countup timer, triggers interrupt when reaches alarm value. + */ +typedef struct { + uint32_t load; + uint32_t count; + union { + struct { + uint32_t intr_hold : 1; + uint32_t reserved1 : 1; + uint32_t clk_div : 2; + uint32_t reserved2 : 2; + uint32_t reload : 1; + uint32_t enable : 1; + uint32_t intr_sta : 1; + uint32_t reserved3 : 23; + }; + uint32_t val; + } ctrl; + union { + struct { + uint32_t clear : 1; + uint32_t reserved1: 31; + }; + uint32_t val; + } intr; + uint32_t alarm; +} frc2_struct_t; + +/* + * linker script esp8266.peripherals.ld will make sure this points to the + * hardware register address + */ +extern volatile frc2_struct_t frc2; + +typedef struct { + uint32_t alarm; /**< alarm */ + rtt_cb_t alarm_cb; /**< alarm callback */ + rtt_cb_t overflow_cb; /**< overflow callback */ + void *alarm_arg; /**< argument for alarm callback */ + void *overflow_arg; /**< argument for overflow callback */ +} rtt_config_t; + +static rtt_config_t rtt_config; + +static uint32_t RTC_BSS_ATTR _rtt_counter_saved; +static uint32_t RTC_BSS_ATTR _rtc_counter_saved; + +extern uint32_t pm_rtc_clock_cali_proc(void); +extern uint32_t pm_rtc2usec(uint32_t rtc_cycles, uint32_t period); + +void rtt_restore_counter(void); + +void IRAM rtt_cb(void *arg) +{ + /* triggered alarm */ + uint32_t alarm = frc2.alarm; + + if (alarm == rtt_config.alarm) { + rtt_cb_t alarm_cb = rtt_config.alarm_cb; + void * alarm_arg = rtt_config.alarm_arg; + /* clear the alarm first (includes setting next alarm to overflow) */ + rtt_clear_alarm(); + /* call the alarm handler afterwards if callback was defined*/ + if (alarm_cb) { + alarm_cb(alarm_arg); + } + } + + if (alarm == 0) { + /* set next alarm which is either an alarm if configured or overflow */ + frc2.alarm = rtt_config.alarm; + /* call the overflow handler if configured */ + if (rtt_config.overflow_cb) { + rtt_config.overflow_cb(rtt_config.overflow_arg); + } + } +} + +void rtt_init(void) +{ + DEBUG("%s saved rtt=%u rtc=%u\n", + __func__, _rtt_counter_saved, _rtc_counter_saved); + + frc2.ctrl.clk_div = FRC2_CLK_DIV_256; + frc2.ctrl.reload = 0; + frc2.ctrl.intr_hold = 0; + frc2.ctrl.enable = 1; + + /* initialize rtt_config structure after reboot or deep sleep */ + rtt_clear_alarm(); + rtt_clear_overflow_cb(); + + if (_rtt_counter_saved || _rtc_counter_saved) { + /* if not in init after power on, restore the RTT counter value */ + rtt_restore_counter(); + } + else { + frc2.load = 0; + DEBUG("%s after power on\n", __func__); + } + + /* emulate overflow interrupt */ + frc2.alarm = 0; + + ets_isr_attach (ETS_FRC2_INUM, rtt_cb, NULL); + ets_isr_unmask (BIT(ETS_FRC2_INUM)); + DPORT.INT_ENABLE |= DPORT_INT_ENABLE_FRC2; +} + +void rtt_poweron(void) +{ + /* power on simply reactivates the FRC2 counter */ + frc2.ctrl.enable = 1; +} + +void rtt_poweroff(void) +{ + /* power off simply deactivates the FRC2 counter */ + frc2.ctrl.enable = 0; +} + +void rtt_set_overflow_cb(rtt_cb_t cb, void *arg) +{ + /* there is no overflow interrupt, we emulate */ + rtt_config.overflow_cb = cb; + rtt_config.overflow_arg = arg; +} + +void rtt_clear_overflow_cb(void) +{ + /* there is no overflow interrupt, we emulate */ + rtt_config.overflow_cb = NULL; + rtt_config.overflow_arg = NULL; +} + +uint32_t rtt_get_counter(void) +{ + return frc2.count; +} + +void rtt_set_counter(uint32_t counter) +{ + frc2.load = counter; + + if (counter > frc2.alarm) { + /* overflow is the next interrupt event */ + frc2.alarm = 0; + } +} + +void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) +{ + rtt_config.alarm = alarm; + rtt_config.alarm_cb = cb; + rtt_config.alarm_arg = arg; + + if (frc2.count < alarm) { + frc2.alarm = alarm; + } +} + +uint32_t rtt_get_alarm(void) +{ + return rtt_config.alarm; +} + +void rtt_clear_alarm(void) +{ + frc2.alarm = 0; + rtt_config.alarm = 0; + rtt_config.alarm_cb = NULL; + rtt_config.alarm_arg = NULL; +} + +void rtt_save_counter(void) +{ + /* save counters before going to sleep or reboot */ + _rtt_counter_saved = frc2.count; + _rtc_counter_saved = RTC.COUNTER; + + DEBUG("%s saved rtt=%u rtc=%u\n", + __func__, _rtt_counter_saved, _rtc_counter_saved); +} + +void rtt_restore_counter(void) +{ + uint32_t rtc_diff = RTC.COUNTER - _rtc_counter_saved; + uint32_t rtc_diff_us = pm_rtc2usec(rtc_diff, pm_rtc_clock_cali_proc()); + uint32_t rtt_diff = RTT_US_TO_TICKS(rtc_diff_us); + + frc2.load = _rtt_counter_saved + rtt_diff; + + DEBUG("%s rtc_diff=%u rtt_diff=%u load=%u\n", __func__, + rtc_diff, rtt_diff, _rtt_counter_saved + rtt_diff); +} diff --git a/cpu/esp8266/startup.c b/cpu/esp8266/startup.c index 8c994f91c1..fadedd9134 100644 --- a/cpu/esp8266/startup.c +++ b/cpu/esp8266/startup.c @@ -33,6 +33,7 @@ #include "esp/common_macros.h" #include "esp_log.h" +#include "esp_system.h" #include "exceptions.h" #include "stdio_base.h" #include "syscalls.h" @@ -50,6 +51,13 @@ extern uint32_t hwrand (void); void esp_riot_init(void) { + /* clear RTC bss data */ + extern uint8_t _rtc_bss_start, _rtc_bss_end; + esp_reset_reason_t reset_reason = esp_reset_reason(); + if (reset_reason != ESP_RST_DEEPSLEEP && reset_reason != ESP_RST_SW) { + memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start)); + } + /* enable cached read from flash */ Cache_Read_Enable_New(); diff --git a/cpu/esp8266/vendor/esp-idf/esp8266/source/reset_reason.c b/cpu/esp8266/vendor/esp-idf/esp8266/source/reset_reason.c index ec3370696c..0a49edf963 100644 --- a/cpu/esp8266/vendor/esp-idf/esp8266/source/reset_reason.c +++ b/cpu/esp8266/vendor/esp-idf/esp8266/source/reset_reason.c @@ -36,7 +36,7 @@ static const char *TAG = "reset_reason"; static uint32_t s_reset_reason; -static inline void esp_reset_reason_clear_hint() +static inline void esp_reset_reason_clear_hint(void) { rtc_sys_info.hint = 0; } @@ -46,7 +46,7 @@ static inline uint32_t esp_reset_reason_get_hint(uint32_t hw_reset) if (hw_reset == POWERON_RESET && rtc_sys_info.hint != ESP_RST_SW) { uint32_t *p = (uint32_t *)&rtc_sys_info; - for (int i = 0; i < RTC_SYS_RAM_SIZE / sizeof(uint32_t); i++) + for (unsigned i = 0; i < RTC_SYS_RAM_SIZE / sizeof(uint32_t); i++) *p++ = 0; } @@ -73,10 +73,19 @@ static inline uint32_t get_reset_reason(uint32_t rtc_reset_reason, uint32_t rese return reset_reason_hint; return ESP_RST_POWERON; case EXT_RESET: +#ifdef RIOT_VERSION + if (reset_reason_hint == ESP_RST_DEEPSLEEP || + reset_reason_hint == ESP_RST_SW || + reset_reason_hint == ESP_RST_POWERON) { + return reset_reason_hint; + } +#else if (reset_reason_hint == ESP_RST_DEEPSLEEP) { return reset_reason_hint; } +#endif return ESP_RST_EXT; + case SW_RESET: if (reset_reason_hint == ESP_RST_PANIC || reset_reason_hint == ESP_RST_BROWNOUT || @@ -113,7 +122,7 @@ void esp_reset_reason_init(void) esp_reset_reason_clear_hint(); } - ESP_LOGI(TAG, "RTC reset %u wakeup %u store %u, reason is %u", hw_reset, hw_wakeup, hint, s_reset_reason); + ESP_LOGD(TAG, "RTC reset %u wakeup %u store %u, reason is %u", hw_reset, hw_wakeup, hint, s_reset_reason); } /**