diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index f717c15881..bc241eb392 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -213,7 +213,8 @@ uint32_t EspClass::getCycleCount() __asm__ __volatile__("esync; rsr %0,ccount":"=a" (ccount)); return ccount; } -#endif + +#endif // !defined(CORE_MOCK) extern EspClass ESP; diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 2633c63182..fa57e217e5 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -1,7 +1,6 @@ #include #include #include "Arduino.h" -#include // Duplicate typedefs from core_esp8266_wiring_digital_c typedef void (*voidFuncPtr)(void); @@ -17,7 +16,6 @@ void ICACHE_RAM_ATTR interruptFunctional(void* arg) if (localArg->functionInfo->reqScheduledFunction) { schedule_function(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo)))); -// scheduledInterrupts->scheduleFunctionReg(std::bind(localArg->functionInfo->reqScheduledFunction,InterruptInfo(*(localArg->interruptInfo))), false, true); } if (localArg->functionInfo->reqFunction) { @@ -54,10 +52,6 @@ void attachInterrupt(uint8_t pin, std::function intRoutine, int mode void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode) { - if (!scheduledInterrupts) - { - scheduledInterrupts = new ScheduledFunctions(32); - } InterruptInfo* ii = new InterruptInfo; FunctionInfo* fi = new FunctionInfo; diff --git a/cores/esp8266/FunctionalInterrupt.h b/cores/esp8266/FunctionalInterrupt.h index a6d53188ae..968793e499 100644 --- a/cores/esp8266/FunctionalInterrupt.h +++ b/cores/esp8266/FunctionalInterrupt.h @@ -4,7 +4,6 @@ #include #include #include -#include extern "C" { #include "c_types.h" @@ -29,7 +28,6 @@ struct ArgStructure { FunctionInfo* functionInfo = nullptr; }; -static ScheduledFunctions* scheduledInterrupts; void attachInterrupt(uint8_t pin, std::function intRoutine, int mode); void attachScheduledInterrupt(uint8_t pin, std::function scheduledIntRoutine, int mode); diff --git a/cores/esp8266/PolledTimeout.h b/cores/esp8266/PolledTimeout.h index 95157d3772..fe9c9ca4bc 100644 --- a/cores/esp8266/PolledTimeout.h +++ b/cores/esp8266/PolledTimeout.h @@ -153,7 +153,7 @@ class timeoutTemplate reset(userTimeout); } - ICACHE_RAM_ATTR + IRAM_ATTR // fast bool expired() { YieldPolicyT::execute(); //in case of DoNothing: gets optimized away @@ -162,7 +162,7 @@ class timeoutTemplate return expiredOneShot(); } - ICACHE_RAM_ATTR + IRAM_ATTR // fast operator bool() { return expired(); @@ -178,6 +178,7 @@ class timeoutTemplate return _timeout != alwaysExpired; } + IRAM_ATTR // called from ISR void reset(const timeType newUserTimeout) { reset(); @@ -185,6 +186,7 @@ class timeoutTemplate _neverExpires = (newUserTimeout < 0) || (newUserTimeout > timeMax()); } + IRAM_ATTR // called from ISR void reset() { _start = TimePolicyT::time(); @@ -208,7 +210,7 @@ class timeoutTemplate private: - ICACHE_RAM_ATTR + IRAM_ATTR // fast bool checkExpired(const timeType internalUnit) const { // canWait() is not checked here @@ -218,7 +220,7 @@ class timeoutTemplate protected: - ICACHE_RAM_ATTR + IRAM_ATTR // fast bool expiredRetrigger() { if (!canWait()) @@ -234,7 +236,7 @@ class timeoutTemplate return false; } - ICACHE_RAM_ATTR + IRAM_ATTR // fast bool expiredOneShot() const { // returns "always expired" or "has expired" diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 27b9731954..bf968743e2 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -1,78 +1,131 @@ + +#include + #include "Schedule.h" +#include "PolledTimeout.h" +#include "interrupts.h" + +typedef std::function mFuncT; struct scheduled_fn_t { - scheduled_fn_t* mNext; - std::function mFunc; + scheduled_fn_t* mNext = nullptr; + mFuncT mFunc; + esp8266::polledTimeout::periodicFastUs callNow; + + scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { } }; -static scheduled_fn_t* sFirst = 0; -static scheduled_fn_t* sLast = 0; +static scheduled_fn_t* sFirst = nullptr; +static scheduled_fn_t* sLast = nullptr; -static scheduled_fn_t* sFirstUnused = 0; -static scheduled_fn_t* sLastUnused = 0; +static scheduled_fn_t* sUnused = nullptr; static int sCount = 0; -static scheduled_fn_t* get_fn() { - scheduled_fn_t* result = NULL; +IRAM_ATTR // called from ISR +static scheduled_fn_t* get_fn_unsafe() +{ + scheduled_fn_t* result = nullptr; // try to get an item from unused items list - if (sFirstUnused) { - result = sFirstUnused; - sFirstUnused = result->mNext; - if (sFirstUnused == NULL) { - sLastUnused = NULL; - } + if (sUnused) + { + result = sUnused; + sUnused = sUnused->mNext; + result->mNext = nullptr; } // if no unused items, and count not too high, allocate a new one - else if (sCount != SCHEDULED_FN_MAX_COUNT) { + else if (sCount < SCHEDULED_FN_MAX_COUNT) + { result = new scheduled_fn_t; - result->mNext = NULL; ++sCount; } return result; } -static void recycle_fn(scheduled_fn_t* fn) +static void recycle_fn_unsafe(scheduled_fn_t* fn) { - if (!sLastUnused) { - sFirstUnused = fn; - } - else { - sLastUnused->mNext = fn; - } - fn->mNext = NULL; - sLastUnused = fn; + fn->mFunc = nullptr; // special overload in c++ std lib + fn->mNext = sUnused; + sUnused = fn; } -bool schedule_function(std::function fn) +IRAM_ATTR // (not only) called from ISR +bool schedule_function_us(std::function&& fn, uint32_t repeat_us) { - scheduled_fn_t* item = get_fn(); - if (!item) { + assert(repeat_us < decltype(scheduled_fn_t::callNow)::neverExpires); //~26800000us (26.8s) + + InterruptLock lockAllInterruptsInThisScope; + + scheduled_fn_t* item = get_fn_unsafe(); + if (!item) return false; - } + + if (repeat_us) + item->callNow.reset(repeat_us); + item->mFunc = fn; - item->mNext = NULL; - if (!sFirst) { - sFirst = item; - } - else { + if (sFirst) sLast->mNext = item; - } + else + sFirst = item; sLast = item; + return true; } +IRAM_ATTR // (not only) called from ISR +bool schedule_function_us(const std::function& fn, uint32_t repeat_us) +{ + return schedule_function_us(std::function(fn), repeat_us); +} + +IRAM_ATTR // called from ISR +bool schedule_function(std::function&& fn) +{ + return schedule_function_us([fn]() { fn(); return false; }, 0); +} + +IRAM_ATTR // called from ISR +bool schedule_function(const std::function& fn) +{ + return schedule_function(std::function(fn)); +} + void run_scheduled_functions() { - scheduled_fn_t* rFirst = sFirst; - sFirst = NULL; - sLast = NULL; - while (rFirst) { - scheduled_fn_t* item = rFirst; - rFirst = item->mNext; - item->mFunc(); - item->mFunc = std::function(); - recycle_fn(item); + // Note to the reader: + // There is no exposed API to remove a scheduled function: + // Scheduled functions are removed only from this function, and + // its purpose is that it is never called from an interrupt + // (always on cont stack). + + scheduled_fn_t* lastRecurring = nullptr; + scheduled_fn_t* toCall = sFirst; + while (toCall) + { + scheduled_fn_t* item = toCall; + toCall = toCall->mNext; + if (item->callNow) + { + if (item->mFunc()) + { + lastRecurring = item; + } + else + { + InterruptLock lockAllInterruptsInThisScope; + + if (sFirst == item) + sFirst = sFirst->mNext; + else if (lastRecurring) + lastRecurring->mNext = item->mNext; + + if (sLast == item) + sLast = lastRecurring; + + recycle_fn_unsafe(item); + } + } } } diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index 3399972945..bda3ebf48e 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -4,22 +4,28 @@ #include #define SCHEDULED_FN_MAX_COUNT 32 -#define SCHEDULED_FN_INITIAL_COUNT 4 -// Warning -// This API is not considered stable. -// Function signatures will change. +// This API was not considered stable but is now stabilizing. +// Function signatures may change, queue must stay FIFO. // You have been warned. -// Run given function next time `loop` function returns, +// Run given function ONCE next time `loop` function returns, +// or `yield` is called, // or `run_scheduled_functions` is called. // Use std::bind to pass arguments to a function, or call a class member function. // Note: there is no mechanism for cancelling scheduled functions. // Keep that in mind when binding functions to objects which may have short lifetime. // Returns false if the number of scheduled functions exceeds SCHEDULED_FN_MAX_COUNT. -bool schedule_function(std::function fn); +//bool schedule_function(std::function&& fn); +bool schedule_function(const std::function& fn); -// Run all scheduled functions. +// Run given function periodically about every microseconds until it returns false. +// Note that it may be more than microseconds between calls if `yield` is not called +// frequently, and therefore should not be used for timing critical operations. +//bool schedule_function_us(std::function&& fn, uint32_t repeat_us); +bool schedule_function_us(const std::function& fn, uint32_t repeat_us); + +// Run all scheduled functions. // Use this function if your are not using `loop`, or `loop` does not return // on a regular basis. void run_scheduled_functions(); diff --git a/cores/esp8266/ScheduledFunctions.cpp b/cores/esp8266/ScheduledFunctions.cpp deleted file mode 100644 index 25bc58db61..0000000000 --- a/cores/esp8266/ScheduledFunctions.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * ScheduledFunctions.cpp - * - * Created on: 27 apr. 2018 - * Author: Herman - */ -#include "ScheduledFunctions.h" - -std::list ScheduledFunctions::scheduledFunctions; - -ScheduledFunctions::ScheduledFunctions() -:ScheduledFunctions(UINT_MAX) -{ -} - -ScheduledFunctions::ScheduledFunctions(unsigned int reqMax) -{ - maxElements = reqMax; -} - -ScheduledFunctions::~ScheduledFunctions() { -} - -ScheduledRegistration ScheduledFunctions::insertElement(ScheduledElement se, bool front) -{ - if (countElements >= maxElements) - { - return nullptr; - } - else - { - countElements++; - if (front) - { - scheduledFunctions.push_front(se); - return scheduledFunctions.begin()->registration; - } - else - { - scheduledFunctions.push_back(se); - return scheduledFunctions.rbegin()->registration; - } - } -} - -std::list::iterator ScheduledFunctions::eraseElement(std::list::iterator it) -{ - countElements--; - return scheduledFunctions.erase(it); -} - -bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf, bool continuous, bool front) -{ - return (insertElement({this,continuous,nullptr,sf}, front) == nullptr); -} - -bool ScheduledFunctions::scheduleFunction(ScheduledFunction sf) -{ - return scheduleFunction(sf, false, false); -} - -ScheduledRegistration ScheduledFunctions::scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front) -{ - return insertElement({this,continuous,std::make_shared(1),sf},front); -} - -void ScheduledFunctions::runScheduledFunctions() -{ - auto lastElement = scheduledFunctions.end(); // do not execute elements added during runScheduledFunctions - auto it = scheduledFunctions.begin(); - while (it != lastElement) - { - bool erase = false; - if (it->registration == nullptr) - { - it->function(); - } - else - { - if (it->registration.use_count() > 1) - { - it->function(); - } - else - { - erase = true; - } - } - if ((!it->continuous) || (erase)) - { - it = it->_this->eraseElement(it); - } - else - { - it++; - } - } -} - -void ScheduledFunctions::removeFunction(ScheduledRegistration sr) -{ - auto it = scheduledFunctions.begin(); - bool removed = false; - while ((!removed) && (it != scheduledFunctions.end())) - { - if (it->registration == sr) - { - it = eraseElement(it); - removed = true; - } - else - { - it++; - } - } -} - diff --git a/cores/esp8266/ScheduledFunctions.h b/cores/esp8266/ScheduledFunctions.h deleted file mode 100644 index 0129635364..0000000000 --- a/cores/esp8266/ScheduledFunctions.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * ScheduledFunctions.h - * - * Created on: 27 apr. 2018 - * Author: Herman - */ -#include "Arduino.h" -#include "Schedule.h" - -#include -#include -#include -#include - -#ifndef SCHEDULEDFUNCTIONS_H_ -#define SCHEDULEDFUNCTIONS_H_ - -typedef std::function ScheduledFunction; -typedef std::shared_ptr ScheduledRegistration; - -class ScheduledFunctions { - -public: - ScheduledFunctions(); - ScheduledFunctions(unsigned int reqMax); - virtual ~ScheduledFunctions(); - - struct ScheduledElement - { - ScheduledFunctions* _this; - bool continuous; - ScheduledRegistration registration; - ScheduledFunction function; - }; - - ScheduledRegistration insertElement(ScheduledElement se, bool front); - std::list::iterator eraseElement(std::list::iterator); - bool scheduleFunction(ScheduledFunction sf, bool continuous, bool front); - bool scheduleFunction(ScheduledFunction sf); - ScheduledRegistration scheduleFunctionReg (ScheduledFunction sf, bool continuous, bool front); - static void runScheduledFunctions(); - void removeFunction(ScheduledRegistration sr); - - - static std::list scheduledFunctions; - unsigned int maxElements; - unsigned int countElements = 0; - -}; - -#endif /* SCHEDULEDFUNCTIONS_H_ */ diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 5d0fb54e65..7ab1e56bf7 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -91,13 +91,15 @@ extern "C" void esp_yield() { } extern "C" void esp_schedule() { + // always on CONT stack here + run_scheduled_functions(); ets_post(LOOP_TASK_PRIORITY, 0, 0); } extern "C" void __yield() { if (cont_can_yield(g_pcont)) { esp_schedule(); - esp_yield(); + cont_yield(g_pcont); //esp_yield(); } else { panic(); @@ -122,7 +124,6 @@ static void loop_wrapper() { setup_done = true; } loop(); - run_scheduled_functions(); esp_schedule(); } diff --git a/tests/host/common/Arduino.h b/tests/host/common/Arduino.h index 9b75fc114c..02f0d9695f 100644 --- a/tests/host/common/Arduino.h +++ b/tests/host/common/Arduino.h @@ -143,26 +143,12 @@ extern "C" { #define __STRINGIFY(a) #a #endif - // these low level routines provide a replacement for SREG interrupt save that AVR uses - // but are esp8266 specific. A normal use pattern is like - // - //{ - // uint32_t savedPS = xt_rsil(1); // this routine will allow level 2 and above - // // do work here - // xt_wsr_ps(savedPS); // restore the state - //} - // - // level (0-15), interrupts of the given level and above will be active - // level 15 will disable ALL interrupts, - // level 0 will enable ALL interrupts, - // -#define xt_rsil(level) (__extension__({uint32_t state; __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)); state;})) -#define xt_wsr_ps(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") - +#define xt_rsil(level) (level) +#define xt_wsr_ps(state) do { (void)(state); } while (0) + #define interrupts() xt_rsil(0) #define noInterrupts() xt_rsil(15) - - + #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) diff --git a/tests/host/common/c_types.h b/tests/host/common/c_types.h index 897c2cbc6e..867f2f7726 100644 --- a/tests/host/common/c_types.h +++ b/tests/host/common/c_types.h @@ -102,6 +102,9 @@ typedef enum { #define ICACHE_RODATA_ATTR #endif /* ICACHE_FLASH */ +// counterpart https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp8266-compat.h +#define IRAM_ATTR ICACHE_RAM_ATTR + #define STORE_ATTR __attribute__((aligned(4))) #ifndef __cplusplus diff --git a/tests/host/common/mock.h b/tests/host/common/mock.h index 067411139f..e681d64975 100644 --- a/tests/host/common/mock.h +++ b/tests/host/common/mock.h @@ -131,8 +131,6 @@ void mockUDPSwallow (size_t copied, char* ccinbuf, size_t& ccinbufsize); class UdpContext; void register_udp (int sock, UdpContext* udp = nullptr); -class InterruptLock { }; - // void mock_start_spiffs (const String& fname, size_t size_kb, size_t block_kb = 8, size_t page_b = 512); diff --git a/tools/sdk/include/c_types.h b/tools/sdk/include/c_types.h index 40eb5bedf9..88dc147213 100644 --- a/tools/sdk/include/c_types.h +++ b/tools/sdk/include/c_types.h @@ -93,6 +93,9 @@ typedef enum { #define ICACHE_RAM_ATTR #endif /* ICACHE_FLASH */ +// counterpart https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp8266-compat.h +#define IRAM_ATTR ICACHE_RAM_ATTR + #define STORE_ATTR __attribute__((aligned(4))) #ifndef __cplusplus