diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 8e898b7bfe..dd8563dcf2 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -97,7 +97,7 @@ example_test_001B_V3: example_test_001C: extends: .example_test_esp32_template - parallel: 3 + parallel: 4 tags: - ESP32 - Example_GENERIC diff --git a/components/esp_hw_support/CMakeLists.txt b/components/esp_hw_support/CMakeLists.txt index 236f930ec5..40dcb22947 100644 --- a/components/esp_hw_support/CMakeLists.txt +++ b/components/esp_hw_support/CMakeLists.txt @@ -1,6 +1,5 @@ idf_build_get_property(target IDF_TARGET) -set(priv_requires efuse) set(requires soc) set(priv_requires efuse bootloader_support spi_flash) if(${target} STREQUAL "esp32") @@ -17,7 +16,7 @@ if(NOT BOOTLOADER_BUILD) "mac_addr.c" "sleep_modes.c" "regi2c_ctrl.c") - list(APPEND priv_requires esp_ipc) + list(APPEND requires esp_ipc) else() # Requires "_esp_error_check_failed()" function list(APPEND priv_requires "esp_system") diff --git a/components/esp_hw_support/include/soc/esp32/dport_access.h b/components/esp_hw_support/include/soc/esp32/dport_access.h index 4f473940ad..8e04674951 100644 --- a/components/esp_hw_support/include/soc/esp32/dport_access.h +++ b/components/esp_hw_support/include/soc/esp32/dport_access.h @@ -3,30 +3,28 @@ * * SPDX-License-Identifier: Apache-2.0 */ + +#pragma once + #include - #include - -#ifndef _ESP_DPORT_ACCESS_H_ -#define _ESP_DPORT_ACCESS_H_ - #include "xtensa/xtruntime.h" #ifdef __cplusplus extern "C" { #endif -void esp_dport_access_stall_other_cpu_start(void); -void esp_dport_access_stall_other_cpu_end(void); -void esp_dport_access_int_init(void); -void esp_dport_access_int_pause(void); -void esp_dport_access_int_resume(void); +void esp_dport_access_stall_other_cpu_start(void) __attribute__ ((deprecated)); +void esp_dport_access_stall_other_cpu_end(void) __attribute__ ((deprecated)); +void esp_dport_access_int_init(void) __attribute__ ((deprecated)); +void esp_dport_access_int_pause(void) __attribute__ ((deprecated)); +void esp_dport_access_int_resume(void) __attribute__ ((deprecated)); void esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words); uint32_t esp_dport_access_reg_read(uint32_t reg); uint32_t esp_dport_access_sequence_reg_read(uint32_t reg); //This routine does not stop the dport routines in any way that is recoverable. Please //only call in case of panic(). -void esp_dport_access_int_abort(void); +void esp_dport_access_int_abort(void) __attribute__ ((deprecated)); #if defined(BOOTLOADER_BUILD) || !defined(CONFIG_ESP32_DPORT_WORKAROUND) || !defined(ESP_PLATFORM) #define DPORT_STALL_OTHER_CPU_START() @@ -34,8 +32,9 @@ void esp_dport_access_int_abort(void); #define DPORT_INTERRUPT_DISABLE() #define DPORT_INTERRUPT_RESTORE() #else -#define DPORT_STALL_OTHER_CPU_START() esp_dport_access_stall_other_cpu_start() -#define DPORT_STALL_OTHER_CPU_END() esp_dport_access_stall_other_cpu_end() +#include "esp_ipc_isr.h" +#define DPORT_STALL_OTHER_CPU_START() esp_ipc_isr_stall_other_cpu() +#define DPORT_STALL_OTHER_CPU_END() esp_ipc_isr_release_other_cpu() #define DPORT_INTERRUPT_DISABLE() unsigned int intLvl = XTOS_SET_INTLEVEL(CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL) #define DPORT_INTERRUPT_RESTORE() XTOS_RESTORE_JUST_INTLEVEL(intLvl) #endif @@ -43,5 +42,3 @@ void esp_dport_access_int_abort(void); #ifdef __cplusplus } #endif - -#endif /* _ESP_DPORT_ACCESS_H_ */ diff --git a/components/esp_hw_support/port/esp32/dport_access.c b/components/esp_hw_support/port/esp32/dport_access.c index 99aa2cb410..6c5c6ad440 100644 --- a/components/esp_hw_support/port/esp32/dport_access.c +++ b/components/esp_hw_support/port/esp32/dport_access.c @@ -4,202 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -/* - * DPORT access is used for do protection when dual core access DPORT internal register and APB register via DPORT simultaneously - * This function will be initialize after FreeRTOS startup. - * When cpu0 want to access DPORT register, it should notify cpu1 enter in high-priority interrupt for be mute. When cpu1 already in high-priority interrupt, - * cpu0 can access DPORT register. Currently, cpu1 will wait for cpu0 finish access and exit high-priority interrupt. - */ - #include #include - -#include "esp_attr.h" -#include "esp_err.h" -#include "esp_intr_alloc.h" - -#include "soc/cpu.h" +#include #include "soc/dport_reg.h" -#include "soc/spi_periph.h" -#include "hal/cpu_hal.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" - -#include "sdkconfig.h" - -#ifndef CONFIG_FREERTOS_UNICORE -static portMUX_TYPE g_dport_mux = portMUX_INITIALIZER_UNLOCKED; - -#define DPORT_CORE_STATE_IDLE 0 -#define DPORT_CORE_STATE_RUNNING 1 -static uint32_t volatile dport_core_state[portNUM_PROCESSORS]; //cpu is already run - -/* these global variables are accessed from interrupt vector, hence not declared as static */ -uint32_t volatile dport_access_start[portNUM_PROCESSORS]; //dport register could be accessed -uint32_t volatile dport_access_end[portNUM_PROCESSORS]; //dport register is accessed over - -static uint32_t volatile dport_access_ref[portNUM_PROCESSORS]; //dport access reference - -#ifdef DPORT_ACCESS_BENCHMARK -#define DPORT_ACCESS_BENCHMARK_STORE_NUM -static uint32_t ccount_start[portNUM_PROCESSORS]; -static uint32_t ccount_end[portNUM_PROCESSORS]; -static uint32_t ccount_margin[portNUM_PROCESSORS][DPORT_ACCESS_BENCHMARK_STORE_NUM]; -static uint32_t ccount_margin_cnt; -#endif - - -static BaseType_t oldInterruptLevel[2]; -#endif // CONFIG_FREERTOS_UNICORE - -/* stall other cpu that this cpu is pending to access dport register start */ -void IRAM_ATTR esp_dport_access_stall_other_cpu_start(void) -{ -#ifndef CONFIG_FREERTOS_UNICORE - if (dport_core_state[0] == DPORT_CORE_STATE_IDLE - || dport_core_state[1] == DPORT_CORE_STATE_IDLE) { - return; - } - - BaseType_t intLvl = portENTER_CRITICAL_NESTED(); - - int cpu_id = xPortGetCoreID(); - -#ifdef DPORT_ACCESS_BENCHMARK - ccount_start[cpu_id] = cpu_hal_get_cycle_count(); -#endif - - if (dport_access_ref[cpu_id] == 0) { - portENTER_CRITICAL_ISR(&g_dport_mux); - - oldInterruptLevel[cpu_id]=intLvl; - - dport_access_start[cpu_id] = 0; - dport_access_end[cpu_id] = 0; - - if (cpu_id == 0) { - _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_3_REG, DPORT_CPU_INTR_FROM_CPU_3); //interrupt on cpu1 - } else { - _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_2_REG, DPORT_CPU_INTR_FROM_CPU_2); //interrupt on cpu0 - } - - while (!dport_access_start[cpu_id]) {}; - - REG_READ(SPI_DATE_REG(3)); //just read a APB register sure that the APB-bus is idle - } - - dport_access_ref[cpu_id]++; - - if (dport_access_ref[cpu_id] > 1) { - /* Interrupts are already disabled by the parent, we're nested here. */ - portEXIT_CRITICAL_NESTED(intLvl); - } -#endif /* CONFIG_FREERTOS_UNICORE */ -} - -/* stall other cpu that this cpu is pending to access dport register end */ -void IRAM_ATTR esp_dport_access_stall_other_cpu_end(void) -{ -#ifndef CONFIG_FREERTOS_UNICORE - int cpu_id = xPortGetCoreID(); - - if (dport_core_state[0] == DPORT_CORE_STATE_IDLE - || dport_core_state[1] == DPORT_CORE_STATE_IDLE) { - return; - } - - if (dport_access_ref[cpu_id] == 0) { - assert(0); - } - - dport_access_ref[cpu_id]--; - - if (dport_access_ref[cpu_id] == 0) { - dport_access_end[cpu_id] = 1; - - portEXIT_CRITICAL_ISR(&g_dport_mux); - - portEXIT_CRITICAL_NESTED(oldInterruptLevel[cpu_id]); - } - -#ifdef DPORT_ACCESS_BENCHMARK - ccount_end[cpu_id] = cpu_hal_get_cycle_count(); - ccount_margin[cpu_id][ccount_margin_cnt] = ccount_end[cpu_id] - ccount_start[cpu_id]; - ccount_margin_cnt = (ccount_margin_cnt + 1)&(DPORT_ACCESS_BENCHMARK_STORE_NUM - 1); -#endif -#endif /* CONFIG_FREERTOS_UNICORE */ -} - - -#ifndef CONFIG_FREERTOS_UNICORE -static void dport_access_init_core(void *arg) -{ - int core_id = 0; - uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE; - - - core_id = xPortGetCoreID(); - if (core_id == 1) { - intr_source = ETS_FROM_CPU_INTR3_SOURCE; - } - - ESP_INTR_DISABLE(ETS_DPORT_INUM); - intr_matrix_set(core_id, intr_source, ETS_DPORT_INUM); - ESP_INTR_ENABLE(ETS_DPORT_INUM); - - dport_access_ref[core_id] = 0; - dport_access_start[core_id] = 0; - dport_access_end[core_id] = 0; - dport_core_state[core_id] = DPORT_CORE_STATE_RUNNING; - - /* If this fails then the minimum stack size for this config is too close to running out */ - assert(uxTaskGetStackHighWaterMark(NULL) > 128); - - vTaskDelete(NULL); -} -#endif - -/* Defer initialisation until after scheduler is running */ -void esp_dport_access_int_init(void) -{ -#ifndef CONFIG_FREERTOS_UNICORE - portBASE_TYPE res = xTaskCreatePinnedToCore(&dport_access_init_core, "dport", configMINIMAL_STACK_SIZE, NULL, 5, NULL, xPortGetCoreID()); - assert(res == pdTRUE); - (void)res; -#endif -} - -void IRAM_ATTR esp_dport_access_int_pause(void) -{ -#ifndef CONFIG_FREERTOS_UNICORE - portENTER_CRITICAL_ISR(&g_dport_mux); - dport_core_state[0] = DPORT_CORE_STATE_IDLE; - dport_core_state[1] = DPORT_CORE_STATE_IDLE; - portEXIT_CRITICAL_ISR(&g_dport_mux); -#endif -} - -//Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux. -void IRAM_ATTR esp_dport_access_int_abort(void) -{ -#ifndef CONFIG_FREERTOS_UNICORE - dport_core_state[0] = DPORT_CORE_STATE_IDLE; - dport_core_state[1] = DPORT_CORE_STATE_IDLE; -#endif -} - -void IRAM_ATTR esp_dport_access_int_resume(void) -{ -#ifndef CONFIG_FREERTOS_UNICORE - portENTER_CRITICAL_ISR(&g_dport_mux); - dport_core_state[0] = DPORT_CORE_STATE_RUNNING; - dport_core_state[1] = DPORT_CORE_STATE_RUNNING; - portEXIT_CRITICAL_ISR(&g_dport_mux); -#endif -} +#include "xtensa/core-macros.h" /** * @brief Read a sequence of DPORT registers to the buffer, SMP-safe version. diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 92870b6641..3683858a72 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -24,6 +24,7 @@ #include "esp_log.h" #include "esp_newlib.h" #include "esp_timer.h" +#include "esp_ipc_isr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "soc/soc_caps.h" @@ -701,7 +702,7 @@ esp_err_t esp_light_sleep_start(void) uint64_t frc_time_at_start = esp_system_get_time(); uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); - DPORT_STALL_OTHER_CPU_START(); + esp_ipc_isr_stall_other_cpu(); // Decide which power domains can be powered down uint32_t pd_flags = get_power_down_flags(); @@ -825,7 +826,7 @@ esp_err_t esp_light_sleep_start(void) esp_set_time_from_rtc(); esp_timer_private_unlock(); - DPORT_STALL_OTHER_CPU_END(); + esp_ipc_isr_release_other_cpu(); if (!wdt_was_enabled) { wdt_hal_write_protect_disable(&rtc_wdt_ctx); wdt_hal_disable(&rtc_wdt_ctx); diff --git a/components/esp_ipc/CMakeLists.txt b/components/esp_ipc/CMakeLists.txt index ecfdaceda4..b164a765d0 100644 --- a/components/esp_ipc/CMakeLists.txt +++ b/components/esp_ipc/CMakeLists.txt @@ -1,2 +1,10 @@ -idf_component_register(SRCS "ipc.c" +set(srcs "src/esp_ipc.c") + +if(CONFIG_ESP_IPC_ISR_ENABLE) + list(APPEND srcs "src/esp_ipc_isr/esp_ipc_isr.c" + "src/esp_ipc_isr/esp_ipc_isr_handler.S" + "src/esp_ipc_isr/esp_ipc_isr_routines.S") +endif() + +idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "include") diff --git a/components/esp_ipc/Kconfig b/components/esp_ipc/Kconfig new file mode 100644 index 0000000000..b97f978007 --- /dev/null +++ b/components/esp_ipc/Kconfig @@ -0,0 +1,39 @@ +menu "IPC (Inter-Processor Call)" + + config ESP_IPC_TASK_STACK_SIZE + int "Inter-Processor Call (IPC) task stack size" + range 512 65536 if !APPTRACE_ENABLE + range 2048 65536 if APPTRACE_ENABLE + default 2048 if APPTRACE_ENABLE + default 1024 + help + Configure the IPC tasks stack size. One IPC task runs on each core + (in dual core mode), and allows for cross-core function calls. + + See IPC documentation for more details. + + The default stack size should be enough for most common use cases. + It can be shrunk if you are sure that you do not use any custom + IPC functionality. + + config ESP_IPC_USES_CALLERS_PRIORITY + bool "IPC runs at caller's priority" + default y + depends on !FREERTOS_UNICORE + help + If this option is not enabled then the IPC task will keep behavior + same as prior to that of ESP-IDF v4.0, and hence IPC task will run + at (configMAX_PRIORITIES - 1) priority. + + config ESP_IPC_ISR_ENABLE + bool + default y if !FREERTOS_UNICORE + help + This feature servers a similar purpose to the IPC except that the callback function is run + in the context of a level 4 interrupt (i.e., high priority/level interrupt). The IPC ISR + feature is intended for low latency execution of simple functions written in assembly on + another CPU. Due to being run in higher level interrupt context, the assembly functions + should be written in a particular way (see esp_test_ipc_isr_asm() and the "High-Level Interrupts" + chapter in hlinterrupts.rst for more details). + +endmenu # "IPC (Inter-Processor Call) diff --git a/components/esp_ipc/component.mk b/components/esp_ipc/component.mk index ebd7a7d59b..4e2e89bb30 100644 --- a/components/esp_ipc/component.mk +++ b/components/esp_ipc/component.mk @@ -1,3 +1,9 @@ # # Component Makefile # + +COMPONENT_SRCDIRS := src +ifdef CONFIG_ESP_IPC_ISR_ENABLE + COMPONENT_SRCDIRS += src/esp_ipc_isr +endif +COMPONENT_ADD_INCLUDEDIRS := include diff --git a/components/esp_ipc/include/esp_ipc.h b/components/esp_ipc/include/esp_ipc.h index 477b3d0af4..40914a02a7 100644 --- a/components/esp_ipc/include/esp_ipc.h +++ b/components/esp_ipc/include/esp_ipc.h @@ -20,6 +20,9 @@ #ifdef __cplusplus extern "C" { #endif + +#ifndef CONFIG_FREERTOS_UNICORE + /** @cond */ typedef void (*esp_ipc_func_t)(void* arg); /** @endcond */ @@ -85,6 +88,7 @@ esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void* arg); */ esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg); +#endif // not CONFIG_FREERTOS_UNICORE #ifdef __cplusplus } diff --git a/components/esp_ipc/include/esp_ipc_isr.h b/components/esp_ipc/include/esp_ipc_isr.h new file mode 100644 index 0000000000..7b6e060f1d --- /dev/null +++ b/components/esp_ipc/include/esp_ipc_isr.h @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_ESP_IPC_ISR_ENABLE + +/** @cond */ +typedef void (*esp_ipc_isr_func_t)(void* arg); +/** @endcond */ + +/** + * @brief Initialize inter-processor call module which based on #4 high-interrupt. + * + * This function is called on CPU start and should not be called from the application. + * + * This function starts two tasks, one on each CPU. These tasks register + * #4 High-interrupt and after that, the tasks are deleted. + * The next API functions work with this functionality: + * esp_ipc_isr_asm_call + * esp_ipc_isr_asm_call_blocking + * They allow to run an asm function on other CPU. + */ +void esp_ipc_isr_init(void); + +/** + * @brief Execute an asm function on the other CPU (uses the #4 high-priority interrupt) + * + * @note In single-core mode, it is not available. + * This function calls the #4 high-priority interrupt on the other CPU. + * The given function is called in the context of the interrupt by CALLX0 command and + * operates with registers a2, a3, a4. + * + * @param[in] func Pointer to a function of type void func(void* arg) to be executed + * @param[in] arg Arbitrary argument of type void* to be passed into the function + */ +void esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg); + +/** + * @brief Execute an asm function on the other CPU and blocks until it completes (uses the #4 high-priority interrupt) + * + * @note In single-core mode, it is not available. + * This function calls the #4 high-priority interrupt on the other CPU. + * The given function is called in the context of the interrupt by CALLX0 command. + * + * @param[in] func Pointer to a function of type void func(void* arg) to be executed + * @param[in] arg Arbitrary argument of type void* to be passed into the function + */ +void esp_ipc_isr_asm_call_blocking(esp_ipc_isr_func_t func, void* arg); + +/** + * @brief Stall the other CPU and the current CPU disables interrupts with level 3 and lower. + * + * @note In single-core mode, it is not available. + * This function calls the #4 high-priority interrupt on the other CPU. + * The esp_ipc_isr_finish_cmd() function is called on the other CPU in the context of the #4 high-priority interrupt. + * The esp_ipc_isr_finish_cmd is called by CALLX0 command. + * It is waiting for the end command. The command will be sent by esp_ipc_isr_release_other_cpu(). + * This function is used for DPORT workaround. + * + * This function blocks other CPU until the release call esp_ipc_isr_release_other_cpu(). + * + * This fucntion is used for the DPORT workaround: stall other cpu that this cpu is pending to access dport register start. + */ +void esp_ipc_isr_stall_other_cpu(void); + +/** + * @brief Release the other CPU + * + * @note In single-core mode, it is not available. + * This function will send the end command to release the stall other CPU. + * This function is used for DPORT workaround: stall other cpu that this cpu is pending to access dport register end. + * + */ +void esp_ipc_isr_release_other_cpu(void); + +/** + * @brief Pause stall the other CPU + */ +void esp_ipc_isr_stall_pause(void); + +/** + * @brief Abort stall the other CPU + * + * This routine does not stop the stall routines in any way that is recoverable. + * Please only call in case of panic(). + * Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux. + */ +void esp_ipc_isr_stall_abort(void); + +/** + * @brief Resume stall the other CPU + */ +void esp_ipc_isr_stall_resume(void); + +#else // not CONFIG_ESP_IPC_ISR_ENABLE + +#define esp_ipc_isr_stall_other_cpu() +#define esp_ipc_isr_release_other_cpu() +#define esp_ipc_isr_stall_pause() +#define esp_ipc_isr_stall_abort() +#define esp_ipc_isr_stall_resume() + +#endif // CONFIG_ESP_IPC_ISR_ENABLE + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_ipc/ipc.c b/components/esp_ipc/src/esp_ipc.c similarity index 97% rename from components/esp_ipc/ipc.c rename to components/esp_ipc/src/esp_ipc.c index 7d63d6f87f..fc734a11ae 100644 --- a/components/esp_ipc/ipc.c +++ b/components/esp_ipc/src/esp_ipc.c @@ -18,12 +18,15 @@ #include #include "esp_err.h" #include "esp_ipc.h" +#include "esp_ipc_isr.h" #include "esp_attr.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +#ifndef CONFIG_FREERTOS_UNICORE + static TaskHandle_t s_ipc_task_handle[portNUM_PROCESSORS]; static SemaphoreHandle_t s_ipc_mutex[portNUM_PROCESSORS]; // This mutex is used as a global lock for esp_ipc_* APIs static SemaphoreHandle_t s_ipc_sem[portNUM_PROCESSORS]; // Two semaphores used to wake each of ipc tasks @@ -86,6 +89,9 @@ static void esp_ipc_init(void) __attribute__((constructor)); static void esp_ipc_init(void) { +#ifdef CONFIG_ESP_IPC_ISR_ENABLE + esp_ipc_isr_init(); +#endif char task_name[15]; for (int i = 0; i < portNUM_PROCESSORS; ++i) { snprintf(task_name, sizeof(task_name), "ipc%d", i); @@ -144,3 +150,5 @@ esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg) { return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_END); } + +#endif // not CONFIG_FREERTOS_UNICORE diff --git a/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr.c b/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr.c new file mode 100644 index 0000000000..a893713e8b --- /dev/null +++ b/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr.c @@ -0,0 +1,215 @@ +/* + * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include "esp_err.h" +#include "esp_attr.h" +#include "soc/cpu.h" +#include "soc/soc.h" +#include "soc/dport_access.h" +#ifdef CONFIG_IDF_TARGET_ESP32 +#include "soc/dport_reg.h" +#else +#include "soc/periph_defs.h" +#include "soc/system_reg.h" +#endif +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/portmacro.h" +#include "esp_intr_alloc.h" +#include "esp_ipc_isr.h" +#include "xtensa/core-macros.h" +#include "sdkconfig.h" + +static portMUX_TYPE s_ipc_isr_mux = portMUX_INITIALIZER_UNLOCKED; +uint32_t volatile esp_ipc_isr_start_fl; // the flag shows that it is about to run esp_ipc_func() +uint32_t volatile esp_ipc_isr_end_fl = 1; // the flag shows that esp_ipc_func() is done +esp_ipc_isr_func_t volatile esp_ipc_func; // the function which will be run in the ipc_isr context +void * volatile esp_ipc_func_arg; // the argument of esp_ipc_func() + +typedef enum { + STALL_STATE_IDLE = 0, + STALL_STATE_RUNNING = 1, +} stall_state_t; + +static stall_state_t volatile s_stall_state = STALL_STATE_IDLE; +static int32_t volatile s_count_of_nested_calls[portNUM_PROCESSORS] = { 0 }; +static BaseType_t s_stored_interrupt_level; +static uint32_t volatile esp_ipc_isr_finish_cmd; + + +/** + * @brief Type of calling + */ +typedef enum { + IPC_ISR_WAIT_FOR_START = 0, /*!< The caller is waiting for the start */ + IPC_ISR_WAIT_FOR_END = 1, /*!< The caller is waiting for the end */ +} esp_ipc_isr_wait_t; + +#define IPC_ISR_ENTER_CRITICAL() portENTER_CRITICAL_SAFE(&s_ipc_isr_mux) +#define IPC_ISR_EXIT_CRITICAL() portEXIT_CRITICAL_SAFE(&s_ipc_isr_mux) + +static void esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ipc_isr_wait_t wait_for); + + +/* Initializing IPC_ISR */ + +static void esp_ipc_isr_init_cpu(void* arg) +{ + (void) arg; + const uint32_t cpuid = xPortGetCoreID(); + uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE + cpuid; // ETS_FROM_CPU_INTR2_SOURCE and ETS_FROM_CPU_INTR3_SOURCE + ESP_INTR_DISABLE(ETS_IPC_ISR_INUM); + intr_matrix_set(cpuid, intr_source, ETS_IPC_ISR_INUM); + ESP_INTR_ENABLE(ETS_IPC_ISR_INUM); + + /* If this fails then the minimum stack size for this config is too close to running out */ + assert(uxTaskGetStackHighWaterMark(NULL) > 128); + + if (cpuid != 0) { + s_stall_state = STALL_STATE_RUNNING; + } + vTaskDelete(NULL); +} + +void esp_ipc_isr_init(void) +{ + for (unsigned i = 0; i < portNUM_PROCESSORS; ++i) { + portBASE_TYPE res = xTaskCreatePinnedToCore(esp_ipc_isr_init_cpu, "ipc_isr_init", configMINIMAL_STACK_SIZE, NULL, 5, NULL, i); + assert(res == pdTRUE); + (void)res; + } +} + +/* End initializing IPC_ISR */ + + +/* Public API functions */ + +void IRAM_ATTR esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg) +{ + IPC_ISR_ENTER_CRITICAL(); + esp_ipc_isr_call_and_wait(func, arg, IPC_ISR_WAIT_FOR_START); + IPC_ISR_EXIT_CRITICAL(); +} + +void IRAM_ATTR esp_ipc_isr_asm_call_blocking(esp_ipc_isr_func_t func, void* arg) +{ + IPC_ISR_ENTER_CRITICAL(); + esp_ipc_isr_call_and_wait(func, arg, IPC_ISR_WAIT_FOR_END); + IPC_ISR_EXIT_CRITICAL(); +} + +// This asm function is from esp_ipc_isr_routines.S. +// It is waiting for the finish_cmd command in a loop. +void esp_ipc_isr_waiting_for_finish_cmd(void* finish_cmd); + +/* + * esp_ipc_isr_stall_other_cpu is used for: + * - stall other CPU, + * - do protection when dual core access DPORT internal register and APB register via DPORT simultaneously. + * This function will be initialize after FreeRTOS startup. + * When cpu0 wants to access DPORT register, it should notify cpu1 enter in high-priority interrupt for be mute. + * When cpu1 already in high-priority interrupt, cpu0 can access DPORT register. + * Currently, cpu1 will wait for cpu0 finish access and exit high-priority interrupt. + */ +void IRAM_ATTR esp_ipc_isr_stall_other_cpu(void) +{ + if (s_stall_state == STALL_STATE_RUNNING) { + BaseType_t intLvl = portENTER_CRITICAL_NESTED(); + const uint32_t cpu_id = xPortGetCoreID(); + if (s_count_of_nested_calls[cpu_id]++ == 0) { + IPC_ISR_ENTER_CRITICAL(); + s_stored_interrupt_level = intLvl; + esp_ipc_isr_finish_cmd = 0; + esp_ipc_isr_call_and_wait(&esp_ipc_isr_waiting_for_finish_cmd, (void*)&esp_ipc_isr_finish_cmd, IPC_ISR_WAIT_FOR_START); + return; + } + + /* Interrupts are already disabled by the parent, we're nested here. */ + portEXIT_CRITICAL_NESTED(intLvl); + } +} + +void IRAM_ATTR esp_ipc_isr_release_other_cpu(void) +{ + if (s_stall_state == STALL_STATE_RUNNING) { + const uint32_t cpu_id = xPortGetCoreID(); + if (--s_count_of_nested_calls[cpu_id] == 0) { + esp_ipc_isr_finish_cmd = 1; + IPC_ISR_EXIT_CRITICAL(); + portEXIT_CRITICAL_NESTED(s_stored_interrupt_level); + } else if (s_count_of_nested_calls[cpu_id] < 0) { + assert(0); + } + } +} + +void IRAM_ATTR esp_ipc_isr_stall_pause(void) +{ + IPC_ISR_ENTER_CRITICAL(); + s_stall_state = STALL_STATE_IDLE; + IPC_ISR_EXIT_CRITICAL(); +} + +void IRAM_ATTR esp_ipc_isr_stall_abort(void) +{ + s_stall_state = STALL_STATE_IDLE; +} + +void IRAM_ATTR esp_ipc_isr_stall_resume(void) +{ + IPC_ISR_ENTER_CRITICAL(); + s_stall_state = STALL_STATE_RUNNING; + IPC_ISR_EXIT_CRITICAL(); +} + +void esp_dport_access_stall_other_cpu_start(void) __attribute__((alias("esp_ipc_isr_stall_other_cpu"))); +void esp_dport_access_stall_other_cpu_end(void) __attribute__((alias("esp_ipc_isr_release_other_cpu"))); +void esp_dport_access_int_pause(void) __attribute__((alias("esp_ipc_isr_stall_pause"))); +void esp_dport_access_int_abort(void) __attribute__((alias("esp_ipc_isr_stall_abort"))); +void esp_dport_access_int_resume(void) __attribute__((alias("esp_ipc_isr_stall_resume"))); + +/* End public API functions */ + + +/* Private functions*/ + +static void IRAM_ATTR esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ipc_isr_wait_t wait_for) +{ + const uint32_t cpu_id = xPortGetCoreID(); + + // waiting for the end of the previous call + while (!esp_ipc_isr_end_fl) {}; + + esp_ipc_func = func; + esp_ipc_func_arg = arg; + + esp_ipc_isr_start_fl = 0; + esp_ipc_isr_end_fl = 0; + + if (cpu_id == 0) { + // it runs an interrupt on cpu1 + DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_3_REG, SYSTEM_CPU_INTR_FROM_CPU_3); + } else { + // it runs an interrupt on cpu0 + DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_2_REG, SYSTEM_CPU_INTR_FROM_CPU_2); + } + + // IPC_ISR handler will be called and `...isr_start` and `...isr_end` will be updated there + + if (wait_for == IPC_ISR_WAIT_FOR_START) { + while (!esp_ipc_isr_start_fl) {}; + } else { + // IPC_ISR_WAIT_FOR_END + while (!esp_ipc_isr_end_fl) {}; + } +} + +/* End private functions*/ diff --git a/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_handler.S b/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_handler.S new file mode 100644 index 0000000000..c58ee7567e --- /dev/null +++ b/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_handler.S @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "freertos/xtensa_context.h" +#include "esp_private/panic_reason.h" +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/dport_reg.h" + +/* 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 + .data +_l4_intr_stack: + .space L4_INTR_STACK_SIZE + .section .iram1,"ax" + .global esp_ipc_isr_handler + .type esp_ipc_isr_handler,@function + .align 4 +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 + interrupt at one time, there's no need to have two + _l4_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 + + /* disable nested iterrupts */ + /* PS.EXCM is changed from 1 to 0 . It allows using usually exception handler instead of the Double exception handler. */ + /* PS_UM = 1 */ + movi a0, PS_INTLEVEL(5) | PS_UM + wsr a0, PS + rsync + /* restore PS will be done by rfi the end */ + + /* + * Reset isr interrupt flags + */ + /* This int is edge-triggered and needs clearing. */ + movi a3, (1 << ETS_IPC_ISR_INUM) + wsr a3, INTCLEAR + + /* get CORE_ID */ + getcoreid a3 + beqz a3, 1f + + /* current cpu is 1 */ + movi a3, SYSTEM_CPU_INTR_FROM_CPU_3_REG + movi a4, 0 + s32i a4, a3, 0 /* clear intr */ + j 2f +1: + /* current cpu is 0 */ + movi a3, SYSTEM_CPU_INTR_FROM_CPU_2_REG + movi a4, 0 + s32i a4, a3, 0 /* clear intr */ +2: + + /* set the start flag */ + movi a0, esp_ipc_isr_start_fl + s32i a0, a0, 0 + + /* Call the esp_ipc_function(void* arg) */ + movi a0, esp_ipc_func + l32i a0, a0, 0 + movi a2, esp_ipc_func_arg + l32i a2, a2, 0 + 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 + + /* 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 diff --git a/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_routines.S b/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_routines.S new file mode 100644 index 0000000000..77b6b0406b --- /dev/null +++ b/components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_routines.S @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/* esp_ipc_isr_waiting_for_finish_cmd(void* finish_cmd) + * + * It should be called by the CALLX0 command from the handler of High-priority interrupt (4 lvl). + * Only these registers [a2, a3, a4] can be used here. + */ + .section .iram1, "ax" + .align 4 + .global esp_ipc_isr_waiting_for_finish_cmd + .type esp_ipc_isr_waiting_for_finish_cmd, @function +// Args: +// a2 - finish_cmd (pointer on esp_ipc_isr_finish_cmd) +esp_ipc_isr_waiting_for_finish_cmd: + /* waiting for the finish command */ +.check_finish_cmd: + l32i a3, a2, 0 + beqz a3, .check_finish_cmd + ret diff --git a/components/esp_ipc/test/CMakeLists.txt b/components/esp_ipc/test/CMakeLists.txt index 24591e199b..7a1793d816 100644 --- a/components/esp_ipc/test/CMakeLists.txt +++ b/components/esp_ipc/test/CMakeLists.txt @@ -1,4 +1,4 @@ -if(IDF_TARGET STREQUAL "esp32") +if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s3") idf_component_register(SRC_DIRS "." PRIV_INCLUDE_DIRS "." PRIV_REQUIRES cmock test_utils esp_ipc) diff --git a/components/esp_ipc/test/component.mk b/components/esp_ipc/test/component.mk index ce464a212a..ed5fa25e2e 100644 --- a/components/esp_ipc/test/component.mk +++ b/components/esp_ipc/test/component.mk @@ -1 +1,2 @@ +COMPONENT_SRCDIRS := . COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/esp_ipc/test/test_ipc_isr.S b/components/esp_ipc/test/test_ipc_isr.S new file mode 100644 index 0000000000..a448e38997 --- /dev/null +++ b/components/esp_ipc/test/test_ipc_isr.S @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/* esp_test_ipc_isr_asm(void *arg) + * + * It should be called by the CALLX0 command from the handler of High-priority interrupt (4 lvl). + * Only these registers [a2, a3, a4] can be used here. + */ + .section .iram1, "ax" + .align 4 + .global esp_test_ipc_isr_asm + .type esp_test_ipc_isr_asm, @function +// Args: +// a2 - void* arg +esp_test_ipc_isr_asm: + movi a3, 0xa5a5 + s32i a3, a2, 0 + ret + + +/* esp_test_ipc_isr_get_other_core_id(void *arg) + * + * this function puts the core_id of the other CPU in the arg. + * use only a2, a3 and a4 regs here. +*/ + .section .iram1, "ax" + .align 4 + .global esp_test_ipc_isr_get_other_core_id + .type esp_test_ipc_isr_get_other_core_id, @function + // Args: + // a2 - void* arg + esp_test_ipc_isr_get_other_core_id: + rsr.prid a3 + extui a3, a3, 13, 1 + s32i a3, a2, 0 + ret + + +/* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) + * + * this function puts CCOUNT of the other CPU in the arg. + * use only a2, a3 and a4 regs here. +*/ + .section .iram1, "ax" + .align 4 + .global esp_test_ipc_isr_get_cycle_count_other_cpu + .type esp_test_ipc_isr_get_cycle_count_other_cpu, @function + // Args: + // a2 - void* arg + esp_test_ipc_isr_get_cycle_count_other_cpu: + rsr.ccount a3 + s32i a3, a2, 0 + ret diff --git a/components/esp_ipc/test/test_ipc_isr.c b/components/esp_ipc/test/test_ipc_isr.c new file mode 100644 index 0000000000..cad29e814c --- /dev/null +++ b/components/esp_ipc/test/test_ipc_isr.c @@ -0,0 +1,85 @@ +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "unity.h" +#include "test_utils.h" +#include "esp_rom_sys.h" +#include "esp_ipc_isr.h" + +#ifdef CONFIG_ESP_IPC_ISR_ENABLE + +void esp_test_ipc_isr_asm(void* arg); + +TEST_CASE("Test ipc_isr blocking IPC function calls a ASM function", "[ipc]") +{ + int val = 0x5a5a; + esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, &val); + TEST_ASSERT_EQUAL_HEX(val, 0xa5a5); +} + +void esp_test_ipc_isr_get_other_core_id(void* arg); + + +TEST_CASE("Test ipc_isr blocking IPC function calls get_other_core_id", "[ipc]") +{ + int val = 0x5a5a; + esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_get_other_core_id, &val); + TEST_ASSERT_EQUAL_HEX(val, 1); +} + +TEST_CASE("Test ipc_isr exception in asm func leads to StoreProhibited not to Unhandled debug exception", "[ipc][reset=StoreProhibited,SW_CPU_RESET]") +{ + esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, NULL); +} + +void esp_test_ipc_isr_get_cycle_count_other_cpu(void* arg); + +TEST_CASE("Test ipc_isr blocking IPC function calls get_cycle_count_other_cpu", "[ipc]") +{ + int val = 0x5a5a; + esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, &val); + esp_rom_printf("CCOUNT CPU0 = %d\n", cpu_ll_get_cycle_count()); + esp_rom_printf("CCOUNT CPU1 = %d\n", val); +} + +static bool volatile s_stop; + +static void task_asm(void *arg) +{ + xSemaphoreHandle *sema = (xSemaphoreHandle *) arg; + int val; + int counter = 0; + printf("task_asm\n"); + while (s_stop == false) { + val = 0x5a5a; + esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, &val); + TEST_ASSERT_EQUAL_HEX(val, 0xa5a5); + ++counter; + } + printf("task_asm counter = %d\n", counter); + TEST_ASSERT_GREATER_THAN(1000000, counter); + xSemaphoreGive(*sema); + vTaskDelete(NULL); +} + +TEST_CASE("Test ipc_isr two tasks use IPC function calls", "[ipc]") +{ + xSemaphoreHandle exit_sema[2]; + exit_sema[0] = xSemaphoreCreateBinary(); + exit_sema[1] = xSemaphoreCreateBinary(); + s_stop = false; + printf("Test start\n"); + xTaskCreatePinnedToCore(task_asm, "task_asm", 2048, &exit_sema[0], UNITY_FREERTOS_PRIORITY - 1, NULL, 0); + xTaskCreatePinnedToCore(task_asm, "task_asm", 2048, &exit_sema[1], UNITY_FREERTOS_PRIORITY - 1, NULL, 1); + vTaskDelay(5000 / portTICK_PERIOD_MS); + s_stop = true; + xSemaphoreTake(exit_sema[0], portMAX_DELAY); + xSemaphoreTake(exit_sema[1], portMAX_DELAY); + printf("Test end\n"); + + vSemaphoreDelete(exit_sema[0]); + vSemaphoreDelete(exit_sema[1]); +} +#endif /* CONFIG_ESP_IPC_ISR_ENABLE */ diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index b99447fbc3..a8a9d9e5c5 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -38,7 +38,7 @@ else() # should be removable once using component init functions # link-time registration is used. esp_pm app_update nvs_flash pthread app_trace esp_gdbstub - espcoredump esp_phy efuse + espcoredump esp_phy efuse esp_ipc LDFRAGMENTS "linker.lf" "app.lf") add_subdirectory(port) diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index 509fbbb4aa..bb3988c495 100644 --- a/components/esp_system/Kconfig +++ b/components/esp_system/Kconfig @@ -389,31 +389,6 @@ menu "ESP System Settings" If this option is enabled, the Task Wtachdog Timer will wach the CPU1 Idle Task. - config ESP_IPC_TASK_STACK_SIZE - int "Inter-Processor Call (IPC) task stack size" - range 512 65536 if !APPTRACE_ENABLE - range 2048 65536 if APPTRACE_ENABLE - default 2048 if APPTRACE_ENABLE - default 1024 - help - Configure the IPC tasks stack size. One IPC task runs on each core - (in dual core mode), and allows for cross-core function calls. - - See IPC documentation for more details. - - The default stack size should be enough for most common use cases. - It can be shrunk if you are sure that you do not use any custom - IPC functionality. - - config ESP_IPC_USES_CALLERS_PRIORITY - bool "IPC runs at caller's priority" - default y - depends on !FREERTOS_UNICORE - help - If this option is not enabled then the IPC task will keep behavior - same as prior to that of ESP-IDF v4.0, and hence IPC task will run - at (configMAX_PRIORITIES - 1) priority. - config ESP_PANIC_HANDLER_IRAM bool "Place panic handler code in IRAM" default n diff --git a/components/esp_system/port/panic_handler.c b/components/esp_system/port/panic_handler.c index d8993f8721..58c27e76dc 100644 --- a/components/esp_system/port/panic_handler.c +++ b/components/esp_system/port/panic_handler.c @@ -14,7 +14,7 @@ #include #include "esp_spi_flash.h" - +#include "esp_ipc_isr.h" #include "esp_private/system_internal.h" #include "soc/soc_memory_layout.h" @@ -165,9 +165,7 @@ static void panic_handler(void *frame, bool pseudo_excause) SOC_HAL_STALL_OTHER_CORES(); #endif -#if CONFIG_IDF_TARGET_ESP32 - esp_dport_access_int_abort(); -#endif + esp_ipc_isr_stall_abort(); if (esp_cpu_in_ocd_debug_mode()) { #if __XTENSA__ @@ -205,9 +203,7 @@ static void IRAM_ATTR panic_enable_cache(void) { int core_id = cpu_hal_get_core_id(); if (!spi_flash_cache_enabled()) { -#ifdef CONFIG_IDF_TARGET_ESP32 - esp_dport_access_int_abort(); -#endif + esp_ipc_isr_stall_abort(); spi_flash_enable_cache(core_id); } } diff --git a/components/esp_system/port/soc/esp32/CMakeLists.txt b/components/esp_system/port/soc/esp32/CMakeLists.txt index 570dbc4b97..e66f3d9234 100644 --- a/components/esp_system/port/soc/esp32/CMakeLists.txt +++ b/components/esp_system/port/soc/esp32/CMakeLists.txt @@ -1,4 +1,4 @@ -set(srcs "dport_panic_highint_hdl.S" +set(srcs "highint_hdl.S" "clk.c" "reset_reason.c" "system_internal.c" @@ -15,7 +15,7 @@ add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs}) target_sources(${COMPONENT_LIB} PRIVATE ${srcs}) -#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the +#ld_include_highint_hdl is added as an undefined symbol because otherwise the #linker will ignore panic_highint_hdl.S as it has no other files depending on any #symbols in it. -set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_panic_highint_hdl") +set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_highint_hdl") diff --git a/components/esp_system/port/soc/esp32/dport_panic_highint_hdl.S b/components/esp_system/port/soc/esp32/highint_hdl.S similarity index 84% rename from components/esp_system/port/soc/esp32/dport_panic_highint_hdl.S rename to components/esp_system/port/soc/esp32/highint_hdl.S index 35e7508b05..02316f612d 100644 --- a/components/esp_system/port/soc/esp32/dport_panic_highint_hdl.S +++ b/components/esp_system/port/soc/esp32/highint_hdl.S @@ -27,7 +27,7 @@ /* Interrupt , a high-priority interrupt, is used for several things: -- Dport access mediation +- IPC_ISR handler - Cache error panic handler - Interrupt watchdog panic handler @@ -64,11 +64,11 @@ _l4_intr_livelock_pro: xt_highint4: #ifndef CONFIG_FREERTOS_UNICORE - /* See if we're here for the dport access interrupt */ + /* See if we're here for the IPC_ISR interrupt */ rsr a0, INTERRUPT - extui a0, a0, ETS_DPORT_INUM, 1 - bnez a0, .handle_dport_access_int -#endif // CONFIG_FREERTOS_UNICORE + extui a0, a0, ETS_IPC_ISR_INUM, 1 + bnez a0, esp_ipc_isr_handler +#endif // not CONFIG_FREERTOS_UNICORE #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT /* See if we're here for the tg1 watchdog interrupt */ @@ -382,7 +382,7 @@ xt_highint4: /* Feed watchdog */ 8: wdt_feed TIMERG1 -9: wsr a0, PS /* restore iterrupt level */ +9: wsr a0, PS /* restore interrupt level */ movi a0, 0 beqz a5, 1f @@ -415,75 +415,9 @@ xt_highint4: #endif - -#ifndef CONFIG_FREERTOS_UNICORE - - .align 4 -.handle_dport_access_int: - /* This section is for dport access register protection */ - /* Allocate exception frame and save minimal context. */ - /* Because the interrupt cause code has protection that only - allows one cpu to enter in the dport section of the L4 - interrupt at one time, there's no need to have two - _l4_intr_stack for each cpu */ - - /* This int is edge-triggered and needs clearing. */ - movi a0, (1< None + + dut = env.get_dut('ipc_isr', 'examples/system/ipc/ipc_isr') + dut.start_app() + + dut.expect_all('example: Start', + 'example: PS_INTLEVEL = 0x5', + 'example: PS_EXCM = 0x0', + 'example: PS_UM = 0x1', + 'example: in[0] = 0x1', + 'example: in[1] = 0x2', + 'example: in[2] = 0x3', + 'example: out[0] = (in[0] | in[1] | in[2]) = 0x3', + 'example: out[1] = (in[0] & in[1] & in[2]) = 0x6', + 'example: out[2] = in[2] = 0x3', + 'example: out[3] = PS of other cpu = 0x25', + 'example: End', + timeout=10) + + +if __name__ == '__main__': + test_examples_ipc_isr() diff --git a/examples/system/ipc/ipc_isr/main/CMakeLists.txt b/examples/system/ipc/ipc_isr/main/CMakeLists.txt new file mode 100644 index 0000000000..2082ac3744 --- /dev/null +++ b/examples/system/ipc/ipc_isr/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" + "asm_funcs.S" + INCLUDE_DIRS ".") diff --git a/examples/system/ipc/ipc_isr/main/asm_funcs.S b/examples/system/ipc/ipc_isr/main/asm_funcs.S new file mode 100644 index 0000000000..49c1b916cb --- /dev/null +++ b/examples/system/ipc/ipc_isr/main/asm_funcs.S @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/* get_ps_other_cpu(void *arg) + * + * It should be called by the CALLX0 command from the handler of High-priority interrupt (4 lvl). + * Only these registers [a2, a3, a4] can be used here. + * Returns PS. + */ + .section .iram1, "ax" + .align 4 + .global get_ps_other_cpu + .type get_ps_other_cpu, @function +// Args: +// a2 - void* arg +get_ps_other_cpu: + rsr a3, PS + s32i a3, a2, 0 + ret + + +/* extended_ipc_isr_asm(void *arg) + * + * It should be called by the CALLX0 command from the handler of High-priority interrupt (4 lvl). + * Only these registers [a2, a3, a4] can be used here. + * This function receives a structure (arg) where can be saved some regs + * to get them available here, at the end of the function we recover the saved regs. + */ + .section .iram1, "ax" + .align 4 + .global extended_ipc_isr_asm + .type extended_ipc_isr_asm, @function + // Args: + // a2 - arg_data_t* arg +extended_ipc_isr_asm: + + /* save all registers (a5-a15 -> regs[11]) */ + s32i a5, a2, 0 + s32i a6, a2, 4 + s32i a7, a2, 8 + s32i a8, a2, 12 + s32i a9, a2, 16 + s32i a10, a2, 20 + s32i a11, a2, 24 + s32i a12, a2, 28 + s32i a13, a2, 32 + s32i a14, a2, 36 + s32i a15, a2, 40 + + /* do some work with a2 - a15 */ + l32i a5, a2, 44 /* a5 <- in[0] */ + l32i a6, a2, 48 /* a6 <- in[1] */ + l32i a7, a2, 52 /* a7 <- in[2] */ + + or a8, a5, a6 + or a8, a8, a7 + + add a9, a5, a6 + add a9, a9, a7 + + mov a10, a7 + + rsr a11, PS + s32i a8, a2, 56 /* a8 -> out[0] */ + s32i a9, a2, 60 /* a9 -> out[1] */ + s32i a10, a2, 64 /* a10 -> out[2] */ + s32i a11, a2, 68 /* a11 -> out[3] */ + + /* restore all saved registers (regs[11] -> a5-a15) */ + l32i a5, a2, 0 + l32i a6, a2, 4 + l32i a7, a2, 8 + l32i a8, a2, 12 + l32i a9, a2, 16 + l32i a10, a2, 20 + l32i a11, a2, 24 + l32i a12, a2, 28 + l32i a13, a2, 32 + l32i a14, a2, 36 + l32i a15, a2, 40 + ret diff --git a/examples/system/ipc/ipc_isr/main/component.mk b/examples/system/ipc/ipc_isr/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/system/ipc/ipc_isr/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/system/ipc/ipc_isr/main/main.c b/examples/system/ipc/ipc_isr/main/main.c new file mode 100644 index 0000000000..52fbcc0f9f --- /dev/null +++ b/examples/system/ipc/ipc_isr/main/main.c @@ -0,0 +1,61 @@ +/* ipc_isr example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include "esp_timer.h" +#include "esp_log.h" +#include "esp_ipc_isr.h" +#include "sdkconfig.h" +#if __XTENSA__ +#include "xtensa/config/core.h" +#else +#error "Doesn't support other architectures" +#endif + +static const char* TAG = "example"; + +typedef struct { + uint32_t regs[11]; + uint32_t in[3]; + uint32_t out[4]; +} arg_data_t; + +void get_ps_other_cpu(void* arg); +void extended_ipc_isr_asm(void* arg); + +void app_main(void) +{ + ESP_LOGI(TAG, "Start"); + uint32_t ps_other_cpu = 0; + ESP_LOGI(TAG, "call get_ps_other_cpu"); + esp_ipc_isr_asm_call_blocking(get_ps_other_cpu, &ps_other_cpu); + ESP_LOGI(TAG, "PS_INTLEVEL = 0x%x", ps_other_cpu & XCHAL_PS_INTLEVEL_MASK); + ESP_LOGI(TAG, "PS_EXCM = 0x%x", (ps_other_cpu & XCHAL_PS_EXCM_MASK) >> XCHAL_PS_EXCM_SHIFT); + ESP_LOGI(TAG, "PS_UM = 0x%x", (ps_other_cpu & XCHAL_PS_UM_MASK) >> XCHAL_PS_UM_SHIFT); + + ESP_LOGI(TAG, "call extended_ipc_isr_asm"); + arg_data_t arg = { 0 }; + arg.in[0] = 0x01; + arg.in[1] = 0x02; + arg.in[2] = 0x03; + ESP_LOGI(TAG, "in[0] = 0x%x", arg.in[0]); + ESP_LOGI(TAG, "in[1] = 0x%x", arg.in[1]); + ESP_LOGI(TAG, "in[2] = 0x%x", arg.in[2]); + esp_ipc_isr_asm_call_blocking(extended_ipc_isr_asm, (void*)&arg); + ESP_LOGI(TAG, "out[0] = (in[0] | in[1] | in[2]) = 0x%x", arg.out[0]); + assert(0x03 == arg.out[0]); + ESP_LOGI(TAG, "out[1] = (in[0] & in[1] & in[2]) = 0x%x", arg.out[1]); + assert(0x06 == arg.out[1]); + ESP_LOGI(TAG, "out[2] = in[2] = 0x%x", arg.out[2]); + assert(0x03 == arg.out[2]); + ESP_LOGI(TAG, "out[3] = PS of other cpu = 0x%x", arg.out[3]); + assert(ps_other_cpu == arg.out[3]); + ESP_LOGI(TAG, "End"); +} diff --git a/examples/system/ipc/ipc_isr/sdkconfig.defaults b/examples/system/ipc/ipc_isr/sdkconfig.defaults new file mode 100644 index 0000000000..e69de29bb2