1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #10656 from gschorcht/esp8266_ets_handling

cpu/esp8266: change of ETS task handling
This commit is contained in:
Sebastian Meiling 2019-01-21 21:57:45 +01:00 committed by GitHub
commit e22e582049
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 222 additions and 111 deletions

View File

@ -1,5 +1,15 @@
# additional modules dependencies # additional modules dependencies
ifneq (, $(filter esp_sdk, $(USEMODULE)))
USEMODULE += core_thread_flags
endif
ifneq (, $(filter esp_spiffs, $(USEMODULE)))
export SPIFFS_STD_OPTION = -std=c99
USEMODULE += spiffs
USEMODULE += vfs
endif
ifneq (, $(filter lua, $(USEPKG))) ifneq (, $(filter lua, $(USEPKG)))
USEMODULE += newlib_syscalls_default USEMODULE += newlib_syscalls_default
USEMODULE += xtimer USEMODULE += xtimer
@ -35,13 +45,3 @@ endif
ifneq (, $(filter newlib_syscalls_default, $(USEMODULE))) ifneq (, $(filter newlib_syscalls_default, $(USEMODULE)))
USEMODULE += stdio_uart USEMODULE += stdio_uart
endif endif
# network interface dependencies
ifneq (, $(filter netdev_default, $(USEMODULE)))
# if NETDEV_DEFAULT is empty, we use module mrf24j40 as default network device
ifndef NETDEV_DEFAULT
USEMODULE += mrf24j40
else
USEMODULE += $(NETDEV_DEFAULT)
endif
endif

View File

@ -24,6 +24,16 @@ endif
ifeq ($(ENABLE_GDBSTUB), 1) ifeq ($(ENABLE_GDBSTUB), 1)
USEMODULE += esp_gdbstub USEMODULE += esp_gdbstub
endif
# SPECIAL module dependencies
# cannot be done in Makefile.dep since Makefile.dep is included too late
ifneq (, $(filter esp_sw_timer, $(USEMODULE)))
USEMODULE += esp_sdk
endif
ifneq (, $(filter esp_gdbstub, $(USEMODULE)))
USEMODULE += esp_gdb USEMODULE += esp_gdb
endif endif
@ -39,7 +49,17 @@ PSEUDOMODULES += esp_sdk_int_handling
PSEUDOMODULES += esp_sw_timer PSEUDOMODULES += esp_sw_timer
PSEUDOMODULES += esp_spiffs PSEUDOMODULES += esp_spiffs
USEMODULE += esp
USEMODULE += mtd
USEMODULE += periph
USEMODULE += periph_common
USEMODULE += ps
USEMODULE += random
USEMODULE += sdk
USEMODULE += xtensa
ifneq (, $(filter pthread, $(USEMODULE))) ifneq (, $(filter pthread, $(USEMODULE)))
# has to be included before $(ESP8266_NEWLIB_DIR)
INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include
endif endif
@ -49,13 +69,14 @@ INCLUDES += -I$(RIOTCPU)/$(CPU)
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/espressif INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/espressif
CFLAGS += -DESP_OPEN_SDK CFLAGS += -DESP_OPEN_SDK -DSCHED_PRIO_LEVELS=32
CFLAGS += -Wno-unused-parameter -Wformat=0 CFLAGS += -Wno-unused-parameter -Wformat=0
CFLAGS += -mlongcalls -mtext-section-literals -fdata-sections CFLAGS += -mlongcalls -mtext-section-literals
CFLAGS += -fdata-sections -fzero-initialized-in-bss
ASFLAGS += --longcalls --text-section-literals ASFLAGS += --longcalls --text-section-literals
ifneq (, $(filter esp_sw_timer, $(USEMODULE))) ifeq (, $(filter esp_sdk_int_handling, $(USEMODULE)))
USEMODULE += esp_sdk CFLAGS += -DCONTEXT_SWITCH_BY_INT
endif endif
ifneq (, $(filter esp_sdk, $(USEMODULE))) ifneq (, $(filter esp_sdk, $(USEMODULE)))
@ -70,17 +91,9 @@ ifneq (, $(filter esp_gdbstub, $(USEMODULE)))
endif endif
ifneq (, $(filter esp_gdb, $(USEMODULE))) ifneq (, $(filter esp_gdb, $(USEMODULE)))
CFLAGS_OPT = -fzero-initialized-in-bss -Og -ggdb -g3 CFLAGS += -Og -ggdb -g3
else else
CFLAGS_OPT = -fzero-initialized-in-bss -O2 CFLAGS += -Os
endif
CFLAGS += $(CFLAGS_OPT)
ifneq (, $(filter esp_spiffs, $(USEMODULE)))
export SPIFFS_STD_OPTION = -std=c99
USEMODULE += spiffs
USEMODULE += vfs
endif endif
ifeq ($(QEMU), 1) ifeq ($(QEMU), 1)
@ -115,15 +128,6 @@ LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/eagle.rom.addr.v6.ld
LINKFLAGS += -nostdlib -lgcc -u ets_run -Wl,-gc-sections # -Wl,--print-gc-sections LINKFLAGS += -nostdlib -lgcc -u ets_run -Wl,-gc-sections # -Wl,--print-gc-sections
LINKFLAGS += -Wl,--warn-unresolved-symbols LINKFLAGS += -Wl,--warn-unresolved-symbols
USEMODULE += esp
USEMODULE += mtd
USEMODULE += periph
USEMODULE += periph_common
USEMODULE += ps
USEMODULE += random
USEMODULE += sdk
USEMODULE += xtensa
# configure preflasher to convert .elf to .bin before flashing # configure preflasher to convert .elf to .bin before flashing
FLASH_SIZE = -fs 8m FLASH_SIZE = -fs 8m
export PREFLASHER ?= esptool.py export PREFLASHER ?= esptool.py
@ -143,5 +147,5 @@ else
export FFLAGS += -p $(PORT) -b $(PROGRAMMER_SPEED) write_flash export FFLAGS += -p $(PORT) -b $(PROGRAMMER_SPEED) write_flash
export FFLAGS += -fm $(FLASH_MODE) export FFLAGS += -fm $(FLASH_MODE)
export FFLAGS += 0 $(ELFFILE)-0x00000.bin export FFLAGS += 0 $(ELFFILE)-0x00000.bin
export FFLAGS += 0x10000 $(ELFFILE)-0x10000.bin export FFLAGS += 0x10000 $(ELFFILE)-0x10000.bin; esptool.py -p $(PORT) run
endif endif

View File

@ -710,13 +710,43 @@ INCLUDES += -I$(APPDIR)
# <a name="esp8266_sdk_task_handling"> SDK Task Handling </a> &nbsp;[[TOC](#esp8266_toc)] # <a name="esp8266_sdk_task_handling"> SDK Task Handling </a> &nbsp;[[TOC](#esp8266_toc)]
With make command variable ```USE_SDK=1``` the Espressif SDK is used. This is necessary, for example, if you want to use the built-in WLAN module. The SDK internally uses its own tasks (SDK tasks) and its own scheduling mechanism to realize event-driven SDK functions such as WiFi functions and software timers, and to keep the system alive. For this purpose, the SDK regularly executes SDK tasks with pending events in an endless loop using the ROM function ```ets_run```. With make command variable `USE_SDK=1`, the Espressif SDK is used. This is
necessary, for example, if you want to use the built-in WLAN module. The
SDK is also used automatically when software timers are used by activating
the `esp_sw_timer` module.
Interrupt service routines do not process interrupts directly but use the ```ets_post``` ROM function to send an event to one of these SDK tasks, which then processes the interrupts asynchronously. A context switch is not possible in the interrupt service routines. Internally, the SDK uses its own priority-based multitasking sytsem,
the **ETS**, to handle hardware components such as the WiFi interface, or to
implement event-driven functions such as software timers. ETS periodically
executes all ETS tasks with pending events in an infinite loop with the ROM
function `ets_run`.
In the RIOT port, the task management of the SDK is replaced by the task management of the RIOT. To handle SDK tasks with pending events so that the SDK functions work and the system keeps alive, the ROM functions ```ets_run``` and ```ets_post``` are overwritten. The ```ets_run``` function performs all SDK tasks with pending events exactly once. It is executed at the end of the ```ets_post``` function and thus usually at the end of an SDK interrupt service routine or before the system goes into the lowest power mode. ETS doesn't process interrupts directly in interrupt service routines.
Instead, they use the `ets_post` ROM function to send an event to one of the
ETS tasks, which then processes the interrupts asynchronously. Context
switches are not possible in interrupt service routines.
@note Since the non-SDK version of RIOT is much smaller and faster than the SDK version, you should always compile your application without the SDK (```USE_SDK=0```, the default) if you don't need the built-in WiFi module. To use SDK functions and keep the system alive, ETS tasks with pending events
have to be handled. For that purpose
- the `ets_task_func` RIOT thread with highest possible priority is used
- the ROM functions `ets_run` and `ets_post` are overwritten.
The `ets_task_func` RIOT thread is waiting for a thread flag, which is set
by the `ets_post` function at the end of an ETS interrupt service routine.
The flag indicates that there are ETS tasks with pending events that need
to be executed. The `ets_task_func` RIOT thread then calls the `ets_run`
function, which performs all ETS tasks with pending events exactly once.
Thus, when a hardware component used by the SDK triggers an interrupt, e.g.
the WiFi interface, the interrupt sevice routine posts an event to the ETS
task by calling the `ets_post` function. The overwritten version of this
function sets the thread flag of the `ets_task_func` thread. The thread
then calls function `ets_run` to process pending events.
@note Since the non-SDK version of RIOT is much smaller and faster than the
SDK version, you should always compile your application without the SDK
(```USE_SDK=0```, the default) if you don't need the built-in WiFi module.
# <a name="esp8266_qemu_mode_and_gdb"> QEMU Mode and GDB </a> &nbsp;[[TOC](#esp8266_toc)] # <a name="esp8266_qemu_mode_and_gdb"> QEMU Mode and GDB </a> &nbsp;[[TOC](#esp8266_toc)]

View File

@ -29,18 +29,64 @@ extern "C" {
#endif #endif
/** /**
* @brief Stack size configuration * @name Stack size configuration
* @{ * @{
*/ */
#ifdef MODULE_ESP_SDK_INT_HANDLING #ifdef MODULE_ESP_SDK_INT_HANDLING
#ifndef THREAD_EXTRA_STACKSIZE_PRINTF
#define THREAD_EXTRA_STACKSIZE_PRINTF (0) #define THREAD_EXTRA_STACKSIZE_PRINTF (0)
#define THREAD_STACKSIZE_DEFAULT (2048)
#define THREAD_STACKSIZE_IDLE (2048)
#else
#define THREAD_EXTRA_STACKSIZE_PRINTF (0)
#define THREAD_STACKSIZE_DEFAULT (2048)
#define THREAD_STACKSIZE_IDLE (2048)
#endif #endif
#ifndef THREAD_STACKSIZE_DEFAULT
#define THREAD_STACKSIZE_DEFAULT (1536)
#endif
#ifndef THREAD_STACKSIZE_IDLE
#define THREAD_STACKSIZE_IDLE (1536)
#endif
#ifndef THREAD_STACKSIZE_MAIN
#define THREAD_STACKSIZE_MAIN (3072)
#endif
#ifndef GNRC_PKTDUMP_STACKSIZE
#define GNRC_PKTDUMP_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#endif
#ifndef ESP_NOW_STACKSIZE
#define ESP_NOW_STACKSIZE (2560)
#endif
#ifndef ETS_THREAD_STACKSIZE
#define ETS_THREAD_STACKSIZE (2048)
#endif
#else /* MODULE_ESP_SDK_INT_HANDLING */
#ifndef THREAD_EXTRA_STACKSIZE_PRINTF
#define THREAD_EXTRA_STACKSIZE_PRINTF (0)
#endif
#ifndef THREAD_STACKSIZE_DEFAULT
#define THREAD_STACKSIZE_DEFAULT (1024)
#endif
#ifndef THREAD_STACKSIZE_IDLE
#define THREAD_STACKSIZE_IDLE (1024)
#endif
#ifndef THREAD_STACKSIZE_MAIN
#define THREAD_STACKSIZE_MAIN (3072)
#endif
#ifndef GNRC_PKTDUMP_STACKSIZE
#define GNRC_PKTDUMP_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#endif
#ifndef ESP_NOW_STACKSIZE
#define ESP_NOW_STACKSIZE (2560)
#endif
#ifndef ETS_THREAD_STACKSIZE
#define ETS_THREAD_STACKSIZE (1536)
#endif
#endif /* MODULE_ESP_SDK_INT_HANDLING */
/** @} */ /** @} */
/** /**
@ -60,7 +106,7 @@ extern "C" {
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* CPU_CONF_H */ #endif
#endif /* CPU_CONF_H */ #endif /* CPU_CONF_H */
/** @} */ /** @} */

View File

@ -35,10 +35,6 @@ void pm_set_lowest(void)
{ {
DEBUG ("%s\n", __func__); DEBUG ("%s\n", __func__);
/* execute all pending system tasks before going to sleep */
/* is it really necessary, the timer interrupt is thrown every some ms? */
ets_tasks_run ();
#if !defined(QEMU) #if !defined(QEMU)
DEBUG ("%s enter to sleep @%u\n", __func__, phy_get_mactime()); DEBUG ("%s enter to sleep @%u\n", __func__, phy_get_mactime());
@ -47,13 +43,6 @@ void pm_set_lowest(void)
DEBUG ("%s exit from sleep @%u\n", __func__, phy_get_mactime()); DEBUG ("%s exit from sleep @%u\n", __func__, phy_get_mactime());
#endif #endif
/*
* We could execute all pending system tasks after an interrupt before
* continuing RIOT. However, to give RIOT tasks the highest priority,
* *ets_tasks_run* should be called only before going to sleep
*/
ets_tasks_run ();
} }
void pm_off(void) void pm_off(void)

View File

@ -8,6 +8,37 @@
* PLEASE NOTE: This file is only used in SDK version * PLEASE NOTE: This file is only used in SDK version
*/ */
/*
* Internally, the SDK uses its own priority-based multitasking system,
* the *ETS*, to handle hardware components such as the WiFi interface, or to
* implement event-driven functions such as software timers. ETS periodically
* executes all ETS tasks with pending events in an infinite loop with the ROM
* function *ets_run*.
*
* ETS doesn't process interrupts directly in interrupt service routines.
* Instead, they use the *ets_post* ROM function to send an event to one of the
* ETS tasks, which then processes the interrupts asynchronously. Context
* switches are not possible in interrupt service routines.
*
* To use SDK functions and keep the system alive, ETS tasks with pending
* events have to be handled. For that purpose
*
* - the *ets_task_func* RIOT thread with highest possible priority is used
* - the ROM functions *ets_run* and *ets_post* are overwritten.
*
* The *ets_task_func* RIOT thread is waiting for a thread flag, which is set
* by the *ets_post function* at the end of an ETS interrupt service routine.
* The flag indicates that there are ETS tasks with pending events that need
* to be executed. The *ets_task_func* RIOT thread then calls the *ets_run*
* function, which performs all ETS tasks with pending events exactly once.
*
* Thus, when a hardware component used by the SDK triggers an interrupt, e.g.
* the WiFi interface, the interrupt sevice routine posts an event to the ETS
* task by calling the *ets_post* function. The overwritten version of this
* function sets the thread flag of the *ets_task_func* thread. The thread
* then calls function *ets_run* to process pending events.
*/
#ifdef MODULE_ESP_SDK #ifdef MODULE_ESP_SDK
#define ENABLE_DEBUG 0 #define ENABLE_DEBUG 0
@ -21,21 +52,19 @@
#include "sdk/ets_task.h" #include "sdk/ets_task.h"
#include "sdk/sdk.h" #include "sdk/sdk.h"
#define TIMER_TASK_PRIORITY 31
static uint8_t min_prio = 0; static uint8_t min_prio = 0;
/* helper function for *ets_run* */
uint8_t ets_highest_1_bit (uint32_t mask) uint8_t ets_highest_1_bit (uint32_t mask)
{ {
__asm__ volatile ("nsau %0, %1;" :"=r"(mask) : "r"(mask)); __asm__ volatile ("nsau %0, %1;" :"=r"(mask) : "r"(mask));
return 32 - mask; return 32 - mask;
} }
/** /*
* @brief Perform execution of all pending ETS system tasks. * Perform the execution of all pending ETS tasks. This is necessary to
* * keep the underlying ETS system used by the SDK alive. It is called from
* This is necessary to keep the underlying ETS system used by the * the RIOT thread *ets_task_func*.
* SDK alive.
*/ */
void IRAM ets_tasks_run (void) void IRAM ets_tasks_run (void)
{ {
@ -81,56 +110,54 @@ void IRAM ets_tasks_run (void)
system_soft_wdt_feed(); system_soft_wdt_feed();
} }
/**
* To realize event-driven SDK functions such as WiFi functions and software #define THREAD_FLAG_ETS_THREAD (1 << 0)
* timers, and to keep the system alive, the SDK internally uses its own static volatile thread_t* ets_thread = NULL;
* tasks (SDK tasks) and its own scheduling mechanism. For this purpose, the
* SDK regularly executes SDK tasks with pending events in an endless loop /*
* using the ROM function *ets_run*. * Thread *ets_task_func* is waiting for the thread flag THREAD_FLAG_ETS_THREAD
* * indicating that ETS tasks have pending events and need to be executed. When
* Interrupt service routines do not process interrupts directly but use * the thread flag is set, it calls the *ets_run* function, which performs
* the *ets_post* ROM function to send an event to one of these SDK tasks, * all ETS tasks with pending events exactly once. The thread flag is set by
* which then processes the interrupts asynchronously. A context switch is * the *ets_post* function, which is called at the end of an ETS interrupt
* not possible in the interrupt service routines. * service routine.
*
* In the RIOT port, the task management of the SDK is replaced by the task
* management of the RIOT. To handle SDK tasks with pending events so that
* the SDK functions work and the system keeps alive, the ROM functions
* *ets_run* and *ets_post* are overwritten. The *ets_run* function performs
* all SDK tasks with pending events exactly once. It is executed at the end
* of the *ets_post* function and thus usually at the end of an SDK interrupt
* service routine or before the system goes into the lowest power mode.
*
* PLEASE REMEBER: we are doing that in interrupt context
*
* -> it must not take to much time (how can we ensure that)?
*
* -> we have to indicate that we are in interrupt context see *irq_is_in*
* and *irq_interrupt_nesting* (as realized by the level 1 exception handler
* in non SDK task handling environment, option MODULE_ESP_SDK_INT_HANDLING=0,
* the default)
*
* -> we must not execute a context switch or we have to execute the context
* switch from interrupt as following (as realized by the level 1
* interrupt exception handler in non SDK task handling environment, option
* MODULE_ESP_SDK_INT_HANDLING=0, the default)
* _frxt_int_enter();
* _frxt_switch_context();
* _frxt_int_exit();
*/ */
void *ets_task_func(void *arg)
{
(void) arg;
ets_thread = sched_active_thread;
while (1) {
thread_flags_wait_one(THREAD_FLAG_ETS_THREAD);
ets_tasks_run();
}
return NULL;
}
/* helper macro for *ets_post */
#define irom_cache_enabled() (*((uint32_t*)0x60000208) & (1 << 17))
/* ETS timer task priority */
#define TIMER_TASK_PRIORITY 31
/* reference to the *ets_post* ROM function */
typedef uint32_t (*ets_post_function_t)(uint32_t prio, ETSSignal sig, ETSParam par); typedef uint32_t (*ets_post_function_t)(uint32_t prio, ETSSignal sig, ETSParam par);
static ets_post_function_t ets_post_rom = (ets_post_function_t)0x40000e24; static ets_post_function_t ets_post_rom = (ets_post_function_t)0x40000e24;
#ifdef MODULE_ESP_SDK /*
#define irom_cache_enabled() (*((uint32_t*)0x60000208) & (1 << 17)) * Overwritten version of ROM function *ets_post*.
#else *
#define irom_cache_enabled() (1) * ETS doesn't process interrupts directly in interrupt service routines.
#endif * Instead, they use the *ets_post* ROM function to send an event to one of the
* ETS tasks, which then processes the interrupts asynchronously. Context
* switches are not possible in interrupt service routines.
*
* Please note: *ets_post* is executed in interrupt context
*/
uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par) uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
{ {
/* This function is executed in interrupt context */
uint32_t ret; uint32_t ret;
critical_enter(); critical_enter();
@ -154,6 +181,10 @@ uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
system_soft_wdt_feed(); system_soft_wdt_feed();
} }
if (ets_thread && irom_cache_enabled()) {
thread_flags_set((thread_t*)ets_thread, THREAD_FLAG_ETS_THREAD);
}
critical_exit(); critical_exit();
return ret; return ret;
@ -161,7 +192,7 @@ uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
void ets_tasks_init(void) void ets_tasks_init(void)
{ {
/* there is nothing to do at the moment */ /* there is nothing to be done here at the moment */
} }
#endif /* MODULE_ESP_SDK */ #endif /* MODULE_ESP_SDK */

View File

@ -65,6 +65,14 @@ extern uint8_t _eheap;
#include "sdk/ets_task.h" #include "sdk/ets_task.h"
/* EST Task priority */
#define ETS_TASK_PRIORITY (1)
/* stack for the ETS task */
static char ets_task_stack[ETS_THREAD_STACKSIZE];
/* ETS task code */
extern void *ets_task_func(void *arg);
/** /**
* @brief System main loop called by the ETS * @brief System main loop called by the ETS
* *
@ -115,6 +123,12 @@ void ets_run(void)
ets_isr_unmask(BIT(ETS_SOFT_INUM)); ets_isr_unmask(BIT(ETS_SOFT_INUM));
#endif #endif
thread_create(ets_task_stack, sizeof(ets_task_stack),
ETS_TASK_PRIORITY,
THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST,
ets_task_func, NULL, "ets");
/* does not return */ /* does not return */
kernel_init(); kernel_init();
} }

View File

@ -208,9 +208,6 @@ void thread_yield_higher(void)
/* reset hardware watchdog */ /* reset hardware watchdog */
system_soft_wdt_feed(); system_soft_wdt_feed();
/* handle pending ets tasks first to keep system alive */
ets_tasks_run();
/* yield next task */ /* yield next task */
#if defined(ENABLE_DEBUG) && defined(DEVELHELP) #if defined(ENABLE_DEBUG) && defined(DEVELHELP)
if (sched_active_thread) { if (sched_active_thread) {