nodemcu-firmware/docs/extn-developer-faq.md

97 lines
5.4 KiB
Markdown

# Extension Developer FAQ
**# # # Work in Progress # # #**
## How does the non-OS SDK structure execution
Details of the execution model for the **non-OS SDK** is not well documented by
Espressif. This section summarises the project's understanding of how this execution
model works based on the Espressif-supplied examples and SDK documentation, plus
various posts on the Espressif BBS and other forums, and an examination of the
BootROM code.
The ESP8266 boot ROM contains a set of primitive tasking and dispatch functions
which are also used by the SDK. In this model, execution units are either:
- **INTERRUPT SERVICE ROUTINES (ISRs)** which are declared and controlled
through the `ets_isr_attach()` and other `ets_isr_*` and `ets_intr_*`
functions. ISRs can be defined on a range of priorities, where a higher
priority ISR is able to interrupt a lower priority one. ISRs are time
critical and should complete in no more than 50 µSec.
ISR code and data constants should be run out of RAM or ROM, for two reasons:
if an ISR interrupts a flash I/O operation (which must disable the Flash
instruction cache) and a cache miss occurs, then the ISR will trigger a
fatal exception; secondly, the
execution time for Flash memory (that is located in the `irom0` load section)
is indeterminate: whilst cache-hits can run at full memory bandwidth, any
cache-misses require the code to be read from Flash; and even though
H/W-based, this is at roughly 26x slower than memory bandwidth (for DIO
flash); this will cause ISR execution to fall outside the require time
guidelines. (Note that any time critical code within normal execution and that
is bracketed by interrupt lock / unlock guards should also follow this 50
µSec guideline.)<br/><br/>
- **TASKS**. A task is a normal execution unit running at a non-interrupt priority.
Tasks can be executed from Flash memory. An executing task can be interrupted
by one or more ISRs being delivered, but it won't be preempted by another
queued task. The Espressif guideline is that no individual task should run for
more than 15 mSec, before returning control to the SDK.
The ROM will queue up to 32 pending tasks at priorities 0..31 and will
execute the highest priority queued task next (or wait on interrupt if none
is runnable). The SDK tasking system is layered on this ROM dispatcher and
it reserves 29 of these task priorities for its own use, including the
implementation of the various SDK timer, WiFi and other callback mechanisms
such as the software WDT.
Three of these task priorities are allocated for and exposed directly at an
application level. The application can declare a single task handler for each
level, and associate a task queue with the level. Tasks can be posted to this
queue. (The post will fail is the queue is full). Tasks are then delivered
FIFO within task priority.
How the three user task priorities USER0 .. USER2 are positioned relative to
the SDK task priorities is undocumented, but some SDK tasks definitely run at
a lower priority than USER0. As a result if you always have a USER task queued
for execution, then you can starve SDK housekeeping tasks and you will start
to get WiFi and other failures. Espressif therefore recommends that you don't
stay computable for more than 500 mSec to avoid such timeouts.
Note that the 50µS, 15mSec and 500mSec limits are guidelines
and not hard constraints -- that is if you break them (slightly) then your code
may (usually) work, but you can get very difficult to diagnose and intermittent
failures. Also running ISRs from Flash may work until there is a collision with
SPIFFS I/O which will then a cause CPU exception.
Also note that the SDK API function `system_os_post()`, and the `task_post_*()`
macros which generate this can be safely called from an ISR.
The Lua runtime is NOT reentrant, and hence any code which calls any Lua API
must run within a task context. Any such task is what we call a _Lua-Land Task_
(or **LLT**). _ISRs must not access the Lua API or Lua resources._ LLTs can be
executed as SDK API callbacks or OS tasks. They can also, of course, call the
Lua execution system to execute Lua code (e.g. `luaL_dofile()` and related
calls).
Also since the application has no control over the relative time ordering of
tasks and SDK API callbacks, LLTs can't make any assumptions about whether a
task and any posted successors will run consecutively.
This API is designed to complement the Lua library model, so that a library can
declare one or more task handlers and that both ISPs and LLTs can then post a
message for delivery to a task handler. Each task handler has a unique message
associated with it, and may bind a single uint32 parameter. How this parameter
is interpreted is left to the task poster and task handler to coordinate.
The interface is exposed through `#include "task/task.h"` and involves two API
calls. Any task handlers are declared, typically in the module_init function by
assigning `task_get_id(some_task_callback)` to a (typically globally) accessible
handle variable, say `XXX_callback_handle`. This can then be used in an ISR or
normal LLT to execute a `task_post_YYY(XXX_callback_handle,param)` where YYY is
one of `low`, `medium`, `high`. The callback will then be executed when the SDK
delivers the task.
_Note_: `task_post_YYY` can fail with a false return if the task Q is full.