Просмотр исходного кода

Merge branch 'feature/high_level_interrupt_in_bluetooth' into 'master'

Bluetooth: High level interrupt in bluetooth

See merge request espressif/esp-idf!11156
Bao Hong De 4 лет назад
Родитель
Сommit
7fc071e133

+ 5 - 1
components/bt/CMakeLists.txt

@@ -1,6 +1,8 @@
 if(CONFIG_BT_ENABLED)
     if(CONFIG_IDF_TARGET_ESP32)
-        set(srcs "controller/esp32/bt.c")
+        set(srcs "controller/esp32/bt.c"
+                 "controller/esp32/hli_api.c"
+                 "controller/esp32/hli_vectors.S")
     elseif(CONFIG_IDF_TARGET_ESP32C3)
         set(srcs "controller/esp32c3/bt.c")
     elseif(CONFIG_IDF_TARGET_ESP32S3)
@@ -608,6 +610,8 @@ if(CONFIG_BT_ENABLED)
     if(CONFIG_IDF_TARGET_ESP32)
         target_link_libraries(${COMPONENT_LIB} INTERFACE "-L${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32/esp32")
         target_link_libraries(${COMPONENT_LIB} PUBLIC btdm_app)
+
+        target_link_libraries(${COMPONENT_LIB} INTERFACE "-u ld_include_hli_vectors_bt")
     elseif(CONFIG_IDF_TARGET_ESP32C3)
         target_link_libraries(${COMPONENT_LIB} INTERFACE
                 "-L${CMAKE_CURRENT_LIST_DIR}/controller/lib_esp32c3_family/esp32c3")

+ 1 - 0
components/bt/component.mk

@@ -10,6 +10,7 @@ COMPONENT_ADD_INCLUDEDIRS := include
 LIBS := btdm_app
 
 COMPONENT_ADD_LDFLAGS     := -lbt -L $(COMPONENT_PATH)/controller/lib_esp32/esp32 \
+                          -u ld_include_hli_vectors_bt \
                            $(addprefix -l,$(LIBS))
 
 # re-link program if BT binary libs change

+ 7 - 0
components/bt/controller/esp32/Kconfig.in

@@ -415,3 +415,10 @@ config BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD
         of ADV packets lost in the controller reaches this threshold. It is better to set a larger value.
         If you set `BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD` to a small value or printf every adv lost event, it
         may cause adv packets lost more.
+
+config BTDM_CTRL_HLI
+    bool "High level interrupt"
+    depends on BT_ENABLED
+    default y
+    help
+        Using Level 4 interrupt for Bluetooth.

+ 279 - 23
components/bt/controller/esp32/bt.c

@@ -40,6 +40,7 @@
 #include "driver/periph_ctrl.h"
 #include "soc/rtc.h"
 #include "soc/soc_memory_layout.h"
+#include "soc/dport_reg.h"
 #include "esp32/clk.h"
 #include "esp_coexist_internal.h"
 #if !CONFIG_FREERTOS_UNICORE
@@ -47,6 +48,7 @@
 #endif
 
 #include "esp_rom_sys.h"
+#include "hli_api.h"
 
 #if CONFIG_BT_ENABLED
 
@@ -54,6 +56,7 @@
  ************************************************************************
  */
 
+#define UNUSED(x)                           (void)(x)
 #define BTDM_LOG_TAG                        "BTDM_INIT"
 
 #define BTDM_INIT_PERIOD                    (5000)    /* ms */
@@ -92,7 +95,7 @@ do{\
 } while(0)
 
 #define OSI_FUNCS_TIME_BLOCKING  0xffffffff
-#define OSI_VERSION              0x00010002
+#define OSI_VERSION              0x00010003
 #define OSI_MAGIC_VALUE          0xFADEBEAD
 
 /* SPIRAM Configuration */
@@ -184,6 +187,10 @@ struct osi_funcs_t {
     void *(* _coex_schm_curr_phase_get)(void);
     int (* _coex_wifi_channel_get)(uint8_t *primary, uint8_t *secondary);
     int (* _coex_register_wifi_channel_change_callback)(void *cb);
+    xt_handler (*_set_isr_l3)(int n, xt_handler f, void *arg);
+    void (*_interrupt_l3_disable)(void);
+    void (*_interrupt_l3_restore)(void);
+    void *(* _customer_queue_create)(uint32_t queue_len, uint32_t item_size);
     uint32_t _magic;
 };
 
@@ -268,8 +275,13 @@ extern uint32_t _btdm_data_end;
 static bool btdm_queue_generic_register(const btdm_queue_item_t *queue);
 static bool btdm_queue_generic_deregister(btdm_queue_item_t *queue);
 #endif /* CONFIG_SPIRAM_USE_MALLOC */
-static void IRAM_ATTR interrupt_disable(void);
-static void IRAM_ATTR interrupt_restore(void);
+
+#if CONFIG_BTDM_CTRL_HLI
+static xt_handler set_isr_hlevel_wrapper(int n, xt_handler f, void *arg);
+static void IRAM_ATTR interrupt_hlevel_disable(void);
+static void IRAM_ATTR interrupt_hlevel_restore(void);
+#endif /* CONFIG_BTDM_CTRL_HLI */
+static void IRAM_ATTR task_yield(void);
 static void IRAM_ATTR task_yield_from_isr(void);
 static void *semphr_create_wrapper(uint32_t max, uint32_t init);
 static void semphr_delete_wrapper(void *semphr);
@@ -281,12 +293,21 @@ static void *mutex_create_wrapper(void);
 static void mutex_delete_wrapper(void *mutex);
 static int32_t mutex_lock_wrapper(void *mutex);
 static int32_t mutex_unlock_wrapper(void *mutex);
+#if CONFIG_BTDM_CTRL_HLI
+static void *queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size);
+static void queue_delete_hlevel_wrapper(void *queue);
+static int32_t IRAM_ATTR queue_send_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms);
+static int32_t IRAM_ATTR queue_send_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw);
+static int32_t IRAM_ATTR queue_recv_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms);
+static int32_t IRAM_ATTR queue_recv_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw);
+#else
 static void *queue_create_wrapper(uint32_t queue_len, uint32_t item_size);
 static void queue_delete_wrapper(void *queue);
-static int32_t queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms);
+static int32_t IRAM_ATTR queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms);
 static int32_t IRAM_ATTR queue_send_from_isr_wrapper(void *queue, void *item, void *hptw);
-static int32_t queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms);
+static int32_t IRAM_ATTR queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms);
 static int32_t IRAM_ATTR queue_recv_from_isr_wrapper(void *queue, void *item, void *hptw);
+#endif /* CONFIG_BTDM_CTRL_HLI */
 static int32_t task_create_wrapper(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id);
 static void task_delete_wrapper(void *task_handle);
 static bool IRAM_ATTR is_in_isr_wrapper(void);
@@ -317,17 +338,30 @@ static uint8_t coex_schm_curr_period_get_wrapper(void);
 static void * coex_schm_curr_phase_get_wrapper(void);
 static int coex_wifi_channel_get_wrapper(uint8_t *primary, uint8_t *secondary);
 static int coex_register_wifi_channel_change_callback_wrapper(void *cb);
+#if CONFIG_BTDM_CTRL_HLI
+static void *customer_queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size);
+#endif /* CONFIG_BTDM_CTRL_HLI */
+static void IRAM_ATTR interrupt_l3_disable(void);
+static void IRAM_ATTR interrupt_l3_restore(void);
+
 /* Local variable definition
  ***************************************************************************
  */
 /* OSI funcs */
 static const struct osi_funcs_t osi_funcs_ro = {
     ._version = OSI_VERSION,
+#if CONFIG_BTDM_CTRL_HLI
+    ._set_isr = set_isr_hlevel_wrapper,
+    ._ints_on = xt_ints_on,
+    ._interrupt_disable = interrupt_hlevel_disable,
+    ._interrupt_restore = interrupt_hlevel_restore,
+#else
     ._set_isr = xt_set_interrupt_handler,
     ._ints_on = xt_ints_on,
-    ._interrupt_disable = interrupt_disable,
-    ._interrupt_restore = interrupt_restore,
-    ._task_yield = vPortYield,
+    ._interrupt_disable = interrupt_l3_disable,
+    ._interrupt_restore = interrupt_l3_restore,
+#endif /* CONFIG_BTDM_CTRL_HLI */
+    ._task_yield = task_yield,
     ._task_yield_from_isr = task_yield_from_isr,
     ._semphr_create = semphr_create_wrapper,
     ._semphr_delete = semphr_delete_wrapper,
@@ -339,12 +373,21 @@ static const struct osi_funcs_t osi_funcs_ro = {
     ._mutex_delete = mutex_delete_wrapper,
     ._mutex_lock = mutex_lock_wrapper,
     ._mutex_unlock = mutex_unlock_wrapper,
+#if CONFIG_BTDM_CTRL_HLI
+    ._queue_create = queue_create_hlevel_wrapper,
+    ._queue_delete = queue_delete_hlevel_wrapper,
+    ._queue_send = queue_send_hlevel_wrapper,
+    ._queue_send_from_isr = queue_send_from_isr_hlevel_wrapper,
+    ._queue_recv = queue_recv_hlevel_wrapper,
+    ._queue_recv_from_isr = queue_recv_from_isr_hlevel_wrapper,
+#else
     ._queue_create = queue_create_wrapper,
     ._queue_delete = queue_delete_wrapper,
     ._queue_send = queue_send_wrapper,
     ._queue_send_from_isr = queue_send_from_isr_wrapper,
     ._queue_recv = queue_recv_wrapper,
     ._queue_recv_from_isr = queue_recv_from_isr_wrapper,
+#endif /* CONFIG_BTDM_CTRL_HLI */
     ._task_create = task_create_wrapper,
     ._task_delete = task_delete_wrapper,
     ._is_in_isr = is_in_isr_wrapper,
@@ -378,6 +421,14 @@ static const struct osi_funcs_t osi_funcs_ro = {
     ._coex_schm_curr_phase_get = coex_schm_curr_phase_get_wrapper,
     ._coex_wifi_channel_get = coex_wifi_channel_get_wrapper,
     ._coex_register_wifi_channel_change_callback = coex_register_wifi_channel_change_callback_wrapper,
+    ._set_isr_l3 = xt_set_interrupt_handler,
+    ._interrupt_l3_disable = interrupt_l3_disable,
+    ._interrupt_l3_restore = interrupt_l3_restore,
+#if CONFIG_BTDM_CTRL_HLI
+    ._customer_queue_create = customer_queue_create_hlevel_wrapper,
+#else
+    ._customer_queue_create = NULL,
+#endif /* CONFIG_BTDM_CTRL_HLI */
     ._magic = OSI_MAGIC_VALUE,
 };
 
@@ -494,7 +545,48 @@ static bool btdm_queue_generic_deregister(btdm_queue_item_t *queue)
 
 #endif /* CONFIG_SPIRAM_USE_MALLOC */
 
-static void IRAM_ATTR interrupt_disable(void)
+#if CONFIG_BTDM_CTRL_HLI
+struct interrupt_hlevel_cb{
+    uint32_t status;
+    uint8_t nested;
+};
+
+static DRAM_ATTR struct interrupt_hlevel_cb hli_cb = {
+    .status = 0,
+    .nested = 0,
+};
+
+static xt_handler set_isr_hlevel_wrapper(int mask, xt_handler f, void *arg)
+{
+    esp_err_t err = hli_intr_register((intr_handler_t) f, arg, DPORT_PRO_INTR_STATUS_0_REG, mask);
+    if (err == ESP_OK) {
+        return f;
+    } else {
+        return 0;
+    }
+ }
+
+static void IRAM_ATTR interrupt_hlevel_disable(void)
+{
+    assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE);
+    assert(hli_cb.nested != ~0);
+    uint32_t status = hli_intr_disable();
+    if (hli_cb.nested++ == 0) {
+        hli_cb.status = status;
+    }
+}
+
+static void IRAM_ATTR interrupt_hlevel_restore(void)
+{
+    assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE);
+    assert(hli_cb.nested > 0);
+    if (--hli_cb.nested == 0) {
+        hli_intr_restore(hli_cb.status);
+    }
+}
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
+static void IRAM_ATTR interrupt_l3_disable(void)
 {
     if (xPortInIsrContext()) {
         portENTER_CRITICAL_ISR(&global_int_mux);
@@ -503,7 +595,7 @@ static void IRAM_ATTR interrupt_disable(void)
     }
 }
 
-static void IRAM_ATTR interrupt_restore(void)
+static void IRAM_ATTR interrupt_l3_restore(void)
 {
     if (xPortInIsrContext()) {
         portEXIT_CRITICAL_ISR(&global_int_mux);
@@ -512,6 +604,12 @@ static void IRAM_ATTR interrupt_restore(void)
     }
 }
 
+static void IRAM_ATTR task_yield(void)
+{
+    vPortYield();
+}
+
+
 static void IRAM_ATTR task_yield_from_isr(void)
 {
     portYIELD_FROM_ISR();
@@ -519,18 +617,19 @@ static void IRAM_ATTR task_yield_from_isr(void)
 
 static void *semphr_create_wrapper(uint32_t max, uint32_t init)
 {
+    void *handle = NULL;
+
 #if !CONFIG_SPIRAM_USE_MALLOC
-    return (void *)xSemaphoreCreateCounting(max, init);
+    handle = (void *)xSemaphoreCreateCounting(max, init);
 #else
     StaticQueue_t *queue_buffer = NULL;
-    QueueHandle_t handle = NULL;
 
     queue_buffer = heap_caps_malloc(sizeof(StaticQueue_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
     if (!queue_buffer) {
         goto error;
     }
 
-    handle = xSemaphoreCreateCountingStatic(max, init, queue_buffer);
+    handle = (void *)xSemaphoreCreateCountingStatic(max, init, queue_buffer);
     if (!handle) {
         goto error;
     }
@@ -544,8 +643,19 @@ static void *semphr_create_wrapper(uint32_t max, uint32_t init)
     if (!btdm_queue_generic_register(&item)) {
         goto error;
     }
+#endif
+
+#if CONFIG_BTDM_CTRL_HLI
+    SemaphoreHandle_t downstream_semaphore = handle;
+    assert(downstream_semaphore);
+    hli_queue_handle_t s_semaphore = hli_semaphore_create(max, downstream_semaphore);
+    assert(downstream_semaphore);
+    return s_semaphore;
+#else
     return handle;
+#endif /* CONFIG_BTDM_CTRL_HLI */
 
+#if CONFIG_SPIRAM_USE_MALLOC
  error:
     if (handle) {
         vSemaphoreDelete(handle);
@@ -560,11 +670,22 @@ static void *semphr_create_wrapper(uint32_t max, uint32_t init)
 
 static void semphr_delete_wrapper(void *semphr)
 {
+    void *handle = NULL;
+#if CONFIG_BTDM_CTRL_HLI
+    if (((hli_queue_handle_t)semphr)->downstream != NULL) {
+        handle = ((hli_queue_handle_t)semphr)->downstream;
+     }
+
+    hli_queue_delete(semphr);
+#else
+    handle = semphr;
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
 #if !CONFIG_SPIRAM_USE_MALLOC
-    vSemaphoreDelete(semphr);
+    vSemaphoreDelete(handle);
 #else
     btdm_queue_item_t item = {
-        .handle = semphr,
+        .handle = handle,
         .storage = NULL,
         .buffer = NULL,
     };
@@ -573,33 +694,55 @@ static void semphr_delete_wrapper(void *semphr)
         vSemaphoreDelete(item.handle);
         free(item.buffer);
     }
-
-    return;
 #endif
 }
 
 static int32_t IRAM_ATTR semphr_take_from_isr_wrapper(void *semphr, void *hptw)
 {
+#if CONFIG_BTDM_CTRL_HLI
+    return (int32_t)xSemaphoreTakeFromISR(((hli_queue_handle_t)semphr)->downstream, hptw);
+#else
     return (int32_t)xSemaphoreTakeFromISR(semphr, hptw);
+#endif /* CONFIG_BTDM_CTRL_HLI */
 }
 
 static int32_t IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw)
 {
+#if CONFIG_BTDM_CTRL_HLI
+    UNUSED(hptw);
+    assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE);
+    return hli_semaphore_give(semphr);
+#else
     return (int32_t)xSemaphoreGiveFromISR(semphr, hptw);
+#endif /* CONFIG_BTDM_CTRL_HLI */
 }
 
 static int32_t semphr_take_wrapper(void *semphr, uint32_t block_time_ms)
 {
+    bool ret;
+#if CONFIG_BTDM_CTRL_HLI
+    if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
+        ret = xSemaphoreTake(((hli_queue_handle_t)semphr)->downstream, portMAX_DELAY);
+    } else {
+        ret = xSemaphoreTake(((hli_queue_handle_t)semphr)->downstream, block_time_ms / portTICK_PERIOD_MS);
+    }
+#else
     if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
-        return (int32_t)xSemaphoreTake(semphr, portMAX_DELAY);
+        ret = xSemaphoreTake(semphr, portMAX_DELAY);
     } else {
-        return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS);
+        ret = xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS);
     }
+#endif /* CONFIG_BTDM_CTRL_HLI */
+    return (int32_t)ret;
 }
 
 static int32_t semphr_give_wrapper(void *semphr)
 {
+#if CONFIG_BTDM_CTRL_HLI
+    return (int32_t)xSemaphoreGive(((hli_queue_handle_t)semphr)->downstream);
+#else
     return (int32_t)xSemaphoreGive(semphr);
+#endif /* CONFIG_BTDM_CTRL_HLI */
 }
 
 static void *mutex_create_wrapper(void)
@@ -745,6 +888,79 @@ static void queue_delete_wrapper(void *queue)
 #endif
 }
 
+#if CONFIG_BTDM_CTRL_HLI
+static void *queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size)
+{
+    QueueHandle_t downstream_queue = queue_create_wrapper(queue_len, item_size);
+    assert(downstream_queue);
+    hli_queue_handle_t queue = hli_queue_create(queue_len, item_size, downstream_queue);
+    assert(queue);
+    return queue;
+}
+
+static void *customer_queue_create_hlevel_wrapper(uint32_t queue_len, uint32_t item_size)
+{
+    QueueHandle_t downstream_queue = queue_create_wrapper(queue_len, item_size);
+    assert(downstream_queue);
+    hli_queue_handle_t queue = hli_customer_queue_create(queue_len, item_size, downstream_queue);
+    assert(queue);
+    return queue;
+}
+
+static void queue_delete_hlevel_wrapper(void *queue)
+{
+    if (((hli_queue_handle_t)queue)->downstream != NULL) {
+        queue_delete_wrapper(((hli_queue_handle_t)queue)->downstream);
+    }
+    hli_queue_delete(queue);
+}
+
+static int32_t queue_send_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms)
+{
+    if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
+        return (int32_t)xQueueSend(((hli_queue_handle_t)queue)->downstream, item, portMAX_DELAY);
+    } else {
+        return (int32_t)xQueueSend(((hli_queue_handle_t)queue)->downstream, item, block_time_ms / portTICK_PERIOD_MS);
+    }
+}
+
+/**
+ * Queue send from isr
+ * @param  queue The queue which will send to
+ * @param  item  The message which will be send
+ * @param  hptw  need do task yield or not
+ * @return       send success or not
+ *               There is an issue here:  When the queue is full, it may reture true but it send fail to the queue, sometimes.
+ *               But in Bluetooth controller's isr, We don't care about the return value.
+ *               It only required tp send success when the queue is empty all the time.
+ *               So, this function meets the requirement.
+ */
+static int32_t IRAM_ATTR queue_send_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw)
+{
+    UNUSED(hptw);
+    assert(xPortGetCoreID() == CONFIG_BTDM_CTRL_PINNED_TO_CORE);
+    return hli_queue_put(queue, item);
+}
+
+static int32_t queue_recv_hlevel_wrapper(void *queue, void *item, uint32_t block_time_ms)
+{
+    bool ret;
+    if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
+        ret = xQueueReceive(((hli_queue_handle_t)queue)->downstream, item, portMAX_DELAY);
+    } else {
+        ret = xQueueReceive(((hli_queue_handle_t)queue)->downstream, item, block_time_ms / portTICK_PERIOD_MS);
+    }
+
+    return (int32_t)ret;
+}
+
+static int32_t IRAM_ATTR queue_recv_from_isr_hlevel_wrapper(void *queue, void *item, void *hptw)
+{
+    return (int32_t)xQueueReceiveFromISR(((hli_queue_handle_t)queue)->downstream, item, hptw);
+}
+
+#else
+
 static int32_t queue_send_wrapper(void *queue, void *item, uint32_t block_time_ms)
 {
     if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
@@ -760,18 +976,23 @@ static int32_t IRAM_ATTR queue_send_from_isr_wrapper(void *queue, void *item, vo
 }
 
 static int32_t queue_recv_wrapper(void *queue, void *item, uint32_t block_time_ms)
-{
+ {
+    bool ret;
     if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
-        return (int32_t)xQueueReceive(queue, item, portMAX_DELAY);
+        ret = xQueueReceive(queue, item, portMAX_DELAY);
     } else {
-        return (int32_t)xQueueReceive(queue, item, block_time_ms / portTICK_PERIOD_MS);
+        ret = xQueueReceive(queue, item, block_time_ms / portTICK_PERIOD_MS);
     }
-}
+
+    return (int32_t)ret;
+ }
 
 static int32_t IRAM_ATTR queue_recv_from_isr_wrapper(void *queue, void *item, void *hptw)
 {
     return (int32_t)xQueueReceiveFromISR(queue, item, hptw);
 }
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
 
 static int32_t task_create_wrapper(void *task_func, const char *name, uint32_t stack_depth, void *param, uint32_t prio, void *task_handle, uint32_t core_id)
 {
@@ -1317,11 +1538,35 @@ esp_err_t esp_bt_mem_release(esp_bt_mode_t mode)
     return ESP_OK;
 }
 
+#if CONFIG_BTDM_CTRL_HLI
+static void hli_queue_setup_cb(void* arg)
+{
+    hli_queue_setup();
+}
+
+static void hli_queue_setup_pinned_to_core(int core_id)
+{
+#if CONFIG_FREERTOS_UNICORE
+    hli_queue_setup_cb(NULL);
+#else /* CONFIG_FREERTOS_UNICORE */
+    if (xPortGetCoreID() == core_id) {
+        hli_queue_setup_cb(NULL);
+    } else {
+        esp_ipc_call(core_id, hli_queue_setup_cb, NULL);
+    }
+#endif /* !CONFIG_FREERTOS_UNICORE */
+}
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
 esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg)
 {
     esp_err_t err;
     uint32_t btdm_cfg_mask = 0;
 
+#if CONFIG_BTDM_CTRL_HLI
+    hli_queue_setup_pinned_to_core(CONFIG_BTDM_CTRL_PINNED_TO_CORE);
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
     //if all the bt available memory was already released, cannot initialize bluetooth controller
     if (btdm_dram_available_region[0].mode == ESP_BT_MODE_IDLE) {
         return ESP_ERR_INVALID_STATE;
@@ -1736,4 +1981,15 @@ esp_err_t esp_ble_scan_dupilcate_list_flush(void)
     return ESP_OK;
 }
 
+/**
+ * This function re-write controller's function,
+ * As coredump can not show paramerters in function which is in a .a file.
+ *
+ * After coredump fixing this issue, just delete this function.
+ */
+void IRAM_ATTR r_assert(const char *condition, int param0, int param1, const char *file, int line)
+{
+    __asm__ __volatile__("ill\n");
+}
+
 #endif /*  CONFIG_BT_ENABLED */

+ 306 - 0
components/bt/controller/esp32/hli_api.c

@@ -0,0 +1,306 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#include <string.h>
+#include "esp_log.h"
+#include "esp_heap_caps.h"
+#include "xtensa/core-macros.h"
+#include "soc/dport_reg.h"
+#include "hli_api.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+
+#if CONFIG_BTDM_CTRL_HLI
+#define HLI_MAX_HANDLERS    4
+
+typedef struct {
+    intr_handler_t handler;
+    void* arg;
+    uint32_t intr_reg;
+    uint32_t intr_mask;
+} hli_handler_info_t;
+
+typedef struct {
+#define CUSTOMER_TYPE_REQUEST (0)
+#define CUSTOMER_TYPE_RELEASE (1)
+    struct {
+        uint32_t cb_type;
+        union {
+            int (* request)(uint32_t, uint32_t, uint32_t);
+            int (* release)(uint32_t);
+        } cb;
+    } customer_cb;
+    uint32_t arg0, arg1, arg2;
+} customer_swisr_t;
+
+static void IRAM_ATTR customer_swisr_handle(customer_swisr_t *cus_swisr)
+{
+    if (cus_swisr->customer_cb.cb_type == CUSTOMER_TYPE_REQUEST) {
+        if (cus_swisr->customer_cb.cb.request != NULL) {
+            cus_swisr->customer_cb.cb.request(cus_swisr->arg0, cus_swisr->arg1, cus_swisr->arg2);
+        }
+    } else if(cus_swisr->customer_cb.cb_type == CUSTOMER_TYPE_RELEASE) {
+        if (cus_swisr->customer_cb.cb.release != NULL) {
+            cus_swisr->customer_cb.cb.release(cus_swisr->arg0);
+        }
+    }
+}
+
+static DRAM_ATTR hli_handler_info_t s_hli_handlers[HLI_MAX_HANDLERS];
+
+esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask)
+{
+    for (hli_handler_info_t* hip = s_hli_handlers;
+         hip < s_hli_handlers + HLI_MAX_HANDLERS;
+         ++hip) {
+        if (hip->handler == NULL) {
+            hip->arg = arg;
+            hip->intr_reg = intr_reg;
+            hip->intr_mask = intr_mask;
+            hip->handler = handler;     /* set last, indicates the entry as valid */
+            return ESP_OK;
+        }
+    }
+    return ESP_ERR_NO_MEM;
+}
+
+void IRAM_ATTR hli_c_handler(void)
+{
+    bool handled = false;
+    /* Iterate over registered interrupt handlers,
+     * and check if the expected mask is present in the interrupt status register.
+     */
+    for (hli_handler_info_t* hip = s_hli_handlers;
+         hip < s_hli_handlers + HLI_MAX_HANDLERS;
+         ++hip) {
+        if (hip->handler == NULL) {
+            continue;
+        }
+        uint32_t reg = hip->intr_reg;
+        uint32_t val;
+        if (reg == 0) { /* special case for CPU internal interrupts */
+            val = XTHAL_GET_INTERRUPT();
+        } else {
+            /* "reg" might not be in DPORT, but this will work in any case */
+            val = DPORT_REG_READ(reg);
+        }
+        if ((val & hip->intr_mask) != 0) {
+            handled = true;
+            (*hip->handler)(hip->arg);
+        }
+    }
+    if (!handled) {
+        /* no handler found, it is OK in this case. */
+    }
+}
+
+uint32_t IRAM_ATTR hli_intr_disable(void)
+{
+    /* disable level 4 and below */
+    return XTOS_SET_INTLEVEL(XCHAL_DEBUGLEVEL - 2);
+}
+
+void IRAM_ATTR hli_intr_restore(uint32_t state)
+{
+    XTOS_RESTORE_JUST_INTLEVEL(state);
+}
+
+#define HLI_META_QUEUE_SIZE     16
+#define HLI_QUEUE_MAX_ELEM_SIZE 32
+#define HLI_QUEUE_SW_INT_NUM    29
+
+#define HLI_QUEUE_FLAG_SEMAPHORE    BIT(0)
+#define HLI_QUEUE_FLAG_CUSTOMER     BIT(1)
+
+static DRAM_ATTR struct hli_queue_t *s_meta_queue_ptr = NULL;
+static intr_handle_t ret_handle;
+
+static inline char* IRAM_ATTR wrap_ptr(hli_queue_handle_t queue, char *ptr)
+{
+    return (ptr == queue->bufend) ? queue->buf : ptr;
+}
+
+static inline bool IRAM_ATTR queue_empty(hli_queue_handle_t queue)
+{
+    return queue->begin == queue->end;
+}
+
+static inline bool IRAM_ATTR queue_full(hli_queue_handle_t queue)
+{
+    return wrap_ptr(queue, queue->end + queue->elem_size) == queue->begin;
+}
+
+static void IRAM_ATTR queue_isr_handler(void* arg)
+{
+    int do_yield = pdFALSE;
+    XTHAL_SET_INTCLEAR(BIT(HLI_QUEUE_SW_INT_NUM));
+    hli_queue_handle_t queue;
+
+    while (hli_queue_get(s_meta_queue_ptr, &queue)) {
+        static DRAM_ATTR char scratch[HLI_QUEUE_MAX_ELEM_SIZE];
+        while (hli_queue_get(queue, scratch)) {
+            int res = pdPASS;
+            if ((queue->flags & HLI_QUEUE_FLAG_CUSTOMER) != 0) {
+                customer_swisr_handle((customer_swisr_t *)scratch);
+            } else if ((queue->flags & HLI_QUEUE_FLAG_SEMAPHORE) != 0) {
+                res = xSemaphoreGiveFromISR((SemaphoreHandle_t) queue->downstream, &do_yield);
+            } else {
+                res = xQueueSendFromISR(queue->downstream, scratch, &do_yield);
+            }
+            if (res == pdFAIL) {
+                /* Failed to send to downstream queue, it is OK in this case. */
+            }
+        }
+    }
+    if (do_yield) {
+        portYIELD_FROM_ISR();
+    }
+}
+
+/* Notify the level 3 handler that an element is added to the given hli queue.
+ * Do this by placing the queue handle onto s_meta_queue, and raising a SW interrupt.
+ *
+ * This function must be called with HL interrupts disabled!
+ */
+static void IRAM_ATTR queue_signal(hli_queue_handle_t queue)
+{
+    /* See if the queue is already in s_meta_queue, before adding */
+    bool found = false;
+    const hli_queue_handle_t *end = (hli_queue_handle_t*) s_meta_queue_ptr->end;
+    hli_queue_handle_t *item = (hli_queue_handle_t*) s_meta_queue_ptr->begin;
+    for (;item != end; item = (hli_queue_handle_t*) wrap_ptr(s_meta_queue_ptr, (char*) (item + 1))) {
+        if (*item == queue) {
+            found = true;
+            break;
+        }
+    }
+    if (!found) {
+        bool res = hli_queue_put(s_meta_queue_ptr, &queue);
+        if (!res) {
+            esp_rom_printf(DRAM_STR("Fatal error in queue_signal: s_meta_queue full\n"));
+            abort();
+        }
+        XTHAL_SET_INTSET(BIT(HLI_QUEUE_SW_INT_NUM));
+    }
+}
+
+static void queue_init(hli_queue_handle_t queue, size_t buf_size, size_t elem_size, QueueHandle_t downstream)
+{
+    queue->elem_size = elem_size;
+    queue->begin = queue->buf;
+    queue->end = queue->buf;
+    queue->bufend = queue->buf + buf_size;
+    queue->downstream = downstream;
+    queue->flags = 0;
+}
+
+void hli_queue_setup(void)
+{
+    if (s_meta_queue_ptr == NULL) {
+        s_meta_queue_ptr = hli_queue_create(HLI_META_QUEUE_SIZE, sizeof(void*), NULL);
+        ESP_ERROR_CHECK(esp_intr_alloc(ETS_INTERNAL_SW1_INTR_SOURCE, ESP_INTR_FLAG_IRAM, queue_isr_handler, NULL, &ret_handle));
+        xt_ints_on(BIT(HLI_QUEUE_SW_INT_NUM));
+    }
+}
+
+void hli_queue_shutdown(void)
+{
+    if (s_meta_queue_ptr != NULL) {
+        hli_queue_delete(s_meta_queue_ptr);
+        s_meta_queue_ptr = NULL;
+        esp_intr_free(ret_handle);
+        xt_ints_off(BIT(HLI_QUEUE_SW_INT_NUM));
+    }
+}
+
+hli_queue_handle_t hli_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream)
+{
+    const size_t buf_elem = nelem + 1;
+    if (elem_size > HLI_QUEUE_MAX_ELEM_SIZE) {
+        return NULL;
+    }
+    size_t buf_size = buf_elem * elem_size;
+    hli_queue_handle_t res = (hli_queue_handle_t) heap_caps_malloc(sizeof(struct hli_queue_t) + buf_size,
+        MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
+    if (res == NULL) {
+        return NULL;
+    }
+    queue_init(res, buf_size, elem_size, downstream);
+    return res;
+}
+
+hli_queue_handle_t hli_customer_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream)
+{
+    hli_queue_handle_t res = hli_queue_create(nelem, elem_size, (QueueHandle_t) downstream);
+    if (res == NULL) {
+        return NULL;
+    }
+    res->flags |= HLI_QUEUE_FLAG_CUSTOMER;
+    return res;
+}
+
+hli_queue_handle_t hli_semaphore_create(size_t max_count, SemaphoreHandle_t downstream)
+{
+    const size_t elem_size = 1;
+    hli_queue_handle_t res = hli_queue_create(max_count, elem_size, (QueueHandle_t) downstream);
+    if (res == NULL) {
+        return NULL;
+    }
+    res->flags |= HLI_QUEUE_FLAG_SEMAPHORE;
+    return res;
+}
+
+void hli_queue_delete(hli_queue_handle_t queue)
+{
+    free(queue);
+}
+
+bool IRAM_ATTR hli_queue_get(hli_queue_handle_t queue, void* out)
+{
+    uint32_t int_state = hli_intr_disable();
+    bool res = false;
+    if (!queue_empty(queue)) {
+        memcpy(out, queue->begin, queue->elem_size);
+        queue->begin = wrap_ptr(queue, queue->begin + queue->elem_size);
+        res = true;
+    }
+    hli_intr_restore(int_state);
+    return res;
+}
+
+bool IRAM_ATTR hli_queue_put(hli_queue_handle_t queue, const void* data)
+{
+    uint32_t int_state = hli_intr_disable();
+    bool res = false;
+    bool was_empty = queue_empty(queue);
+    if (!queue_full(queue)) {
+        memcpy(queue->end, data, queue->elem_size);
+        queue->end = wrap_ptr(queue, queue->end + queue->elem_size);
+        if (was_empty && queue != s_meta_queue_ptr) {
+            queue_signal(queue);
+        }
+        res = true;
+    }
+    hli_intr_restore(int_state);
+    return res;
+}
+
+bool IRAM_ATTR hli_semaphore_give(hli_queue_handle_t queue)
+{
+    uint8_t data = 0;
+    return hli_queue_put(queue, &data);
+}
+
+#endif /* CONFIG_BTDM_CTRL_HLI */

+ 176 - 0
components/bt/controller/esp32/hli_api.h

@@ -0,0 +1,176 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#pragma once
+
+#include <stdint.h>
+#include "esp_err.h"
+#include "esp_intr_alloc.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if CONFIG_BTDM_CTRL_HLI
+
+/*** Queues ***/
+
+struct hli_queue_t
+{
+    size_t elem_size;
+    char* begin;
+    char* end;
+    const char* bufend;
+    QueueHandle_t downstream;
+    int flags;
+    char buf[0];
+};
+
+/**
+ * @brief Register a high level interrupt function
+ *
+ * @param handler  interrupt handler function
+ * @param arg      argument to pass to the interrupt handler
+ * @param intr_reg   address of the peripheral register containing the interrupt status,
+ *                   or value 0 to get the status from CPU INTERRUPT register
+ * @param intr_mask  mask of the interrupt, in the interrupt status register
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_NO_MEM if too many handlers are registered
+ */
+esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask);
+
+/**
+ * @brief Mask all interrupts (including high level ones) on the current CPU
+ *
+ * @return uint32_t interrupt status, pass it to hli_intr_restore
+ */
+uint32_t hli_intr_disable(void);
+
+/**
+ * @brief Re-enable interrupts
+ *
+ * @param state value returned by hli_intr_disable
+ */
+void hli_intr_restore(uint32_t state);
+
+/**
+ * @brief Type of a hli queue
+ */
+typedef struct hli_queue_t* hli_queue_handle_t;
+
+/**
+ * @brief Initialize hli_queue module. Must be called once before using hli queue APIs.
+ */
+void hli_queue_setup(void);
+
+/**
+ * @brief Shutdown hli_queue module.
+ */
+void hli_queue_shutdown(void);
+
+/**
+ * @brief Create a hli queue, wrapping a FreeRTOS queue
+ *
+ * This queue can be used from high level interrupts,
+ * but **ONLY ON THE CPU WHERE hli_queue_setup WAS CALLED**. Values sent to this
+ * queue are automatically forwarded to "downstream" FreeRTOS queue using a level 3
+ * software interrupt.
+ *
+ * @param nelem  number of elements in the queue
+ * @param elem_size  size of one element; must match element size of a downstream queue
+ * @param downstream  FreeRTOS queue to send the values to
+ * @return hli_queue_handle_t  handle of the created queue, or NULL on failure
+ */
+hli_queue_handle_t hli_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream);
+
+/**
+ * @brief Create a customer hli queue, wrapping a FreeRTOS queue
+ *
+ * This queue can be used from high level interrupts,
+ * but **ONLY ON THE CPU WHERE hli_queue_setup WAS CALLED**. Values sent to this
+ * queue are automatically forwarded to "downstream" FreeRTOS queue using a level 3
+ * software interrupt.
+ *
+ * @param nelem  number of elements in the queue
+ * @param elem_size  size of one element; must match element size of a downstream queue
+ * @param downstream  FreeRTOS queue to send the values to
+ * @return hli_queue_handle_t  handle of the created queue, or NULL on failure
+ */
+hli_queue_handle_t hli_customer_queue_create(size_t nelem, size_t elem_size, QueueHandle_t downstream);
+
+/**
+ * @brief Create a hli queue, wrapping a FreeRTOS semaphore
+ *
+ * See notes on hli_queue_create.
+ *
+ * @param max_count  maximum semaphore count
+ * @param downstream  FreeRTOS semaphore to forward the calls to
+ * @return hli_queue_handle_t  handle of the created queue, or NULL on failure
+ */
+hli_queue_handle_t hli_semaphore_create(size_t max_count, SemaphoreHandle_t downstream);
+
+/**
+ * @brief Delete a hli queue
+ *
+ * Make sure noone is using the queue before deleting it.
+ *
+ * @param queue  handle returned by hli_queue_create or hli_semaphore_create
+ */
+void hli_queue_delete(hli_queue_handle_t queue);
+
+/**
+ * @brief Get one element from a hli queue
+ *
+ * Usually not used, values get sent to a downstream FreeRTOS queue automatically.
+ * However if downstream queue is NULL, this API can be used to get values from a hli queue.
+ *
+ * @param queue  handle of a queue
+ * @param out  pointer where to store the element
+ * @return true if the element was successfully read from the queue
+ */
+bool hli_queue_get(hli_queue_handle_t queue, void* out);
+
+/**
+ * @brief Put one element into a hli queue
+ *
+ * This puts copies an element into the queue and raises a software interrupt (level 3).
+ * In the interrupt, the value is copied to a FreeRTOS "downstream" queue.
+ *
+ * Note that if the value does not fit into a downstream queue, no error is returned,
+ * and the value is lost.
+ *
+ * @param queue handle of a queue
+ * @param data  pointer to the element to be sent
+ * @return true if data was placed into the hli queue successfully
+ */
+bool hli_queue_put(hli_queue_handle_t queue, const void* data);
+
+/**
+ * @brief "Give" a semaphore wrapped by a hli queue
+ *
+ * @param queue  handle returned by hli_semaphore_create
+ * @return true  if the event was sent to a hli queue successfully
+ */
+bool hli_semaphore_give(hli_queue_handle_t queue);
+
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
+#ifdef __cplusplus
+}
+#endif

+ 275 - 0
components/bt/controller/esp32/hli_vectors.S

@@ -0,0 +1,275 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#include <xtensa/coreasm.h>
+#include <xtensa/corebits.h>
+#include <xtensa/config/system.h>
+#include "freertos/xtensa_context.h"
+#include "sdkconfig.h"
+#include "soc/soc.h"
+
+#if CONFIG_BTDM_CTRL_HLI
+
+/* Interrupt stack size, for C code.
+ * TODO: reduce and make configurable.
+ */
+#define L4_INTR_STACK_SIZE  4096
+
+/* Save area for the CPU state:
+ * - 64 words for the general purpose registers
+ * - 7 words for some of the special registers:
+ *   - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
+ *   - SAR, LBEG, LEND, LCOUNT — since the C code might use these
+ *   - EPC1 — since the C code might cause window overflow exceptions
+ * This is not laid out as standard exception frame structure
+ * for simplicity of the save/restore code.
+ */
+#define REG_FILE_SIZE         (64 * 4)
+#define SPECREG_OFFSET        REG_FILE_SIZE
+#define SPECREG_SIZE          (7 * 4)
+#define REG_SAVE_AREA_SIZE    (SPECREG_OFFSET + SPECREG_SIZE)
+
+    .data
+_l4_intr_stack:
+    .space      L4_INTR_STACK_SIZE
+_l4_save_ctx:
+    .space      REG_SAVE_AREA_SIZE
+
+    .section .iram1,"ax"
+    .global     xt_highint4
+    .type       xt_highint4,@function
+    .align      4
+
+xt_highint4:
+
+#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
+    /*
+    Here, Timer2 is used to count a little time(50us).
+    The subsequent dram0 write operation is blocked due to live lock, which will
+    cause timer2 to timeout and trigger a level 5 interrupt.
+    */
+    rsr.ccount  a0
+    addmi   a0, a0, (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ*50)
+    wsr     a0, CCOMPARE2
+
+    /* Enable Timer 2 interrupt */
+    rsr     a0, INTENABLE
+    extui   a0, a0, 16, 1
+    bnez    a0, 1f
+    movi    a0, 0
+    xsr     a0, INTENABLE     /* disable all interrupts */
+    /* And a0 with (1 << 16) for Timer 2 interrupt mask */
+    addmi   a0, a0, (1<<14)
+    addmi   a0, a0, (1<<14)
+    addmi   a0, a0, (1<<14)
+    addmi   a0, a0, (1<<14)
+    wsr     a0, INTENABLE    /* Enable Timer 2 */
+1:
+#endif
+
+    movi    a0, _l4_save_ctx
+    /* save 4 lower registers */
+    s32i    a1, a0, 4
+    s32i    a2, a0, 8
+    s32i    a3, a0, 12
+    rsr     a2, EXCSAVE_4  /* holds the value of a0 */
+    s32i    a2, a0, 0
+
+    /* Save special registers */
+    addi    a0, a0, SPECREG_OFFSET
+    rsr     a2, WINDOWBASE
+    s32i    a2, a0, 0
+    rsr     a2, WINDOWSTART
+    s32i    a2, a0, 4
+    rsr     a2, SAR
+    s32i    a2, a0, 8
+    rsr     a2, LBEG
+    s32i    a2, a0, 12
+    rsr     a2, LEND
+    s32i    a2, a0, 16
+    rsr     a2, LCOUNT
+    s32i    a2, a0, 20
+    rsr     a2, EPC1
+    s32i    a2, a0, 24
+
+#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
+    movi    a0, 0
+    xsr     a0, INTENABLE     /* disable all interrupts */
+    movi    a2, ~(1<<16)
+    and     a0, a2, a0
+    wsr     a0, INTENABLE
+#endif
+
+    /* disable exception mode, window overflow */
+    movi    a0, PS_INTLEVEL(5) | PS_EXCM
+    wsr     a0, PS
+    rsync
+
+    /* Save the remaining physical registers.
+     * 4 registers are already saved, which leaves 60 registers to save.
+     * (FIXME: consider the case when the CPU is configured with physical 32 registers)
+     * These 60 registers are saved in 5 iterations, 12 registers at a time.
+     */
+    movi    a1, 5
+    movi    a3, _l4_save_ctx + 4 * 4
+
+    /* This is repeated 5 times, each time the window is shifted by 12 registers.
+     * We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
+     */
+1:
+    s32i    a4, a3, 0
+    s32i    a5, a3, 4
+    s32i    a6, a3, 8
+    s32i    a7, a3, 12
+    s32i    a8, a3, 16
+    s32i    a9, a3, 20
+    s32i    a10, a3, 24
+    s32i    a11, a3, 28
+    s32i    a12, a3, 32
+    s32i    a13, a3, 36
+    s32i    a14, a3, 40
+    s32i    a15, a3, 44
+
+    /* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
+     * Copy a0-a3 to a12-15 to still have access to these values.
+     * At the same time we can decrement the counter and adjust the save area pointer
+     */
+
+    /* a0 is constant (_l4_save_ctx), no need to copy */
+    addi    a13, a1, -1  /* copy and decrement the downcounter */
+    /* a2 is scratch so no need to copy */
+    addi    a15, a3, 48  /* copy and adjust the save area pointer */
+    beqz    a13, 2f      /* have saved all registers ? */
+    rotw    3            /* rotate the window and go back */
+    j       1b
+
+    /* the loop is complete */
+2:
+    rotw 4      /* this brings us back to the original window */
+    /* a0 still points to _l4_save_ctx */
+
+    /* Can clear WINDOWSTART now, all registers are saved */
+    rsr     a2, WINDOWBASE
+    /* WINDOWSTART = (1 << WINDOWBASE) */
+    movi    a3, 1
+    ssl     a2
+    sll     a3, a3
+    wsr     a3, WINDOWSTART
+
+_highint4_stack_switch:
+    movi    a0, 0
+    movi    sp, _l4_intr_stack + L4_INTR_STACK_SIZE - 16
+    s32e    a0, sp, -12         /* For GDB: set null SP */
+    s32e    a0, sp, -16         /* For GDB: set null PC */
+    movi    a0, _highint4_stack_switch     /* For GDB: cosmetics, for the frame where stack switch happened */
+
+    /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
+    movi    a6, PS_INTLEVEL(4) | PS_UM | PS_WOE
+    wsr     a6, PS
+    rsync
+
+    /* Call C handler */
+    mov     a6, sp
+    call4   hli_c_handler
+
+    l32e    sp, sp, -12                     /* switch back to the original stack */
+
+    /* Done with C handler; re-enable exception mode, disabling window overflow */
+    movi    a2, PS_INTLEVEL(5) | PS_EXCM    /* TOCHECK */
+    wsr     a2, PS
+    rsync
+
+    /* Restore the special registers.
+     * WINDOWSTART will be restored near the end.
+     */
+    movi    a0, _l4_save_ctx + SPECREG_OFFSET
+    l32i    a2, a0, 8
+    wsr     a2, SAR
+    l32i    a2, a0, 12
+    wsr     a2, LBEG
+    l32i    a2, a0, 16
+    wsr     a2, LEND
+    l32i    a2, a0, 20
+    wsr     a2, LCOUNT
+    l32i    a2, a0, 24
+    wsr     a2, EPC1
+
+    /* Restoring the physical registers.
+     * This is the reverse to the saving process above.
+     */
+
+    /* Rotate back to the final window, then start loading 12 registers at a time,
+     * in 5 iterations.
+     * Again, a1 is the downcounter and a3 is the save area pointer.
+     * After each rotation, a1 and a3 are copied from a13 and a15.
+     * To simplify the loop, we put the initial values into a13 and a15.
+     */
+    rotw     -4
+    movi    a15, _l4_save_ctx + 64 * 4  /* point to the end of the save area */
+    movi    a13, 5
+
+1:
+    /* Copy a1 and a3 from their previous location,
+     * at the same time decrementing and adjusting the save area pointer.
+     */
+    addi    a1, a13, -1
+    addi    a3, a15, -48
+
+    /* Load 12 registers */
+    l32i    a4, a3, 0
+    l32i    a5, a3, 4
+    l32i    a6, a3, 8
+    l32i    a7, a3, 12
+    l32i    a8, a3, 16
+    l32i    a9, a3, 20
+    l32i    a10, a3, 24
+    l32i    a11, a3, 28                                /* ensure PS and EPC written */
+    l32i    a12, a3, 32
+    l32i    a13, a3, 36
+    l32i    a14, a3, 40
+    l32i    a15, a3, 44
+
+    /* Done with the loop? */
+    beqz    a1, 2f
+    /* If no, rotate the window and repeat */
+    rotw    -3
+    j       1b
+
+2:
+    /* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
+     * to be restored. Also need to restore WINDOWSTART, since all the general
+     * registers are now in place.
+     */
+    movi    a0, _l4_save_ctx
+
+    l32i    a2, a0, SPECREG_OFFSET + 4
+    wsr     a2, WINDOWSTART
+
+    l32i    a1, a0, 4
+    l32i    a2, a0, 8
+    l32i    a3, a0, 12
+    rsr     a0, EXCSAVE_4  /* holds the value of a0 before the interrupt handler */
+
+    /* Return from the interrupt, restoring PS from EPS_4 */
+    rfi     4
+
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
+/* The linker has no reason to link in this file; all symbols it exports are already defined
+   (weakly!) in the default int handler. Define a symbol here so we can use it to have the
+   linker inspect this anyway. */
+
+    .global ld_include_hli_vectors_bt
+ld_include_hli_vectors_bt:

+ 1 - 1
components/bt/controller/lib_esp32

@@ -1 +1 @@
-Subproject commit fb49791b7c1a8a35f06e68124c90022667b4cff1
+Subproject commit cfbb0571fb424ca4a68a0c172cbff1fdc79fd91b

+ 8 - 0
components/bt/include/esp32/include/esp_bt.h

@@ -149,6 +149,12 @@ the adv packet will be discarded until the memory is restored. */
 #define BTDM_CTRL_AUTO_LATENCY_EFF false
 #endif
 
+#ifdef CONFIG_BTDM_CTRL_HLI
+#define BTDM_CTRL_HLI CONFIG_BTDM_CTRL_HLI
+#else
+#define BTDM_CTRL_HLI false
+#endif
+
 #ifdef CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF
 #define BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF
 #else
@@ -183,6 +189,7 @@ the adv packet will be discarded until the memory is restored. */
     .ble_sca = CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF,             \
     .pcm_role = CONFIG_BTDM_CTRL_PCM_ROLE_EFF,                             \
     .pcm_polar = CONFIG_BTDM_CTRL_PCM_POLAR_EFF,                           \
+    .hli = BTDM_CTRL_HLI,                                                  \
     .magic = ESP_BT_CONTROLLER_CONFIG_MAGIC_VAL,                           \
 };
 
@@ -224,6 +231,7 @@ typedef struct {
     uint8_t ble_sca;                        /*!< BLE low power crystal accuracy index */
     uint8_t pcm_role;                       /*!< PCM role (master & slave)*/
     uint8_t pcm_polar;                      /*!< PCM polar trig (falling clk edge & rising clk edge) */
+    bool hli;                               /*!< Using high level interrupt or not */
     uint32_t magic;                         /*!< Magic number */
 } esp_bt_controller_config_t;
 

+ 3 - 0
components/esp_hw_support/test/test_dport_xt_highint5.S

@@ -10,6 +10,7 @@
 #include "soc/dport_reg.h"
 
 #ifndef CONFIG_FREERTOS_UNICORE
+#ifndef CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
 
 #define L5_INTR_STACK_SIZE  12
 #define L5_INTR_A2_OFFSET   0
@@ -77,6 +78,8 @@ xt_highint5:
 
     .global ld_include_test_dport_xt_highint5
 ld_include_test_dport_xt_highint5:
+
+#endif // CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
 #endif // CONFIG_FREERTOS_UNICORE
 
 #endif // CONFIG_IDF_TARGET_ESP32

+ 43 - 22
components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_handler.S

@@ -15,14 +15,30 @@
 
 /* High-priority interrupt - IPC_ISR handler */
 
-#define L4_INTR_STACK_SIZE  16
-#define L4_INTR_A0_OFFSET   0
-#define L4_INTR_A2_OFFSET   4
-#define L4_INTR_A3_OFFSET   8
-#define L4_INTR_A4_OFFSET   12
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+#define LX_INTR_STACK_SIZE  16
+#define LX_INTR_A0_OFFSET   0
+#define LX_INTR_A2_OFFSET   4
+#define LX_INTR_A3_OFFSET   8
+#define LX_INTR_A4_OFFSET   12
+#define EXCSAVE_X           EXCSAVE_5
+#define RFI_X               5
+
+#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
+
+#define LX_INTR_STACK_SIZE  16
+#define LX_INTR_A0_OFFSET   0
+#define LX_INTR_A2_OFFSET   4
+#define LX_INTR_A3_OFFSET   8
+#define LX_INTR_A4_OFFSET   12
+#define EXCSAVE_X           EXCSAVE_4
+#define RFI_X               4
+
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
+
     .data
-_l4_intr_stack:
-    .space      L4_INTR_STACK_SIZE
+_lx_intr_stack:
+    .space      LX_INTR_STACK_SIZE
     .section .iram1,"ax"
     .global     esp_ipc_isr_handler
     .type       esp_ipc_isr_handler,@function
@@ -30,17 +46,17 @@ _l4_intr_stack:
 esp_ipc_isr_handler:
     /* Allocate exception frame and save minimal context. */
     /* Because the interrupt cause code has protection that only
-       allows one cpu to enter in the IPC_ISR section of the L4
+       allows one cpu to enter in the IPC_ISR section of the LX
        interrupt at one time, there's no need to have two
-       _l4_intr_stack for each cpu */
+       _lx_intr_stack for each cpu */
 
     /* Save A0, A2, A3, A4 so we can use those registers further*/
-    movi    a0, _l4_intr_stack
-    s32i    a2, a0, L4_INTR_A2_OFFSET
-    s32i    a3, a0, L4_INTR_A3_OFFSET
-    s32i    a4, a0, L4_INTR_A4_OFFSET
-    rsr     a2, EXCSAVE_4
-    s32i    a2, a0, L4_INTR_A0_OFFSET
+    movi    a0, _lx_intr_stack
+    s32i    a2, a0, LX_INTR_A2_OFFSET
+    s32i    a3, a0, LX_INTR_A3_OFFSET
+    s32i    a4, a0, LX_INTR_A4_OFFSET
+    rsr     a2, EXCSAVE_X
+    s32i    a2, a0, LX_INTR_A0_OFFSET
 
     /* disable nested iterrupts */
     /* PS.EXCM is changed from 1 to 0 . It allows using usually exception handler instead of the Double exception handler. */
@@ -53,9 +69,14 @@ esp_ipc_isr_handler:
     /*
     * Reset isr interrupt flags
     */
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+    /* This int is level-triggered and doesn't need clearing.
+       Do nothing here and clear int status by peripheral register later.*/
+#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
     /* This int is edge-triggered and needs clearing. */
     movi    a3, (1 << ETS_IPC_ISR_INUM)
     wsr     a3, INTCLEAR
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
 
     /* get CORE_ID */
     getcoreid   a3
@@ -85,16 +106,16 @@ esp_ipc_isr_handler:
     callx0  a0
 
     /* Done. Restore registers and return. */
-    movi    a0, _l4_intr_stack
-    l32i    a2, a0, L4_INTR_A2_OFFSET
-    l32i    a3, a0, L4_INTR_A3_OFFSET
-    l32i    a4, a0, L4_INTR_A4_OFFSET
+    movi    a0, _lx_intr_stack
+    l32i    a2, a0, LX_INTR_A2_OFFSET
+    l32i    a3, a0, LX_INTR_A3_OFFSET
+    l32i    a4, a0, LX_INTR_A4_OFFSET
 
     /* set the end flag */
     movi    a0, esp_ipc_isr_end_fl
     s32i    a0, a0, 0
 
     /* restore a0 */
-    rsr     a0, EXCSAVE_4
-    /* restores PS from EPS[4] and jumps to the address in EPC[4] */
-    rfi     4
+    rsr     a0, EXCSAVE_X
+    /* restores PS from EPS[X] and jumps to the address in EPC[X] */
+    rfi     RFI_X

+ 18 - 0
components/esp_system/Kconfig

@@ -462,5 +462,23 @@ menu "ESP System Settings"
             Debug stubs are used by OpenOCD to execute pre-compiled onboard code
             which does some useful debugging stuff, e.g. GCOV data dump.
 
+    choice ESP_SYSTEM_CHECK_INT_LEVEL
+        prompt "Interrupt level to use for Interrupt Watchdog and other system checks"
+        default ESP_SYSTEM_CHECK_INT_LEVEL_4
+        help
+            Interrupt level to use for Interrupt Watchdog and other system checks.
+
+        config ESP_SYSTEM_CHECK_INT_LEVEL_5
+            bool "Level 5 interrupt"
+            depends on IDF_TARGET_ESP32
+            help
+                Using level 5 interrupt for Interrupt Watchdog and other system checks.
+
+        config ESP_SYSTEM_CHECK_INT_LEVEL_4
+            bool "Level 4 interrupt"
+            depends on !BTDM_CTRL_HLI
+            help
+                Using level 4 interrupt for Interrupt Watchdog and other system checks.
+    endchoice
 
 endmenu  # ESP System Settings

+ 37 - 5
components/esp_system/int_wdt.c

@@ -51,7 +51,7 @@ static wdt_hal_context_t iwdt_context;
  */
 #define IWDT_LIVELOCK_TIMEOUT_MS    (20)
 
-extern uint32_t _l4_intr_livelock_counter, _l4_intr_livelock_max;
+extern uint32_t _lx_intr_livelock_counter, _lx_intr_livelock_max;
 #endif
 
 //Take care: the tick hook can also be called before esp_int_wdt_init() is called.
@@ -70,9 +70,9 @@ static void IRAM_ATTR tick_hook(void)
             wdt_hal_write_protect_disable(&iwdt_context);
             //Reconfigure stage timeouts
 #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX
-            _l4_intr_livelock_counter = 0;
+            _lx_intr_livelock_counter = 0;
             wdt_hal_config_stage(&iwdt_context, WDT_STAGE0,
-                                 CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_l4_intr_livelock_max + 1), WDT_STAGE_ACTION_INT);                    //Set timeout before interrupt
+                                CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US / (_lx_intr_livelock_max + 1), WDT_STAGE_ACTION_INT);                    //Set timeout before interrupt
 #else
             wdt_hal_config_stage(&iwdt_context, WDT_STAGE0, CONFIG_ESP_INT_WDT_TIMEOUT_MS * 1000 / IWDT_TICKS_PER_US, WDT_STAGE_ACTION_INT);          //Set timeout before interrupt
 #endif
@@ -116,6 +116,38 @@ void esp_int_wdt_init(void)
     //Enable WDT
     wdt_hal_enable(&iwdt_context);
     wdt_hal_write_protect_enable(&iwdt_context);
+
+
+#if (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
+
+#define APB_DCRSET      (0x200c)
+#define APB_ITCTRL      (0x3f00)
+
+#define ERI_ADDR(APB)   (0x100000 + (APB))
+
+#define _SYM2STR(x)     # x
+#define SYM2STR(x)      _SYM2STR(x)
+    uint32_t eriadrs, scratch = 0, immediate = 0;
+    if (soc_has_cache_lock_bug()) {
+        if (xPortGetCoreID() != CONFIG_BTDM_CTRL_PINNED_TO_CORE) {
+            __asm__ __volatile__ (
+                    /* Enable Xtensa Debug Module Integration Mode */
+                    "movi   %[ERI], " SYM2STR(ERI_ADDR(APB_ITCTRL)) "\n"
+                    "rer    %[REG], %[ERI]\n"
+                    "movi   %[IMM], 1\n"
+                    "or     %[REG], %[IMM], %[REG]\n"
+                    "wer    %[REG], %[ERI]\n"
+                    /* Enable Xtensa Debug Module BreakIn signal */
+                    "movi   %[ERI], " SYM2STR(ERI_ADDR(APB_DCRSET)) "\n"
+                    "rer    %[REG], %[ERI]\n"
+                    "movi   %[IMM], 0x10000\n"
+                    "or     %[REG], %[IMM], %[REG]\n"
+                    "wer    %[REG], %[ERI]\n"
+                    : [ERI] "=r" (eriadrs), [REG] "+r" (scratch), [IMM] "+r" (immediate)
+                );
+        }
+    }
+#endif
 }
 
 void esp_int_wdt_cpu_init(void)
@@ -136,11 +168,11 @@ void esp_int_wdt_cpu_init(void)
      * This is a workaround for issue 3.15 in "ESP32 ECO and workarounds for
      * Bugs" document.
      */
-    _l4_intr_livelock_counter = 0;
+    _lx_intr_livelock_counter = 0;
     if (soc_has_cache_lock_bug()) {
         assert((portTICK_PERIOD_MS << 1) <= IWDT_LIVELOCK_TIMEOUT_MS);
         assert(CONFIG_ESP_INT_WDT_TIMEOUT_MS >= (IWDT_LIVELOCK_TIMEOUT_MS * 3));
-        _l4_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1;
+        _lx_intr_livelock_max = CONFIG_ESP_INT_WDT_TIMEOUT_MS / IWDT_LIVELOCK_TIMEOUT_MS - 1;
     }
 #endif
 

+ 1 - 1
components/esp_system/port/arch/xtensa/panic_handler_asm.S

@@ -54,7 +54,7 @@ _xt_panic:
     s32i    a0, sp, XT_STK_A0
 
     /* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
-    movi    a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
+    movi    a0, PS_INTLEVEL(XCHAL_DEBUGLEVEL - 2) | PS_UM | PS_WOE
     wsr     a0, PS
 
     //Call panic handler

+ 236 - 108
components/esp_system/port/soc/esp32/highint_hdl.S

@@ -33,61 +33,186 @@ Interrupt , a high-priority interrupt, is used for several things:
 
 */
 
-#define L4_INTR_STACK_SIZE  12
-#define L4_INTR_A2_OFFSET   0
-#define L4_INTR_A3_OFFSET   4
-#define L4_INTR_A4_OFFSET   8
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+
+#define LX_INTR_STACK_SIZE  12
+#define LX_INTR_A2_OFFSET   0
+#define LX_INTR_A3_OFFSET   4
+#define LX_INTR_A4_OFFSET   8
+#define EPC_X               EPC_5
+#define EXCSAVE_X           EXCSAVE_5
+#define RFI_X               5
+#define xt_highintx         xt_highint5
+
+#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
+
+#define LX_INTR_STACK_SIZE  12
+#define LX_INTR_A2_OFFSET   0
+#define LX_INTR_A3_OFFSET   4
+#define LX_INTR_A4_OFFSET   8
+#define EPC_X               EPC_4
+#define EXCSAVE_X           EXCSAVE_4
+#define RFI_X               4
+#define xt_highintx         xt_highint4
+
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
+
+/*
+--------------------------------------------------------------------------------
+  Macro wdt_clr_intr_status - Clear the WDT interrupt status.
+  Macro wdt_feed            - Feed the WDT.
+
+  Input  : "dev" - Beginning address of the peripheral registers
+
+  Macro get_int_status_tg1wdt - Get the ETS_TG1_WDT_LEVEL_INTR_SOURCE bit in interrupt status
+
+  output  : "reg" - Store the result into the reg
+--------------------------------------------------------------------------------
+*/
+
+#define TIMG1_REG_OFFSET(reg)               ((reg) - REG_TIMG_BASE(1))
+#define TIMG1_WDTWPROTECT_OFFSET            TIMG1_REG_OFFSET(TIMG_WDTWPROTECT_REG(1))
+#define TIMG1_INT_CLR_OFFSET                TIMG1_REG_OFFSET(TIMG_INT_CLR_TIMERS_REG(1))
+#define TIMG1_WDT_STG0_HOLD_OFFSET          TIMG1_REG_OFFSET(TIMG_WDTCONFIG2_REG(1))
+#define TIMG1_WDT_STG1_HOLD_OFFSET          TIMG1_REG_OFFSET(TIMG_WDTCONFIG3_REG(1))
+#define TIMG1_WDT_FEED_OFFSET               TIMG1_REG_OFFSET(TIMG_WDTFEED_REG(1))
+#define UART0_DATA_REG                      (0x3FF40078)
+
+    .macro wdt_clr_intr_status  dev
+    movi    a2, \dev
+    movi    a3, TIMG_WDT_WKEY_VALUE
+    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* disable write protect */
+    memw
+    l32i    a4, a2, TIMG1_INT_CLR_OFFSET
+    memw
+    movi    a3, 4
+    or      a3, a4, a3
+    s32i    a3, a2, TIMG1_INT_CLR_OFFSET        /* clear 1st stage timeout interrupt */
+    memw
+    movi    a3, 0
+    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* enable write protect */
+    memw
+    .endm
+
+    .macro wdt_feed dev
+    movi    a2, \dev
+    movi    a3, TIMG_WDT_WKEY_VALUE
+    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* disable write protect */
+    memw
+    movi    a4, _lx_intr_livelock_max
+    l32i    a4, a4, 0
+    memw
+    addi    a4, a4, 1
+    movi    a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<1)
+    quou    a3, a3, a4
+    s32i    a3, a2, TIMG1_WDT_STG0_HOLD_OFFSET  /* set timeout before interrupt */
+    memw
+    movi    a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<2)
+    s32i    a3, a2, TIMG1_WDT_STG1_HOLD_OFFSET  /* set timeout before system reset */
+    memw
+    movi    a3, 1
+    s32i    a3, a2, TIMG1_WDT_FEED_OFFSET       /* feed wdt */
+    memw
+    movi    a3, 0
+    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* enable write protect */
+    memw
+    .endm
+
+    .macro get_int_status_tg1wdt reg
+    rsr     \reg, INTERRUPT
+    extui   \reg, \reg, ETS_T1_WDT_CACHEERR_INUM, 1
+    beqz    \reg, 99f        /* not ETS_T1_WDT_INUM or ETS_CACHEERR_INUM */
+
+    getcoreid   \reg
+    bnez    \reg, 98f
+    /* core 0 */
+    movi    \reg, UART0_DATA_REG
+    l32i    \reg, \reg, 0     /* Workaround for DPORT read error, for silicon revision 0~2 (ECO V0 ~ ECO V2). */
+    movi    \reg, DPORT_PRO_INTR_STATUS_0_REG
+    l32i    \reg, \reg, 0
+    extui   \reg, \reg, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 1
+    j       99f
+
+98: /* core 1 */
+    movi    \reg, UART0_DATA_REG
+    l32i    \reg, \reg, 0     /* Workaround for DPORT read error, for silicon revision 0~2 (ECO V0 ~ ECO V2). */
+    movi    \reg, DPORT_APP_INTR_STATUS_0_REG
+    l32i    \reg, \reg, 0
+    extui   \reg, \reg, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 1
+99:
+    .endm
+
     .data
-_l4_intr_stack:
-    .space      L4_INTR_STACK_SIZE*portNUM_PROCESSORS /* This allocates stacks for each individual CPU. */
+_lx_intr_stack:
+    .space      LX_INTR_STACK_SIZE*portNUM_PROCESSORS /* This allocates stacks for each individual CPU. */
 
 #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
-    .global _l4_intr_livelock_counter
-    .global _l4_intr_livelock_max
+    .global _lx_intr_livelock_counter
+    .global _lx_intr_livelock_max
     .align  16
-_l4_intr_livelock_counter:
+_lx_intr_livelock_counter:
     .word   0
-_l4_intr_livelock_max:
+_lx_intr_livelock_max:
     .word   0
-_l4_intr_livelock_sync:
+_lx_intr_livelock_sync:
     .word   0, 0
-_l4_intr_livelock_app:
+_lx_intr_livelock_app:
     .word   0
-_l4_intr_livelock_pro:
+_lx_intr_livelock_pro:
     .word   0
 #endif
 
     .section .iram1,"ax"
-    .global     xt_highint4
-    .type       xt_highint4,@function
+    .global     xt_highintx
+    .type       xt_highintx,@function
     .align      4
-xt_highint4:
+xt_highintx:
 
 #ifndef CONFIG_FREERTOS_UNICORE
     /* See if we're here for the IPC_ISR interrupt */
     rsr     a0, INTERRUPT
     extui   a0, a0, ETS_IPC_ISR_INUM, 1
-    bnez    a0, esp_ipc_isr_handler
-#endif // not CONFIG_FREERTOS_UNICORE
+    beqz    a0, 1f
+    j       esp_ipc_isr_handler
+1:
+#endif /* not CONFIG_FREERTOS_UNICORE */
 
 #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
+
+#if CONFIG_BTDM_CTRL_HLI
+    /* Timer 2 interrupt */
+    rsr     a0, INTENABLE
+    extui   a0, a0, 16, 1
+    beqz    a0, 1f
+    rsr     a0, INTERRUPT
+    extui   a0, a0, 16, 1
+    bnez    a0, .handle_multicore_debug_int
+1:
+#endif /* CONFIG_BTDM_CTRL_HLI */
+
+    /* ETS_T1_WDT_INUM */
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+    get_int_status_tg1wdt a0
+#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
     /* See if we're here for the tg1 watchdog interrupt */
     rsr     a0, INTERRUPT
     extui   a0, a0, ETS_T1_WDT_INUM, 1
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
     beqz    a0, 1f
 
     wsr     a5, depc                        /* use DEPC as temp storage */
-    movi    a0, _l4_intr_livelock_counter
+    movi    a0, _lx_intr_livelock_counter
     l32i    a0, a0, 0
-    movi    a5, _l4_intr_livelock_max
+    movi    a5, _lx_intr_livelock_max
     l32i    a5, a5, 0
-    bltu    a0, a5, .handle_livelock_int    /* _l4_intr_livelock_counter < _l4_intr_livelock_max */
+    bltu    a0, a5, .handle_livelock_int    /* _lx_intr_livelock_counter < _lx_intr_livelock_max */
 
     rsr     a5, depc                        /* restore a5 */
 #endif
 
+1:  /* ETS_CACHEERR_INUM or ETS_T1_WDT_INUM */
     /* Allocate exception frame and save minimal context. */
-1:  mov     a0, sp
+    mov     a0, sp
     addi    sp, sp, -XT_STK_FRMSZ
     s32i    a0, sp, XT_STK_A1
     #if XCHAL_HAVE_WINDOWED
@@ -95,7 +220,7 @@ xt_highint4:
     #endif
     rsr     a0, PS                          /* save interruptee's PS */
     s32i    a0, sp, XT_STK_PS
-    rsr     a0, EPC_4                       /* save interruptee's PC */
+    rsr     a0, EPC_X                       /* save interruptee's PC */
     s32i    a0, sp, XT_STK_PC
     #if XCHAL_HAVE_WINDOWED
     s32e    a0, sp, -16                     /* for debug backtrace */
@@ -109,10 +234,17 @@ xt_highint4:
     s32i    a0, sp, XT_STK_EXCVADDR
 
     /* Figure out reason, save into EXCCAUSE reg */
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+    get_int_status_tg1wdt a0
+    bnez    a0, 1f
 
+    /* TODO: Clear the MEMACCESS_ERR interrupt status. */
+#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
     rsr     a0, INTERRUPT
     extui   a0, a0, ETS_MEMACCESS_ERR_INUM, 1 /* get cacheerr int bit */
     beqz    a0, 1f
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
+
     /* Kill this interrupt; we cannot reset it. */
     rsr     a0, INTENABLE
     movi    a4, ~(1<<ETS_MEMACCESS_ERR_INUM)
@@ -121,6 +253,10 @@ xt_highint4:
     movi    a0, PANIC_RSN_CACHEERR
     j 9f
 1:
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+   /* Clear the WDT interrupt status. */
+    wdt_clr_intr_status TIMERG1
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
 #if CONFIG_ESP_INT_WDT_CHECK_CPU1
     /* Check if the cause is the app cpu failing to tick.*/
     movi    a0, int_wdt_app_cpu_ticked
@@ -138,7 +274,7 @@ xt_highint4:
     s32i    a0, sp, XT_STK_EXCCAUSE
 
     /* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
-    rsr     a0, EXCSAVE_4                   /* save interruptee's a0 */
+    rsr     a0, EXCSAVE_X                   /* save interruptee's a0 */
 
     s32i    a0, sp, XT_STK_A0
 
@@ -146,7 +282,7 @@ xt_highint4:
     movi    a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
     wsr     a0, PS
 
-    //Call panic handler
+    /* Call panic handler */
     mov     a6,sp
     call4   panicHandler
 
@@ -154,16 +290,62 @@ xt_highint4:
     l32i    a0, sp, XT_STK_PS               /* retrieve interruptee's PS */
     wsr     a0, PS
     l32i    a0, sp, XT_STK_PC               /* retrieve interruptee's PC */
-    wsr     a0, EPC_4
+    wsr     a0, EPC_X
     l32i    a0, sp, XT_STK_A0               /* retrieve interruptee's A0 */
     l32i    sp, sp, XT_STK_A1               /* remove exception frame */
     rsync                                   /* ensure PS and EPC written */
 
-    rsr     a0, EXCSAVE_4                   /* restore a0 */
-    rfi     4
+    rsr     a0, EXCSAVE_X                   /* restore a0 */
+    rfi     RFI_X
 
 
 #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
+#if CONFIG_BTDM_CTRL_HLI
+#define APB_ITCTRL              (0x3f00)
+#define APB_DCRSET              (0x200c)
+
+#define ERI_ADDR(APB)           (0x100000 + (APB))
+
+    .align      4
+.handle_multicore_debug_int:
+
+    wsr     a2, depc                        /* temp storage */
+
+    rsr.ccount  a2
+    addmi   a2, a2, (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ*50)
+    wsr     a2, CCOMPARE2
+
+    /* Enable Integration Mode */
+    movi    a2, ERI_ADDR(APB_ITCTRL)
+    rer     a0, a2
+    addi    a0, a0, 1
+    wer     a0, a2
+
+    /* Enable and emit BreakOut signal */
+    movi    a2, ERI_ADDR(APB_DCRSET)
+    rer     a0, a2
+    movi    a2, 0x1020000
+    or      a0, a2, a0
+    movi    a2, ERI_ADDR(APB_DCRSET)
+    wer     a0, a2
+
+    .rept   4
+    nop
+    .endr
+
+    /* Enable Normal Mode */
+    movi    a2, ERI_ADDR(APB_ITCTRL)
+    rer     a0, a2
+    movi    a2, ~0x1
+    and     a0, a2, a0
+    movi    a2, ERI_ADDR(APB_ITCTRL)
+    wer     a0, a2
+
+    rsr     a2, depc
+
+    rsr     a0, EXCSAVE_5                   /* restore a0 */
+    rfi     5
+#endif /* CONFIG_BTDM_CTRL_HLI */
 
 /*
 --------------------------------------------------------------------------------
@@ -184,61 +366,7 @@ xt_highint4:
     memw
     .endm
 
-/*
---------------------------------------------------------------------------------
-  Macro wdt_clr_intr_status - Clear the WDT interrupt status.
-  Macro wdt_feed            - Feed the WDT.
-
-  Input  : "dev" - Beginning address of the peripheral registers
---------------------------------------------------------------------------------
-*/
-
-#define TIMG1_REG_OFFSET(reg)               ((reg) - REG_TIMG_BASE(1))
-#define TIMG1_WDTWPROTECT_OFFSET            TIMG1_REG_OFFSET(TIMG_WDTWPROTECT_REG(1))
-#define TIMG1_INT_CLR_OFFSET                TIMG1_REG_OFFSET(TIMG_INT_CLR_TIMERS_REG(1))
-#define TIMG1_WDT_STG0_HOLD_OFFSET          TIMG1_REG_OFFSET(TIMG_WDTCONFIG2_REG(1))
-#define TIMG1_WDT_STG1_HOLD_OFFSET          TIMG1_REG_OFFSET(TIMG_WDTCONFIG3_REG(1))
-#define TIMG1_WDT_FEED_OFFSET               TIMG1_REG_OFFSET(TIMG_WDTFEED_REG(1))
 
-    .macro wdt_clr_intr_status  dev
-    movi    a2, \dev
-    movi    a3, TIMG_WDT_WKEY_VALUE
-    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* disable write protect */
-    memw
-    l32i    a4, a2, TIMG1_INT_CLR_OFFSET
-    memw
-    movi    a3, 4
-    or      a3, a4, a3
-    s32i    a3, a2, TIMG1_INT_CLR_OFFSET        /* clear 1st stage timeout interrupt */
-    memw
-    movi    a3, 0
-    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* enable write protect */
-    memw
-    .endm
-
-    .macro wdt_feed dev
-    movi    a2, \dev
-    movi    a3, TIMG_WDT_WKEY_VALUE
-    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* disable write protect */
-    memw
-    movi    a4, _l4_intr_livelock_max
-    l32i    a4, a4, 0
-    memw
-    addi    a4, a4, 1
-    movi    a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<1)
-    quou    a3, a3, a4
-    s32i    a3, a2, TIMG1_WDT_STG0_HOLD_OFFSET  /* set timeout before interrupt */
-    memw
-    movi    a3, (CONFIG_ESP_INT_WDT_TIMEOUT_MS<<2)
-    s32i    a3, a2, TIMG1_WDT_STG1_HOLD_OFFSET  /* set timeout before system reset */
-    memw
-    movi    a3, 1
-    s32i    a3, a2, TIMG1_WDT_FEED_OFFSET       /* feed wdt */
-    memw
-    movi    a3, 0
-    s32i    a3, a2, TIMG1_WDTWPROTECT_OFFSET    /* enable write protect */
-    memw
-    .endm
 
     .align      4
 .handle_livelock_int:
@@ -246,13 +374,13 @@ xt_highint4:
     getcoreid   a5
 
     /* Save A2, A3, A4 so we can use those registers */
-    movi    a0, L4_INTR_STACK_SIZE
+    movi    a0, LX_INTR_STACK_SIZE
     mull    a5, a5, a0
-    movi    a0, _l4_intr_stack
+    movi    a0, _lx_intr_stack
     add     a0, a0, a5
-    s32i    a2, a0, L4_INTR_A2_OFFSET
-    s32i    a3, a0, L4_INTR_A3_OFFSET
-    s32i    a4, a0, L4_INTR_A4_OFFSET
+    s32i    a2, a0, LX_INTR_A2_OFFSET
+    s32i    a3, a0, LX_INTR_A3_OFFSET
+    s32i    a4, a0, LX_INTR_A4_OFFSET
 
     /* Here, we can use a0, a2, a3, a4, a5 registers */
     getcoreid   a5
@@ -260,18 +388,18 @@ xt_highint4:
     rsil    a0, CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL /* disable nested interrupt */
 
     beqz    a5, 1f
-    movi    a2, _l4_intr_livelock_app
+    movi    a2, _lx_intr_livelock_app
     l32i    a3, a2, 0
     addi    a3, a3, 1
     s32i    a3, a2, 0
 
     /* Dual core synchronization, ensuring that both cores enter interrupts */
 1:  movi    a4, 0x1
-    movi    a2, _l4_intr_livelock_sync
+    movi    a2, _lx_intr_livelock_sync
     addx4   a3, a5, a2
     s32i    a4, a3, 0
 
-1:  movi    a2, _l4_intr_livelock_sync
+1:  movi    a2, _lx_intr_livelock_sync
     movi    a3, 1
     addx4   a3, a3, a2
     l32i    a2, a2, 0
@@ -281,10 +409,10 @@ xt_highint4:
 
     beqz    a5, 1f                          /* Pro cpu (Core 0) jump bypass */
 
-    movi    a2, _l4_intr_livelock_app
+    movi    a2, _lx_intr_livelock_app
     l32i    a2, a2, 0
     bnei    a2, 2, 1f
-    movi    a2, _l4_intr_livelock_counter   /* _l4_intr_livelock_counter++ */
+    movi    a2, _lx_intr_livelock_counter   /* _lx_intr_livelock_counter++ */
     l32i    a3, a2, 0
     addi    a3, a3, 1
     s32i    a3, a2, 0
@@ -333,17 +461,17 @@ xt_highint4:
     bltu    a4, a3, 2b
 
     beqz    a5, 2f
-    movi    a2, _l4_intr_livelock_app
+    movi    a2, _lx_intr_livelock_app
     l32i    a2, a2, 0
     beqi    a2, 2, 8f
     j       3f
 
-2:  movi    a2, _l4_intr_livelock_pro
+2:  movi    a2, _lx_intr_livelock_pro
     l32i    a4, a2, 0
     addi    a4, a4, 1
     s32i    a4, a2, 0
 
-    movi    a2, _l4_intr_livelock_sync
+    movi    a2, _lx_intr_livelock_sync
     movi    a3, 1
     addx4   a3, a3, a2
     l32i    a2, a2, 0
@@ -357,7 +485,7 @@ xt_highint4:
     /*
     Pro cpu (Core 0) jump bypass, continue waiting, App cpu (Core 1)
     can execute to here, unmap itself tg1 1st stage timeout interrupt
-    then restore registers and exit highint4.
+    then restore registers and exit highint5/4.
     */
 3:  intr_matrix_map DPORT_APP_MAC_INTR_MAP_REG, ETS_TG1_WDT_LEVEL_INTR_SOURCE, 16
     j       9f
@@ -368,13 +496,13 @@ xt_highint4:
     */
 4:  intr_matrix_map DPORT_APP_MAC_INTR_MAP_REG, ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T1_WDT_INUM
 
-1:  movi    a2, _l4_intr_livelock_sync
+1:  movi    a2, _lx_intr_livelock_sync
     movi    a4, 1
     addx4   a3, a4, a2
     l32i    a2, a2, 0
     l32i    a3, a3, 0
     and     a2, a2, a3
-    beqz    a2, 1b                          /* Wait for App cpu to enter highint4 again */
+    beqz    a2, 1b                          /* Wait for App cpu to enter highint5/4 again */
 
     wdt_clr_intr_status TIMERG1
     j       9f
@@ -386,32 +514,32 @@ xt_highint4:
 
     movi    a0, 0
     beqz    a5, 1f
-    movi    a2, _l4_intr_livelock_app
+    movi    a2, _lx_intr_livelock_app
     l32i    a3, a2, 0
     bnei    a3, 2, 1f
     s32i    a0, a2, 0
 
 1:  bnez    a5, 2f
-    movi    a2, _l4_intr_livelock_pro
+    movi    a2, _lx_intr_livelock_pro
     s32i    a0, a2, 0
-2:  movi    a2, _l4_intr_livelock_sync
+2:  movi    a2, _lx_intr_livelock_sync
     addx4   a2, a5, a2
     s32i    a0, a2, 0
 
     /* Done. Restore registers and return. */
-    movi    a0, L4_INTR_STACK_SIZE
+    movi    a0, LX_INTR_STACK_SIZE
     mull    a5, a5, a0
-    movi    a0, _l4_intr_stack
+    movi    a0, _lx_intr_stack
     add     a0, a0, a5
-    l32i    a2, a0, L4_INTR_A2_OFFSET
-    l32i    a3, a0, L4_INTR_A3_OFFSET
-    l32i    a4, a0, L4_INTR_A4_OFFSET
+    l32i    a2, a0, LX_INTR_A2_OFFSET
+    l32i    a3, a0, LX_INTR_A3_OFFSET
+    l32i    a4, a0, LX_INTR_A4_OFFSET
     rsync                                   /* ensure register restored */
 
     rsr     a5, depc
 
-    rsr     a0, EXCSAVE_4                   /* restore a0 */
-    rfi     4
+    rsr     a0, EXCSAVE_X                   /* restore a0 */
+    rfi     RFI_X
 
 #endif
 

+ 75 - 0
components/freertos/port/xtensa/xtensa_vector_defaults.S

@@ -32,6 +32,21 @@ The default behaviour is to just exit the interrupt or call the panic handler on
     .align      4
 
 _xt_debugexception:
+#if (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
+#define XT_DEBUGCAUSE_DI        (5)
+    getcoreid   a0
+#if (CONFIG_BTDM_CTRL_PINNED_TO_CORE == PRO_CPU_NUM)
+    beqz    a0, 1f
+#else
+    bnez    a0, 1f
+#endif
+
+    rsr     a0, DEBUGCAUSE
+    extui   a0, a0, XT_DEBUGCAUSE_DI, 1
+    bnez    a0, _xt_debug_di_exc
+1:
+#endif //(CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
+
     movi    a0,PANIC_RSN_DEBUGEXCEPTION
     wsr     a0,EXCCAUSE
     /* _xt_panic assumes a level 1 exception. As we're
@@ -44,6 +59,66 @@ _xt_debugexception:
     call0   _xt_panic                       /* does not return */
     rfi     XCHAL_DEBUGLEVEL
 
+#if (CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
+    .align  4
+_xt_debug_di_exc:
+
+    /*
+    The delay time can be calculated by the following formula:
+      T = ceil(0.25 + max(t1, t2)) us
+
+      t1 = 80 / f1, t2 = (1 + 14/N) * 20 / f2
+
+      f1: PSRAM access frequency, unit: MHz.
+      f2: Flash access frequency, unit: MHz.
+
+      When flash is slow/fast read, N = 1.
+      When flash is DOUT/DIO read, N = 2.
+      When flash is QOUT/QIO read, N = 4.
+
+      And after testing, when CPU frequency is 240 MHz, it will take 1us to loop 27 times.
+    */
+#if defined(CONFIG_ESPTOOLPY_FLASHMODE_QIO) || defined(CONFIG_ESPTOOLPY_FLASHMODE_QOUT)
+
+# if defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_80M)
+    movi    a0, 54
+# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_40M)
+    movi    a0, 81
+# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_40M) && defined(CONFIG_SPIRAM_SPEED_40M)
+    movi    a0, 81
+# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_26M) && defined(CONFIG_SPIRAM_SPEED_40M)
+    movi    a0, 108
+# else
+    movi    a0, 135
+# endif
+
+#elif defined(CONFIG_ESPTOOLPY_FLASHMODE_DIO) || defined(CONFIG_ESPTOOLPY_FLASHMODE_DOUT)
+
+# if defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_80M)
+    movi    a0, 81
+# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_80M) && defined(CONFIG_SPIRAM_SPEED_40M)
+    movi    a0, 81
+# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_40M) && defined(CONFIG_SPIRAM_SPEED_40M)
+    movi    a0, 135
+# elif defined(CONFIG_ESPTOOLPY_FLASHFREQ_26M) && defined(CONFIG_SPIRAM_SPEED_40M)
+    movi    a0, 189
+# else
+    movi    a0, 243
+# endif
+
+#else
+    movi    a0, 243
+#endif
+
+1:  addi    a0, a0, -1         /* delay_us(N) */
+    .rept   4
+    nop
+    .endr
+    bnez    a0, 1b
+
+    rsr     a0, EXCSAVE+XCHAL_DEBUGLEVEL
+    rfi     XCHAL_DEBUGLEVEL
+#endif //(CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_BTDM_CTRL_HLI)
 #endif /* Debug exception */
 
 

+ 3 - 3
components/freertos/port/xtensa/xtensa_vectors.S

@@ -639,11 +639,11 @@ _xt_user_exc:
     rsr     a0, EXCSAVE_1                   /* save interruptee's a0 */
     s32i    a0, sp, XT_STK_A0
 
-    /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */
+    /* Set up PS for C, reenable debug and NMI interrupts, and clear EXCM. */
     #ifdef __XTENSA_CALL0_ABI__
-    movi    a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM
+    movi    a0, PS_INTLEVEL(XCHAL_DEBUGLEVEL - 2) | PS_UM
     #else
-    movi    a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE
+    movi    a0, PS_INTLEVEL(XCHAL_DEBUGLEVEL - 2) | PS_UM | PS_WOE
     #endif
     wsr     a0, PS
 

+ 56 - 0
components/soc/esp32/include/soc/soc.h

@@ -351,6 +351,60 @@
 #define ETS_CACHE_IA_INTR_SOURCE                68/**< interrupt of Cache Invalied Access, LEVEL*/
 #define ETS_MAX_INTR_SOURCE                     69/**< total number of interrupt sources*/
 
+#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
+//interrupt cpu using table, Please see the core-isa.h
+/*************************************************************************************************************
+ *      Intr num                Level           Type                    PRO CPU usage           APP CPU uasge
+ *      0                       1               extern level            WMAC                    Reserved
+ *      1                       1               extern level            BT/BLE Host HCI DMA     BT/BLE Host HCI DMA
+ *      2                       1               extern level
+ *      3                       1               extern level
+ *      4                       1               extern level            WBB
+ *      5                       1               extern level
+ *      6                       1               timer                   FreeRTOS Tick(L1)       FreeRTOS Tick(L1)
+ *      7                       1               software                BT/BLE VHCI             BT/BLE VHCI
+ *      8                       1               extern level            BT/BLE BB(RX/TX)        BT/BLE BB(RX/TX)
+ *      9                       1               extern level
+ *      10                      1               extern edge
+ *      11                      3               profiling
+ *      12                      1               extern level
+ *      13                      1               extern level
+ *      14                      7               nmi                     Reserved                Reserved
+ *      15                      3               timer                   FreeRTOS Tick(L3)       FreeRTOS Tick(L3)
+ *      16                      5               timer                   Reserved                Reserved
+ *      17                      1               extern level
+ *      18                      1               extern level
+ *      19                      2               extern level
+ *      20                      2               extern level
+ *      21                      2               extern level
+ *      22                      3               extern edge
+ *      23                      3               extern level
+ *      24                      4               extern level
+ *      25                      4               extern level            BT/BLE Controller       BT/BLE Controller
+ *      26                      5               extern level            TG1_WDT & CACHEERR
+ *      27                      3               extern level            Reserved                Reserved
+ *      28                      4               extern edge
+ *      29                      3               software                BT/BLE hli              BT/BLE hli
+ *      30                      4               extern edge             Reserved                Reserved
+ *      31                      5               extern level            IPC_ISR                 IPC_ISR
+ *************************************************************************************************************
+ */
+
+//CPU0 Interrupt number reserved, not touch this.
+#define ETS_WMAC_INUM                           0
+#define ETS_BT_HOST_INUM                        1
+#define ETS_WBB_INUM                            4
+#define ETS_TG0_T1_INUM                         10 /**< use edge interrupt*/
+#define ETS_FRC1_INUM                           22
+#define ETS_T1_WDT_CACHEERR_INUM                26
+#define ETS_T1_WDT_INUM                         ETS_T1_WDT_CACHEERR_INUM
+#define ETS_MEMACCESS_ERR_INUM                  ETS_T1_WDT_CACHEERR_INUM
+/* backwards compatibility only, use ETS_MEMACCESS_ERR_INUM instead*/
+#define ETS_CACHEERR_INUM                       ETS_MEMACCESS_ERR_INUM
+#define ETS_IPC_ISR_INUM                        31
+
+#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
+
 //interrupt cpu using table, Please see the core-isa.h
 /*************************************************************************************************************
  *      Intr num                Level           Type                    PRO CPU usage           APP CPU uasge
@@ -401,6 +455,8 @@
 #define ETS_CACHEERR_INUM                       ETS_MEMACCESS_ERR_INUM
 #define ETS_IPC_ISR_INUM                        28
 
+#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
+
 //CPU0 Interrupt number used in ROM, should be cancelled in SDK
 #define ETS_SLC_INUM                            1
 #define ETS_UART0_INUM                          5

+ 37 - 10
docs/en/api-guides/hlinterrupts.rst

@@ -9,16 +9,39 @@ The Xtensa architecture has support for 32 interrupts, divided over 8 levels, pl
 Interrupt Levels
 ----------------
 
-===== ================= ====================================================
-Level Symbol            Remark
-===== ================= ====================================================
-1     N/A               Exception and level 0 interrupts. Handled by ESP-IDF
-2-3   N/A               Medium level interrupts. Handled by ESP-IDF
-4     xt_highint4       Normally used by ESP-IDF debug logic
-5     xt_highint5       Free to use
-NMI   xt_nmi            Free to use
-dbg   xt_debugexception Debug exception. Called on e.g. a BREAK instruction.
-===== ================= ====================================================
+.. only:: esp32
+
+    ===== ================= ====================================================
+    Level Symbol            Remark
+    ===== ================= ====================================================
+    1     N/A               Exception and level 0 interrupts. Handled by ESP-IDF
+    2-3   N/A               Medium level interrupts. Handled by ESP-IDF
+    4     xt_highint4       Free to use :ref:`(See 1) <hlinterrupts-pin-notes>`
+    5     xt_highint5       Normally used by ESP-IDF debug logic :ref:`(See 1) <hlinterrupts-pin-notes>`
+    NMI   xt_nmi            Free to use
+    dbg   xt_debugexception Debug exception. Called on e.g. a BREAK instruction. :ref:`(See 2) <hlinterrupts-pin-notes>`
+    ===== ================= ====================================================
+
+
+    .. _hlinterrupts-pin-notes:
+
+    The following notes give more information about the items in the tables above.
+
+        1. ESP-IDF debug logic can be configured to run on `xt_highint4` or `xt_highint5` in :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`. Bluetooth's interrupt can be configured to run on level 4 by enabling :ref:`CONFIG_BTDM_CTRL_HLI`. If :ref:`CONFIG_BTDM_CTRL_HLI` is enabled, ESP-IDF debug logic must be running on level 5 interrupt.
+        2. If :ref:`CONFIG_BTDM_CTRL_HLI` is enabled, `xt_debugexception` is used to fix `live lock issue <https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf>`_ in ESP32 ECO3.
+
+.. only:: not esp32
+
+    ===== ================= ====================================================
+    Level Symbol            Remark
+    ===== ================= ====================================================
+    1     N/A               Exception and level 0 interrupts. Handled by ESP-IDF
+    2-3   N/A               Medium level interrupts. Handled by ESP-IDF
+    4     xt_highint4       Normally used by ESP-IDF debug logic
+    5     xt_highint5       Free to use
+    NMI   xt_nmi            Free to use
+    dbg   xt_debugexception Debug exception. Called on e.g. a BREAK instruction.
+    ===== ================= ====================================================
 
 Using these symbols is done by creating an assembly file (suffix .S) and defining the named symbols, like this::
 
@@ -41,6 +64,10 @@ Notes
    (The panic handler interrupt does call normal C code, but this is OK because there is no intention of returning to the normal code
    flow afterwards.)
 
+   .. only:: esp32
+
+       And if :ref:`CONFIG_BTDM_CTRL_HLI` is enabled, it does call normal C code in high-level interrupt, but this is OK becase we add some protection for it.
+
  - Make sure your assembly code gets linked in. If the interrupt handler symbol is the only symbol the rest of the code uses from this
    file, the linker will take the default ISR instead and not link the assembly file into the final project. To get around this, in the
    assembly file, define a symbol, like this::