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
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)))
USEMODULE += newlib_syscalls_default
USEMODULE += xtimer
@ -35,13 +45,3 @@ endif
ifneq (, $(filter newlib_syscalls_default, $(USEMODULE)))
USEMODULE += stdio_uart
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)
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
endif
@ -39,7 +49,17 @@ PSEUDOMODULES += esp_sdk_int_handling
PSEUDOMODULES += esp_sw_timer
PSEUDOMODULES += esp_spiffs
USEMODULE += esp
USEMODULE += mtd
USEMODULE += periph
USEMODULE += periph_common
USEMODULE += ps
USEMODULE += random
USEMODULE += sdk
USEMODULE += xtensa
ifneq (, $(filter pthread, $(USEMODULE)))
# has to be included before $(ESP8266_NEWLIB_DIR)
INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include
endif
@ -49,13 +69,14 @@ INCLUDES += -I$(RIOTCPU)/$(CPU)
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor
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 += -mlongcalls -mtext-section-literals -fdata-sections
CFLAGS += -mlongcalls -mtext-section-literals
CFLAGS += -fdata-sections -fzero-initialized-in-bss
ASFLAGS += --longcalls --text-section-literals
ifneq (, $(filter esp_sw_timer, $(USEMODULE)))
USEMODULE += esp_sdk
ifeq (, $(filter esp_sdk_int_handling, $(USEMODULE)))
CFLAGS += -DCONTEXT_SWITCH_BY_INT
endif
ifneq (, $(filter esp_sdk, $(USEMODULE)))
@ -70,17 +91,9 @@ ifneq (, $(filter esp_gdbstub, $(USEMODULE)))
endif
ifneq (, $(filter esp_gdb, $(USEMODULE)))
CFLAGS_OPT = -fzero-initialized-in-bss -Og -ggdb -g3
CFLAGS += -Og -ggdb -g3
else
CFLAGS_OPT = -fzero-initialized-in-bss -O2
endif
CFLAGS += $(CFLAGS_OPT)
ifneq (, $(filter esp_spiffs, $(USEMODULE)))
export SPIFFS_STD_OPTION = -std=c99
USEMODULE += spiffs
USEMODULE += vfs
CFLAGS += -Os
endif
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 += -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
FLASH_SIZE = -fs 8m
export PREFLASHER ?= esptool.py
@ -143,5 +147,5 @@ else
export FFLAGS += -p $(PORT) -b $(PROGRAMMER_SPEED) write_flash
export FFLAGS += -fm $(FLASH_MODE)
export FFLAGS += 0 $(ELFFILE)-0x00000.bin
export FFLAGS += 0x10000 $(ELFFILE)-0x10000.bin
export FFLAGS += 0x10000 $(ELFFILE)-0x10000.bin; esptool.py -p $(PORT) run
endif

View File

@ -710,13 +710,43 @@ INCLUDES += -I$(APPDIR)
# <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)]

View File

@ -29,18 +29,64 @@ extern "C" {
#endif
/**
* @brief Stack size configuration
* @name Stack size configuration
* @{
*/
#ifdef MODULE_ESP_SDK_INT_HANDLING
#ifndef THREAD_EXTRA_STACKSIZE_PRINTF
#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
#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
}
#endif /* CPU_CONF_H */
#endif
#endif /* CPU_CONF_H */
/** @} */

View File

@ -35,10 +35,6 @@ void pm_set_lowest(void)
{
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)
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());
#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)

View File

@ -8,6 +8,37 @@
* 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
#define ENABLE_DEBUG 0
@ -21,21 +52,19 @@
#include "sdk/ets_task.h"
#include "sdk/sdk.h"
#define TIMER_TASK_PRIORITY 31
static uint8_t min_prio = 0;
/* helper function for *ets_run* */
uint8_t ets_highest_1_bit (uint32_t mask)
{
__asm__ volatile ("nsau %0, %1;" :"=r"(mask) : "r"(mask));
return 32 - mask;
}
/**
* @brief Perform execution of all pending ETS system tasks.
*
* This is necessary to keep the underlying ETS system used by the
* SDK alive.
/*
* 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
* the RIOT thread *ets_task_func*.
*/
void IRAM ets_tasks_run (void)
{
@ -81,56 +110,54 @@ void IRAM ets_tasks_run (void)
system_soft_wdt_feed();
}
/**
* To realize event-driven SDK functions such as WiFi functions and software
* timers, and to keep the system alive, the SDK internally uses its own
* 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*.
*
* 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.
*
* 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();
#define THREAD_FLAG_ETS_THREAD (1 << 0)
static volatile thread_t* ets_thread = NULL;
/*
* 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
* the thread flag is set, it calls the *ets_run* function, which performs
* all ETS tasks with pending events exactly once. The thread flag is set by
* the *ets_post* function, which is called at the end of an ETS interrupt
* service routine.
*/
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);
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))
#else
#define irom_cache_enabled() (1)
#endif
/*
* Overwritten version of ROM function *ets_post*.
*
* 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.
*
* Please note: *ets_post* is executed in interrupt context
*/
uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
{
/* This function is executed in interrupt context */
uint32_t ret;
critical_enter();
@ -154,6 +181,10 @@ uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
system_soft_wdt_feed();
}
if (ets_thread && irom_cache_enabled()) {
thread_flags_set((thread_t*)ets_thread, THREAD_FLAG_ETS_THREAD);
}
critical_exit();
return ret;
@ -161,7 +192,7 @@ uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
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 */

View File

@ -65,6 +65,14 @@ extern uint8_t _eheap;
#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
*
@ -115,6 +123,12 @@ void ets_run(void)
ets_isr_unmask(BIT(ETS_SOFT_INUM));
#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 */
kernel_init();
}

View File

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