From 1ebbece0e33235b715297d16d2071415e0a178de Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 17 Nov 2016 01:49:27 +0200 Subject: [PATCH 1/3] Reverse engineered ets_timers.o --- open_esplibs/include/open_esplibs.h | 3 + open_esplibs/libmain/ets_timer.c | 391 ++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+) create mode 100644 open_esplibs/libmain/ets_timer.c diff --git a/open_esplibs/include/open_esplibs.h b/open_esplibs/include/open_esplibs.h index fce49a17..35f34ff5 100644 --- a/open_esplibs/include/open_esplibs.h +++ b/open_esplibs/include/open_esplibs.h @@ -35,6 +35,9 @@ #ifndef OPEN_LIBMAIN_USER_INTERFACE #define OPEN_LIBMAIN_USER_INTERFACE (OPEN_LIBMAIN) #endif +#ifndef OPEN_LIBMAIN_ETS_TIMER +#define OPEN_LIBMAIN_ETS_TIMER (OPEN_LIBMAIN) +#endif #ifndef OPEN_LIBNET80211 #define OPEN_LIBNET80211 (OPEN_ESPLIBS) diff --git a/open_esplibs/libmain/ets_timer.c b/open_esplibs/libmain/ets_timer.c new file mode 100644 index 00000000..f2962c08 --- /dev/null +++ b/open_esplibs/libmain/ets_timer.c @@ -0,0 +1,391 @@ +/** + * Recreated Espressif libmain ets_timer.o contents. + * + * Copyright (C) 2015 Espressif Systems. Derived from MIT Licensed SDK libraries. + * BSD Licensed as described in the file LICENSE + * + * Copyright (c) 2016 sheinz (https://github.com/sheinz) + * + * This module seems to be adapted from NONOS SDK by Espressif to fit into + * RTOS SDK. Function sdk_ets_timer_handler_isr is no longer an ISR handler + * but still holds its name. Espressif just added a task that receives events + * from the real FRC2 timer ISR handler and calls former ISR handler. + * So, timer callbacks are called from the task context rather than an interrupt. + * + * This timer should be used with coution together with other tasks. As the + * timer callback is executed within timer task context, access to data that + * other tasks accessing should be protected. + */ +#include "open_esplibs.h" + +#if OPEN_LIBMAIN_ETS_TIMER + +#include "open_esplibs.h" +#include +#include +#include +#include +#include +#include +#include + +typedef void ets_timer_func_t(void *); + +/** + * This structure is used for both timers: ets_timer.c and timer.c + */ +typedef struct ets_timer_st { + struct ets_timer_st *next; + TimerHandle_t timer_handle; // not used in ets_timer.c + uint32_t fire_ticks; // FRC2 timer value when timer should fire + uint32_t period_ticks; // timer value in FRC2 ticks for rpeating timers + ets_timer_func_t *callback; + bool repeat; // not used in ets_timer.c + void *timer_arg; +} ets_timer_t; + +/** + * Special values of ets_timer_t::next field + */ +#define ETS_TIMER_NOT_ARMED (ets_timer_t*)(0xffffffff) +#define ETS_TIMER_LIST_END (ets_timer_t*)(0) + +/** + * Linked list of timers + */ +static ets_timer_t* timer_list = 0; + +/** + * Those debug variables are set but never used. + */ +static ets_timer_func_t *debug_timerfn; +static ets_timer_t *debug_timer; + +/** + * Timer queue + */ +static QueueHandle_t queue; + +/** + * Unknown stuff + * Some counters + */ +static uint8_t queue_len = 0; +static uint8_t buf_param_index = 0; +static uint64_t _unknown_buf[4]; + + +void sdk_ets_timer_setfn(ets_timer_t *timer, ets_timer_func_t *func, void *parg) +{ + timer->callback = func; + timer->timer_arg = parg; + timer->fire_ticks = 0; + timer->period_ticks = 0; + timer->next = ETS_TIMER_NOT_ARMED; +} + +/** + * .Lfunc004 + */ +static void set_alarm_value(uint32_t value) +{ + TIMER_FRC2.ALARM = value; +} + +/** + * .Lfunc005 + * + * Set timer alarm and make sure the alarm is set in the future + * and will not be missed by the timer. + */ +static void set_alarm(uint32_t ticks) +{ + uint32_t curr_time = TIMER_FRC2.COUNT; + int32_t delta = (int32_t)ticks - curr_time; + if ((delta - 40) < 1) { + if (delta < 1) { + set_alarm_value(curr_time + 40); + } else { + set_alarm_value(ticks + 44); + } + } else { + set_alarm_value(ticks); + } +} + +/** + * .Lfunc006 + * + * Pending timer list example: + * + * | Timer: | T0 | T1 | T2 | T3 | + * |-------------|----|----|----|----| + * | fire_ticks: | 10 | 20 | 30 | 40 | + * | next: | T1 | T2 | T3 | 0 | + * + * + * For example we need to add a timer that should fire at 25 ticks: + * + * | Timer: | T0 | T1 | new | T2 | T3 | + * |-------------|----|-----|-----|----|----| + * | fire_ticks: | 10 | 20 | 25 | 30 | 40 | + * | next: | T1 | new | T2 | T3 | 0 | + * + * We squeeze the timer into the list so the list will always remain sorted + * + * Note: if add the same timer twice the system halts +*/ +static void add_pending_timer(uint32_t ticks, ets_timer_t *timer) +{ + ets_timer_t *prev = 0; + ets_timer_t *curr = timer_list; + while (curr) { + if (((int32_t)ticks - (int32_t)curr->fire_ticks) < 1) { + // found a timer that should fire later + // so our timer should fire earlier + break; + } + prev = curr; + curr = curr->next; + } + + timer->next = curr; + timer->fire_ticks = ticks; + + if (prev != 0) { + prev->next = timer; + } else { + // Our timer is the first in the line to fire + timer_list = timer; + set_alarm(ticks); + } + + // This situation might happen if adding the same timer twice + if (timer == timer->next) { + // This seems like an error: %s is used for line number + // In the recent SDK Espressif fixed the format to "%s %u\n" + printf("%s %s \n", "ets_timer.c", (char*)209); + while (1); + } +} + +/** + * In the Espressif SDK 0.9.9 if try to arm already armed timer the system halts + * with error message. In the later SDK version Espressif changed the behavior. + * If the timer was previously armed it is disarmed and then armed without errors. + * This version recreates behavior of SDK 0.9.9 + */ +void sdk_ets_timer_arm_ms_us(ets_timer_t *timer, uint32_t value, + bool repeat_flag, bool value_in_ms) +{ + uint32_t ticks = 0; + + if (timer->next != ETS_TIMER_NOT_ARMED) { + // The error message doesn't tell what is wrong + printf("arm new %x %x\n", (uint32_t)timer, (uint32_t)timer->next); + while(1); // halt + } + + if (value_in_ms) { + value *= 1000; + } + + if (value != 0) { + // Why to do multiplication for values greater than 858 + // and do 'shift and add' for other? + // What is the magic number 858 ? + if (858 < value) { + ticks = (value << 2) + value; // ticks = value * 5 + } else { + // It is the same as just multiply by 5 + // No idea why to do it this way + ticks = (value * 5000000) / 1000000; + } + } + + if (repeat_flag) { + timer->period_ticks = ticks; + } + vPortEnterCritical(); + add_pending_timer(TIMER_FRC2.COUNT + ticks, timer); + vPortExitCritical(); +} + +void sdk_ets_timer_arm(ets_timer_t *timer, uint32_t milliseconds, + bool repeat_flag) +{ + sdk_ets_timer_arm_ms_us(timer, milliseconds, repeat_flag, + /*value in ms=*/true); +} + +/** + * Function removes a timer from the pending timers list. + */ +void sdk_ets_timer_disarm(ets_timer_t *timer) +{ + vPortEnterCritical(); + ets_timer_t *curr = timer_list; + ets_timer_t *prev = 0; + while (curr) { + if (curr == timer) { + if (prev) { + prev->next = curr->next; + } else { + timer_list = curr->next; + } + break; + } + prev = curr; + curr = curr->next; + } + vPortExitCritical(); + timer->next = ETS_TIMER_NOT_ARMED; + timer->period_ticks = 0; +} + +/** + * Check the list of pending timers for expired ones and process them. + * This function is not called from the interrupt regardless of its name. + */ +void sdk_ets_timer_handler_isr() +{ + vPortEnterCritical(); + int32_t ticks = TIMER_FRC2.COUNT; + while (timer_list) { + if (((int32_t)timer_list->fire_ticks - ticks) < 1) { + debug_timerfn = timer_list->callback; + debug_timer = timer_list; + + ets_timer_t *timer = timer_list; + timer_list = timer->next; + timer->next = ETS_TIMER_NOT_ARMED; + + vPortExitCritical(); + timer->callback(timer->timer_arg); + vPortEnterCritical(); + + if (timer->next == ETS_TIMER_NOT_ARMED) { + if (timer->period_ticks) { + timer->fire_ticks = timer->fire_ticks + timer->period_ticks; + add_pending_timer(timer->fire_ticks, timer); + } + } + ticks = TIMER_FRC2.COUNT; + } else { + if (timer_list) { + set_alarm(timer_list->fire_ticks); + } + break; + } + } + vPortExitCritical(); +} + + +/** + * .Lfunc001 + * + * Mysterious function. + * It seems like it keeps track of the queue size and returns 0 if + * the queue gets longer than 5. + * Also it seems like it returns some buffers that are used in the queue. + * But those buffers in the queue are not used at all. + * If anybody knows what is this all about please leave a comment. + */ +static void* IRAM func001() +{ + uint8_t *p = (uint8_t*)_unknown_buf; + + queue_len++; + if (queue_len < 5) { + p += (buf_param_index*8); + if (buf_param_index + 1 < 4) { + buf_param_index++; + } else { + buf_param_index = 0; + } + return p; + } else { + queue_len--; + return 0; + } +} + +/** + * .Lfunc002 + */ +static void IRAM frc2_isr() +{ + void *p = func001(); + + if (!p) { + printf("TIMQ_NUL\n"); + return; + } + + BaseType_t task_woken = 0; + + BaseType_t result = xQueueGenericSendFromISR(queue, p, &task_woken, 0); + if (result != pdTRUE) { + printf("TIMQ_FL:%d!!", (uint32_t)result); + } + if (task_woken) { + vTaskSwitchContext(); + } +} + +/** + * .Lfunc003 + */ +static void func003() +{ + vPortEnterCritical(); + queue_len--; + vPortExitCritical(); +} + +/** + * .Lfunc007 + * + * Timer task + */ +static void timer_task(void* param) +{ + uint32_t *local0; + while (true) { + if (xQueueGenericReceive(queue, &local0, 0xffffffff, 0) == 1) { + sdk_ets_timer_handler_isr(); + func003(); + } + } +} + +void sdk_ets_timer_init() +{ + timer_list = 0; + + _xt_isr_attach(INUM_TIMER_FRC2, frc2_isr); + + queue = xQueueGenericCreate(/*length=*/4, /*item size=*/4, + /*queu type: base*/0); + + TaskHandle_t handle = 0; + + /* Original code calls xTaskGenericCreate: + * xTaskGenericCreate(timer_task, "rtc_timer_task", 200, 0, 12, &handle, + * NULL, NULL); + */ + xTaskCreate(timer_task, "rtc_timer_task", 200, 0, 12, &handle); + printf("frc2_timer_task_hdl:%x, prio:%d, stack:%d\n", (uint32_t)handle, 12, 200); + + TIMER_FRC2.ALARM = 0; + TIMER_FRC2.CTRL = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_16) + | TIMER_CTRL_RUN; + TIMER_FRC2.LOAD = 0; + + DPORT.INT_ENABLE |= DPORT_INT_ENABLE_TIMER1; + + _xt_isr_unmask(BIT(INUM_TIMER_FRC2)); +} + +#endif /* OPEN_LIBMAIN_ETS_TIMER */ From 611bbca7a5ec19387abcb7d062611a0f5a701790 Mon Sep 17 00:00:00 2001 From: sheinz Date: Thu, 17 Nov 2016 13:01:35 +0200 Subject: [PATCH 2/3] Refactoring of ets_timer.c Switch from FreeRTOS queue to task notification. Removed unknown/unused code. --- open_esplibs/libmain/ets_timer.c | 100 +++++-------------------------- 1 file changed, 15 insertions(+), 85 deletions(-) diff --git a/open_esplibs/libmain/ets_timer.c b/open_esplibs/libmain/ets_timer.c index f2962c08..1ef9bd2c 100644 --- a/open_esplibs/libmain/ets_timer.c +++ b/open_esplibs/libmain/ets_timer.c @@ -12,6 +12,12 @@ * from the real FRC2 timer ISR handler and calls former ISR handler. * So, timer callbacks are called from the task context rather than an interrupt. * + * Modifications from the original reverese engineered version: + * - FreeRTOS queue is replaced with Task notifications. + * - Removed unknown queue lenght monitoring and parameters allocation. + * - Removed unused debug variables + * - xTaskGenericCreate is replaced with xTaskCreate + * * This timer should be used with coution together with other tasks. As the * timer callback is executed within timer task context, access to data that * other tasks accessing should be protected. @@ -55,25 +61,7 @@ typedef struct ets_timer_st { */ static ets_timer_t* timer_list = 0; -/** - * Those debug variables are set but never used. - */ -static ets_timer_func_t *debug_timerfn; -static ets_timer_t *debug_timer; - -/** - * Timer queue - */ -static QueueHandle_t queue; - -/** - * Unknown stuff - * Some counters - */ -static uint8_t queue_len = 0; -static uint8_t buf_param_index = 0; -static uint64_t _unknown_buf[4]; - +static TaskHandle_t task_handle = NULL; void sdk_ets_timer_setfn(ets_timer_t *timer, ets_timer_func_t *func, void *parg) { @@ -87,7 +75,7 @@ void sdk_ets_timer_setfn(ets_timer_t *timer, ets_timer_func_t *func, void *parg) /** * .Lfunc004 */ -static void set_alarm_value(uint32_t value) +static inline void set_alarm_value(uint32_t value) { TIMER_FRC2.ALARM = value; } @@ -247,15 +235,12 @@ void sdk_ets_timer_disarm(ets_timer_t *timer) * Check the list of pending timers for expired ones and process them. * This function is not called from the interrupt regardless of its name. */ -void sdk_ets_timer_handler_isr() +void IRAM sdk_ets_timer_handler_isr() { vPortEnterCritical(); int32_t ticks = TIMER_FRC2.COUNT; while (timer_list) { if (((int32_t)timer_list->fire_ticks - ticks) < 1) { - debug_timerfn = timer_list->callback; - debug_timer = timer_list; - ets_timer_t *timer = timer_list; timer_list = timer->next; timer->next = ETS_TIMER_NOT_ARMED; @@ -281,67 +266,19 @@ void sdk_ets_timer_handler_isr() vPortExitCritical(); } - -/** - * .Lfunc001 - * - * Mysterious function. - * It seems like it keeps track of the queue size and returns 0 if - * the queue gets longer than 5. - * Also it seems like it returns some buffers that are used in the queue. - * But those buffers in the queue are not used at all. - * If anybody knows what is this all about please leave a comment. - */ -static void* IRAM func001() -{ - uint8_t *p = (uint8_t*)_unknown_buf; - - queue_len++; - if (queue_len < 5) { - p += (buf_param_index*8); - if (buf_param_index + 1 < 4) { - buf_param_index++; - } else { - buf_param_index = 0; - } - return p; - } else { - queue_len--; - return 0; - } -} - /** * .Lfunc002 */ static void IRAM frc2_isr() { - void *p = func001(); - - if (!p) { - printf("TIMQ_NUL\n"); - return; - } - BaseType_t task_woken = 0; - BaseType_t result = xQueueGenericSendFromISR(queue, p, &task_woken, 0); + BaseType_t result = xTaskNotifyFromISR(task_handle, 0, eNoAction, &task_woken); if (result != pdTRUE) { printf("TIMQ_FL:%d!!", (uint32_t)result); } - if (task_woken) { - vTaskSwitchContext(); - } -} -/** - * .Lfunc003 - */ -static void func003() -{ - vPortEnterCritical(); - queue_len--; - vPortExitCritical(); + portEND_SWITCHING_ISR(task_woken); } /** @@ -351,11 +288,9 @@ static void func003() */ static void timer_task(void* param) { - uint32_t *local0; while (true) { - if (xQueueGenericReceive(queue, &local0, 0xffffffff, 0) == 1) { + if (xTaskNotifyWait(0, 0, NULL, portMAX_DELAY) == pdTRUE) { sdk_ets_timer_handler_isr(); - func003(); } } } @@ -366,17 +301,12 @@ void sdk_ets_timer_init() _xt_isr_attach(INUM_TIMER_FRC2, frc2_isr); - queue = xQueueGenericCreate(/*length=*/4, /*item size=*/4, - /*queu type: base*/0); - - TaskHandle_t handle = 0; - /* Original code calls xTaskGenericCreate: - * xTaskGenericCreate(timer_task, "rtc_timer_task", 200, 0, 12, &handle, + * xTaskGenericCreate(task_handle, "rtc_timer_task", 200, 0, 12, &handle, * NULL, NULL); */ - xTaskCreate(timer_task, "rtc_timer_task", 200, 0, 12, &handle); - printf("frc2_timer_task_hdl:%x, prio:%d, stack:%d\n", (uint32_t)handle, 12, 200); + xTaskCreate(timer_task, "rtc_timer_task", 200, 0, 12, &task_handle); + printf("frc2_timer_task_hdl:%p, prio:%d, stack:%d\n", task_handle, 12, 200); TIMER_FRC2.ALARM = 0; TIMER_FRC2.CTRL = VAL2FIELD(TIMER_CTRL_CLKDIV, TIMER_CLKDIV_16) From 7ae344740ccd4af801b88a86d7b88452156ee040 Mon Sep 17 00:00:00 2001 From: sheinz Date: Mon, 21 Nov 2016 23:23:50 +0200 Subject: [PATCH 3/3] Fixed ets_timer.c review comments Rename sdk_ets_handler_isr to process_pending_timers Add function for microseconds Simplify time to ticks conversion --- open_esplibs/libmain/ets_timer.c | 38 ++++++++++++++------------------ 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/open_esplibs/libmain/ets_timer.c b/open_esplibs/libmain/ets_timer.c index 1ef9bd2c..b7c9e9e0 100644 --- a/open_esplibs/libmain/ets_timer.c +++ b/open_esplibs/libmain/ets_timer.c @@ -12,13 +12,14 @@ * from the real FRC2 timer ISR handler and calls former ISR handler. * So, timer callbacks are called from the task context rather than an interrupt. * - * Modifications from the original reverese engineered version: + * Modifications from the original reverse engineered version: * - FreeRTOS queue is replaced with Task notifications. - * - Removed unknown queue lenght monitoring and parameters allocation. + * - Removed unknown queue length monitoring and parameters allocation. * - Removed unused debug variables * - xTaskGenericCreate is replaced with xTaskCreate + * - simplified time to ticks conversion (simply multiply by 5) * - * This timer should be used with coution together with other tasks. As the + * This timer should be used with caution together with other tasks. As the * timer callback is executed within timer task context, access to data that * other tasks accessing should be protected. */ @@ -175,20 +176,9 @@ void sdk_ets_timer_arm_ms_us(ets_timer_t *timer, uint32_t value, } if (value_in_ms) { - value *= 1000; - } - - if (value != 0) { - // Why to do multiplication for values greater than 858 - // and do 'shift and add' for other? - // What is the magic number 858 ? - if (858 < value) { - ticks = (value << 2) + value; // ticks = value * 5 - } else { - // It is the same as just multiply by 5 - // No idea why to do it this way - ticks = (value * 5000000) / 1000000; - } + ticks = value * 5000; + } else { + ticks = value * 5; } if (repeat_flag) { @@ -206,6 +196,13 @@ void sdk_ets_timer_arm(ets_timer_t *timer, uint32_t milliseconds, /*value in ms=*/true); } +void sdk_ets_timer_arm_us(ets_timer_t *timer, uint32_t useconds, + bool repeat_flag) +{ + sdk_ets_timer_arm_ms_us(timer, useconds, repeat_flag, + /*value in ms=*/false); +} + /** * Function removes a timer from the pending timers list. */ @@ -233,9 +230,8 @@ void sdk_ets_timer_disarm(ets_timer_t *timer) /** * Check the list of pending timers for expired ones and process them. - * This function is not called from the interrupt regardless of its name. */ -void IRAM sdk_ets_timer_handler_isr() +static inline void process_pending_timers() { vPortEnterCritical(); int32_t ticks = TIMER_FRC2.COUNT; @@ -290,7 +286,7 @@ static void timer_task(void* param) { while (true) { if (xTaskNotifyWait(0, 0, NULL, portMAX_DELAY) == pdTRUE) { - sdk_ets_timer_handler_isr(); + process_pending_timers(); } } } @@ -305,7 +301,7 @@ void sdk_ets_timer_init() * xTaskGenericCreate(task_handle, "rtc_timer_task", 200, 0, 12, &handle, * NULL, NULL); */ - xTaskCreate(timer_task, "rtc_timer_task", 200, 0, 12, &task_handle); + xTaskCreate(timer_task, "ets_timer_task", 200, 0, 12, &task_handle); printf("frc2_timer_task_hdl:%p, prio:%d, stack:%d\n", task_handle, 12, 200); TIMER_FRC2.ALARM = 0;