瀏覽代碼

esp_timer: Adds AFFINITY options for task and ISR

These new settings allow you to balance the load on cores.
Closes: https://github.com/espressif/esp-idf/issues/10457
KonstantinKondrashov 3 年之前
父節點
當前提交
449e4bcae7

+ 1 - 1
components/esp_system/system_init_fn.txt

@@ -14,7 +14,7 @@
 
 
 
 
 # esp_timer has to be initialized early, since it is used by several other components
 # esp_timer has to be initialized early, since it is used by several other components
-100: esp_timer_startup_init in components/esp_timer/src/esp_timer.c on BIT(0)
+100: esp_timer_startup_init in components/esp_timer/src/esp_timer.c on CONFIG_ESP_TIMER_ISR_AFFINITY
 
 
 # esp_sleep doesn't have init dependencies
 # esp_sleep doesn't have init dependencies
 105: esp_sleep_startup_init in components/esp_hw_support/sleep_gpio.c on BIT(0)
 105: esp_sleep_startup_init in components/esp_hw_support/sleep_gpio.c on BIT(0)

+ 64 - 1
components/esp_timer/Kconfig

@@ -30,7 +30,7 @@ menu "High resolution timer (esp_timer)"
 
 
             Note that this is not the same as FreeRTOS timer task. To configure
             Note that this is not the same as FreeRTOS timer task. To configure
             FreeRTOS timer task size, see "FreeRTOS timer task stack size" option
             FreeRTOS timer task size, see "FreeRTOS timer task stack size" option
-            in "FreeRTOS" menu.
+            in "FreeRTOS".
 
 
     config ESP_TIMER_INTERRUPT_LEVEL
     config ESP_TIMER_INTERRUPT_LEVEL
         int "Interrupt level"
         int "Interrupt level"
@@ -41,6 +41,69 @@ menu "High resolution timer (esp_timer)"
             It sets the interrupt level for esp_timer ISR in range 1..3.
             It sets the interrupt level for esp_timer ISR in range 1..3.
             A higher level (3) helps to decrease the ISR esp_timer latency.
             A higher level (3) helps to decrease the ISR esp_timer latency.
 
 
+    config ESP_TIMER_SHOW_EXPERIMENTAL
+        bool "show esp_timer's experimental features"
+        help
+            This shows some hidden features of esp_timer.
+            Note that they may break other features, use them with care.
+
+    config ESP_TIMER_TASK_AFFINITY
+        hex
+        default 0x0 if ESP_TIMER_TASK_AFFINITY_CPU0
+        default 0x1 if ESP_TIMER_TASK_AFFINITY_CPU1
+        default FREERTOS_NO_AFFINITY if ESP_TIMER_TASK_AFFINITY_NO_AFFINITY
+
+    choice ESP_TIMER_TASK_AFFINITY
+        prompt "esp_timer task core affinity"
+        default ESP_TIMER_TASK_AFFINITY_CPU0
+        help
+            The default settings: timer TASK on CPU0 and timer ISR on CPU0.
+            Other settings may help in certain cases, but note that they may break
+            other features, use them with care.
+            - "CPU0": (default) esp_timer task is processed by CPU0.
+            - "CPU1": esp_timer task is processed by CPU1.
+            - "No affinity": esp_timer task can be processed by any CPU.
+
+        config ESP_TIMER_TASK_AFFINITY_CPU0
+            bool "CPU0"
+        config ESP_TIMER_TASK_AFFINITY_CPU1
+            bool "CPU1"
+            depends on !FREERTOS_UNICORE && ESP_TIMER_SHOW_EXPERIMENTAL
+        config ESP_TIMER_TASK_AFFINITY_NO_AFFINITY
+            bool "No affinity"
+            depends on !FREERTOS_UNICORE && ESP_TIMER_SHOW_EXPERIMENTAL
+    endchoice
+
+    config ESP_TIMER_ISR_AFFINITY
+        hex
+        default 0x1 if ESP_TIMER_ISR_AFFINITY_CPU0
+        default 0x2 if ESP_TIMER_ISR_AFFINITY_CPU1
+        default FREERTOS_NO_AFFINITY if ESP_TIMER_ISR_AFFINITY_NO_AFFINITY
+
+    choice ESP_TIMER_ISR_AFFINITY
+        prompt "timer interrupt core affinity"
+        default ESP_TIMER_ISR_AFFINITY_CPU0
+        help
+            The default settings: timer TASK on CPU0 and timer ISR on CPU0.
+            Other settings may help in certain cases, but note that they may break
+            other features, use them with care.
+            - "CPU0": (default) timer interrupt is processed by CPU0.
+            - "CPU1": timer interrupt is processed by CPU1.
+            - "No affinity": timer interrupt can be processed by any CPU. It helps
+            to reduce latency but there is a disadvantage it leads to the timer ISR
+            running on every core. It increases the CPU time usage for timer ISRs
+            by N on an N-core system.
+
+        config ESP_TIMER_ISR_AFFINITY_CPU0
+            bool "CPU0"
+        config ESP_TIMER_ISR_AFFINITY_CPU1
+            bool "CPU1"
+            depends on !FREERTOS_UNICORE && ESP_TIMER_SHOW_EXPERIMENTAL
+        config ESP_TIMER_ISR_AFFINITY_NO_AFFINITY
+            bool "No affinity"
+            depends on !FREERTOS_UNICORE && ESP_TIMER_SHOW_EXPERIMENTAL
+    endchoice
+
     config ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
     config ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
         bool "Support ISR dispatch method"
         bool "Support ISR dispatch method"
         default n
         default n

+ 5 - 0
components/esp_timer/include/esp_timer.h

@@ -98,6 +98,11 @@ esp_err_t esp_timer_early_init(void);
  * Before calling this function, esp_timer_early_init must be called by the
  * Before calling this function, esp_timer_early_init must be called by the
  * startup code.
  * startup code.
  *
  *
+ * This function will be called from startup code on every core
+ * if CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY is enabled,
+ * It allocates the timer ISR on MULTIPLE cores and
+ * creates the timer task which can be run on any core.
+ *
  * @return
  * @return
  *      - ESP_OK on success
  *      - ESP_OK on success
  *      - ESP_ERR_NO_MEM if allocation has failed
  *      - ESP_ERR_NO_MEM if allocation has failed

+ 43 - 23
components/esp_timer/src/esp_timer.c

@@ -15,6 +15,7 @@
 #include "freertos/FreeRTOS.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/task.h"
 #include "freertos/semphr.h"
 #include "freertos/semphr.h"
+#include "esp_ipc.h"
 #include "esp_timer.h"
 #include "esp_timer.h"
 #include "esp_timer_impl.h"
 #include "esp_timer_impl.h"
 
 
@@ -479,37 +480,58 @@ esp_err_t esp_timer_early_init(void)
     return ESP_OK;
     return ESP_OK;
 }
 }
 
 
-esp_err_t esp_timer_init(void)
+static esp_err_t init_timer_task(void)
 {
 {
-    esp_err_t err;
+    esp_err_t err = ESP_OK;
     if (is_initialized()) {
     if (is_initialized()) {
-        return ESP_ERR_INVALID_STATE;
-    }
-
-    int ret = xTaskCreatePinnedToCore(&timer_task, "esp_timer",
-            ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO, &s_timer_task, PRO_CPU_NUM);
-    if (ret != pdPASS) {
-        err = ESP_ERR_NO_MEM;
-        goto out;
-    }
-
-    err = esp_timer_impl_init(&timer_alarm_handler);
-    if (err != ESP_OK) {
-        goto out;
+        ESP_EARLY_LOGE(TAG, "Task is already initialized");
+        err = ESP_ERR_INVALID_STATE;
+    } else {
+        int ret = xTaskCreatePinnedToCore(
+                    &timer_task, "esp_timer",
+                    ESP_TASK_TIMER_STACK, NULL, ESP_TASK_TIMER_PRIO,
+                    &s_timer_task, CONFIG_ESP_TIMER_TASK_AFFINITY);
+        if (ret != pdPASS) {
+            ESP_EARLY_LOGE(TAG, "Not enough memory to create timer task");
+            err = ESP_ERR_NO_MEM;
+        }
     }
     }
+    return err;
+}
 
 
-    return ESP_OK;
-
-out:
+static void deinit_timer_task(void)
+{
     if (s_timer_task) {
     if (s_timer_task) {
         vTaskDelete(s_timer_task);
         vTaskDelete(s_timer_task);
         s_timer_task = NULL;
         s_timer_task = NULL;
     }
     }
+}
 
 
-    return ESP_ERR_NO_MEM;
+esp_err_t esp_timer_init(void)
+{
+    esp_err_t err = ESP_OK;
+#ifndef CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY
+    err = init_timer_task();
+#else
+    /* This function will be run on all cores if CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY is enabled,
+     * We do it that way because we need to allocate the timer ISR on MULTIPLE cores.
+     * timer task will be created by CPU0.
+     */
+    if (xPortGetCoreID() == 0) {
+        err = init_timer_task();
+    }
+#endif // CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY
+    if (err == ESP_OK) {
+        err = esp_timer_impl_init(&timer_alarm_handler);
+        if (err != ESP_OK) {
+            ESP_EARLY_LOGE(TAG, "ISR init failed");
+            deinit_timer_task();
+        }
+    }
+    return err;
 }
 }
 
 
-ESP_SYSTEM_INIT_FN(esp_timer_startup_init, BIT(0), 100)
+ESP_SYSTEM_INIT_FN(esp_timer_startup_init, CONFIG_ESP_TIMER_ISR_AFFINITY, 100)
 {
 {
     return esp_timer_init();
     return esp_timer_init();
 }
 }
@@ -539,9 +561,7 @@ esp_err_t esp_timer_deinit(void)
 #endif
 #endif
 
 
     esp_timer_impl_deinit();
     esp_timer_impl_deinit();
-
-    vTaskDelete(s_timer_task);
-    s_timer_task = NULL;
+    deinit_timer_task();
     return ESP_OK;
     return ESP_OK;
 }
 }
 
 

+ 84 - 24
components/esp_timer/src/esp_timer_impl_lac.c

@@ -83,8 +83,15 @@ typedef struct {
 
 
 static const char* TAG = "esp_timer_impl";
 static const char* TAG = "esp_timer_impl";
 
 
+#define NOT_USED 0xBAD00FAD
+
 /* Interrupt handle returned by the interrupt allocator */
 /* Interrupt handle returned by the interrupt allocator */
-static intr_handle_t s_timer_interrupt_handle;
+#ifdef CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY
+#define ISR_HANDLERS (portNUM_PROCESSORS)
+#else
+#define ISR_HANDLERS (1)
+#endif
+static intr_handle_t s_timer_interrupt_handle[ISR_HANDLERS] = { NULL };
 
 
 /* Function from the upper layer to be called when the interrupt happens.
 /* Function from the upper layer to be called when the interrupt happens.
  * Registered in esp_timer_impl_init.
  * Registered in esp_timer_impl_init.
@@ -180,10 +187,47 @@ void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp)
 
 
 static void IRAM_ATTR timer_alarm_isr(void *arg)
 static void IRAM_ATTR timer_alarm_isr(void *arg)
 {
 {
+#if ISR_HANDLERS == 1
     /* Clear interrupt status */
     /* Clear interrupt status */
     REG_WRITE(INT_CLR_REG, TIMG_LACT_INT_CLR);
     REG_WRITE(INT_CLR_REG, TIMG_LACT_INT_CLR);
-    /*  Call the upper layer handler */
+    /* Call the upper layer handler */
     (*s_alarm_handler)(arg);
     (*s_alarm_handler)(arg);
+#else
+    static volatile uint32_t processed_by = NOT_USED;
+    static volatile bool pending_alarm = false;
+    /* CRITICAL section ensures the read/clear is atomic between cores */
+    portENTER_CRITICAL_ISR(&s_time_update_lock);
+    if (REG_GET_FIELD(INT_ST_REG, TIMG_LACT_INT_ST)) {
+        // Clear interrupt status
+        REG_WRITE(INT_CLR_REG, TIMG_LACT_INT_CLR);
+        // Is the other core already processing a previous alarm?
+        if (processed_by == NOT_USED) {
+            // Current core is not processing an alarm yet
+            processed_by = xPortGetCoreID();
+            do {
+                pending_alarm = false;
+                // Clear interrupt status
+                REG_WRITE(INT_CLR_REG, TIMG_LACT_INT_CLR);
+                portEXIT_CRITICAL_ISR(&s_time_update_lock);
+
+                (*s_alarm_handler)(arg);
+
+                portENTER_CRITICAL_ISR(&s_time_update_lock);
+               // Another alarm could have occurred while were handling the previous alarm.
+               // Check if we need to call the s_alarm_handler again:
+               //   1) if the alarm has already been fired, it helps to handle it immediately without an additional ISR call.
+               //   2) handle pending alarm that was cleared by the other core in time when this core worked with the current alarm.
+            } while (REG_GET_FIELD(INT_ST_REG, TIMG_LACT_INT_ST) || pending_alarm);
+            processed_by = NOT_USED;
+        } else {
+            // Current core arrived at ISR but the other core is still handling a previous alarm.
+            // Once we already cleared the ISR status we need to let the other core know that it was.
+            // Set the flag to handle the current alarm by the other core later.
+            pending_alarm = true;
+        }
+    }
+    portEXIT_CRITICAL_ISR(&s_time_update_lock);
+#endif // ISR_HANDLERS != 1
 }
 }
 
 
 void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
 void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
@@ -232,34 +276,46 @@ esp_err_t esp_timer_impl_early_init(void)
 
 
 esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
 esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
 {
 {
-    s_alarm_handler = alarm_handler;
+    if (s_timer_interrupt_handle[(ISR_HANDLERS == 1) ? 0 : xPortGetCoreID()] != NULL) {
+        ESP_EARLY_LOGE(TAG, "timer ISR is already initialized");
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    int isr_flags = ESP_INTR_FLAG_INTRDISABLED
+                    | ((1 << CONFIG_ESP_TIMER_INTERRUPT_LEVEL) & ESP_INTR_FLAG_LEVELMASK)
+                    | ESP_INTR_FLAG_IRAM;
 
 
-    const int interrupt_lvl = (1 << CONFIG_ESP_TIMER_INTERRUPT_LEVEL) & ESP_INTR_FLAG_LEVELMASK;
-    esp_err_t err = esp_intr_alloc(INTR_SOURCE_LACT,
-            ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM | interrupt_lvl,
-            &timer_alarm_isr, NULL, &s_timer_interrupt_handle);
+    esp_err_t err = esp_intr_alloc(INTR_SOURCE_LACT, isr_flags,
+                                   &timer_alarm_isr, NULL,
+                                   &s_timer_interrupt_handle[(ISR_HANDLERS == 1) ? 0 : xPortGetCoreID()]);
 
 
     if (err != ESP_OK) {
     if (err != ESP_OK) {
-        ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%0x)", err);
+        ESP_EARLY_LOGE(TAG, "Can not allocate ISR handler (0x%0x)", err);
         return err;
         return err;
     }
     }
 
 
-    /* In theory, this needs a shared spinlock with the timer group driver.
-     * However since esp_timer_impl_init is called early at startup, this
-     * will not cause issues in practice.
-     */
-    REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
+    if (s_alarm_handler == NULL) {
+        s_alarm_handler = alarm_handler;
+        /* In theory, this needs a shared spinlock with the timer group driver.
+        * However since esp_timer_impl_init is called early at startup, this
+        * will not cause issues in practice.
+        */
+        REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
 
 
-    esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
+        esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
 
 
-    // Set the step for the sleep mode when the timer will work
-    // from a slow_clk frequency instead of the APB frequency.
-    uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * TICKS_PER_US;
-    REG_SET_FIELD(RTC_STEP_REG, TIMG_LACT_RTC_STEP_LEN, slowclk_ticks_per_us);
+        // Set the step for the sleep mode when the timer will work
+        // from a slow_clk frequency instead of the APB frequency.
+        uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * TICKS_PER_US;
+        REG_SET_FIELD(RTC_STEP_REG, TIMG_LACT_RTC_STEP_LEN, slowclk_ticks_per_us);
+    }
 
 
-    ESP_ERROR_CHECK( esp_intr_enable(s_timer_interrupt_handle) );
+    err = esp_intr_enable(s_timer_interrupt_handle[(ISR_HANDLERS == 1) ? 0 : xPortGetCoreID()]);
+    if (err != ESP_OK) {
+        ESP_EARLY_LOGE(TAG, "Can not enable ISR (0x%0x)", err);
+    }
 
 
-    return ESP_OK;
+    return err;
 }
 }
 
 
 void esp_timer_impl_deinit(void)
 void esp_timer_impl_deinit(void)
@@ -267,10 +323,14 @@ void esp_timer_impl_deinit(void)
     REG_WRITE(CONFIG_REG, 0);
     REG_WRITE(CONFIG_REG, 0);
     REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
     REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
     /* TODO: also clear TIMG_LACT_INT_ENA; however see the note in esp_timer_impl_init. */
     /* TODO: also clear TIMG_LACT_INT_ENA; however see the note in esp_timer_impl_init. */
-
-    esp_intr_disable(s_timer_interrupt_handle);
-    esp_intr_free(s_timer_interrupt_handle);
-    s_timer_interrupt_handle = NULL;
+    for (unsigned i = 0; i < ISR_HANDLERS; i++) {
+        if (s_timer_interrupt_handle[i] != NULL) {
+            esp_intr_disable(s_timer_interrupt_handle[i]);
+            esp_intr_free(s_timer_interrupt_handle[i]);
+            s_timer_interrupt_handle[i] = NULL;
+        }
+    }
+    s_alarm_handler = NULL;
 }
 }
 
 
 /* FIXME: This value is safe for 80MHz APB frequency, should be modified to depend on clock frequency. */
 /* FIXME: This value is safe for 80MHz APB frequency, should be modified to depend on clock frequency. */

+ 77 - 31
components/esp_timer/src/esp_timer_impl_systimer.c

@@ -36,8 +36,15 @@
 
 
 static const char *TAG = "esp_timer_systimer";
 static const char *TAG = "esp_timer_systimer";
 
 
+#define NOT_USED 0xBAD00FAD
+
 /* Interrupt handle returned by the interrupt allocator */
 /* Interrupt handle returned by the interrupt allocator */
-static intr_handle_t s_timer_interrupt_handle;
+#ifdef CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY
+#define ISR_HANDLERS (portNUM_PROCESSORS)
+#else
+#define ISR_HANDLERS (1)
+#endif
+static intr_handle_t s_timer_interrupt_handle[ISR_HANDLERS] = { NULL };
 
 
 /* Function from the upper layer to be called when the interrupt happens.
 /* Function from the upper layer to be called when the interrupt happens.
  * Registered in esp_timer_impl_init.
  * Registered in esp_timer_impl_init.
@@ -91,10 +98,47 @@ void IRAM_ATTR esp_timer_impl_set_alarm(uint64_t timestamp)
 
 
 static void IRAM_ATTR timer_alarm_isr(void *arg)
 static void IRAM_ATTR timer_alarm_isr(void *arg)
 {
 {
+#if ISR_HANDLERS == 1
     // clear the interrupt
     // clear the interrupt
     systimer_ll_clear_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER);
     systimer_ll_clear_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER);
     /* Call the upper layer handler */
     /* Call the upper layer handler */
     (*s_alarm_handler)(arg);
     (*s_alarm_handler)(arg);
+#else
+    static volatile uint32_t processed_by = NOT_USED;
+    static volatile bool pending_alarm = false;
+    /* CRITICAL section ensures the read/clear is atomic between cores */
+    portENTER_CRITICAL_ISR(&s_time_update_lock);
+    if (systimer_ll_is_alarm_int_fired(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER)) {
+        // Clear interrupt status
+        systimer_ll_clear_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER);
+        // Is the other core already processing a previous alarm?
+        if (processed_by == NOT_USED) {
+            // Current core is not processing an alarm yet
+            processed_by = xPortGetCoreID();
+            do {
+                pending_alarm = false;
+                // Clear interrupt status
+                systimer_ll_clear_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER);
+                portEXIT_CRITICAL_ISR(&s_time_update_lock);
+
+                (*s_alarm_handler)(arg);
+
+                portENTER_CRITICAL_ISR(&s_time_update_lock);
+               // Another alarm could have occurred while were handling the previous alarm.
+               // Check if we need to call the s_alarm_handler again:
+               //   1) if the alarm has already been fired, it helps to handle it immediately without an additional ISR call.
+               //   2) handle pending alarm that was cleared by the other core in time when this core worked with the current alarm.
+            } while (systimer_ll_is_alarm_int_fired(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER) || pending_alarm);
+            processed_by = NOT_USED;
+        } else {
+            // Current core arrived at ISR but the other core is still handling a previous alarm.
+            // Once we already cleared the ISR status we need to let the other core know that it was.
+            // Set the flag to handle the current alarm by the other core later.
+            pending_alarm = true;
+        }
+    }
+    portEXIT_CRITICAL_ISR(&s_time_update_lock);
+#endif // ISR_HANDLERS != 1
 }
 }
 
 
 void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
 void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
@@ -148,53 +192,55 @@ esp_err_t esp_timer_impl_early_init(void)
 
 
 esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
 esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
 {
 {
-    s_alarm_handler = alarm_handler;
-    const int interrupt_lvl = (1 << CONFIG_ESP_TIMER_INTERRUPT_LEVEL) & ESP_INTR_FLAG_LEVELMASK;
-#if SOC_SYSTIMER_INT_LEVEL
-    int int_type = 0;
-#else
-    int int_type = ESP_INTR_FLAG_EDGE;
-#endif // SOC_SYSTIMER_INT_LEVEL
-    esp_err_t err = esp_intr_alloc(ETS_SYSTIMER_TARGET2_EDGE_INTR_SOURCE,
-                                   ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM | int_type | interrupt_lvl,
-                                   &timer_alarm_isr, NULL, &s_timer_interrupt_handle);
+    if (s_timer_interrupt_handle[(ISR_HANDLERS == 1) ? 0 : xPortGetCoreID()] != NULL) {
+        ESP_EARLY_LOGE(TAG, "timer ISR is already initialized");
+        return ESP_ERR_INVALID_STATE;
+    }
 
 
+    int isr_flags = ESP_INTR_FLAG_INTRDISABLED
+                    | ((1 << CONFIG_ESP_TIMER_INTERRUPT_LEVEL) & ESP_INTR_FLAG_LEVELMASK)
+#if !SOC_SYSTIMER_INT_LEVEL
+                    | ESP_INTR_FLAG_EDGE
+#endif
+                    | ESP_INTR_FLAG_IRAM;
+
+    esp_err_t err = esp_intr_alloc(ETS_SYSTIMER_TARGET2_EDGE_INTR_SOURCE, isr_flags,
+                                   &timer_alarm_isr, NULL,
+                                   &s_timer_interrupt_handle[(ISR_HANDLERS == 1) ? 0 : xPortGetCoreID()]);
     if (err != ESP_OK) {
     if (err != ESP_OK) {
         ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%x)", err);
         ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%x)", err);
-        goto err_intr_alloc;
+        return err;
     }
     }
 
 
-    /* TODO: if SYSTIMER is used for anything else, access to SYSTIMER_INT_ENA_REG has to be
-    * protected by a shared spinlock. Since this code runs as part of early startup, this
-    * is practically not an issue.
-    */
-    systimer_hal_enable_alarm_int(&systimer_hal, SYSTIMER_ALARM_ESPTIMER);
+    if (s_alarm_handler == NULL) {
+        s_alarm_handler = alarm_handler;
+        /* TODO: if SYSTIMER is used for anything else, access to SYSTIMER_INT_ENA_REG has to be
+        * protected by a shared spinlock. Since this code runs as part of early startup, this
+        * is practically not an issue.
+        */
+        systimer_hal_enable_alarm_int(&systimer_hal, SYSTIMER_ALARM_ESPTIMER);
+    }
 
 
-    err = esp_intr_enable(s_timer_interrupt_handle);
+    err = esp_intr_enable(s_timer_interrupt_handle[(ISR_HANDLERS == 1) ? 0 : xPortGetCoreID()]);
     if (err != ESP_OK) {
     if (err != ESP_OK) {
-        ESP_EARLY_LOGE(TAG, "esp_intr_enable failed (0x%x)", err);
-        goto err_intr_en;
+        ESP_EARLY_LOGE(TAG, "Can not enable ISR (0x%0x)", err);
     }
     }
-    return ESP_OK;
 
 
-err_intr_en:
-    systimer_ll_enable_alarm(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
-    /* TODO: may need a spinlock, see the note related to SYSTIMER_INT_ENA_REG in systimer_hal_init */
-    systimer_ll_enable_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
-    esp_intr_free(s_timer_interrupt_handle);
-err_intr_alloc:
-    s_alarm_handler = NULL;
     return err;
     return err;
 }
 }
 
 
 void esp_timer_impl_deinit(void)
 void esp_timer_impl_deinit(void)
 {
 {
-    esp_intr_disable(s_timer_interrupt_handle);
     systimer_ll_enable_alarm(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
     systimer_ll_enable_alarm(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
     /* TODO: may need a spinlock, see the note related to SYSTIMER_INT_ENA_REG in systimer_hal_init */
     /* TODO: may need a spinlock, see the note related to SYSTIMER_INT_ENA_REG in systimer_hal_init */
     systimer_ll_enable_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
     systimer_ll_enable_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
-    esp_intr_free(s_timer_interrupt_handle);
-    s_timer_interrupt_handle = NULL;
+    for (unsigned i = 0; i < ISR_HANDLERS; i++) {
+        if (s_timer_interrupt_handle[i] != NULL) {
+            esp_intr_disable(s_timer_interrupt_handle[i]);
+            esp_intr_free(s_timer_interrupt_handle[i]);
+            s_timer_interrupt_handle[i] = NULL;
+        }
+    }
     s_alarm_handler = NULL;
     s_alarm_handler = NULL;
 }
 }
 
 

+ 40 - 1
components/esp_timer/test_apps/main/test_esp_timer.c

@@ -1,5 +1,5 @@
 /*
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  *
  * SPDX-License-Identifier: Apache-2.0
  * SPDX-License-Identifier: Apache-2.0
  */
  */
@@ -1177,4 +1177,43 @@ TEST_CASE("Test ESP_TIMER_ISR, stop API cleans alarm reg if ISR timer list is em
     vSemaphoreDelete(done);
     vSemaphoreDelete(done);
     printf("timer deleted\n");
     printf("timer deleted\n");
 }
 }
+
+#ifndef CONFIG_FREERTOS_UNICORE
+static void task_callback3(void* arg)
+{
+    int *data = (int *)arg;
+    ++*data;
+    esp_rom_printf("callback from CPU%d\n", xPortGetCoreID());
+#if defined(CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY) || defined(CONFIG_ESP_TIMER_ISR_AFFINITY_CPU1)
+    TEST_ASSERT_EQUAL_INT(1, xPortGetCoreID());
+#endif // CONFIG_ESP_TIMER_AFFINITY_NO_AFFINITY
+}
+
+TEST_CASE("Test that CPU1 can handle esp_timer ISR even when CPU0 is blocked", "[esp_timer][isr_dispatch]")
+{
+    int data = 0;
+
+    esp_timer_handle_t timer;
+    const esp_timer_create_args_t timer_args = {
+        .callback = &task_callback3,
+        .dispatch_method = ESP_TIMER_ISR,
+        .arg = &data,
+        .name = "test",
+    };
+    TEST_ESP_OK(esp_timer_create(&timer_args, &timer));
+    TEST_ESP_OK(esp_timer_start_periodic(timer, 10000));
+
+    portDISABLE_INTERRUPTS();
+    TEST_ASSERT_EQUAL_INT(0, xPortGetCoreID());
+    esp_rom_printf("CPU%d is blocked\n", xPortGetCoreID());
+    esp_rom_delay_us(100000);
+    esp_rom_printf("CPU%d is released\n", xPortGetCoreID());
+    portENABLE_INTERRUPTS();
+
+    TEST_ESP_OK(esp_timer_stop(timer));
+    TEST_ESP_OK(esp_timer_dump(stdout));
+    TEST_ASSERT_INT_WITHIN(3, 10, data);
+    TEST_ESP_OK(esp_timer_delete(timer));
+}
+#endif // not CONFIG_FREERTOS_UNICORE
 #endif // CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
 #endif // CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD

+ 4 - 0
components/esp_timer/test_apps/pytest_esp_timer_ut.py

@@ -10,6 +10,10 @@ CONFIGS = [
     pytest.param('single_core', marks=[pytest.mark.esp32]),
     pytest.param('single_core', marks=[pytest.mark.esp32]),
     pytest.param('freertos_compliance', marks=[pytest.mark.esp32]),
     pytest.param('freertos_compliance', marks=[pytest.mark.esp32]),
     pytest.param('isr_dispatch_esp32', marks=[pytest.mark.esp32]),
     pytest.param('isr_dispatch_esp32', marks=[pytest.mark.esp32]),
+    pytest.param('cpu1_esp32', marks=[pytest.mark.esp32]),
+    pytest.param('any_cpu_esp32', marks=[pytest.mark.esp32]),
+    pytest.param('cpu1_esp32s3', marks=[pytest.mark.esp32s3]),
+    pytest.param('any_cpu_esp32s3', marks=[pytest.mark.esp32s3]),
 ]
 ]
 
 
 
 

+ 5 - 0
components/esp_timer/test_apps/sdkconfig.ci.any_cpu_esp32

@@ -0,0 +1,5 @@
+CONFIG_IDF_TARGET="esp32"
+CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
+CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
+CONFIG_ESP_TIMER_TASK_AFFINITY_NO_AFFINITY=y
+CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY=y

+ 5 - 0
components/esp_timer/test_apps/sdkconfig.ci.any_cpu_esp32s3

@@ -0,0 +1,5 @@
+CONFIG_IDF_TARGET="esp32s3"
+CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
+CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
+CONFIG_ESP_TIMER_TASK_AFFINITY_NO_AFFINITY=y
+CONFIG_ESP_TIMER_ISR_AFFINITY_NO_AFFINITY=y

+ 5 - 0
components/esp_timer/test_apps/sdkconfig.ci.cpu1_esp32

@@ -0,0 +1,5 @@
+CONFIG_IDF_TARGET="esp32"
+CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
+CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
+CONFIG_ESP_TIMER_TASK_AFFINITY_CPU1=y
+CONFIG_ESP_TIMER_ISR_AFFINITY_CPU1=y

+ 5 - 0
components/esp_timer/test_apps/sdkconfig.ci.cpu1_esp32s3

@@ -0,0 +1,5 @@
+CONFIG_IDF_TARGET="esp32s3"
+CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD=y
+CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL=y
+CONFIG_ESP_TIMER_TASK_AFFINITY_CPU1=y
+CONFIG_ESP_TIMER_ISR_AFFINITY_CPU1=y