Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpu/esp8266/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
include $(RIOTCPU)/esp_common/Makefile.features

FEATURES_PROVIDED += arch_esp8266
FEATURES_PROVIDED += periph_rtt
14 changes: 14 additions & 0 deletions cpu/esp8266/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
*
Expand Down
1 change: 1 addition & 0 deletions cpu/esp8266/include/sdk_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions cpu/esp8266/ld/esp8266.peripherals.ld
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ PROVIDE ( uart0 = 0x60000000 );
PROVIDE ( uart1 = 0x60000f00 );

PROVIDE ( frc1 = 0x60000600 );
PROVIDE ( frc2 = 0x60000620 );

PROVIDE ( rtc_sys_info = 0x60001100 );

Expand Down
7 changes: 7 additions & 0 deletions cpu/esp8266/ld/esp8266.riot-os.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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(.);
Expand Down
14 changes: 10 additions & 4 deletions cpu/esp8266/periph/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -36,21 +36,27 @@ 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();
}

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 ();
Expand Down
233 changes: 233 additions & 0 deletions cpu/esp8266/periph/rtt.c
Original file line number Diff line number Diff line change
@@ -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 <gunar@schorcht.net>
*
* @}
*/

#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;
Comment thread
benpicco marked this conversation as resolved.

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);
}
}
Comment thread
benpicco marked this conversation as resolved.

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;
Comment thread
benpicco marked this conversation as resolved.

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;

@benpicco benpicco Mar 16, 2020

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alarm can happen after the wrap around, so the overflow callback should be triggered before.

e.g. frc2.count = 1000, rtt_set_alarm(50, …) is called.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. It requires also to change the interrupt handling in _rtt_cb.

BTW, configured alarms will not survive deep sleep and reboots. Otherwise the whole rtt_config_t structure would have to be placed in RTC RAM which has only 256 bytes. But since the system is restarted after a reboot or deep sleep, preserving configured interrupts makes no sense.

@gschorcht gschorcht Mar 17, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, seems to work now:

> rtt setalarm 1000
> rtt set 4294967290
The alarm rang

> rtt setoverflow
> rtt setalarm 1000
> rtt set 4294967290
RTT overflow
The alarm rang

> rtt setalarm 4294967295
> rtt set 4294967290
The alarm rang
RTT overflow

> rtt set 4294967290
RTT overflow

> rtt setalarm 2000000000
> rtt set 4294967290
RTT overflow

> rtt set 1999999990
The alarm rang

Because of the interrupt latency, the only case that cannot be handled with this emulated overflow interrupt is when the alarm is set to 0.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the interrupt latency, the only case that cannot be handled with this emulated overflow interrupt is when the alarm is set to 0.

But if you can set an overflow 'alarm' you can also set an alarm at 0, they should happen at the same time.

@gschorcht gschorcht Mar 17, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benpicco Thanks for your suggestions. I had simpler solution already

> rtt setoverflow
> rtt setalarm 0
> rtt set 4294967290
The alarm rang
RTT overflow

> rtt set 4294967290
RTT overflow

> rtt setalarm 1
> rtt set 4294967290
RTT overflow
The alarm rang

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My problem was that I tried to test the current counter value, which of course can be larger than the configured alarm value due to the interrupt latency.

So the solution was not to test the actual counter value but the configured alarm value. Since _rtt_cb is always called for the actually set alarm value, the value in the frc2.alarm register is always exactly the relevant 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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does RTC.COUNTER come from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In contrast to all other peripherals for which the SDK defines the registers as a struct, the registers of the RTC module aren't defined by the SDK at all, see rtc_register.h.

Therefore, another piece of vendor code is used which was initially extracted from the esp-open-rtos. It tries to define the RTC registers as a struct. BTW, this struct is defined exactly in the way you mentioned with volatile members and without linker stuff.

ESP8266 is a terrible platform. It has the worst documentation I have ever seen and there is no consistent development environment.


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);
}
8 changes: 8 additions & 0 deletions cpu/esp8266/startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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();

Expand Down
Loading