Merge branch 'feature/high_level_interrupt_in_bluetooth' into 'master'

Bluetooth: High level interrupt in bluetooth

See merge request espressif/esp-idf!11156
This commit is contained in:
Bao Hong De 2021-09-10 09:50:39 +00:00
commit 7fc071e133
19 changed files with 1633 additions and 240 deletions

View File

@ -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")

View File

@ -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

View File

@ -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.

View File

@ -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;
}
return handle;
#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) {
return (int32_t)xSemaphoreTake(semphr, portMAX_DELAY);
ret = xSemaphoreTake(((hli_queue_handle_t)semphr)->downstream, portMAX_DELAY);
} else {
return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS);
ret = xSemaphoreTake(((hli_queue_handle_t)semphr)->downstream, block_time_ms / portTICK_PERIOD_MS);
}
#else
if (block_time_ms == OSI_FUNCS_TIME_BLOCKING) {
ret = xSemaphoreTake(semphr, portMAX_DELAY);
} else {
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 */

View File

@ -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 */

View File

@ -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

View File

@ -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 @@
Subproject commit fb49791b7c1a8a35f06e68124c90022667b4cff1
Subproject commit cfbb0571fb424ca4a68a0c172cbff1fdc79fd91b

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -33,156 +33,29 @@ 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
.data
_l4_intr_stack:
.space L4_INTR_STACK_SIZE*portNUM_PROCESSORS /* This allocates stacks for each individual CPU. */
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
.global _l4_intr_livelock_counter
.global _l4_intr_livelock_max
.align 16
_l4_intr_livelock_counter:
.word 0
_l4_intr_livelock_max:
.word 0
_l4_intr_livelock_sync:
.word 0, 0
_l4_intr_livelock_app:
.word 0
_l4_intr_livelock_pro:
.word 0
#endif
#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
.section .iram1,"ax"
.global xt_highint4
.type xt_highint4,@function
.align 4
xt_highint4:
#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
#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
#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
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
/* See if we're here for the tg1 watchdog interrupt */
rsr a0, INTERRUPT
extui a0, a0, ETS_T1_WDT_INUM, 1
beqz a0, 1f
wsr a5, depc /* use DEPC as temp storage */
movi a0, _l4_intr_livelock_counter
l32i a0, a0, 0
movi a5, _l4_intr_livelock_max
l32i a5, a5, 0
bltu a0, a5, .handle_livelock_int /* _l4_intr_livelock_counter < _l4_intr_livelock_max */
rsr a5, depc /* restore a5 */
#endif
/* Allocate exception frame and save minimal context. */
1: mov a0, sp
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -12 /* for debug backtrace */
#endif
rsr a0, PS /* save interruptee's PS */
s32i a0, sp, XT_STK_PS
rsr a0, EPC_4 /* save interruptee's PC */
s32i a0, sp, XT_STK_PC
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -16 /* for debug backtrace */
#endif
s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
call0 _xt_context_save
/* Save vaddr into exception frame */
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* Figure out reason, save into EXCCAUSE reg */
rsr a0, INTERRUPT
extui a0, a0, ETS_MEMACCESS_ERR_INUM, 1 /* get cacheerr int bit */
beqz a0, 1f
/* Kill this interrupt; we cannot reset it. */
rsr a0, INTENABLE
movi a4, ~(1<<ETS_MEMACCESS_ERR_INUM)
and a0, a4, a0
wsr a0, INTENABLE
movi a0, PANIC_RSN_CACHEERR
j 9f
1:
#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
l32i a0, a0, 0
bnez a0, 2f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
j 9f
2:
#endif
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
9:
/* Found the reason, now save it. */
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 */
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
wsr a0, PS
//Call panic handler
mov a6,sp
call4 panicHandler
call0 _xt_context_restore
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
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
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
/*
--------------------------------------------------------------------------------
Macro intr_matrix_map - Attach an CPU interrupt to a hardware source.
Input : "addr" - Interrupt map configuration base address
Input : "src" - Interrupt source.
Input : "inum" - Interrupt number.
--------------------------------------------------------------------------------
*/
.macro intr_matrix_map addr src inum
movi a2, \src
slli a2, a2, 2
movi a3, \addr
add a3, a3, a2
movi a2, \inum
s32i a2, a3, 0
memw
.endm
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
/*
--------------------------------------------------------------------------------
@ -190,6 +63,10 @@ xt_highint4:
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
--------------------------------------------------------------------------------
*/
@ -199,6 +76,7 @@ xt_highint4:
#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
@ -221,7 +99,7 @@ xt_highint4:
movi a3, TIMG_WDT_WKEY_VALUE
s32i a3, a2, TIMG1_WDTWPROTECT_OFFSET /* disable write protect */
memw
movi a4, _l4_intr_livelock_max
movi a4, _lx_intr_livelock_max
l32i a4, a4, 0
memw
addi a4, a4, 1
@ -240,19 +118,269 @@ xt_highint4:
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
_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 _lx_intr_livelock_counter
.global _lx_intr_livelock_max
.align 16
_lx_intr_livelock_counter:
.word 0
_lx_intr_livelock_max:
.word 0
_lx_intr_livelock_sync:
.word 0, 0
_lx_intr_livelock_app:
.word 0
_lx_intr_livelock_pro:
.word 0
#endif
.section .iram1,"ax"
.global xt_highintx
.type xt_highintx,@function
.align 4
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
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, _lx_intr_livelock_counter
l32i a0, a0, 0
movi a5, _lx_intr_livelock_max
l32i a5, a5, 0
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. */
mov a0, sp
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -12 /* for debug backtrace */
#endif
rsr a0, PS /* save interruptee's PS */
s32i a0, sp, XT_STK_PS
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 */
#endif
s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */
s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */
call0 _xt_context_save
/* Save vaddr into exception frame */
rsr a0, EXCVADDR
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)
and a0, a4, a0
wsr a0, INTENABLE
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
l32i a0, a0, 0
bnez a0, 2f
/* It is. Modify cause. */
movi a0,PANIC_RSN_INTWDT_CPU1
j 9f
2:
#endif
/* Set EXCCAUSE to reflect cause of the wdt int trigger */
movi a0,PANIC_RSN_INTWDT_CPU0
9:
/* Found the reason, now save it. */
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_X /* save interruptee's a0 */
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
wsr a0, PS
/* Call panic handler */
mov a6,sp
call4 panicHandler
call0 _xt_context_restore
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_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_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 */
/*
--------------------------------------------------------------------------------
Macro intr_matrix_map - Attach an CPU interrupt to a hardware source.
Input : "addr" - Interrupt map configuration base address
Input : "src" - Interrupt source.
Input : "inum" - Interrupt number.
--------------------------------------------------------------------------------
*/
.macro intr_matrix_map addr src inum
movi a2, \src
slli a2, a2, 2
movi a3, \addr
add a3, a3, a2
movi a2, \inum
s32i a2, a3, 0
memw
.endm
.align 4
.handle_livelock_int:
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

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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::