1 // Copyright 2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <zephyr.h>
16 #include <sys/param.h>
17 #include <string.h>
18 #include "soc/soc.h"
19 #include "esp_types.h"
20 #include "esp_attr.h"
21 #include "esp_err.h"
22 #include "esp32/rom/ets_sys.h"
23 #include "esp_task.h"
24 #include "esp_log.h"
25 #include "freertos/FreeRTOS.h"
26 #include "freertos/task.h"
27 #include "freertos/semphr.h"
28 // #include "soc/spinlock.h"
29 #include "esp_timer.h"
30 #include "esp_timer_impl.h"
31 #include "sdkconfig.h"
32
33 #include "esp_private/startup_internal.h"
34 #include "esp_private/esp_timer_private.h"
35 #include "esp_private/system_internal.h"
36 #define LOG_MODULE_NAME esp_timer
37 #include <logging/log.h>
38 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
39
40
41 #if CONFIG_IDF_TARGET_ESP32
42 #include "esp32/rtc.h"
43 #elif CONFIG_IDF_TARGET_ESP32S2
44 #include "esp32s2/rtc.h"
45 #elif CONFIG_IDF_TARGET_ESP32S3
46 #include "esp32s3/rtc.h"
47 #elif CONFIG_IDF_TARGET_ESP32C3
48 #include "esp32c3/rtc.h"
49 #endif
50
51 #ifdef CONFIG_ESP_TIMER_PROFILING
52 #define WITH_PROFILING 1
53 #endif
54
55 #ifndef NDEBUG
56 // Enable built-in checks in queue.h in debug builds
57 #define INVARIANTS
58 #endif
59 #include "sys/queue.h"
60
61 #define EVENT_ID_DELETE_TIMER 0xF0DE1E1E
62
63 #define TIMER_EVENT_QUEUE_SIZE 16
64 typedef enum {
65 FL_DISPATCH_METHOD = (1 << 0), //!< 0=Callback is called from timer task, 1=Callback is called from timer ISR
66 FL_SKIP_UNHANDLED_EVENTS = (1 << 1), //!< 0=NOT skip unhandled events for periodic timers, 1=Skip unhandled events for periodic timers
67 } flags_t;
68
69 struct esp_timer {
70 uint64_t alarm;
71 uint64_t period:56;
72 flags_t flags:8;
73 union {
74 esp_timer_cb_t callback;
75 uint32_t event_id;
76 };
77 void* arg;
78 #if WITH_PROFILING
79 const char* name;
80 size_t times_triggered;
81 size_t times_armed;
82 size_t times_skipped;
83 uint64_t total_callback_run_time;
84 #endif // WITH_PROFILING
85 LIST_ENTRY(esp_timer) list_entry;
86 };
87
88 K_KERNEL_STACK_MEMBER(timer_task_stack, 4096);
89 static bool init_status = false;
90
91 static bool is_initialized(void);
92 static esp_err_t timer_insert(esp_timer_handle_t timer);
93 static esp_err_t timer_remove(esp_timer_handle_t timer);
94 static bool timer_armed(esp_timer_handle_t timer);
95 static void timer_list_lock(void);
96 static void timer_list_unlock(void);
97
98 #if WITH_PROFILING
99 static void timer_insert_inactive(esp_timer_handle_t timer);
100 static void timer_remove_inactive(esp_timer_handle_t timer);
101 #endif // WITH_PROFILING
102
103 // list of currently armed timers
104 static LIST_HEAD(esp_timer_list, esp_timer) s_timers =
105 LIST_HEAD_INITIALIZER(s_timers);
106 #if WITH_PROFILING
107 // list of unarmed timers, used only to be able to dump statistics about
108 // all the timers
109 static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers =
110 LIST_HEAD_INITIALIZER(s_timers);
111 #endif
112 // task used to dispatch timer callbacks
113 static struct k_thread s_timer_task;
114 // counting semaphore used to notify the timer task from ISR
115 static struct k_sem s_timer_semaphore;
116
117 #if CONFIG_SPIRAM_USE_MALLOC
118 // memory for s_timer_semaphore
119 static StaticQueue_t s_timer_semaphore_memory;
120 #endif
121
122 // lock protecting s_timers, s_inactive_timers
123 static unsigned int s_timer_lock;
124
esp_timer_create(const esp_timer_create_args_t * args,esp_timer_handle_t * out_handle)125 esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
126 esp_timer_handle_t* out_handle)
127 {
128 if (!is_initialized()) {
129 return ESP_ERR_INVALID_STATE;
130 }
131 if (args == NULL || args->callback == NULL || out_handle == NULL) {
132 return ESP_ERR_INVALID_ARG;
133 }
134 esp_timer_handle_t result = (esp_timer_handle_t) k_calloc(1, sizeof(*result));
135 if (result == NULL) {
136 return ESP_ERR_NO_MEM;
137 }
138 result->callback = args->callback;
139 result->arg = args->arg;
140 result->flags = (args->dispatch_method ? FL_DISPATCH_METHOD : 0) |
141 (args->skip_unhandled_events ? FL_SKIP_UNHANDLED_EVENTS : 0);
142 #if WITH_PROFILING
143 result->name = args->name;
144 timer_insert_inactive(result);
145 #endif
146 *out_handle = result;
147 return ESP_OK;
148 }
149
esp_timer_start_once(esp_timer_handle_t timer,uint64_t timeout_us)150 esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
151 {
152 if (timer == NULL) {
153 return ESP_ERR_INVALID_ARG;
154 }
155 if (!is_initialized() || timer_armed(timer)) {
156 return ESP_ERR_INVALID_STATE;
157 }
158 timer_list_lock();
159 timer->alarm = esp_timer_get_time() + timeout_us;
160 timer->period = 0;
161 #if WITH_PROFILING
162 timer->times_armed++;
163 #endif
164 esp_err_t err = timer_insert(timer);
165 timer_list_unlock();
166 return err;
167 }
168
esp_timer_start_periodic(esp_timer_handle_t timer,uint64_t period_us)169 esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
170 {
171 if (timer == NULL) {
172 return ESP_ERR_INVALID_ARG;
173 }
174 if (!is_initialized() || timer_armed(timer)) {
175 return ESP_ERR_INVALID_STATE;
176 }
177 timer_list_lock();
178 period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
179 timer->alarm = esp_timer_get_time() + period_us;
180 timer->period = period_us;
181 #if WITH_PROFILING
182 timer->times_armed++;
183 timer->times_skipped = 0;
184 #endif
185 esp_err_t err = timer_insert(timer);
186 timer_list_unlock();
187 return err;
188 }
189
esp_timer_stop(esp_timer_handle_t timer)190 esp_err_t IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
191 {
192 if (timer == NULL) {
193 return ESP_ERR_INVALID_ARG;
194 }
195 if (!is_initialized() || !timer_armed(timer)) {
196 return ESP_ERR_INVALID_STATE;
197 }
198 return timer_remove(timer);
199 }
200
esp_timer_delete(esp_timer_handle_t timer)201 esp_err_t esp_timer_delete(esp_timer_handle_t timer)
202 {
203 if (timer == NULL) {
204 return ESP_ERR_INVALID_ARG;
205 }
206 if (timer_armed(timer)) {
207 return ESP_ERR_INVALID_STATE;
208 }
209 timer_list_lock();
210 timer->event_id = EVENT_ID_DELETE_TIMER;
211 timer->alarm = esp_timer_get_time();
212 timer->period = 0;
213 timer_insert(timer);
214 timer_list_unlock();
215 return ESP_OK;
216 }
217
timer_insert(esp_timer_handle_t timer)218 static IRAM_ATTR esp_err_t timer_insert(esp_timer_handle_t timer)
219 {
220 #if WITH_PROFILING
221 timer_remove_inactive(timer);
222 #endif
223 esp_timer_handle_t it, last = NULL;
224 if (LIST_FIRST(&s_timers) == NULL) {
225 LIST_INSERT_HEAD(&s_timers, timer, list_entry);
226 } else {
227 LIST_FOREACH(it, &s_timers, list_entry) {
228 if (timer->alarm < it->alarm) {
229 LIST_INSERT_BEFORE(it, timer, list_entry);
230 break;
231 }
232 last = it;
233 }
234 if (it == NULL) {
235 assert(last);
236 LIST_INSERT_AFTER(last, timer, list_entry);
237 }
238 }
239 if (timer == LIST_FIRST(&s_timers)) {
240 esp_timer_impl_set_alarm(timer->alarm);
241 }
242 return ESP_OK;
243 }
244
timer_remove(esp_timer_handle_t timer)245 static IRAM_ATTR esp_err_t timer_remove(esp_timer_handle_t timer)
246 {
247 timer_list_lock();
248 LIST_REMOVE(timer, list_entry);
249 timer->alarm = 0;
250 timer->period = 0;
251 #if WITH_PROFILING
252 timer_insert_inactive(timer);
253 #endif
254 timer_list_unlock();
255 return ESP_OK;
256 }
257
258 #if WITH_PROFILING
259
timer_insert_inactive(esp_timer_handle_t timer)260 static IRAM_ATTR void timer_insert_inactive(esp_timer_handle_t timer)
261 {
262 /* May be locked or not, depending on where this is called from.
263 * Lock recursively.
264 */
265 timer_list_lock();
266 esp_timer_handle_t head = LIST_FIRST(&s_inactive_timers);
267 if (head == NULL) {
268 LIST_INSERT_HEAD(&s_inactive_timers, timer, list_entry);
269 } else {
270 /* Insert as head element as this is the fastest thing to do.
271 * Removal is O(1) anyway.
272 */
273 LIST_INSERT_BEFORE(head, timer, list_entry);
274 }
275 timer_list_unlock();
276 }
277
timer_remove_inactive(esp_timer_handle_t timer)278 static IRAM_ATTR void timer_remove_inactive(esp_timer_handle_t timer)
279 {
280 timer_list_lock();
281 LIST_REMOVE(timer, list_entry);
282 timer_list_unlock();
283 }
284
285 #endif // WITH_PROFILING
286
timer_armed(esp_timer_handle_t timer)287 static IRAM_ATTR bool timer_armed(esp_timer_handle_t timer)
288 {
289 return timer->alarm > 0;
290 }
291
timer_list_lock(void)292 static IRAM_ATTR void timer_list_lock(void)
293 {
294 s_timer_lock = irq_lock();
295 }
296
timer_list_unlock(void)297 static IRAM_ATTR void timer_list_unlock(void)
298 {
299 irq_unlock(s_timer_lock);
300 }
301
timer_process_alarm(esp_timer_dispatch_t dispatch_method)302 static void timer_process_alarm(esp_timer_dispatch_t dispatch_method)
303 {
304 /* unused, provision to allow running callbacks from ISR */
305 (void) dispatch_method;
306
307 timer_list_lock();
308 esp_timer_handle_t it;
309 while (1) {
310 it = LIST_FIRST(&s_timers);
311 int64_t now = esp_timer_impl_get_time();
312 if (it == NULL || it->alarm > now) {
313 break;
314 }
315 LIST_REMOVE(it, list_entry);
316 if (it->event_id == EVENT_ID_DELETE_TIMER) {
317 k_free(it);
318 it = NULL;
319 } else {
320 if (it->period > 0) {
321 int skipped = (now - it->alarm) / it->period;
322 if ((it->flags & FL_SKIP_UNHANDLED_EVENTS) && (skipped > 1)) {
323 it->alarm = now + it->period;
324 #if WITH_PROFILING
325 it->times_skipped += skipped;
326 #endif
327 } else {
328 it->alarm += it->period;
329 }
330 timer_insert(it);
331 } else {
332 it->alarm = 0;
333 #if WITH_PROFILING
334 timer_insert_inactive(it);
335 #endif
336 }
337 #if WITH_PROFILING
338 uint64_t callback_start = now;
339 #endif
340 esp_timer_cb_t callback = it->callback;
341 void* arg = it->arg;
342 timer_list_unlock();
343 (*callback)(arg);
344 timer_list_lock();
345 #if WITH_PROFILING
346 it->times_triggered++;
347 it->total_callback_run_time += esp_timer_impl_get_time() - callback_start;
348 #endif
349 }
350 }
351 if (it) {
352 esp_timer_impl_set_alarm(it->alarm);
353 }
354 timer_list_unlock();
355 }
356
timer_task(void * arg)357 static void timer_task(void* arg)
358 {
359 while (true){
360 k_sem_take(&s_timer_semaphore, K_FOREVER);
361 // all deferred events are processed at a time
362 timer_process_alarm(ESP_TIMER_TASK);
363 }
364 }
365
timer_alarm_handler(void * arg)366 static void IRAM_ATTR timer_alarm_handler(void* arg)
367 {
368 k_sem_give(&s_timer_semaphore);
369 }
370
is_initialized(void)371 static IRAM_ATTR inline bool is_initialized(void)
372 {
373 return init_status;
374 }
375
esp_timer_init(void)376 esp_err_t esp_timer_init(void)
377 {
378 esp_err_t err;
379 if (is_initialized()) {
380 return ESP_ERR_INVALID_STATE;
381 }
382
383 int ret = k_sem_init(&s_timer_semaphore, 0, TIMER_EVENT_QUEUE_SIZE);
384 if (ret != 0)
385 {
386 goto out;
387 }
388
389 k_tid_t tid = k_thread_create(&s_timer_task, timer_task_stack,
390 4096, (k_thread_entry_t)timer_task, NULL, NULL, NULL,
391 3, K_INHERIT_PERMS, K_NO_WAIT);
392
393 if (!tid)
394 {
395 goto out;
396 }
397
398 k_thread_name_set(tid, "esp_timer");
399
400 err = esp_timer_impl_init(&timer_alarm_handler);
401 if (err != ESP_OK) {
402 goto out;
403 }
404
405 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
406 // [refactor-todo] this logic, "esp_rtc_get_time_us() - g_startup_time", is also
407 // the weak definition of esp_system_get_time; find a way to remove this duplication.
408 esp_timer_private_advance(esp_rtc_get_time_us() - g_startup_time);
409 #endif
410
411 init_status = true;
412
413 return ESP_OK;
414
415 out:
416 LOG_ERR("could not start esp timer");
417 k_free(&s_timer_task);
418 init_status = false;
419
420 return ESP_ERR_NO_MEM;
421 }
422
esp_timer_deinit(void)423 esp_err_t esp_timer_deinit(void)
424 {
425 if (!is_initialized()) {
426 return ESP_ERR_INVALID_STATE;
427 }
428
429 /* Check if there are any active timers */
430 if (!LIST_EMPTY(&s_timers)) {
431 return ESP_ERR_INVALID_STATE;
432 }
433
434 /* We can only check if there are any timers which are not deleted if
435 * profiling is enabled.
436 */
437 #if WITH_PROFILING
438 if (!LIST_EMPTY(&s_inactive_timers)) {
439 return ESP_ERR_INVALID_STATE;
440 }
441 #endif
442
443 esp_timer_impl_deinit();
444
445 k_free(&s_timer_task);
446 init_status = false;
447 return ESP_OK;
448 }
449
print_timer_info(esp_timer_handle_t t,char ** dst,size_t * dst_size)450 static void print_timer_info(esp_timer_handle_t t, char** dst, size_t* dst_size)
451 {
452 #if WITH_PROFILING
453 size_t cb;
454 // name is optional, might be missed.
455 if (t->name) {
456 cb = snprintf(*dst, *dst_size, "%-12s ", t->name);
457 } else {
458 cb = snprintf(*dst, *dst_size, "timer@%p ", t);
459 }
460 cb += snprintf(*dst + cb, *dst_size + cb, "%12lld %12lld %9d %9d %6d %12lld\n",
461 (uint64_t)t->period, t->alarm, t->times_armed,
462 t->times_triggered, t->times_skipped, t->total_callback_run_time);
463 /* keep this in sync with the format string, used in esp_timer_dump */
464 #define TIMER_INFO_LINE_LEN 90
465 #else
466 size_t cb = snprintf(*dst, *dst_size, "timer@%p %12lld %12lld\n", t, (uint64_t)t->period, t->alarm);
467 #define TIMER_INFO_LINE_LEN 46
468 #endif
469 *dst += cb;
470 *dst_size -= cb;
471 }
472
473
esp_timer_dump(FILE * stream)474 esp_err_t esp_timer_dump(FILE* stream)
475 {
476 /* Since timer lock is a critical section, we don't want to print directly
477 * to stdout, since that may cause a deadlock if stdout is interrupt-driven
478 * (via the UART driver). Allocate sufficiently large chunk of memory first,
479 * print to it, then dump this memory to stdout.
480 */
481
482 esp_timer_handle_t it;
483
484 /* First count the number of timers */
485 size_t timer_count = 0;
486 timer_list_lock();
487 LIST_FOREACH(it, &s_timers, list_entry) {
488 ++timer_count;
489 }
490 #if WITH_PROFILING
491 LIST_FOREACH(it, &s_inactive_timers, list_entry) {
492 ++timer_count;
493 }
494 #endif
495 timer_list_unlock();
496
497 /* Allocate the memory for this number of timers. Since we have unlocked,
498 * we may find that there are more timers. There's no bulletproof solution
499 * for this (can't allocate from a critical section), but we allocate
500 * slightly more and the output will be truncated if that is not enough.
501 */
502 size_t buf_size = TIMER_INFO_LINE_LEN * (timer_count + 3);
503 char* print_buf = k_calloc(1, buf_size + 1);
504 if (print_buf == NULL) {
505 return ESP_ERR_NO_MEM;
506 }
507
508 /* Print to the buffer */
509 timer_list_lock();
510 char* pos = print_buf;
511 LIST_FOREACH(it, &s_timers, list_entry) {
512 print_timer_info(it, &pos, &buf_size);
513 }
514 #if WITH_PROFILING
515 LIST_FOREACH(it, &s_inactive_timers, list_entry) {
516 print_timer_info(it, &pos, &buf_size);
517 }
518 #endif
519 timer_list_unlock();
520
521 /* Print the buffer */
522 fputs(print_buf, stream);
523
524 k_free(print_buf);
525 return ESP_OK;
526 }
527
esp_timer_get_next_alarm(void)528 int64_t IRAM_ATTR esp_timer_get_next_alarm(void)
529 {
530 int64_t next_alarm = INT64_MAX;
531 timer_list_lock();
532 esp_timer_handle_t it = LIST_FIRST(&s_timers);
533 if (it) {
534 next_alarm = it->alarm;
535 }
536 timer_list_unlock();
537 return next_alarm;
538 }
539
540 // Provides strong definition for system time functions relied upon
541 // by core components.
542 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
esp_system_get_time(void)543 int64_t IRAM_ATTR esp_system_get_time(void)
544 {
545 return esp_timer_get_time();
546 }
547
esp_system_get_time_resolution(void)548 uint32_t IRAM_ATTR esp_system_get_time_resolution(void)
549 {
550 return 1000;
551 }
552 #endif
553