Skip to content

Commit e154f4d

Browse files
dok-nethasenradball
authored andcommitted
Implement esp_yield() as a replacement for delay(0)
esp_yield() now also calls esp_schedule(), original esp_yield() function renamed to esp_suspend(). Don't use delay(0) in the Core internals, libraries and examples. Use yield() when the code is supposed to be called from CONT, use esp_yield() when the code can be called from either CONT or SYS. Clean-up esp_yield() and esp_schedule() declarations across the code and use coredecls.h instead. Implement helper functions for libraries that were previously using esp_yield(), esp_schedule() and esp_delay() directly to wait for certain SYS context tasks to complete. Correctly use esp_delay() for timeouts, make sure scheduled functions have a chance to run (e.g. LwIP_Ethernet uses recurrent) Related issues: - esp8266#6107 - discussion about the esp_yield() and esp_delay() usage in ClientContext - esp8266#6212 - discussion about replacing delay() with a blocking loop - esp8266#6680 - pull request introducing LwIP-based Ethernet - esp8266#7146 - discussion that originated UART code changes - esp8266#7969 - proposal to remove delay(0) from the example code - esp8266#8291 - discussion related to the run_scheduled_recurrent_functions() usage in LwIP Ethernet - esp8266#8317 - yieldUntil() implementation, similar to the esp_delay() overload with a timeout and a 0 interval
1 parent bdd7e9e commit e154f4d

32 files changed

+286
-213
lines changed

cores/esp8266/Esp.cpp

+3-5
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,18 @@ void EspClass::wdtFeed(void)
115115
system_soft_wdt_feed();
116116
}
117117

118-
extern "C" void esp_yield();
119-
120118
void EspClass::deepSleep(uint64_t time_us, WakeMode mode)
121119
{
122120
system_deep_sleep_set_option(static_cast<int>(mode));
123121
system_deep_sleep(time_us);
124-
esp_yield();
122+
esp_suspend();
125123
}
126124

127125
void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode)
128126
{
129127
system_deep_sleep_set_option(static_cast<int>(mode));
130128
system_deep_sleep_instant(time_us);
131-
esp_yield();
129+
esp_suspend();
132130
}
133131

134132
//this calculation was taken verbatim from the SDK api reference for SDK 2.1.0.
@@ -200,7 +198,7 @@ void EspClass::reset(void)
200198
void EspClass::restart(void)
201199
{
202200
system_restart();
203-
esp_yield();
201+
esp_suspend();
204202
}
205203

206204
[[noreturn]] void EspClass::rebootIntoUartDownloadMode()

cores/esp8266/HardwareSerial.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis)
143143
if ((detectedBaudrate = testBaudrate())) {
144144
break;
145145
}
146-
delay(100);
146+
esp_delay(100);
147147
}
148148
return detectedBaudrate;
149149
}

cores/esp8266/PolledTimeout.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <limits> // std::numeric_limits
2828
#include <type_traits> // std::is_unsigned
2929
#include <core_esp8266_features.h>
30+
#include <coredecls.h>
3031

3132
namespace esp8266
3233
{
@@ -45,7 +46,7 @@ struct DoNothing
4546

4647
struct YieldOrSkip
4748
{
48-
static void execute() {delay(0);}
49+
static void execute() {esp_yield();}
4950
};
5051

5152
template <unsigned long delayMs>

cores/esp8266/Schedule.cpp

+7-11
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,6 @@ bool schedule_recurrent_function_us(const std::function<bool(void)>& fn,
135135

136136
void run_scheduled_functions()
137137
{
138-
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
139-
140138
// prevent scheduling of new functions during this run
141139
auto stop = sLast;
142140
bool done = false;
@@ -161,13 +159,10 @@ void run_scheduled_functions()
161159
recycle_fn_unsafe(to_recycle);
162160
}
163161

164-
if (yieldNow)
165-
{
166-
// because scheduled functions might last too long for watchdog etc,
167-
// this is yield() in cont stack:
168-
esp_schedule();
169-
cont_yield(g_pcont);
170-
}
162+
// scheduled functions might last too long for watchdog etc.
163+
// yield() is allowed in scheduled functions, therefore
164+
// recursion into run_scheduled_recurrent_functions() is permitted
165+
optimistic_yield(100000);
171166
}
172167
}
173168

@@ -241,9 +236,10 @@ void run_scheduled_recurrent_functions()
241236
if (yieldNow)
242237
{
243238
// because scheduled functions might last too long for watchdog etc,
244-
// this is yield() in cont stack:
239+
// this is yield() in cont stack, but need to call cont_suspend directly
240+
// to prevent recursion into run_scheduled_recurrent_functions()
245241
esp_schedule();
246-
cont_yield(g_pcont);
242+
cont_suspend(g_pcont);
247243
}
248244
} while (current && !done);
249245

cores/esp8266/cont.S

+10-10
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
.section .irom0.text
2222
.align 4
2323
.literal_position
24-
.global cont_yield
25-
.type cont_yield, @function
26-
cont_yield:
24+
.global cont_suspend
25+
.type cont_suspend, @function
26+
cont_suspend:
2727
/* a1: sp */
2828
/* a2: void* cont_ctx */
2929
/* adjust stack and save registers */
@@ -35,10 +35,10 @@ cont_yield:
3535
s32i a0, a1, 16
3636
s32i a2, a1, 20
3737

38-
/* &cont_continue -> cont_ctx.pc_yield */
38+
/* &cont_continue -> cont_ctx.pc_suspend */
3939
movi a3, cont_continue
4040
s32i a3, a2, 8
41-
/* sp -> cont_ctx.sp_yield */
41+
/* sp -> cont_ctx.sp_suspend */
4242
s32i a1, a2, 12
4343

4444
/* a0 <- cont_ctx.pc_ret */
@@ -56,7 +56,7 @@ cont_continue:
5656
l32i a2, a1, 20
5757
addi a1, a1, 24
5858
ret
59-
.size cont_yield, . - cont_yield
59+
.size cont_suspend, . - cont_suspend
6060

6161
////////////////////////////////////////////////////
6262

@@ -108,7 +108,7 @@ cont_run:
108108
/* sp -> cont_ctx.sp_ret */
109109
s32i a1, a2, 4
110110

111-
/* if cont_ctx.pc_yield != 0, goto cont_resume */
111+
/* if cont_ctx.pc_suspend != 0, goto cont_resume */
112112
l32i a4, a2, 8
113113
bnez a4, cont_resume
114114
/* else */
@@ -119,12 +119,12 @@ cont_run:
119119
jx a2
120120

121121
cont_resume:
122-
/* a1 <- cont_ctx.sp_yield */
122+
/* a1 <- cont_ctx.sp_suspend */
123123
l32i a1, a2, 12
124-
/* reset yield flag, 0 -> cont_ctx.pc_yield */
124+
/* reset yield flag, 0 -> cont_ctx.pc_suspend */
125125
movi a3, 0
126126
s32i a3, a2, 8
127-
/* jump to saved cont_ctx.pc_yield */
127+
/* jump to saved cont_ctx.pc_suspend */
128128
movi a0, cont_ret
129129
jx a4
130130

cores/esp8266/cont.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ typedef struct cont_ {
3535
void (*pc_ret)(void);
3636
unsigned* sp_ret;
3737

38-
void (*pc_yield)(void);
39-
unsigned* sp_yield;
38+
void (*pc_suspend)(void);
39+
unsigned* sp_suspend;
4040

4141
unsigned* stack_end;
4242
unsigned unused1;
@@ -55,12 +55,12 @@ extern cont_t* g_pcont;
5555
void cont_init(cont_t*);
5656

5757
// Run function pfn in a separate stack, or continue execution
58-
// at the point where cont_yield was called
58+
// at the point where cont_suspend was called
5959
void cont_run(cont_t*, void (*pfn)(void));
6060

6161
// Return to the point where cont_run was called, saving the
6262
// execution state (registers and stack)
63-
void cont_yield(cont_t*);
63+
void cont_suspend(cont_t*);
6464

6565
// Check guard bytes around the stack. Return 0 in case everything is ok,
6666
// return 1 if guard bytes were overwritten.
@@ -70,9 +70,9 @@ int cont_check(cont_t* cont);
7070
// and thus weren't used by the user code. i.e. that stack space is free. (high water mark)
7171
int cont_get_free_stack(cont_t* cont);
7272

73-
// Check if yield() may be called. Returns true if we are running inside
73+
// Check if cont_suspend() may be called. Returns true if we are running inside
7474
// continuation stack
75-
bool cont_can_yield(cont_t* cont);
75+
bool cont_can_suspend(cont_t* cont);
7676

7777
// Repaint the stack from the current SP to the end, to allow individual
7878
// routines' stack usages to be calculated by re-painting, checking current

cores/esp8266/cont_util.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ int cont_get_free_stack(cont_t* cont) {
6262
return freeWords * 4;
6363
}
6464

65-
bool IRAM_ATTR cont_can_yield(cont_t* cont) {
65+
bool IRAM_ATTR cont_can_suspend(cont_t* cont) {
6666
return !ETS_INTR_WITHINISR() &&
67-
cont->pc_ret != 0 && cont->pc_yield == 0;
67+
cont->pc_ret != 0 && cont->pc_suspend == 0;
6868
}
6969

7070
// No need for this to be in IRAM, not expected to be IRQ called

cores/esp8266/core_esp8266_main.cpp

+61-16
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ cont_t* g_pcont __attribute__((section(".noinit")));
6262
static os_event_t s_loop_queue[LOOP_QUEUE_SIZE];
6363

6464
/* Used to implement optimistic_yield */
65-
static uint32_t s_cycles_at_yield_start;
65+
static uint32_t s_cycles_at_resume;
6666

6767
/* For ets_intr_lock_nest / ets_intr_unlock_nest
6868
* Max nesting seen by SDK so far is 2.
@@ -80,6 +80,10 @@ const char* core_release =
8080
#else
8181
NULL;
8282
#endif
83+
84+
static os_timer_t delay_timer;
85+
#define ONCE 0
86+
#define REPEAT 1
8387
} // extern "C"
8488

8589
void initVariant() __attribute__((weak));
@@ -106,32 +110,71 @@ extern "C" void __preloop_update_frequency() {
106110
extern "C" void preloop_update_frequency() __attribute__((weak, alias("__preloop_update_frequency")));
107111

108112
extern "C" bool can_yield() {
109-
return cont_can_yield(g_pcont);
113+
return cont_can_suspend(g_pcont);
110114
}
111115

112-
static inline void esp_yield_within_cont() __attribute__((always_inline));
113-
static void esp_yield_within_cont() {
114-
cont_yield(g_pcont);
115-
s_cycles_at_yield_start = ESP.getCycleCount();
116+
static inline void esp_suspend_within_cont() __attribute__((always_inline));
117+
static void esp_suspend_within_cont() {
118+
cont_suspend(g_pcont);
119+
s_cycles_at_resume = ESP.getCycleCount();
116120
run_scheduled_recurrent_functions();
117121
}
118122

119-
extern "C" void __esp_yield() {
120-
if (can_yield()) {
121-
esp_yield_within_cont();
123+
extern "C" void __esp_suspend() {
124+
if (cont_can_suspend(g_pcont)) {
125+
esp_suspend_within_cont();
122126
}
123127
}
124128

125-
extern "C" void esp_yield() __attribute__ ((weak, alias("__esp_yield")));
129+
extern "C" void esp_suspend() __attribute__ ((weak, alias("__esp_suspend")));
126130

127131
extern "C" IRAM_ATTR void esp_schedule() {
128132
ets_post(LOOP_TASK_PRIORITY, 0, 0);
129133
}
130134

135+
// Replacement for delay(0). In CONT, same as yield(). Whereas yield() panics
136+
// in SYS, esp_yield() is safe to call and only schedules CONT. Use yield()
137+
// whereever only called from CONT, use esp_yield() if code is called from SYS
138+
// or both CONT and SYS.
139+
extern "C" void esp_yield() {
140+
esp_schedule();
141+
esp_suspend();
142+
}
143+
144+
void delay_end(void* arg) {
145+
(void)arg;
146+
esp_schedule();
147+
}
148+
149+
extern "C" void __esp_delay(unsigned long ms) {
150+
if (ms) {
151+
os_timer_setfn(&delay_timer, (os_timer_func_t*)&delay_end, 0);
152+
os_timer_arm(&delay_timer, ms, ONCE);
153+
}
154+
else {
155+
esp_schedule();
156+
}
157+
esp_suspend();
158+
if (ms) {
159+
os_timer_disarm(&delay_timer);
160+
}
161+
}
162+
163+
extern "C" void esp_delay(unsigned long ms) __attribute__((weak, alias("__esp_delay")));
164+
165+
bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms) {
166+
uint32_t expired = millis() - start_ms;
167+
if (expired >= timeout_ms) {
168+
return true;
169+
}
170+
esp_delay(std::min((timeout_ms - expired), intvl_ms));
171+
return false;
172+
}
173+
131174
extern "C" void __yield() {
132-
if (can_yield()) {
175+
if (cont_can_suspend(g_pcont)) {
133176
esp_schedule();
134-
esp_yield_within_cont();
177+
esp_suspend_within_cont();
135178
}
136179
else {
137180
panic();
@@ -140,14 +183,17 @@ extern "C" void __yield() {
140183

141184
extern "C" void yield(void) __attribute__ ((weak, alias("__yield")));
142185

186+
// In CONT, actually performs yield() only once the given time interval
187+
// has elapsed since the last time yield() occured. Whereas yield() panics
188+
// in SYS, optimistic_yield() additionally is safe to call and does nothing.
143189
extern "C" void optimistic_yield(uint32_t interval_us) {
144190
const uint32_t intvl_cycles = interval_us *
145191
#if defined(F_CPU)
146192
clockCyclesPerMicrosecond();
147193
#else
148194
ESP.getCpuFreqMHz();
149195
#endif
150-
if ((ESP.getCycleCount() - s_cycles_at_yield_start) > intvl_cycles &&
196+
if ((ESP.getCycleCount() - s_cycles_at_resume) > intvl_cycles &&
151197
can_yield())
152198
{
153199
yield();
@@ -207,16 +253,16 @@ static void loop_wrapper() {
207253

208254
static void loop_task(os_event_t *events) {
209255
(void) events;
210-
s_cycles_at_yield_start = ESP.getCycleCount();
256+
s_cycles_at_resume = ESP.getCycleCount();
211257
ESP.resetHeap();
212258
cont_run(g_pcont, &loop_wrapper);
213259
ESP.setDramHeap();
214260
if (cont_check(g_pcont) != 0) {
215261
panic();
216262
}
217263
}
218-
extern "C" {
219264

265+
extern "C" {
220266
struct object { long placeholder[ 10 ]; };
221267
void __register_frame_info (const void *begin, struct object *ob);
222268
extern char __eh_frame[];
@@ -253,7 +299,6 @@ static void __unhandled_exception_cpp()
253299
}
254300
#endif
255301
}
256-
257302
}
258303

259304
void init_done() {

cores/esp8266/core_esp8266_postmortem.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,12 @@ static void print_stack(uint32_t start, uint32_t end) {
245245
}
246246
}
247247

248-
static void uart_write_char_d(char c) {
248+
static void IRAM_ATTR uart_write_char_d(char c) {
249249
uart0_write_char_d(c);
250250
uart1_write_char_d(c);
251251
}
252252

253-
static void uart0_write_char_d(char c) {
253+
static void IRAM_ATTR uart0_write_char_d(char c) {
254254
while (((USS(0) >> USTXC) & 0xff)) { }
255255

256256
if (c == '\n') {
@@ -259,7 +259,7 @@ static void uart0_write_char_d(char c) {
259259
USF(0) = c;
260260
}
261261

262-
static void uart1_write_char_d(char c) {
262+
static void IRAM_ATTR uart1_write_char_d(char c) {
263263
while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { }
264264

265265
if (c == '\n') {

cores/esp8266/core_esp8266_waveform_phase.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCc
211211
}
212212
std::atomic_thread_fence(std::memory_order_acq_rel);
213213
while (waveform.toSetBits) {
214-
delay(0); // Wait for waveform to update
214+
esp_yield(); // Wait for waveform to update
215215
std::atomic_thread_fence(std::memory_order_acquire);
216216
}
217217
return true;

0 commit comments

Comments
 (0)